Skip to content

gasm ​

Game assembly: write a game once, compile it to WebAssembly, run it everywhere

GitHub · Website & playable demos · ABI spec

About ​

A game is one .wasm file. A runner is a small program that loads it and hands it a stable interface for pixels, sound, input, files, a GPU and a network, and nothing else. There's a native runner (Rust: wasmtime, wgpu, winit), a browser runner (WebAssembly and WebGPU), and a headless one in Node for CI. The same bytes run on all of them.

The idea isn't new. libretro did it for emulator cores, Flash did it for a decade, and the browser already is a universal game runtime. What gasm tries is the browser's reach without the browser: a native runner can compile the module ahead of time for platforms that forbid JIT, a game can't touch anything it wasn't given, and a new platform needs a few hundred lines of runner rather than a port of every game.

The interface ​

The core is 11 functions: log, time, frame rate, launch parameters, an RGBA framebuffer, float audio, four virtual gamepads, and read-only assets. Three optional modules sit on top:

ModuleWhat it is
gasm:gfxA compact WebGPU subset with WGSL shaders. The browser forwards it almost 1:1; natively wgpu runs it on Metal, Vulkan and D3D12
gasm:netWebSocket-style messages on every runner, because browsers can't open raw sockets
gasm:storageA per-game key/value store for saves. The runner picks the namespace, so a game can only see its own

Resources are created from JSON descriptors that mirror WebGPU, and every per-frame call takes plain numbers. That removes all struct marshalling across the 32/64-bit boundary. Pointers are offsets into the game's own memory, and the runner bounds-checks every one.

Determinism is the product ​

Same inputs give the same frames, audio and game state on every engine. It's tested, not assumed: headless runners hash every presented frame, every audio buffer and every GPU upload, and CI compares wasmtime JIT, wasmtime AOT and V8 on Linux, macOS and Windows. Eight cases, from a C test pattern to 2,400 frames of scripted NES gameplay, match to the bit.

Everything else leans on that. The online mode needs no game state on the wire. Test failures reproduce. Replays and rollback netcode become possible later.

Games in the repo ​

GameWhat it shows
sumo3D two-player arena: push the other ball off the platform. WebGPU rendering, deterministic float physics, lockstep netcode through a relay, native players against browser players
nestetanes-core compiled unmodified to wasm. Passes blargg's CPU instruction, CPU timing and APU test ROMs; battery saves go through gasm:storage
triangleThe smallest GPU program, about 50 lines of Rust
test-patternAbout 70 lines of C, proof that the interface doesn't care about the language

Games are written in Rust against a small gasm crate (a Game trait and a game! macro), or in C against gasm.h. The crate also builds natively against a stub host, so any game runs as a normal binary under lldb, and native-vs-wasm parity is one command.

Numbers ​

Measured on an Apple M1 Pro, emulation only, 6,000 frames of the homebrew shooter Blade Buster:

BuildTimevs native
Native Rust (release, LTO)7.1 s1.0×
wasm, wasmtime AOT10.8 s1.5×
wasm, V811.2 s1.6×

Still about 9× faster than the console itself. Sumo holds 60 fps with 4× MSAA in both the native runner and the browser.

Lockstep, briefly ​

Both players run the identical simulation and exchange only inputs, each scheduled four frames (≈66 ms) ahead: 7 bytes per frame. A state hash goes across every two seconds, so a desync would announce itself rather than drift. Player two's camera looks from the other side of the arena, so their arrows are mirrored into world space before they're sent. The relay knows nothing about games. It pairs peers in rooms, announces joins and leaves, and forwards bytes.

Status ​

ABI v0: core, gfx, net, storageExperimental, will change
Native runner: macOS, Linux, WindowsTested in CI on all three
Browser runner (WebGPU, AudioWorklet, Gamepad API)Working in Chrome; Safari 26+ and Firefox should work but are untested
Online playWorks on a LAN or through your own relay; no public relay
ReleasesmacOS .app bundles per demo, CLI archives for macOS, Linux x86_64/arm64, Windows
NextRollback netplay (snapshot the guest's memory), textures, .gasm packages, a wasm2c runner for consoles

MIT licensed. The NES core is tetanes-core (MIT/Apache-2.0).