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.
See the code
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
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.
| Overworld (1-1) | Underground (1-2) |
|---|---|
![]() | ![]() |
| Sky (2-3) | Castle (2-4) |
|---|---|
![]() | ![]() |
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.
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.
| Key | Action |
|---|---|
a/d or arrows | move |
w, space, up | jump |
x (hold) | run · fire when powered |
p / q / k | pause · quit · die on demand |
l | leaderboard (from the title screen) |
i | about screen (from the title screen) |
d | daily challenge (from the title screen) |
r | restart 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.
*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.
.deb packager) is Go standard library.Game.Update(Input) transition drives the
engine. The determinism tests byte-compare full rendered runs, and the replay
verifier leans on the same property.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.mario.New, Feed, Step, Run), so
you can embed the game as an easter egg in your own Go program.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.
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.
234 commits
175 commits
Go
90.4%
JavaScript
3.0%
PLpgSQL
1.6%
Makefile
1.5%
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.
See the code
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
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.
| Overworld (1-1) | Underground (1-2) |
|---|---|
![]() | ![]() |
| Sky (2-3) | Castle (2-4) |
|---|---|
![]() | ![]() |
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.
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.
| Key | Action |
|---|---|
a/d or arrows | move |
w, space, up | jump |
x (hold) | run · fire when powered |
p / q / k | pause · quit · die on demand |
l | leaderboard (from the title screen) |
i | about screen (from the title screen) |
d | daily challenge (from the title screen) |
r | restart 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.
*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.
.deb packager) is Go standard library.Game.Update(Input) transition drives the
engine. The determinism tests byte-compare full rendered runs, and the replay
verifier leans on the same property.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.mario.New, Feed, Step, Run), so
you can embed the game as an easter egg in your own Go program.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.
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.
234 commits
175 commits
Go
90.4%
JavaScript
3.0%
PLpgSQL
1.6%
Makefile
1.5%