pixel8 0.1.0

Pixel8 fantasy console SDK: write carts in Rust, compile to wasm32-unknown-unknown
Documentation
  • Coverage
  • 61.23%
    139 out of 227 items documented1 out of 109 items with examples
  • Size
  • Source code size: 135.69 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 2.43 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 3s Average build duration of successful builds.
  • all releases: 3s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • zeenix/pixel8
    16 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • zeenix

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 pixel8::*;

struct MyGame {
    x: i16,
    y: i16,
}

impl Game for MyGame {
    fn update(&mut self, ctx: &mut Context) {
        if ctx.is_button_down(Button::Right) {
            self.x += 1;
        }
    }

    fn draw(&self, gfx: &mut Graphics) {
        gfx.clear(Color::BLACK);
        gfx.rect_fill(self.x, self.y, 8, 8, Color::WHITE).unwrap();
    }
}

pixel8::game!(MyGame { x: 64, y: 64 });

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: the Game trait, the Context (update-time) and Graphics (draw-time) handles, the 16-color palette and the game! macro that wires it all up. cargo add pixel8 in a cdylib crate and you have a cart.
  • pixel8-console — the desktop console and toolchain. cargo install pixel8-console gives you the pixel8 command: 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 is pixel8-console; the binary it installs is pixel8. The shell and editors are also exposed as a library (the windowed frontend sits behind the default-on window feature), which is how pixel8-tui reuses 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 the pixel8-console library 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 default window backend opens a desktop window with keyboard input; its kms backend is a static-musl KMS/evdev/ALSA binary for retro handhelds. Point it at a folder of .png carts 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.