wasmcart

JavaScript

Write a standard browser game. Ship it as a cartridge.

Canvas 2D, WebGL2, Web Audio, ES modules and gamepad — inside a sandbox with no DOM, no XSS and no filesystem.

the wasmcart-jsgame release licence

Try it in one command

Eight prebuilt carts ship with the release, including a complete arcade shooter and a Three.js scene. Download a .wasc and run it, no Node install for the game and no bundler:

npx wasmcart space.wasc

Each cart carries the QuickJS runtime plus the game's own JavaScript and assets. Nothing to install but Node 22+.

Write ordinary browser JS

No dialect and no engine API to learn. The browser globals you already use are there:

// main.js
const canvas = document.getElementById('game');
canvas.width = 800;
canvas.height = 600;
const ctx = canvas.getContext('2d');

function gameLoop(timestamp) {
    ctx.clearRect(0, 0, 800, 600);
    ctx.fillStyle = '#ff0000';
    ctx.fillRect(100, 100, 200, 150);
    requestAnimationFrame(gameLoop);
}
requestAnimationFrame(gameLoop);

Pack it and run it

Everything below lives in wasmcart/wasmcart-jsgame.

The engine wasm is prebuilt, so there is nothing to compile:

bash pack_game.sh my_game/ my_game.wasc "My Game"

Packing is npx wasmcart pack underneath. The wrapper exists to stage a default font alongside your game first, so text renders on a host that has none.

These are GL carts, so they want a GL-capable host:

npx wasmcart my_game.wasc

The same .wasc then runs on any wasmcart host — a browser, wasmcart-native, RetroArch through the libretro core, or a handheld.

What you get

GPU-accelerated Canvas 2D

Canvas 2D is backed by Skia Ganesh GL, so fills, paths and text go to the hardware GPU rather than a software rasterizer.

Direct WebGL2

WebGL2 calls pass through to the host GPU. Shader-heavy games and 3D engines work without a translation layer in the middle.

Full Web Audio

16 node types and 5 audio decoders, so the audio graph a browser game already builds works unchanged.

120+ browser API shims

Enough of the platform that real games run unmodified rather than being ported: the goal is to move the game, not rewrite it.

Safer than a browser tab

Games run in QuickJS inside WASM: no DOM access, no XSS surface, no arbitrary network, no filesystem. Untrusted JavaScript runs the way a SNES ROM does.

One 5MB binary

The whole runtime is a single prebuilt WASM binary. Your cart ships JavaScript and assets on top of it.

Why sandbox JavaScript at all?

A browser game normally arrives with the browser's entire attack surface attached. Running it inside QuickJS-inside-WASM keeps the APIs a game actually needs and drops the ones it does not, which is what makes it reasonable to hand someone an untrusted .wasc and just run it.

← All languages