A complete mini arcade system that runs on hardware, desktop, and in the browser: play a collection of classic-inspired games on a 64×64 RGB LED matrix (HUB75 + MicroPython), on your computer with a PyGame emulator, or directly in the browser via WebAssembly (pygbag).
- Triple Runtime Support
- MicroPython + HUB75 LED Matrix: Runs on RP2040-based boards (Interstate 75)
- Desktop (CPython) + PyGame: Full emulator for development and testing
- Browser (WebAssembly) + pygbag: Play directly in any modern browser, no install needed
- 50+ Built-in Games: Classics, puzzle games, racers, shooters, reflex challenges, and compact original arcade games built for the 64×64 matrix
- Intro Screen: Animated logo display on startup
- 64×64 Display Layout
- 58-pixel playfield (rows 0-57)
- 6-pixel HUD at bottom (score + clock)
- High Score System: Persistent scores with 3-letter initials entry
- Memory-Optimized: Buffered framebuffer, packed grid storage, lazy font loading
- Controller Support
- MicroPython: Wii Nunchuk-style I2C controller (with auto-detection for variants)
- Desktop: Keyboard emulation (arrow keys + Z/X)
- Quick Start
- Web build (Pygbag)
- Hardware Requirements
- Software Requirements
- Installation
- Make Commands
- Game List
- Controls
- Usage
- Architecture
- Troubleshooting
pip install pygame-ce # or: make install
python main.py # or: make runpip install pygame-ce pygbag==0.9.2 # or: make web-install
python -m pygbag . # build + serve at http://localhost:8000
# Safari: make web-safari (adds required COOP+COEP headers)Push to main — the GitHub Actions workflow builds the
WebAssembly bundle with python -m pygbag --build . (Python 3.11) and deploys it to GitHub Pages automatically.
Local preview (build + serve at http://localhost:8000):
pip install pygame-ce pygbag==0.9.2
python -m pygbag .CI / offline bundle (writes to build/web/):
python -m pygbag --build .GitHub Pages: in your repo go to Settings → Pages → Source and select GitHub Actions. Every push to main triggers the workflow which builds and deploys automatically.
- Interstate 75 (W) by Pimoroni (PIM584) - RP2040-based HUB75 driver board
- 64×64 Pixel RGB LED Matrix with HUB75 connector
- Controller (one of):
- Wii Nunchuk + Breakout Adapter
- Compatible I2C joystick module
- Power Supply: 5V adequate for LED matrix (typically 2-4A)
- Optional:
- MicroPython Firmware (Pimoroni build recommended)
- Optional: Thonny IDE for uploading files
- Optional:
mpy-crossfor compiling to bytecode (reduces boot RAM usage)
- Python 3.7+
- pygame-ce 2.x (Community Edition — drop-in replacement for pygame)
-
Install dependencies:
pip install pygame-ce
-
Run the game:
make run # or manually: python main.py
A 640×640 window will appear showing the emulated LED matrix (10× scale).
pygbag packages the game as WebAssembly so it runs in any modern browser — no Python installation needed.
-
Install dependencies:
pip install pygame-ce pygbag==0.9.2 # or via make: make web-install -
Build and serve locally:
python -m pygbag . # build + serve at http://localhost:8000 make web # same, via Makefile (Chrome / Firefox) make web-safari # Safari (adds required COOP+COEP headers) make web-ios # iPhone/iPad fullscreen-oriented variant make web-ios-safari # iOS/Safari variant with COOP+COEP headers
-
Controls in browser: same keyboard mapping as desktop — Arrow Keys,
Z/Spaceto confirm,X/Escapeto cancel. On touch devices the browser build exposes an on-screen D-pad plusA/Baction buttons; tapping the game canvas triggers the primary action, and swiping the canvas sends directional taps.
Browser support: Chrome and Firefox work out of the box. Safari requires
Cross-Origin-Isolationheaders (make web-safarihandles this automatically).iOS fullscreen: the iOS build keeps the game logic at the original 64×64 matrix resolution and scales the canvas fullscreen with safe-area-aware touch controls. This avoids rewriting every game for a new coordinate system while still filling the phone or tablet screen. On GitHub Pages it is published under
/ios/; locally usemake web-ios-safarifor the closest Safari/iOS behavior.Automated deployment: every push to
maintriggers the GitHub Actions workflow which builds the regular WebAssembly bundle plus the/ios/variant and deploys both to GitHub Pages automatically.Note: High scores are stored in-memory while the page is open and reset on page reload.
The project now uses a tiny bootstrap approach to avoid on-device compilation memory errors:
-
Install
mpy-cross(optional but highly recommended):brew install micropython # macOS # or: pip install mpy-cross
-
Connect your Interstate 75 via USB
-
Upload with Make:
make upload # or manually: ./upload.shThe script will:
- Auto-detect connected devices
- Compile
arcade_app.py→arcade_app.mpy(ifmpy-crossavailable) - Upload both
main.py(tiny bootstrap) and the compiled module
-
Reboot the device - games start automatically
If you prefer manual upload via Thonny or ampy:
- Flash MicroPython firmware to Interstate 75
- Upload
main.py(tiny bootstrap file) - Upload
arcade_app.pyorarcade_app.mpy(the main application) - Optional: Upload
highscores.jsonif you want to preserve scores
For ease of use, a Makefile is provided with the following commands:
make install: Installs desktop dependencies (PyGame)make run: Runs the PyGame emulator locally (python main.py)make web-install: Installs pygbag for browser/WebAssembly buildsmake web-build: Builds the regular browser version intobuild/web/make web-ios-build: Builds the fullscreen-oriented iOS version intobuild/ios/make web-pages-build: Builds the Pages artifact with the regular version at/and the iOS version at/ios/make web: Builds the WebAssembly version and serves it athttp://localhost:8000make web-ios: Builds and serves the iOS fullscreen-oriented version athttp://localhost:8000make web-ios-safari: Serves the iOS build with COOP+COEP headers for Safarimake upload: Compiles and uploads the code to the hardware (./upload.sh)make build: Precompilesarcade_app.pyinto bytecode (arcade_app.mpy)make clean: Cleans up previous build artifacts and pycachemake clean-all: Cleans up all files and the python virtual environment
The configuration block at the very top of arcade_app.py
can hide content for a smaller or curated build. Add menu IDs to
CONFIG_DISABLED_GAMES and effect IDs to CONFIG_DISABLED_DEMOS; leave the
lists empty to show everything. CPU game previews in DEMOS use G:NAME, for
example "G:DOOMLT".
CONFIG_DISABLED_GAMES = ("DOOMLT", "RAYRCR")
CONFIG_DISABLED_DEMOS = ("MANDEL", "G:DOOMLT")The arcade includes demo animations and over 50 games. Each game is documented in detail in the Game Documentation with gameplay notes and technical descriptions.
Detailed per-game documentation is available in docs/games.
| Game ID | Name | Description |
|---|---|---|
DEMOS |
Demo Showcase | Zero-player demos: Snake, Life, Cube, Spark, Plasma, Orbit, Warp, Bounce, Tunnel, Matrix, Fire, Spring, Cradle |
2048 |
2048 | Sliding tile puzzle with merge scoring |
AIRHKY |
Air Hockey | Fast puck-and-mallet game with CPU or 2-player support |
ARENA |
Arena | Top-down wave survival with movement and shooting |
ARTILL |
Artillery Simulator | Turn-based angle-and-power shell duel with wind and deforming terrain |
ASTRD |
Asteroids | Rotate, thrust, shoot asteroids in space |
BEJWL |
Bejeweled | Match-3 gem swapping puzzle |
BILLI |
Billiards | Pool/Snooker-style table game with cue aim, pockets, rails, and ball collisions |
BOMBER |
Bomber | Timed bombs, block clearing, and maze enemies |
BRKOUT |
Breakout | Brick breaker with rainbow bricks and optional powerups |
BTLZON |
Battlezone | Atari-style vector tank combat with radar, rocks, projectile shots, and waves |
CATCH |
Catch | Catch stars, avoid bombs, and keep the basket moving |
CAVEFL |
Cave Flyer | Tunnel navigation (starts wide, narrows progressively) |
CENTI |
Centipede | Atari-style segmented shooter with mushrooms and waves |
CGOLG |
Conway's Game of Life Game | Competitive Life battle with directed gliders and spaceships |
CITY |
City Chase | Top-down city driving with jobs, traffic, police heat, and drop-offs |
CLIMB |
Climber | Platform-jumping tower climb with scrolling height |
DEFUSE |
Defuse | Cut colored wires in sequence before the timer expires |
DIGDUG |
Dig Dug | Dig tunnels, collect gems, and pump burrowing enemies |
DODGE |
Dodge | Avoid falling blocks, dash to dodge |
DOOMLT |
Doom Lite | Target-aware raycaster FPS with textured walls, enemy archetypes, and Quad burst fire; also powers the WINMAZE demo renderer |
FLAPPY |
Flappy Bird | Navigate through moving pipe gaps |
FROGGR |
Frogger | Hop across traffic lanes and advance through harder levels |
GOLF |
Golf | Tiny minigolf courses with aim, power, bounces, and obstacles |
INVADR |
Invaders | Shoot marching alien waves, protect shields, hit saucers |
JOUST |
Joust | Flap between platforms and defeat riders from above |
KEEN |
Keen | Platformer with jumps, gems, keys, enemies, and exit doors |
KERBAL |
Kerbal Arcade | Launch, circularize, and optionally return in a tiny orbital flight sim |
LANDER |
Lunar Lander | Multi-level landing challenge plus optional scrolling v2 route mode with fuel powerups |
LASER |
Laser | Mirror-rotation puzzle: guide the beam into the target |
LIGHTS |
Lights Out | Toggle a light and its neighbors to clear the five-by-five grid |
LOCO |
LocoMotion | Rotating railway puzzle with train routing |
MAZE |
Maze Explorer | Fog-of-war maze with gems, enemies, shooting |
MINES |
Mines | Minesweeper-style reveal puzzle for the LED matrix |
PACMAN |
Pac-Man | Collect pellets, avoid ghosts, power pellets |
PAIRS |
Pairs | Memory card matching on a 4x4 board |
PICROS |
Picross | Solve five-by-five picture logic puzzles from row and column clues |
PINBAL |
Pinball | Plunger launch, flippers, bumpers, targets, and multipliers |
PITFAL |
Pitfall Runner | Endless runner with snakes, pits, treasures (safe start zone) |
PONG |
Pong | Paddle vs. CPU or optional 2-player paddle duel |
QIX |
Qix | Territory capture, avoid the enemy |
RACING |
Top-Down Racing | Overhead circuit racer with curved road, boost, laps, and traffic |
RAYRCR |
Ray Racer | Raytrace-style anti-grav racing with boost, energy gates, and rival hovercars |
REACT |
Reaction Grid | Hit green pads before time expires and avoid red decoys |
REVRS |
Othello/Reversi | Board game with simple CPU opponent |
RTYPE |
R-Type Shooter | Side-scrolling endless shooter |
SABOTR |
Saboteur Stealth | Sneak through multiple patrol maps and reach the objective |
SIMON |
Simon Says | Memory sequence game with colored quadrants |
SLALOM |
Slalom | Carve through downhill gates; tuck for more speed and double points |
SKYWAR |
Sky War | Helicopter battlefield shooter with air and ground targets |
SNAKE |
Snake | Classic snake with red/green targets, wraparound |
SOCCER |
Championship Soccer | Atari-style soccer with direction-based passes and shots |
SOKO |
Sokoban | Multi-level crate-pushing puzzle campaign |
STACK |
Stacker | Timing game: trim and stack moving blocks |
TETRIS |
Tetris | Falling blocks with line clearing |
TRON |
Tron Lightcycle | Leave a trail, steer 90° turns, dodge CPU or optional second player |
TWRDEF |
Tower Defense | Build towers across rotating road and open-field layouts |
UFODEF |
UFO Defense | Missile Command-style defense with turret/base and wave/time settings |
WINGS |
Wings | Carrier strike game with fuel, ammo, targets, and landing |
WORMS |
Worms Mini | Turn-based team artillery with destructible terrain and CPU/2-player settings |
Each game tracks high scores with optional initials entry.
Menu Navigation:
- Up/Down: Navigate menu
- Z (or Space/Enter): Start/Confirm
- C (or X/Escape): Open game options when available; otherwise Back/Cancel
In-Game:
- Directional movement: Arrow keys / Joystick
- Primary action (jump/shoot/rotate): Z button / Space / Enter
- Secondary/Back: C button / X / Escape
| Action | Keys |
|---|---|
| Move | Arrow Keys |
| Confirm/Action | Z, Space, or Enter |
| Back/Cancel | X or Escape |
| Player 1 Move in 2P | W, A, S, D |
| Player 1 Action in 2P | Shift where used |
| Player 2 Move in 2P | Arrow Keys |
| Player 2 Action in 2P | Z, Space, or Enter where used |
AIRHKY:PLAYRswitches between1Pand2P;GOALSsets the match length.BILLI:RULEswitches betweenPOOLandSNOOK;AIMchanges the aim guide length.PONG:PLAYRswitches between1Pand2P.TRON:PLAYRswitches between CPU opponent and2P.CITY:JOBSsets mission count;TRAFtoggles civilian traffic.KERBAL:MISNswitches between orbit-only and return mission;ASSTtoggles arcade flight assist.LANDER:MODEswitches between classicV1and scrollingV2with route pads and fuel pickups.RACING:LAPSsets race length;TRAFtoggles traffic cars.WORMS:PLAYRswitches between CPU opponent and2P;TEAMsets two or three worms per side.
- I2C Address: 0x52 (standard Nunchuk)
- Pins: SCL=21, SDA=20 (configurable in code)
- Buttons:
- C button: Back/Cancel
- Z button: Confirm/Action
- Analog stick: 8-directional movement (includes diagonals)
The code auto-detects controller variants including the "new signature" controllers (A0 20 10 00 FF FF).
Start the app, select a game with Up/Down, and press Z to launch it. If a game
has settings, press C on the game list to open the shared settings menu before
starting the game.
High scores are stored per game ID. The shared game-over menu offers retry, high-score view, and return-to-menu actions.
Developer notes are documented in docs/ARCHITECTURE.md. Per-game gameplay and implementation notes are documented in docs/games/README.md.
The short version:
GameSelect.GAME_REGISTRYcontrols which games appear in the selector.GameSettings.DEFINITIONSdeclares per-game settings for the shared settings menu.- Games that accept a constructor context read settings with
get_context_setting(). - Callback-based games inherit
FrameLoopGamefor portable sync/async frame pacing. - Grid games can inherit
GridCursorGamefor shared, debounced cursor movement. - Games report final results through
set_game_over_score()so the shared lost/won and high-score flow can handle them.
- If the browser build appears blank, rebuild with
python -m pygbag .and check the console for import or asset errors. - If settings do not appear for a game, confirm the game ID in
GameSettings.DEFINITIONSexactly matches the ID inGameSelect.GAME_REGISTRY. - If a game class exists but is missing from the menu, add it to
GameSelect.GAME_REGISTRY. - If desktop input behaves differently from the cabinet, check the keyboard mapping
in the Controls section and the joystick/button helper functions in
arcade_app.py.
