Daviey/mario

Super Mario-style platformer that runs in your terminal: truecolor pixel art, deterministic engine, replay-verified leaderboard. Also in the browser, over ssh/mosh, and bootable via UEFI. Go stdlib only.

Go

1

409 commits

updated Sep 3, 2026

See the code
arcade
cross-platform
game
golang
mosh
platformer
retro-gaming
ssh
terminal
terminal-game
wasm
webassembly

README

SUPER CLI MARIO logo

SUPER CLI MARIO

A complete Super Mario Bros-style platformer that runs in your terminal,
in the browser, on your phone, over plain ssh or mosh with no client install,
as a .deb/.rpm/AUR/Nix package, AppImage or Android APK,
and bootable on bare metal via UEFI.

New: the full build story, seven days start to finish — SUPER CLI MARIO: a complete Mario clone for your terminal in zero-dependency Go

CI latest release Go 1.22+ zero dependencies MIT license

Gameplay: running and jumping through World 1-1, stomping goombas, bumping blocks

Twelve hand-built levels across three worlds and six looks (overworld, underground, underwater, sky, night, castle) — a faithful run through SMB1's worlds 1-3: the warp-zone cellar and vertical lifts of 1-2, the fake-Bowser castles (1-4, 2-4, 3-4 — each an impostor that reveals its true self under fire), the bloober-and-cheep underwater 2-2 with its downward currents, the leaping-cheep bridge of 2-3, the night world 3 with its hammer bros, springboards and balance-lift athletic 3-3, and the princess waiting at the end of 3-4. Mushrooms, fire flowers, star power, hidden blocks, piranha plants, paratroopas, fire bars, podoboos and lava throughout. All of it drawn as truecolor pixel art using half-block terminal glyphs: two square pixels per character cell, a custom 3×5 arcade font for the HUD, and a 16-color fallback for basic terminals.

The engine is fully deterministic: no randomness, no wall clock, so the same input sequence always reproduces the same game. The online leaderboard is built on that property; every score is verified by replay.

Screenshots

Title screen with pixel-art logo, PRESS ANY KEY prompt Online leaderboard: ranks, names, scores, level reached, verified checkmarks

Overworld (1-1)Underground (1-2)
Mario mid-jump over pipes and blocks with goombasUnderground level: blue bricks, ceiling, koopa and coins
Sky (2-3)Castle (2-4)
Sky level: sandstone platforms, coins, enemies, mid-air jumpCastle level: fire bar and lava, jumping the hazard

The same code compiles to a browser build (Go to WebAssembly): an installable PWA with offline support, WebAudio sound, and its own touch gamepad on phones.

The game running in a desktop browser The game running in a phone browser with the on-screen gamepad

Quick start

Terminal (Linux, macOS, Windows, the BSDs, Solaris/illumos; x86-64, arm64, riscv64, 32-bit arm/x86):

go install github.com/Daviey/mario/cmd/mario@latest

Or build from source (needs Go 1.22+; no toolchain? Grab a prebuilt binary below):

git clone https://github.com/Daviey/mario && cd mario
make run          # or: make build && ./mario

Prebuilt binaries, .deb/.rpm, the AppImage, the Android APK, the macOS app and the web bundle are all on the latest release. AppImage: chmod +x mario_*_amd64.AppImage && ./mario_*_amd64.AppImage (on NixOS: nix shell nixpkgs#appimage-run -c appimage-run mario_*_amd64.AppImage). Nix users: nix build from the flake in this repo; Arch users: makepkg -p packaging/aur/PKGBUILD.

macOS app: unzip mario_<version>_macos.app.zip from the release, then right-click and Open it the first time (Gatekeeper has to bless the unsigned binary). Or skip the bundle and run the binary from Terminal: Mario.app/Contents/MacOS/mario.

iOS: sideload mario_<version>_ios_unsigned.ipa with Sideloadly/AltStore, which re-signs it with your Apple ID (free accounts expire every 7 days). Or just play the web build; it's the same game.

Browser: play it at mario.baby or daviey.github.io/mario: no install, works offline once loaded, touch controls on phones.

Bare metal: make efi produces a single-file UEFI executable that boots straight into the game; no OS required.

Legacy BIOS / USB: make iso wraps the same payload in a BIOS-bootable hybrid ISO, and make iso-qemu smoke-boots it headless.

Over SSH: mario -serve :1985 turns any machine into an arcade. Players connect with ssh yourhost -p 1985 and just play: any username works, there are no accounts and nothing to install. (1985 is the year Super Mario Bros. shipped.) The server speaks the whole SSH protocol from the standard library (curve25519 key exchange, ed25519 host key, AES-CTR + HMAC) and offers exactly one service: the game. No shell, no exec, no forwarding; every connection gets its own game, leaderboard identity and replay-verified scores. -hostkey /path pins the host key across restarts, and -basic falls back to 16-color output.

Over mosh: add -mosh auto (plus -mosh-ports 60000:60100 in the firewall) and players connect with mosh yourhost. Same game over mosh's UDP protocol: roaming between Wi-Fi and cellular, surviving laptop sleep/resume, interpolating over packet loss. Input feel is tuned for both transports; on slow links the server drops rendered frames, never simulation ticks, so keystrokes keep flowing.

The server also identifies your terminal and picks the right color depth, with no config and no env vars. GNOME Terminal, iTerm2, kitty, WezTerm, ghostty, Konsole and friends get full truecolor automatically (even through mosh, which normally flattens everything to 256 colors); Terminal.app gets an honest 16-color palette, because that's what it has. When all sessions are taken, extra players queue with a live position and ETA instead of bouncing.

Play it right now, over IPv4 or IPv6: ssh mario.baby (port 22), ssh mario.baby -p 1985, or roam with mosh mario.baby.

Controls

KeyAction
a/d or arrowsmove
w, space, upjump
x (hold)run · fire when powered
p / q / kpause · quit · die on demand
lleaderboard (from the title screen)
iabout screen (from the title screen)
ddaily challenge (from the title screen)
rrestart after game over

Sound comes through the terminal bell: coins, stomps, power-ups, level clear, locally, over ssh and even through mosh. Terminals map it to a beep, flash or urgency hint, and -nobell silences it. (The browser build synthesizes its own WebAudio soundtrack instead.)

mario -cheats lifts the two-fireball cap, but the run is deliberately unrecorded and can never touch the leaderboard.

Levels are plain ASCII text files, so you can play your own: ./mario -level mylevel.txt.

A leaderboard nobody can cheat*

*Not quite nobody: the verifier proves the engine produced the score from a real input sequence, not that a human was holding the keys. Tool-assisted runs still verify because they genuinely earned the score. What nobody can do is claim a number the game didn't produce.

Every run is recorded as a compressed input log. When you submit a score, the recording goes with it, and a verifier replays your inputs against the same deterministic engine and keeps the row only if it reproduces the claimed score, level and engine version:

flowchart LR
    A[You play] -->|inputs recorded| B[Submit score + recording]
    B --> C{GitHub Action<br/>replays the recording}
    C -->|score matches| D[✓ verified row]
    C -->|mismatch| E[row deleted]

Submissions sit behind proof-of-work, per-IP and per-device rate limits, and row-level security; the board itself is a plain Supabase/PostgREST table, so there's no game server to run or trust. Verified rows carry a green ✓ on every board surface (terminal, web, APK). You can also read the board without playing: mario -scores.

Daily mode generates a new challenge level every day from the date seed: same level for everyone, with its own leaderboard. Every seed is proven playable in CI: a checker drives the engine's real player physics and asserts each floating coin is collectable and the flag is reachable. Enter it from the title screen (d), or start straight into a run with mario -daily.

How it's built

  • Zero dependencies. The whole game (engine, renderer, leaderboard client, WASM target, even the .deb packager) is Go standard library.
  • The renderer draws truecolor half-block pixels and streams a bandwidth-minimal diff of only what changed, about 1.2 KB per frame on a 200-column terminal at 60 fps (~73 KiB/s, roughly a third of that in 16-color mode), so remote play feels local. It falls back to 16-color ANSI, follows terminal resizes live, and speaks the kitty keyboard protocol for real press/repeat/release events, with legacy key-repeat inference for terminals that don't.
  • A fixed 60 Hz tick through a pure Game.Update(Input) transition drives the engine. The determinism tests byte-compare full rendered runs, and the replay verifier leans on the same property.
  • Levels are proven playable in tests. A reachability checker runs the engine's real player physics over every built-in level and every daily seed, asserting each floating coin is collectable and the flag reachable, small Mario only, no power-ups. The checker has its own negative control; a proof that cannot fail proves nothing.
  • make release cross-compiles 17 OS/arch pairs from one codebase; make web produces the static PWA, make apk wraps it for Android, make deb / make rpm / AUR / Nix package it for distros, and make efi / make iso build the bootable images. The Windows exe icon is rendered from the game's own sprite data.
  • mario -serve implements its own SSH server on the standard library: transport, key exchange, none auth, session channels. It proxies the mosh roaming handshake (never exec'ing the client's own command line), probes each terminal's real color capabilities via device-attribute queries, and keeps the 60 Hz simulation honest under backpressure and capacity pressure.
  • The root package is a small facade (mario.New, Feed, Step, Run), so you can embed the game as an easter egg in your own Go program.

Development

make build     # native binary (CGO off, fully static)
make check     # fmtcheck + vet + test (the CI gate)
make race      # tests under the race detector
make cover     # coverage summary
make web       # static browser build in dist/web
make test      # go test ./...

Stdlib testing only; assertions are programmatic (pixel reads, ANSI output contains, geometry checks). The suite includes determinism tests, viewport size sweeps, and offline fakes for the leaderboard backend.

License

MIT

Trademark

This is a non-commercial fan project, not affiliated with or endorsed by Nintendo. Super Mario Bros. and Mario are trademarks of Nintendo. Everything in this repository was written from scratch for it; no Nintendo code, ROMs or data files are included. If you are a rights holder with concerns, please open an issue.

Contributors

Daviey

234 commits

Daviey/mario

Super Mario-style platformer that runs in your terminal: truecolor pixel art, deterministic engine, replay-verified leaderboard. Also in the browser, over ssh/mosh, and bootable via UEFI. Go stdlib only.

Go

1

409 commits

updated Sep 3, 2026

See the code
arcade
cross-platform
game
golang
mosh
platformer
retro-gaming
ssh
terminal
terminal-game
wasm
webassembly

README

SUPER CLI MARIO logo

SUPER CLI MARIO

A complete Super Mario Bros-style platformer that runs in your terminal,
in the browser, on your phone, over plain ssh or mosh with no client install,
as a .deb/.rpm/AUR/Nix package, AppImage or Android APK,
and bootable on bare metal via UEFI.

New: the full build story, seven days start to finish — SUPER CLI MARIO: a complete Mario clone for your terminal in zero-dependency Go

CI latest release Go 1.22+ zero dependencies MIT license

Gameplay: running and jumping through World 1-1, stomping goombas, bumping blocks

Twelve hand-built levels across three worlds and six looks (overworld, underground, underwater, sky, night, castle) — a faithful run through SMB1's worlds 1-3: the warp-zone cellar and vertical lifts of 1-2, the fake-Bowser castles (1-4, 2-4, 3-4 — each an impostor that reveals its true self under fire), the bloober-and-cheep underwater 2-2 with its downward currents, the leaping-cheep bridge of 2-3, the night world 3 with its hammer bros, springboards and balance-lift athletic 3-3, and the princess waiting at the end of 3-4. Mushrooms, fire flowers, star power, hidden blocks, piranha plants, paratroopas, fire bars, podoboos and lava throughout. All of it drawn as truecolor pixel art using half-block terminal glyphs: two square pixels per character cell, a custom 3×5 arcade font for the HUD, and a 16-color fallback for basic terminals.

The engine is fully deterministic: no randomness, no wall clock, so the same input sequence always reproduces the same game. The online leaderboard is built on that property; every score is verified by replay.

Screenshots

Title screen with pixel-art logo, PRESS ANY KEY prompt Online leaderboard: ranks, names, scores, level reached, verified checkmarks

Overworld (1-1)Underground (1-2)
Mario mid-jump over pipes and blocks with goombasUnderground level: blue bricks, ceiling, koopa and coins
Sky (2-3)Castle (2-4)
Sky level: sandstone platforms, coins, enemies, mid-air jumpCastle level: fire bar and lava, jumping the hazard

The same code compiles to a browser build (Go to WebAssembly): an installable PWA with offline support, WebAudio sound, and its own touch gamepad on phones.

The game running in a desktop browser The game running in a phone browser with the on-screen gamepad

Quick start

Terminal (Linux, macOS, Windows, the BSDs, Solaris/illumos; x86-64, arm64, riscv64, 32-bit arm/x86):

go install github.com/Daviey/mario/cmd/mario@latest

Or build from source (needs Go 1.22+; no toolchain? Grab a prebuilt binary below):

git clone https://github.com/Daviey/mario && cd mario
make run          # or: make build && ./mario

Prebuilt binaries, .deb/.rpm, the AppImage, the Android APK, the macOS app and the web bundle are all on the latest release. AppImage: chmod +x mario_*_amd64.AppImage && ./mario_*_amd64.AppImage (on NixOS: nix shell nixpkgs#appimage-run -c appimage-run mario_*_amd64.AppImage). Nix users: nix build from the flake in this repo; Arch users: makepkg -p packaging/aur/PKGBUILD.

macOS app: unzip mario_<version>_macos.app.zip from the release, then right-click and Open it the first time (Gatekeeper has to bless the unsigned binary). Or skip the bundle and run the binary from Terminal: Mario.app/Contents/MacOS/mario.

iOS: sideload mario_<version>_ios_unsigned.ipa with Sideloadly/AltStore, which re-signs it with your Apple ID (free accounts expire every 7 days). Or just play the web build; it's the same game.

Browser: play it at mario.baby or daviey.github.io/mario: no install, works offline once loaded, touch controls on phones.

Bare metal: make efi produces a single-file UEFI executable that boots straight into the game; no OS required.

Legacy BIOS / USB: make iso wraps the same payload in a BIOS-bootable hybrid ISO, and make iso-qemu smoke-boots it headless.

Over SSH: mario -serve :1985 turns any machine into an arcade. Players connect with ssh yourhost -p 1985 and just play: any username works, there are no accounts and nothing to install. (1985 is the year Super Mario Bros. shipped.) The server speaks the whole SSH protocol from the standard library (curve25519 key exchange, ed25519 host key, AES-CTR + HMAC) and offers exactly one service: the game. No shell, no exec, no forwarding; every connection gets its own game, leaderboard identity and replay-verified scores. -hostkey /path pins the host key across restarts, and -basic falls back to 16-color output.

Over mosh: add -mosh auto (plus -mosh-ports 60000:60100 in the firewall) and players connect with mosh yourhost. Same game over mosh's UDP protocol: roaming between Wi-Fi and cellular, surviving laptop sleep/resume, interpolating over packet loss. Input feel is tuned for both transports; on slow links the server drops rendered frames, never simulation ticks, so keystrokes keep flowing.

The server also identifies your terminal and picks the right color depth, with no config and no env vars. GNOME Terminal, iTerm2, kitty, WezTerm, ghostty, Konsole and friends get full truecolor automatically (even through mosh, which normally flattens everything to 256 colors); Terminal.app gets an honest 16-color palette, because that's what it has. When all sessions are taken, extra players queue with a live position and ETA instead of bouncing.

Play it right now, over IPv4 or IPv6: ssh mario.baby (port 22), ssh mario.baby -p 1985, or roam with mosh mario.baby.

Controls

KeyAction
a/d or arrowsmove
w, space, upjump
x (hold)run · fire when powered
p / q / kpause · quit · die on demand
lleaderboard (from the title screen)
iabout screen (from the title screen)
ddaily challenge (from the title screen)
rrestart after game over

Sound comes through the terminal bell: coins, stomps, power-ups, level clear, locally, over ssh and even through mosh. Terminals map it to a beep, flash or urgency hint, and -nobell silences it. (The browser build synthesizes its own WebAudio soundtrack instead.)

mario -cheats lifts the two-fireball cap, but the run is deliberately unrecorded and can never touch the leaderboard.

Levels are plain ASCII text files, so you can play your own: ./mario -level mylevel.txt.

A leaderboard nobody can cheat*

*Not quite nobody: the verifier proves the engine produced the score from a real input sequence, not that a human was holding the keys. Tool-assisted runs still verify because they genuinely earned the score. What nobody can do is claim a number the game didn't produce.

Every run is recorded as a compressed input log. When you submit a score, the recording goes with it, and a verifier replays your inputs against the same deterministic engine and keeps the row only if it reproduces the claimed score, level and engine version:

flowchart LR
    A[You play] -->|inputs recorded| B[Submit score + recording]
    B --> C{GitHub Action<br/>replays the recording}
    C -->|score matches| D[✓ verified row]
    C -->|mismatch| E[row deleted]

Submissions sit behind proof-of-work, per-IP and per-device rate limits, and row-level security; the board itself is a plain Supabase/PostgREST table, so there's no game server to run or trust. Verified rows carry a green ✓ on every board surface (terminal, web, APK). You can also read the board without playing: mario -scores.

Daily mode generates a new challenge level every day from the date seed: same level for everyone, with its own leaderboard. Every seed is proven playable in CI: a checker drives the engine's real player physics and asserts each floating coin is collectable and the flag is reachable. Enter it from the title screen (d), or start straight into a run with mario -daily.

How it's built

  • Zero dependencies. The whole game (engine, renderer, leaderboard client, WASM target, even the .deb packager) is Go standard library.
  • The renderer draws truecolor half-block pixels and streams a bandwidth-minimal diff of only what changed, about 1.2 KB per frame on a 200-column terminal at 60 fps (~73 KiB/s, roughly a third of that in 16-color mode), so remote play feels local. It falls back to 16-color ANSI, follows terminal resizes live, and speaks the kitty keyboard protocol for real press/repeat/release events, with legacy key-repeat inference for terminals that don't.
  • A fixed 60 Hz tick through a pure Game.Update(Input) transition drives the engine. The determinism tests byte-compare full rendered runs, and the replay verifier leans on the same property.
  • Levels are proven playable in tests. A reachability checker runs the engine's real player physics over every built-in level and every daily seed, asserting each floating coin is collectable and the flag reachable, small Mario only, no power-ups. The checker has its own negative control; a proof that cannot fail proves nothing.
  • make release cross-compiles 17 OS/arch pairs from one codebase; make web produces the static PWA, make apk wraps it for Android, make deb / make rpm / AUR / Nix package it for distros, and make efi / make iso build the bootable images. The Windows exe icon is rendered from the game's own sprite data.
  • mario -serve implements its own SSH server on the standard library: transport, key exchange, none auth, session channels. It proxies the mosh roaming handshake (never exec'ing the client's own command line), probes each terminal's real color capabilities via device-attribute queries, and keeps the 60 Hz simulation honest under backpressure and capacity pressure.
  • The root package is a small facade (mario.New, Feed, Step, Run), so you can embed the game as an easter egg in your own Go program.

Development

make build     # native binary (CGO off, fully static)
make check     # fmtcheck + vet + test (the CI gate)
make race      # tests under the race detector
make cover     # coverage summary
make web       # static browser build in dist/web
make test      # go test ./...

Stdlib testing only; assertions are programmatic (pixel reads, ANSI output contains, geometry checks). The suite includes determinism tests, viewport size sweeps, and offline fakes for the leaderboard backend.

License

MIT

Trademark

This is a non-commercial fan project, not affiliated with or endorsed by Nintendo. Super Mario Bros. and Mario are trademarks of Nintendo. Everything in this repository was written from scratch for it; no Nintendo code, ROMs or data files are included. If you are a rights holder with concerns, please open an issue.

Contributors

Daviey

234 commits

Languages

Go

90.4%

JavaScript

3.0%

PLpgSQL

1.6%

Makefile

1.5%