# WASM-4 turns a WebAssembly module into a game cartridge

> WASM-4 is a fantasy console with a fixed hardware contract: a 160x160 four-colour screen, 64 KB of RAM and a 64 KB cartridge, so every game ships as a plain .wasm file you can run in a browser or on a microcontroller. Good for weekend projects and game jams, wrong for anything that needs more colours or more memory.

**aduros/wasm4** — Build retro games using WebAssembly for a fantasy console.

- Repository: https://github.com/aduros/wasm4
- Website: https://wasm4.org
- Stars: 1,544 · Forks: 209
- Language: TypeScript
- License: ISC
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/aduros-wasm4

## The console is the API

The usual friction in WebAssembly game development is the space between the module and the page. You end up writing JavaScript and HTML to create a canvas, handle input and pump a loop. WASM-4 removes that layer by defining a fixed set of host functions and calling them straight from the module, which is what the README means by games interfacing directly with the system through a small API.

The other half of the design is that the hardware is deliberately impoverished, and the specs are published as a numbered contract rather than left to the host:

- Display: 160x160 pixels, 4 customizable colors, updated at 60 Hz.
- Memory: 64 KB linear RAM, memory-mapped I/O, save states.
- Cartridge size limit: 64 KB.
- Input: keyboard, mouse, touchscreen, up to 4 gamepads.
- Audio: 2 pulse wave channels, 1 triangle wave channel, 1 noise channel.
- Disk storage: 1024 bytes.

Because the display is fixed and the palette is four colours, a WASM-4 game looks like a specific decade whether or not the author intended it. That is the appeal for game jam participants and a genuine constraint for anyone with a different visual target.

## Any language that emits WebAssembly will do

The language list in the README is worth reading as a policy statement: AssemblyScript, C/C++, D, Go, Nelua, Nim, Odin, Rust, WAT and Zig are supported out of the box. The stated rule is simply that the language can compile to WebAssembly, which is why raw WAT, the text format, is on the list alongside systems languages.

That has a practical consequence the README does not spell out. The console API is invoked through imports, and each toolchain has its own binding convention for declaring imports from a .wasm module. A project using AssemblyScript and a project using Rust are not writing the same kind of glue even though neither writes JavaScript.

The examples directory shows how wide the range is in practice: `examples/pong/`, `examples/snake/`, `examples/platformer-test/`, `examples/watris/`, `examples/sound-demo/`, `examples/multiplayer/` and `examples/raw-assembly/`. That last one is the proof that the API really does not depend on a managed language runtime.

The repository layout reinforces that this is a runtime and a toolchain rather than a single library. There are separate `runtimes/`, `cli/`, `devtools/`, `site/`, `scripts/` and `test/` directories, plus a `.gitlab-ci.yml` alongside `.github/`, meaning both CI systems run builds.

## From npm to a cartridge in four commands

The quickstart in the README is the whole installation. The CLI is distributed through npm under the name `wasm4`, and it scaffolds projects in AssemblyScript as the default example:

```shell
npm install -g wasm4
w4 new --assemblyscript hello-world
cd hello-world
npm install
npm run build
w4 run build/cart.wasm
```

The output of the build is a file called `cart.wasm`, which is the cartridge. There is no packaging step after that, no asset directory and no bundler config, because a cartridge is the .wasm file and nothing else.

Running it locally through `w4 run` opens the same runtime players use in a browser, so what you see on your machine is what a visitor to the showcase sees. For anything beyond that, the README points to the full documentation at wasm4.org/docs.

## The C runtime is what makes it portable off the web

Most WebAssembly work assumes a browser. WASM-4 ships a lightweight runtime written in C, and the README points at both low-powered microcontrollers and obsolete hardware as targets it has run on. The rule it designs to is that anything executing WebAssembly can execute a WASM-4 game.

That is a different distribution story from a typical game. Because the cartridge is the artifact, the same `cart.wasm` can be embedded, emulated or shipped to a device without a port. For a hobbyist project, being able to flash the identical file onto a microcontroller is the feature that makes the idea feel like a console rather than a web toy.

The limits follow the same logic. There is no graphics card abstraction, no filesystem beyond 1024 bytes of disk storage, and no way to open a socket from inside a cartridge. Everything a game can reach is the API and the console the host provides.

## Netplay is automatic, and that is the surprise

Multiplayer is handled by the host rather than by the game. The README states that instant online multiplayer with rollback netcode is built in, and that any game supporting local multiplayer automatically supports netplay, because WASM-4 syncs controller inputs over the Internet.

This is a design decision with a clear scope. Input syncing means both players must run the same build of the same cartridge on the same runtime version, and it means the game logic must be deterministic, since a rollback netcode model assumes both machines compute the same result from the same inputs. A game that calls into a nondeterministic source, real time or a random number generator that is not seeded consistently, will diverge. The README does not document the determinism requirements in detail, so treat this as something to verify in your own game rather than an assumption the console guarantees.

The alternative here is not another WebAssembly console. Writing a netcode layer yourself in JavaScript means owning prediction, rollback, desync detection and the state save format; with WASM-4 the inputs leave the cartridge through the console's own input API and the host does the rest. The price is that you accept whatever transport and the input model the runtime provides.

## Where the 64 KB cartridge limit bites

Two limits interact badly and the build is where you find out. RAM is 64 KB and the cartridge cannot exceed 64 KB, so anything you want to ship inside the module competes with everything else, and a language with a runtime, such as Go or Rust, spends a large part of that budget before your first instruction. Whether a given toolchain's hello world fits is exactly the kind of thing the per-language support work exists to check.

The display is the second ceiling. Four customizable colours on a 160x160 screen means no gradients, no anti-aliasing, and no text rendering at any comfortable size unless you ship a bitmap font in the cartridge, spending bytes you will want later. Audio is limited in the same spirit to two pulse wave channels, one triangle channel and one noise channel.

Then there is the project status. The last push was on 2026-09-11 and the repository is not archived, but the newest tag is v2.7.1 from 2024-12-13, ahead of v2.7.0 in April 2024 and v2.6.2 in February 2024. Development is landing on main ahead of a release tag. The license is ISC, which is permissive, and the repository root carries LICENSE.txt.

## Conclusion

Adopt WASM-4 if you want to finish a small game rather than start an engine, and if a 160x160 four-palette screen and 64 KB of memory are constraints you can design inside. Do not adopt it if your game needs 3D, more than four colours on screen, a save file larger than 1024 bytes, or audio beyond two pulse channels and one triangle channel. Before committing, run the `w4 new --assemblyscript hello-world` path and read the 64 KB cartridge limit against your compiled size, since that limit is enforced at build time rather than at runtime.

## FAQ

### How do I create a new WASM-4 game?

Install the CLI with `npm install -g wasm4`, then scaffold with `w4 new --assemblyscript hello-world`, run `npm install` inside the new directory and `npm run build`, which produces `build/cart.wasm`. Launch it with `w4 run build/cart.wasm`.

### Which languages can I use to write WASM-4 games?

Any language that compiles to WebAssembly. The README lists AssemblyScript, C/C++, D, Go, Nelua, Nim, Odin, Rust, WAT and Zig as supported out of the box.

### What are the WASM-4 hardware limits?

A 160x160 pixel display with 4 customizable colors at 60 Hz, 64 KB linear RAM with memory-mapped I/O, a 64 KB cartridge size limit, up to 4 gamepads, 2 pulse wave channels, 1 triangle wave channel, 1 noise channel, and 1024 bytes of disk storage.

### Does WASM-4 support online multiplayer?

Yes, with rollback netcode built in. The README states that any game supporting local multiplayer automatically supports netplay, because WASM-4 handles syncing controller inputs over the Internet.

### Can WASM-4 games run outside a browser?

The project includes a lightweight runtime written in C and is designed to run on any device that can execute WebAssembly. The README links to examples of it running on low-powered microcontrollers and on obsolete hardware.

## Sources

- [aduros/wasm4 on GitHub](https://github.com/aduros/wasm4)
- [License: ISC](https://github.com/aduros/wasm4/blob/main/LICENSE)
- [Project website](https://wasm4.org)
- [README](https://github.com/aduros/wasm4/blob/main/README.md)
- [Releases](https://github.com/aduros/wasm4/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/aduros-wasm4
