wasmcart
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.
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+.
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);
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.
Canvas 2D is backed by Skia Ganesh GL, so fills, paths and text go to the hardware GPU rather than a software rasterizer.
WebGL2 calls pass through to the host GPU. Shader-heavy games and 3D engines work without a translation layer in the middle.
16 node types and 5 audio decoders, so the audio graph a browser game already builds works unchanged.
Enough of the platform that real games run unmodified rather than being ported: the goal is to move the game, not rewrite it.
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.
The whole runtime is a single prebuilt WASM binary. Your cart ships JavaScript and assets on top of it.
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.