Pixel8
A tiny fantasy console where the games are written in Rust.
Pixel8 (pronounced "pixelate") is a tiny, self-contained game console that never existed: a 128x128 screen, 16 fixed colors, a 4x6 pixel font, four audio channels, 256 sprites, a 128x64 tile map — and a Rust compiler where the Lua interpreter would be. You write a little Rust, it compiles to WebAssembly, and it runs inside the console's sandbox at a steady 60 fps (or 30, the cart's choice). Carts are shareable PNG images with the game embedded inside.
use *;
game!;
The console
The console is the pixel8-console crate; installing it gives you a pixel8 command:
cargo install pixel8-console
pixel8
You land at the boot console. Type help. The workflow is PICO-8's:
> new mygame create ./mygame (a real cargo crate!)
> run compile to wasm + run (esc returns)
> save save code + assets
> export mygame.png write a shareable png cartridge
> load mygame.png load a cart back
Esc flips between the console and the editors; the tab icons (or Alt+←/→) switch between
code, sprite, map, sfx and music editors. All UI is drawn by the console itself
on the same 128x128 screen the games use — there are no native widgets anywhere.
Games are played with the arrow keys plus Z/X (also C/V, N/M). Ctrl+R rebuilds and
runs from anywhere; Ctrl+S saves and kicks off a background build, flashing saved /
building... / build ok in the editor's bottom bar (compile errors land in the console). F6
while a game runs captures the screen as the cartridge label. Type keys in the console for the
full list.
In the terminal
The same console also runs inside a terminal — editors, carts and all — as the separate
pixel8-tui binary (so neither frontend drags in the other's dependencies):
cargo install pixel8-tui
pixel8-tui boot the console in the terminal
pixel8-tui run mygame.png boot, load, and run immediately
Terminals with sixel support (foot, WezTerm, Konsole,
iTerm2, xterm...) get real pixels via a pure-Rust encoder; everywhere else the screen is drawn
with unicode half-blocks. Ctrl+Q quits. On Linux, game input needs either a terminal with the
kitty keyboard protocol or read access to /dev/input (one-time:
sudo usermod -aG input $USER). See
docs/TUI.md for input details and
tuning knobs.
Constraints (they are the point)
| thing | size |
|---|---|
| screen | 128 x 128, 16 fixed colors |
| sprites | 256 of 8x8 pixels, 8 flags each |
| map | 128 x 64 tiles |
| sfx | 64 slots, 32 steps, 8 waveforms |
| music | 64 patterns, 4 channels |
| framerate | 60 fps (or 30, the cart's choice) |
| cart | one PNG file |
Carts also have runtime limits: 128 KiB cart size, 128 KiB RAM, and a 128 K per-frame work budget.
By default a cart is #![no_std] and depends only on the pixel8 SDK — that is what pixel8 new
scaffolds, and it keeps carts tiny. When one needs a growable vector, string or map, it can pull in
heapless for fixed-size collections. Full details in
docs/LIMITS.md.
PNG cartridges
export produces a real PNG image — cartridge art, label, title — with the compiled wasm, all
assets and (by default) the compressed Rust source embedded in a private chunk. Anyone can see the
cart; Pixel8 can play it; and if the source is included, import turns it back into an editable
project. See docs/CART_FORMAT.md.
export mygame.html instead produces a single self-contained web page: the cart and the whole
console runtime (compiled to wasm) embedded in one file you can double-click or host anywhere,
PICO-8-web style. See
docs/WEB_EXPORT.md.
Carts also run via pixel8-player, a pure-Rust player with a console-style cart picker. On the
desktop it opens a window with keyboard input; on retro handhelds (PowKiddy RGB10S, Anbernic
RG351/353 and friends on ArkOS/ROCKNIX) it runs as a static-musl KMS/evdev/ALSA binary — copy it
into the ports folder, drop .png carts next to it, play. See
docs/HANDHELD.md.
Inspired by PICO-8
Pixel8 is heavily inspired by PICO-8. The palette, the
fixed constraints, the editor modes, the > prompt and the overall charm all come from it. What
differs is the whole point of the project: a cart is Rust compiled to WebAssembly rather than Lua,
the font, code and cartridge formats are entirely original, and Pixel8 is free and open source
(GPL-3.0) rather than a paid product.
That shared heritage — the same palette, waveforms and sprite layout — means a PICO-8 cart's assets import almost one-to-one:
pixel8 import-pico8 mygame.p8 mygame # or mygame.p8.png
The graphics, sprite flags, map, sound effects and music transfer into a new project. Only the
assets come across — the cart's Lua code is ignored — and the project gets a stub src/lib.rs to
write your game in Rust. See
docs/PICO8_IMPORT.md.
Projects are real crates
A Pixel8 project is an ordinary Cargo crate that builds a cdylib for wasm32-unknown-unknown,
plus an assets.pixel8.json bundle. The integrated editor is the charming way to work, but
$EDITOR + cargo build works exactly the same — the console hot-reloads the wasm when it
changes on disk.
Headless commands support scripts and CI:
pixel8 new <dir> create a project
pixel8 build <dir> compile it to wasm
pixel8 export <dir> <out.png> build + write a png cart
pixel8 extract <cart.png> <dir> editable cart -> project
pixel8 import-pico8 <c> <dir> pico-8 cart (.p8/.p8.png) -> project
pixel8 export-web <dir> <o.html> one playable web page
pixel8 verify <cart.png> run 60 frames headless
The sandbox
Carts execute inside wasmi with no WASI, no filesystem, no network and no host memory access. The only imports a cart gets are the ~26 small, C-like functions of the Pixel8 ABI (docs/ABI.md) — draw, input, audio, map, log. Fuel metering turns infinite loops into a friendly error screen instead of a hung console.
Crates
Pixel8 is a handful of crates. Most people only ever touch the first two — the SDK a cart is written against, and the console that builds and runs it.
pixel8— the SDK your cart depends on, and the only crate a game links against. Deliberately zero-dependency and#![no_std]-friendly: theGametrait, theContext(update-time) andGraphics(draw-time) handles, the 16-color palette and thegame!macro that wires it all up.cargo add pixel8in acdylibcrate and you have a cart.pixel8-console— the desktop console and toolchain.cargo install pixel8-consolegives you thepixel8command: the boot prompt, the five editors (code, sprite, map, sfx, music), the build-and-hot-reload loop and every headless subcommand (new,build,export,extract,import-pico8,export-web,verify). The crate ispixel8-console; the binary it installs ispixel8. The shell and editors are also exposed as a library (the windowed frontend sits behind the default-onwindowfeature), which is howpixel8-tuireuses them.pixel8-tui— the console in your terminal: the same shell and editors rendered over sixel (pure-Rust encoder) or unicode half-blocks, with crossterm input. A thin frontend over thepixel8-consolelibrary that never builds the winit/wgpu window stack.pixel8-runtime— the console's engine, as a reusable library: the 128x128 indexed framebuffer and software rasterizer, font and palette, the wasmi VM with ABI linking and fuel metering, the input model, the 4-channel synth, the shared asset model and the PNG cart codec. Depend on it to embed Pixel8 in your own frontend or build tools around carts — the console and both players are thin shells over it.pixel8-player— a standalone cart player, no editors. Its defaultwindowbackend opens a desktop window with keyboard input; itskmsbackend is a static-musl KMS/evdev/ALSA binary for retro handhelds. Point it at a folder of.pngcarts and play.
The browser player — pixel8-runtime compiled to wasm and wrapped in a small C-like export surface
— lives in the repo as pixel8-web and powers export mygame.html. It ships inside exported web
pages rather than to crates.io.
Building from source
Requires Rust (with the wasm32-unknown-unknown target for building carts) and, on Linux, ALSA
headers for audio:
rustup target add wasm32-unknown-unknown
sudo apt install libasound2-dev # debian/ubuntu
sudo dnf install alsa-lib-devel # fedora
# (or build silent with `--no-default-features`)
cargo console # alias for: cargo run --release -p pixel8-console
cargo tui # alias for: cargo run --release -p pixel8-tui
Try a bundled cart:
cargo console -- examples/platformer
then type run.