A Game Boy, Game Boy Color, and Game Boy Advance emulator that runs in your terminal.
Written in Rust. Three emulation cores share one frontend — a half-block terminal renderer (with true full-color images on kitty-graphics terminals) and auto-scaling, a ROM picker grouped by hardware, configurable input, audio, battery saves, save-state slots, adjustable play speed, rewind, an in-game pause menu (save/load with thumbnail previews, a controls reference, return to the library), and screenshot/clip capture.
From crates.io (any platform with a Rust toolchain):
cargo install termboyPrebuilt binary — download the tarball for your platform from the latest release, then:
tar xzf termboy-*-aarch64-apple-darwin.tar.gz # match your platform
install termboy-*/termboy /usr/local/bin/Builds are published for macOS (Apple Silicon and Intel) and Linux (x86_64).
Homebrew (macOS / Linux):
brew install roma-888/tap/termboyFrom source:
git clone https://github.com/roma-888/termboy
cd termboy && cargo run --releaseThen run termboy to open the ROM picker for ./roms, or termboy <rom> to
play a file directly. Bring your own ROMs.
Game Boy / Game Boy Color — complete. Full audio (all four APU channels) plays
through your system output. Game Boy Color games run in full color (banked
VRAM/WRAM, color palettes, double-speed CPU, HDMA, MBC5) alongside the original DMG
library. MBC1/MBC3/MBC5 with battery saves (<rom>.sav, auto-flushed) and the MBC3
real-time clock. Blargg cpu_instrs + instr_timing, dmg-acid2 and cgb-acid2 (both
pixel-exact vs official references) all pass.
Game Boy Advance — commercial games play with sound. ARM7TDMI CPU (jsmolka's
arm/thumb test ROMs pass headlessly), the full scanline PPU (tiled and bitmap
modes, affine backgrounds, regular and affine sprites, windows, alpha/brightness
blending, mosaic), all four DMA channels, cascading timers, the IE/IF/IME
interrupt system, an HLE BIOS (IntrWait, CpuSet, LZ77/Huffman decompression,
affine helpers, …), and audio — the four PSG channels plus both Direct Sound
DMA-FIFO channels. Pokémon boots through its intro to the title screen, plays its
music, and reaches the in-game menus. Battery saves persist for every cartridge
save type — SRAM, Flash (64K/128K), and EEPROM (4K/64K) — auto-detected from the
ROM and written to <rom>.sav. In release builds the core emulates well above
real time (~6× on Apple Silicon) with an allocation-free per-frame loop, so games
play at full speed; measure with cargo run --release -p termboy-gba --example throughput -- <rom.gba>. That completes the GBA roadmap — cycle-exact timing
(waitstate/prefetch accuracy) is a deliberate non-goal.
cargo run --release -p termboy— opens a game picker for./roms(GB/GBC/GBA, grouped by hardware; no argument needed)cargo run --release -p termboy -- <rom>— play a.gb/.gbc/.gbadirectly (pixel-perfect when it fits, auto-scaled to fit below that;--exactdisables scaling)--keys swap(A/B swapped) or--keys a=k,b=j,start=spacefor custom bindings--palette green|gray|pocketor four hex colors (--palette '#e0f8d0,#88c070,#346856,#081820') — Game Boy only--graphics auto|kitty|half—auto(default) uses the kitty graphics protocol on terminals that support it (Ghostty/kitty/WezTerm), half-blocks elsewhere;kitty/halfforce the choicecargo run --release -p termboy -- --headless <rom.gb>— run headless, print serial outputcargo test --workspace— full test suite including hardware test ROMs
| Key | Button |
|---|---|
| Arrow keys | D-pad |
| X | A |
| Z | B |
| Enter | Start |
| Tab | Select |
| A | L (GBA) |
| S | R (GBA) |
1–0 |
Save state to slot 1–10 |
Shift + 1–0 |
Load state from that slot |
+ / - |
Speed up / slow down (0.5x, 1x, 2x, 4x) |
| M | Toggle audio mute |
| Backspace (hold) | Rewind |
| P | Save a screenshot (PNG → images/) |
| R | Start/stop recording a clip (animated PNG → clips/) |
| Esc | Open the pause menu |
Pause menu: Esc opens a pause menu over the dimmed game — Resume,
Save/Load (a list of the 10 slots showing how long ago each was saved,
with a preview of the highlighted slot), Speed and
Mute, Controls (a reference of every key binding), Back to library
(return to the ROM picker without quitting), and Quit. ↑/↓ move, Enter selects, ←/→ adjust the speed row, and Esc
backs out (or resumes from the top). The menu is additive — every direct hotkey
above still works during play.
Save states capture the full machine and persist to <rom>.ss0…<rom>.ss9, so
they survive quitting. A brief overlay confirms each save/load. (The load
shortcut sends the shifted number — !@#… on a US keyboard, or number+Shift in
kitty-protocol terminals; a state saved in one game won't load into another.)
+/- step through 0.5x, 1x, 2x, 4x and a brief overlay shows the new
speed. Audio follows the speed — pitch-shifted up when fast-forwarding, down in
slow motion, like a tape — and stays gap-free at every speed; M mutes it
outright. Speed resets to 1x on the next game.
Hold Backspace to rewind: termboy snapshots the machine a few times a second into a memory-bounded ring and replays them backward while held (audio muted). Release to resume from that point. The window is whatever fits a ~64 MB budget — minutes on GB/GBC, around ten seconds on GBA — and resets per game.
Input feels best in a terminal supporting the kitty keyboard protocol (Ghostty, kitty, WezTerm, recent iTerm2/Alacritty) — real key-release events. Elsewhere termboy falls back to timed release driven by OS key repeat; for a snappier hold, reduce your OS key-repeat delay.
Capture: P writes a PNG of the current frame into images/; R toggles
recording a short clip into clips/ (an animated PNG). Files are named
<rom>-<time>.png, and both folders are created where you launch termboy. A
● REC overlay shows while recording, which auto-stops after ~20 seconds.
termboy reads optional defaults from $XDG_CONFIG_HOME/termboy/config (falling
back to ~/.config/termboy/config), or from the path given by --config <path>.
On first run it writes a commented starter file there — every setting shown
but commented out — so there's always one to edit; uncomment a line to change that
setting. It's a flat key = value file. A # starts a comment only at the
beginning of a line — never inline, because hex palette values contain #.
Any command-line flag overrides the matching setting, and every in-game control
still works as usual; these are just the values a game starts with.
# ~/.config/termboy/config
palette = green
keys = a=k,b=j
graphics = auto
exact = false
speed = 1
mute = false
palette—green,gray,pocket, or four hex colors (#e0f8d0,#88c070,#346856,#081820); Game Boy onlykeys—swap, or per-buttona=k,b=j,start=space(same syntax as--keys)graphics—auto,kitty, orhalfexact—truerequires a native-size terminal (no auto-scaling)speed— starting speed:0.5,1,2, or4mute—truestarts with audio mutedcolor-correct— GBA only:trueapplies hardware-accurate color correction (off by default)
A bad value or unknown key is reported and skipped — a config typo never stops a game from launching.
termboy draws with half-block characters (each cell is one pixel wide and two tall) and auto-scales the image to fit your terminal. When the terminal has at least as many character cells as the console's native size — 160×72 for GB/GBC, 240×80 for GBA — the picture is pixel-perfect; below that it's box-filter downscaled, which looks softer.
If the image looks blurry, zoom the terminal out (⌘− / Ctrl−, a smaller font) so there are enough cells for pixel-perfect rendering; zoom in (⌘+ / Ctrl+) to make the picture physically larger at the cost of sharpness. In other words, font size is your resolution dial.
On terminals that implement the kitty graphics protocol (Ghostty, kitty,
WezTerm) termboy skips half-blocks entirely and streams each frame as a true
full-resolution color image, scaled to fill the window — the half-block notes
above don't apply there. This is automatic; force or disable it with
--graphics kitty / --graphics half.
The freely redistributable test ROMs under crates/*/tests/roms/ are not
termboy's work:
- Game Boy CPU/timing — Blargg's gb-test-roms (
cargo-fetched byscripts/fetch-test-roms.sh) - PPU accuracy — dmg-acid2 and cgb-acid2 by Matt Currie (the banner above is termboy's cgb-acid2 output)
- GBA — jsmolka's gba-tests (
arm,thumb,memory,nes,save,shades,stripes), run headlessly.bios.gbais omitted: it requires a real BIOS dump, which termboy doesn't use (HLE BIOS). The mGBA suite (MIT) is a local-only diagnostic (scripts/fetch-mgba-suite.sh), not CI-gated.
Commercial ROMs and save files are git-ignored and never committed; bring your
own and drop them in ./roms.
