🔧WebAssembly text format: no language at all, just wasm. Nothing is hidden - a Color is four bytes you write into memory yourself. Worth reading once to see what every other cart compiles down to.
- Template: cart_wat - press "Use this template" and you have a working game.
- Docker image:
ghcr.io/notnullgames/null0-cart-wat:latest - Your code:
cart/main.wat, compiled with wat2wasm - Bindings:
carts/wat/null0.wat(a reference list to copy from) - WASI is available, so ordinary file and stdio calls work.
- See carts made with WAT
- API docs for WAT
building
You do not install wat2wasm - the docker image has it, and the current null0 bindings, baked in. There is nothing in your project to keep in sync with the engine.
docker run --rm --user $(id -u):$(id -g) \
-v ./cart:/src -v ./webroot:/out \
ghcr.io/notnullgames/null0-cart-wat:latest mygameThe template wraps that in npm start, which also serves the result and rebuilds when you save.
WAT notes
- Raw wasm text - no include mechanism, so copy the imports you need out of null0.wat.
- A
Coloris a *pointer to 4 bytes* you write into memory yourself. - No
malloc, so struct-returning functions do not work on the web host.
a cart, in WAT
This is carts/wat/simple/main.wat in the engine repo - the same file CI builds into the cart below, so it always compiles.
(module
;; every language's `simple` cart draws exactly this - see carts/wat/null0.wat
;; for the full import list
(import "null0" "clear" (func $clear (param i32)))
(import "null0" "draw_circle" (func $draw_circle (param i32 i32 i32 i32)))
(import "null0" "draw_text" (func $draw_text (param i32 i32 i32 i32 i32)))
;; two pages: the data below sits at 65536, which is one page in - with 1
;; page the module fails to instantiate ("data segment is out of bounds").
;; exported because the host reads Color and string pointers out of it.
(memory (export "memory") 2)
(data (i32.const 65536) "\00\79\f1\ff") ;; BLUE = rgba(0, 121, 241, 255)
(global $blue i32 (i32.const 65536))
(data (i32.const 65540) "\e6\29\37\ff") ;; RED = rgba(230, 41, 55, 255)
(global $red i32 (i32.const 65540))
(data (i32.const 65544) "\ff\ff\ff\ff") ;; WHITE = rgba(255, 255, 255, 255)
(global $white i32 (i32.const 65544))
;; "hello from WAT", null-terminated
(data (i32.const 65548) "\68\65\6c\6c\6f\20\66\72\6f\6d\20\57\41\54\00")
(global $text i32 (i32.const 65548))
(func (export "load")
(call $clear (global.get $blue))
(call $draw_circle
(i32.const 100) (i32.const 100) (i32.const 50)
(global.get $red))
(call $draw_text
(i32.const 0) ;; FONT_DEFAULT
(global.get $text)
(i32.const 170) (i32.const 92)
(global.get $white)))
(func (export "update"))
)
carts/wat/simple/main.watcallbacks
A cart implements the callbacks it cares about. In WAT they look like this:
(func (export "update") ...)See anatomy of a cart for the full list and what each one is passed.