wasmcart

Rust

Write games in Rust. Ship the smallest carts there are.

Freestanding wasm32-unknown-unknown: no runtime to embed, no JS glue, no wasm-bindgen. Just structs, constants and one macro.

How small?

Small enough that the number is the reason to pick it:

examples/hello      7,677 bytes wasm   1 import  (env.wc_log)
examples/hello_gl   1,905 bytes wasm  22 imports (env.wc_log + 21 gl.*)

The GL cart is under 2 KB because there is nothing to carry: no interpreter, no garbage collector, no standard library it did not ask for.

Set up

Everything below lives in wasmcart/wasmcart-rust.

rustup target add wasm32-unknown-unknown
cargo add wasmcart

Then in Cargo.toml. The release profile is what keeps carts small, so it is worth copying as-is:

[lib]
crate-type = ["cdylib"]

[profile.release]
opt-level = "z"
lto = true
panic = "abort"
strip = true
codegen-units = 1

A whole cart

Not an excerpt. This is the entire program:

#![no_std]

use wasmcart::*;

wc_cart!(320, 240);

#[no_mangle]
pub extern "C" fn wc_init() {
    wc_log("hello from rust");
}

#[no_mangle]
pub extern "C" fn wc_render() {
    let buf = fb();
    for (i, px) in buf.iter_mut().enumerate() {
        *px = 0xFF00_0000 | (i as u32 & 0xFFFF);
    }
}

wc_cart!(w, h) declares every buffer the ABI needs, the live wc_info_t, wc_get_info, and a panic handler. You write wc_init and wc_render. Notably, a cart author writes no unsafe around a mutable static.

Build and run

cargo build --release --target wasm32-unknown-unknown

npx wasmcart pack --wasm target/wasm32-unknown-unknown/release/my_game.wasm \
  --name my-game --width 320 --height 240 -o my-game.wasc

npx wasmcart my-game.wasc

What you get

No runtime at all

#![no_std] and a freestanding target. Nothing is embedded, nothing is shimmed, and the wasm contains only what your code reached for.

Safe by default

The macro owns the mutable statics the ABI requires, so the buffers reach your code as ordinary slices. Writing a cart takes no unsafe.

Real GL

GL entry points are plain WASM imports, declared at compile time as the ABI requires. The GL example draws through them in under 2 KB.

Native WASM speed

Your code is the cart. There is no interpreter between the game loop and the host, which is the whole trade the compiled path makes.

← All languages