WASABI-16

WASABI-16 is a fantasy console designed around a Wasm runtime that loads and runs Wasm-based, "game cartridges." It showcases how the Wasm Component model may be used to enable a language-agnostic runtime for sandboxed, portable, networked, real-time, graphical, interactive software.

Project Log

Late 2024

What would it be like to pair WIT / Wasm Component sandboxing with a fantasy console?

I started the project by closing the loop on a few key elements:

  • Initialize a Bevy app
  • Load a Wasm "cartridge" within a sandbox runtime (Wasmtime)
  • Invoke cartridge-exported lifecycle functions (init, update, draw) within Bevy systems
  • "Paint" cartridge draw calls using the Bevy 2D pipeline

I named the fledgling project WASABI-16: a blend of "Wasm" and "ABI" in reference to the Wasm Component model.

Animated graphics painted by sandboxed Wasm Component

As a follow-up, I added:

  • Color-palette-driven instanced painting on the GPU, with palette offsets and locks to enable various effects
  • Basic controller input Leafwing Input Manager
  • Support for extracting color palette information and formatting the character atlas via a macro
  • Support for map data via a macro, currently supporting LDtk projects
  • Ability to blit map regions to the frame buffer; maps have up to four layers, and individual layers can be selected per-draw-call with a mask

Although the project is built around Bevy and takes advantage of its graphics pipeline, said pipeline has been heavily customized. The entire virtual console and cartridge is rendered in a single draw call on the GPU.

Demonstration of input, blit-ing and palette shifting

I was able to make use of an unmodified free asset from Kenney in both LDtk and the demo "cartridge" to produce the example in the video. The final cartridge - including all color, atlas and map data - clocked in at about ~50kb.

WASABI-16 allows for 16-color palettes in a limited color range. When a character atlas is transformed for use by the cartridge, the palette is extracted and the pixel data is converted to palette indices. A cartridge programmer can use the palette function to offset these indices when drawing, enabling various effects.

Additionally, they may fix specific colors in-place ("locking" them) acontextnd only cycle across the ones that are not locked.

The "lock" is a 16-bit field where each bit corresponds to a color palette index. Setting a bit to 1 "locks" a color. For example:

// Offset the palette by 10, no colors locked
palette(0, 10, 0); 

// Offset the palette by 10, first color is locked
palette(0, 10, 1);

// Offset the palette by 10, every other color is locked
palette(0, 10, 0b1010101010101010);
Various combinations of palette shift offsets and locks

While developing the palette shift feature, I realized that my ability to inspect the draw state may be extended by taking advantage of my modern graphics pipeline. The next iteration of work included a basic debug view that visualizes all the "draw calls" (as expressed by cartridge API invocations) in real time. Additionally, I implemented:

  • flip(x, y) cartridge API that changes the direction that pixels are painted
  • Basic controls and platformer physics
  • Support for loading and running cartridges on the web
Real-time draw state debug mode

The frontier of the project is networked multiplayer. I added support for web/native cross-play in a local environment.

Network multiplayer cross-play between web and native

A living demo of the WASABI-16 fantasy console can be found at https://wasabi-16.cdata.earth/