r/javascript 3d ago

Showoff Saturday Showoff Saturday (September 05, 2026)

Upvotes

Did you find or create something cool this week in javascript?

Show us here!


r/javascript 1d ago

Subreddit Stats Your /r/javascript recap for the week of August 31 - September 06, 2026

Upvotes

Monday, August 31 - Sunday, September 06, 2026

Top Posts

score comments title & link
22 4 comments RDR2-style volumetric WebGL 2 clouds in the browser
21 2 comments Wasmer SDK: Open Source alternative to WebContainers
15 1 comments Twenty Years of jQuery: How a Little Library Rewired Web Development
12 4 comments WASI 0.3 Launched
11 0 comments Open-Source Gwent Classic - V5.0 Release! (New Factions: Ofir & Novigrad)
10 1 comments Node.js sandboxes powered by QuickJS and WebAssembly
8 0 comments Anyone interested in Git for your test database? useful for assisting QA doing a test.
7 0 comments A free and open source service to get a name.runs-on.dev subdomain for students and builders
6 0 comments Headless phone input controller with no baked-in UI to fight
6 1 comments GitHub - unadlib/localspace: A library that unifies the APIs of IndexedDB, localStorage and other storage into a consistent API

 

Most Commented Posts

score comments title & link
0 35 comments [AskJS] [AskJS] Genuine question, why we hating on well-made but LLM-written projects?
0 15 comments Zustand is now the top state management library in new JS repos, and it's still accelerating (+7.3% this week). Pulled this from a dataset of 5,000+ live repos.
0 11 comments [AskJS] [AskJS] I'm thinking of creating a JS dialect for scientists
0 10 comments [AskJS] [AskJS] AI is destroying JavaScript
2 8 comments [Showoff Saturday] Showoff Saturday (September 05, 2026)

 

Top Ask JS

score comments title & link
2 0 comments [AskJS] [AskJS] Has anyone used Cloudflare/Cap’n Web? Any hidden downsides?
2 4 comments [AskJS] [AskJS] BeeLadybug — Open Source Canvas 2D Debug Overlay in Vanilla JS (Zero Dependencies)
1 2 comments [AskJS] [AskJS] Best way to handle rotating proxies in Node.js?

 

Top Showoffs

score comment
2 /u/muddycleats92 said Hey football fans, I've spent the NFL offseason building The Cut: NFL Survivor Pools. This takes the classic game of NFL Survivor but adds a twists, you select players instead of teams. I created thr...
2 /u/xaelion said I’ve been working on Lumiana, an experimental environment for building personal apps where browser and Node.js APIs can be used together without creating a separate API layer. The browser remains the...
2 /u/Triggerscore said Created a Twitch chat integration for my game PixReveal. A Twitch streamer can stream the game. Pixel images get revealed pixel by pixel. The Twitch chat can play by typing the answer or 1/2/3/4. Set...

 

Top Comments

score comment
16 /u/thecementmixer said Wtf is this post even about? Went from JavaScript and python rant to data centers destroying the environment. Is this an AI post?
14 /u/forloopy said Overcomplicating something that doesn’t need it
13 /u/AM_Dog_IRL said This is decidedly not the dream
13 /u/Merry-Lane said Where does it show "Zustand is the top state management library"? Compared to react query or just using react context? Also, the "back to main" redirects to localhost:3000. I think that whatever t...
13 /u/maria_la_guerta said > JavaScript used to be the dominant programming language for software engineering back in the days of React and Express. This is an extremely narrow view of the "facts".

 


r/javascript 9h ago

Half Past Fetch: how the fetch promise resolves and its consequences

Thumbnail blog.gaborkoos.com
Upvotes

await fetch(url) settles at the headers, while the body is still arriving over an open connection. How connection reuse, clone(), abort and timeouts behave because of that gap. Measured in Node and Chrome.


r/javascript 1h ago

MikroORM 7.2: row level security, to-one relations through a pivot, sql.js driver with a live docs playground, cursor pagination rework, and more

Thumbnail mikro-orm.io
Upvotes

MikroORM 7.2 is out — the second minor on top of v7.

New features:

  • Row level security — PostgreSQL policies as entity metadata, created and diffed by the schema generator, with per-request session context pushed down to the connection; an existing @Filter can compile into a policy, so one declaration enforces at both layers
  • through option for to-one relations — resolve a M:1 / 1:1 via a correlated subquery on a pivot entity, or pick a single row out of a to-many relation (e.g. the latest one) without loading the collection
  • sql.js driver — SQLite compiled to WebAssembly, in memory, in the browser, Node.js, Bun and Deno with no native bindings. It also powers the new live playground in the getting-started guide, so the code in the docs runs against a real database as you read it
  • Cursor pagination rework — a new optional Type.fromJSON() lets a custom type own its cursor wire format (sub-millisecond precision survives), and nullable sort keys now make the emitted order by and the keyset condition agree on where nulls sit
  • Named parameters in em.execute():name for values, :name: for identifiers, as an alternative to the positional array
  • String normalization — opt-in trim and casing on StringType / TextType, applied on writes and query parameters
  • getNativeClient() — reach the underlying client for vendor APIs the ORM doesn't wrap: pg Pool, mysql2 Pool, better-sqlite3 / libsql Database, the PGlite instance, MongoClient
  • await using support — the ORM instance implements Symbol.asyncDispose, so the connection closes with the enclosing scope
  • index option on M:N properties — index the generated pivot table's join columns, which had no override on PostgreSQL before
  • Nub TypeScript loader for the CLI, selected explicitly via tsLoader
  • em.map() can bypass the identity map — map raw rows to entities without touching the current context
  • Per-instance options callback for RequestContext.create() — different fork options per ORM instance
  • migrations.snapshotOnMigrate — keep the snapshot managed solely by migration:create instead of rewriting it from the database on migrate
  • CLI -q to suppress informational output, and cache:generate --combined now takes a path

Full blog post: https://mikro-orm.io/blog/mikro-orm-7-2-released
Changelog: https://github.com/mikro-orm/mikro-orm/releases/tag/v7.2.0

Happy to answer any questions!


r/javascript 8h ago

online boxing game using JavaScript

Thumbnail browserboxing.com
Upvotes

Notable Features
Real-time 1v1 multiplayer
Fight other players or bots
Ranked matchmaking and Top 100 leaderboard
Championship belt system
Create and customize your own boxer
Leveling, XP, stats, and career records
Jabs, crosses, hooks, uppercuts, guarding, and stamina
Knockdowns, KOs, and judge decisions
Spectate live fights and watch fight highlights
Lobby chat, profiles, and direct messages
Keyboard and mobile controls
Interactive training and tutorial
Guest play available, no account required to start
Built around quick fights, competitive rankings, and progressing your own boxer.

No fight engine used, js to canvas artwork & animations.


r/javascript 23h ago

Shipping a Web Worker inside an npm package without asking users to touch their bundler config

Thumbnail kanunilabs.com
Upvotes

r/javascript 20h ago

Browser-wide network chaos for every tab

Thumbnail github.com
Upvotes

A browser-side chaos-testing tool for injecting latency, failures, throttling, rate limits, and mock responses across every controlled tab without changing app fetch calls.

It uses the same middleware configuration model as chaos-fetch, so you can test real frontend behavior under degraded network conditions without rewriting your app.

GitHub: https://www.npmjs.com/package/@fetchkit/chaos-sw


r/javascript 1d ago

Twenty Years of jQuery: How a Little Library Rewired Web Development

Thumbnail infoq.com
Upvotes

r/javascript 1d ago

AskJS [AskJS] Has anyone used Cloudflare/Cap’n Web? Any hidden downsides?

Upvotes

Has anyone here used Cloudflare/Cap’n Web? I want to know if there are any problems or downsides that don’t really show up from a simple test, because the whole idea looks amazing.


r/javascript 1d ago

I Dislike TypeScript Because I've Never Maintained JavaScript Before

Thumbnail mayberay.bearblog.dev
Upvotes

r/javascript 1d ago

responsive-state — a tiny, typed, SSR-safe matchMedia store for responsive JavaScript behavior

Thumbnail github.com
Upvotes

r/javascript 1d ago

AskJS [AskJS] If you could have one Node.js tool/package built for you, what would it be?

Upvotes

Hello devs, I’m looking for an open source project to build, and rather than coming up with another package that nobody asked for, I’d rather start with an actual problem developers have.

So I’m curious:

What is something you wish existed in the Node.js ecosystem?

I’m especially interested in things you’ve actually encountered in a real project, rather than hypothetical ideas.


r/javascript 2d ago

Brightpixels — HDR text and image highlights for the web

Thumbnail github.com
Upvotes

r/javascript 2d ago

Building a fully open-source P2P network for censorship resistant social apps

Thumbnail github.com
Upvotes

Bitsocial is neither federated nor on-chain. Each community is a peer-to-peer swarm using IPFS, closer to BitTorrent than a hosted website: users seed effortlessly, nodes run on cheap hardware, and each community is fully independent and sovereign. A browser tab can join that swarm as a peer, so the web app is not a client of someone else's server.

Anyone can build a Bitsocial app with its own interface, discovery model, or defaults. Apps compete on product quality instead of locking users into a private database, because compatible clients can share the same communities, identities, and network.

Moderation still exists, but it stays local. Community owners set rules for their own spaces and apps can choose what they index or show, yet there is no protocol-level super-admin who can erase a profile or seize a community from the network itself.


r/javascript 2d ago

nos4: A nostalgia right on your browser!

Thumbnail github.com
Upvotes

nos4 is a complete recreation of IOS4 running on web. It was just a little experimental side project but I've decided to make it live and open-sourced. It has all the original apps in the original form, also 2 games (and yes, it runs Doom)

Give it a Star: https://github.com/1etu/nos4

Also you can give it a try by visiting https://nos4.fun/


r/javascript 3d ago

Open-Source Gwent Classic - V5.0 Release! (New Factions: Ofir & Novigrad)

Thumbnail github.com
Upvotes

Hey everyone!

I wanted to share Version 5.0 of Gwent Classic, a browser remake of the original minigame from The Witcher 3: Wild Hunt that I’ve been working on and improving.

In this major update, I’ve incorporated two brand-new factions: Ofir and Novigrad, each coming with its own custom cards, leaders, and unique gameplay mechanics!

The game is completely free-to-play and runs directly in your PC browser. You can test it out right here:

Play on your PC Browser (GitHub Page):

Play v5.0

Play Completely Offline (PC / Laptop):

You can download the source code repository directly from GitHub. Just extract the ZIP file and double-click "index.html" to open it in your browser. It runs 100% offline—no internet required! Here.

Get Android APK:

You can get the APK directly from the "Releases" section of Github, or use the direct link below:

Get APK v5.0

Credits to Arun Sundaram, Reynolds Costa & Sylver, who laid the foundations to create, modify, and improve this game in its early stages.

Special thanks to CD Projekt Red; although they are not involved in this unofficial fan project, it is entirely based on the amazing minigame they created for The Witcher 3.


r/javascript 2d ago

AskJS [AskJS] Genuine question, why we hating on well-made but LLM-written projects?

Upvotes

Wasn’t this the dream? Being able to bring your ideas to life quickly with your own coding patterns and architecture? Doesn’t this accelerate the SDE space and get it out of the stagnation that physics space has been in for the last few decades?

This is in reference to my Pnext posts and all the dislikes. I was really excited about the stuff I have in the pipeline, but this feels discouraging.


r/javascript 4d ago

[ Removed by Reddit ]

Upvotes

[ Removed by Reddit on account of violating the content policy. ]


r/javascript 4d ago

Headless phone input controller with no baked-in UI to fight

Thumbnail github.com
Upvotes

Have you ever been on a project with a polished UI library, animated inputs, a specific look, and then had to add a phone number field? A phone input is not just validation. Under the hood it is real work: formatting the number as you type, keeping the caret in place, and handling events like paste and undo. The ready-made widgets handle that, but each ships its own markup and styling. Matching one to a specific, opinionated design system does not always work.

That is exactly why I built a headless input controller. You pass your styled input into a function, whatever it looks like, and it handles the rest: formatting, validation, the input events, and the caret.

```js import { createPhoneInput } from '@telixon/web-sdk';

const phone = createPhoneInput({ mode: 'international', defaultRegion: 'US', display: { callingCodeInInput: true, plusPrefix: 'erasable' }, input: document.querySelector('input'), });

phone.subscribe((state) => { // state.value, state.region, state.validationError (a typed reason) }); ```

That one call covers the actions people actually do: paste (formatted numbers too, and it dedupes a repeated country code), drag/drop, cut, word/line delete, IME, autofill, and undo/redo. On every keystroke the state carries the fully parsed, validated number, with a typed reason when it is not valid: too short by N, a calling code that cannot exist, and so on.

Under it is a headless controller you can drive directly, with no DOM: insert, deleteBackward, deleteForward, setValue, undo, redo, region/type filters, getPhoneNumber. One controller does national, international with the calling code in or out of the field, and your own country picker. Formatting and validation come from Google's libphonenumber metadata.

Playground to try it live: telixon.dev/playground/input


r/javascript 4d ago

GitHub - unadlib/localspace: A library that unifies the APIs of IndexedDB, localStorage and other storage into a consistent API

Thumbnail github.com
Upvotes

r/javascript 4d ago

AskJS [AskJS] BeeLadybug — Open Source Canvas 2D Debug Overlay in Vanilla JS (Zero Dependencies)

Upvotes

I built BeeLadybug, a zero-dependency visual debugging overlay for my Vanilla JS Canvas engine. It renders AABB hitboxes in world space (green for clear, red for collisions) and a screen-space HUD with FPS, active entities, cycle time, and freeze-frame / slow-motion controls (F2/F3/F4). Here is the clean source code:

export const BEE_LADYBUG_DEFAULTS = Object.freeze({
    toggleKey: 'F2',
    slowKey: 'F3',
    freezeKey: 'F4',
    slowScale: 0.25,
    colorActive: '#3dff6a',
    colorColliding: '#ff3b3b',
    colorInactive: '#8a8a8a',
    overlayX: 12,
    overlayY: 12
});


export class BeeLadybug {
   
    constructor(engine, options = {}) {
        this.engine = engine;
        this.configure(options);


        this.enabled = false;
        this.#entities = [];
        this.#colliding = new Set();
        this.#buttons = [];
        this.#onKeyDown = (event) => this.#handleKey(event);
        this.#bound = false;
    }


    #entities;
    #colliding;
    #buttons;
    #onKeyDown;
    #bound;


    configure(options = {}) {
        const cfg = { ...BEE_LADYBUG_DEFAULTS, ...options };
        this.toggleKey = cfg.toggleKey;
        this.slowKey = cfg.slowKey;
        this.freezeKey = cfg.freezeKey;
        this.slowScale = cfg.slowScale;
        this.colorActive = cfg.colorActive;
        this.colorColliding = cfg.colorColliding;
        this.colorInactive = cfg.colorInactive;
        this.overlayX = cfg.overlayX;
        this.overlayY = cfg.overlayY;
        return this;
    }


    attach() {
        if (this.#bound) return this;
        window.addEventListener('keydown', this.#onKeyDown);
        this.#bound = true;
        return this;
    }


    detach() {
        if (!this.#bound) return this;
        window.removeEventListener('keydown', this.#onKeyDown);
        this.#bound = false;
        return this;
    }


    destroy() {
        this.detach();
        this.enabled = false;
        this.#entities.length = 0;
        this.#colliding.clear();
        this.#buttons.length = 0;
    }


    show() {
        this.enabled = true;
        return this;
    }


    hide() {
        this.enabled = false;
        return this;
    }


    toggle() {
        this.enabled = !this.enabled;
        return this;
    }


    applySlowMo() {
        const time = this.engine.time;
        if (!time) return this;
        if (time.paused) time.resume();
        const next = time.timeScale === this.slowScale ? 1 : this.slowScale;
        this.engine.setTimeScale(next);
        return this;
    }


    toggleFreeze() {
        const time = this.engine.time;
        if (!time) return this;
        time.togglePause();
        return this;
    }


    restoreRealtime() {
        const time = this.engine.time;
        if (!time) return this;
        if (time.paused) time.resume();
        this.engine.setTimeScale(1);
        return this;
    }


    #handleKey(event) {
        if (event.repeat) return;


        if (event.code === this.toggleKey) {
            event.preventDefault();
            this.toggle();
            return;
        }


        if (!this.enabled) return;


        if (event.code === this.slowKey) {
            event.preventDefault();
            this.applySlowMo();
            return;
        }


        if (event.code === this.freezeKey) {
            event.preventDefault();
            this.toggleFreeze();
        }
    }


    #collectEntities() {
        const list = this.#entities;
        list.length = 0;
        const seen = new Set();


        const visit = (entity) => {
            if (!entity || entity.destroyed || seen.has(entity)) return;
            seen.add(entity);
            list.push(entity);
            const kids = entity.children;
            if (!kids) return;
            for (let i = 0; i < kids.length; i++) {
                visit(kids[i]);
            }
        };


        const engine = this.engine;
        const roots = engine.entities;
        if (roots) {
            for (let i = 0; i < roots.length; i++) visit(roots[i]);
        }


        const scene = engine.scenes && engine.scenes.currentScene
            ? engine.scenes.currentScene
            : engine.currentScene;
        if (scene && scene.entities) {
            for (let i = 0; i < scene.entities.length; i++) visit(scene.entities[i]);
        }


        const groups = engine.collisions && engine.collisions.groups;
        if (groups && typeof groups.values === 'function') {
            for (const bucket of groups.values()) {
                if (!bucket) continue;
                for (let i = 0; i < bucket.length; i++) visit(bucket[i]);
            }
        }


        return list;
    }


    #aabbOf(entity) {
        const engine = this.engine;
        if (engine && typeof engine.getEntityDrawBounds === 'function') {
            return engine.getEntityDrawBounds(entity);
        }
        if (!entity) return null;
        return {
            x: typeof entity.worldX === 'number' ? entity.worldX : entity.x,
            y: typeof entity.worldY === 'number' ? entity.worldY : entity.y,
            width: entity.width ?? 0,
            height: entity.height ?? 0
        };
    }


    #refreshCollisions(list) {
        const colliding = this.#colliding;
        colliding.clear();


        const boxes = [];
        for (let i = 0; i < list.length; i++) {
            const bounds = this.#aabbOf(list[i]);
            if (!bounds || bounds.width <= 0 || bounds.height <= 0) {
                boxes.push(null);
                continue;
            }
            boxes.push(bounds);
        }


        for (let i = 0; i < list.length; i++) {
            const a = boxes[i];
            if (!a) continue;
            for (let j = i + 1; j < list.length; j++) {
                const b = boxes[j];
                if (!b) continue;
                if (
                    a.x < b.x + b.width &&
                    a.x + a.width > b.x &&
                    a.y < b.y + b.height &&
                    a.y + a.height > b.y
                ) {
                    colliding.add(list[i]);
                    colliding.add(list[j]);
                }
            }
        }
    }


    
    drawWorld(ctx) {
        if (!this.enabled) return;


        const list = this.#collectEntities();
        this.#refreshCollisions(list);


        ctx.save();
        ctx.lineWidth = 1.5;


        for (let i = 0; i < list.length; i++) {
            const entity = list[i];
            const box = this.#aabbOf(entity);
            if (!box || box.width <= 0 || box.height <= 0) continue;


            const colliding = this.#colliding.has(entity);
            const active = entity.active !== false;
            const color = colliding
                ? this.colorColliding
                : (active ? this.colorActive : this.colorInactive);


            ctx.strokeStyle = color;
            ctx.fillStyle = colliding ? 'rgba(255, 59, 59, 0.16)' : 'rgba(61, 255, 106, 0.08)';
            ctx.fillRect(box.x, box.y, box.width, box.height);
            ctx.strokeRect(box.x + 0.5, box.y + 0.5, box.width, box.height);
        }


        ctx.restore();
    }


    
    drawOverlay(ctx) {
        if (!this.enabled) return;


        const engine = this.engine;
        const time = engine.time;
        const list = this.#entities;
        let activeCount = 0;
        for (let i = 0; i < list.length; i++) {
            if (list[i].active !== false) activeCount += 1;
        }


        const fps = time ? time.fps : 0;
        const cycleMs = time ? time.unscaledDt * 1000 : 0;
        const scale = time ? time.timeScale : 1;
        const frozen = time ? time.paused : false;


        const x = this.overlayX;
        const y = this.overlayY;
        const width = 268;
        const height = 168;


        ctx.save();
        ctx.setTransform(1, 0, 0, 1, 0, 0);


        ctx.fillStyle = 'rgba(10, 12, 18, 0.82)';
        ctx.strokeStyle = '#ff4d4d';
        ctx.lineWidth = 2;
        if (typeof ctx.roundRect === 'function') {
            ctx.beginPath();
            ctx.roundRect(x, y, width, height, 8);
            ctx.fill();
            ctx.stroke();
        } else {
            ctx.fillRect(x, y, width, height);
            ctx.strokeRect(x, y, width, height);
        }


        ctx.fillStyle = '#ff4d4d';
        ctx.beginPath();
        ctx.arc(x + 22, y + 22, 9, 0, Math.PI * 2);
        ctx.fill();
        ctx.fillStyle = '#1a1a1a';
        ctx.beginPath();
        ctx.arc(x + 19, y + 20, 2, 0, Math.PI * 2);
        ctx.arc(x + 25, y + 20, 2, 0, Math.PI * 2);
        ctx.fill();


        ctx.fillStyle = '#ffe08a';
        ctx.font = 'bold 14px monospace';
        ctx.textAlign = 'left';
        ctx.textBaseline = 'top';
        ctx.fillText('BeeLadybug', x + 38, y + 14);


        ctx.fillStyle = '#d8d8d8';
        ctx.font = '12px monospace';
        const lines = [
            `FPS      ${fps.toFixed(0)}`,
            `ENTITA   ${activeCount} attive / ${list.length} in memoria`,
            `CICLO    ${cycleMs.toFixed(2)} ms`,
            `SCALE    ${scale.toFixed(2)}x   SIM ${frozen ? 'FREEZE' : 'RUN'}`
        ];
        for (let i = 0; i < lines.length; i++) {
            ctx.fillText(lines[i], x + 14, y + 42 + i * 16);
        }


        this.#buttons = [
            { id: 'slow', label: 'F3 SLOW', x: x + 12, y: y + 114, w: 78, h: 22 },
            { id: 'freeze', label: 'F4 STOP', x: x + 96, y: y + 114, w: 78, h: 22 },
            { id: 'live', label: '1x LIVE', x: x + 180, y: y + 114, w: 74, h: 22 }
        ];


        for (let i = 0; i < this.#buttons.length; i++) {
            const btn = this.#buttons[i];
            const hot = (btn.id === 'slow' && scale !== 1 && !frozen)
                || (btn.id === 'freeze' && frozen)
                || (btn.id === 'live' && scale === 1 && !frozen);
            ctx.fillStyle = hot ? '#ff4d4d' : '#2a2f3a';
            ctx.fillRect(btn.x, btn.y, btn.w, btn.h);
            ctx.strokeStyle = '#ffe08a';
            ctx.strokeRect(btn.x + 0.5, btn.y + 0.5, btn.w, btn.h);
            ctx.fillStyle = '#fff6d8';
            ctx.font = 'bold 10px monospace';
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            ctx.fillText(btn.label, btn.x + btn.w / 2, btn.y + btn.h / 2);
        }


        ctx.textAlign = 'left';
        ctx.textBaseline = 'top';
        ctx.fillStyle = '#9aa0aa';
        ctx.font = '10px monospace';
        ctx.fillText('F2 mostra/nasconde   verde=ok  rosso=collide', x + 12, y + 146);


        ctx.restore();
    }


   
    poll() {
        if (!this.enabled) return;


        const input = this.engine.input;
        if (!input || !input.mouse || !input.mouse.wasPressed) return;


        const mx = input.mouse.x;
        const my = input.mouse.y;
        for (let i = 0; i < this.#buttons.length; i++) {
            const btn = this.#buttons[i];
            if (mx < btn.x || mx > btn.x + btn.w || my < btn.y || my > btn.y + btn.h) continue;
            if (btn.id === 'slow') this.applySlowMo();
            else if (btn.id === 'freeze') this.toggleFreeze();
            else if (btn.id === 'live') this.restoreRealtime();
            break;
        }
    }
}

r/javascript 4d ago

AskJS [AskJS] Best way to handle rotating proxies in Node.js?

Upvotes

I've got a Node.js script making a decent number of requests and I'm trying to decide where proxy rotation should live. One option is picking a new proxy inside the app, the other is using one endpoint and letting the proxy service rotate behind it. The second sounds cleaner, but I'm not sure how well that works when you need sticky sessions. How are you structuring this in your own projects?


r/javascript 4d ago

I made a WebGL 2D renderer for browser games that keeps pace with PixiJS in my benchmarks

Thumbnail github.com
Upvotes

hi! I recently developed Rapid.js, an open-source, zero-dependency WebGL 2D renderer built specifically for browser games.

It provides an immediate-mode API and does not retain game objects or a scene graph between frames. This leaves developers free to structure their games using OOP, ECS, or any custom architecture—Rapid.js is responsible only for rendering the drawing commands submitted each frame.

It supports sprites, particles, masks, shaders, render textures, and multi-texture batching, and its UMD build is only approximately 20.6 kB gzipped.

Its small size does not come at the expense of performance: in the included browser benchmark, Rapid.js performs comparably to PixiJS.

benchmark:

https://github.com/Nightre/Rapid.js/raw/main/docs/benchmark/benchmark.png

benchmark live:

https://nightre.github.io/Rapid.js/benchmark/


r/javascript 4d ago

Can AI agents run purely in the browser with vanilla JS? Exploring $0 hosting & zero data egress

Thumbnail github.com
Upvotes

AI agents assume a server-side Python chain framework runtime and containers. Running client-side with JavaScript allows pushing agents entirely inside the browser, meaning $0 hosting infrastructure and zero data leaving the client.

Running client-side, you can use agents directly with WebLLM (for fully in-browser) or point them at a local Ollama / vLLM instance for 100% offline usage.

Breakdown In-Browser Loop

An in-browser agent relies on a continuous asynchronous loop that manages state, updates the DOM or application state, and invokes tools based on model outputs. Here is a minimal, clean implementation of a client-side agent loop using WebLLM's engine.chat.completions API:

```javascript import { CreateMLCEngine } from "@mlc-ai/web-llm";

async function agentLoop(task, context = {}) { const engine = await CreateMLCEngine("gemma-4-instruct"); let messages = [{ role: 'user', content: task }]; let running = true;

while (running) { const response = await engine.chat.completions.create({ messages, tools: availableTools }); const choice = response.choices[0].message;

if (choice.tool_calls) {
  for (const call of choice.tool_calls) {
    const toolResult = await executeTool(call.function.name, call.function.arguments);
    messages.push({ role: 'tool', tool_call_id: call.id, content: toolResult });
  }
} else {
  running = false;
  return choice.content;
}

} } ```


r/javascript 5d ago

WASI 0.3 Launched

Thumbnail bytecodealliance.org
Upvotes