Native SDL Single-Game¶
What this is / who it's for¶
This is the fastest single-game flavor. Instead of running your game inside a web browser (like the Chromium flavors), it runs a real, compiled Game program directly using a graphics library called SDL. No browser, no menu — the cabinet boots straight into one game and runs it as fast as the hardware can.
It's the best choice if you:
- Have a 64-bit Raspberry Pi (Pi 3, Pi 4, Pi 5, or Pi Zero 2 W) or a 64-bit PC (x86-64).
- Want a dedicated cabinet for one game.
- Want the best performance (no browser overhead).
- Are getting your game from the PNG to Desktop compiler, which builds the native
Gamebinary.
Needs a 64-bit operating system
The Game binary is a 64-bit (aarch64) program. The original Pi Zero and Pi 1 are not supported because they're 32-bit only. Use a 64-bit OS on a Pi 3/4/5/Zero 2 W, or a 64-bit PC.
Want a menu, or a browser-based game?
This flavor has no menu and no browser. For a menu of games on a Pi 5/PC, use Chromium Kiosk (Menu). For a single game in a browser, use Chromium Single-Game.
What you'll need¶
Hardware
- A 64-bit Raspberry Pi (3, 4, 5, or Zero 2 W) or a 64-bit PC (x86-64, Debian/Ubuntu-based).
- A microSD card (Pi) or hard drive/SSD (PC), power supply, HDMI screen.
- A USB gamepad or USB zero-delay encoder. (This flavor reads USB joysticks directly — no Python input bridge needed.)
- Optional: a momentary push button and two wires for the GPIO reset button (Raspberry Pi only — see Step 7).
- Optional: arcade buttons/joysticks wired to the GPIO pins instead of USB (Raspberry Pi or similar board — see Step 8).
- A way to put the SD card into your regular computer (Pi only).
- An internet connection.
On your regular computer
- A web browser, to use the PNG to Desktop compiler that builds the native
Gamebinary.
The walkthrough¶
Step 1 — Get your game binary from the desktop compiler¶
This flavor runs a native Game binary that you build with the PNG to Desktop compiler, not a raw .elf or a .js file.
- In your browser, open https://www.makecode.games/compilers/desktop.
- Upload your MakeCode Arcade PNG export.
- Choose the architecture that matches your cabinet:
- arm64 for a Raspberry Pi / ARM arcade cabinet.
- x86-64 for a desktop Linux PC.
- Download the resulting
.tar.gzarchive (for exampleSafeName-arm64.tar.gz).
What this does
Builds a compiled Game program for your target machine and packages it (with its helper library libpxt.so) into a downloadable archive.
Step 2 — Flash the operating system¶
For a Raspberry Pi, use the 64-bit Lite version of Raspberry Pi OS.
- On your regular computer, download Raspberry Pi Imager from https://www.raspberrypi.com/software/.
- Put the microSD card into your computer.
- Open Raspberry Pi Imager.
- Under Choose OS, pick Raspberry Pi OS (Other) → Raspberry Pi OS Lite (64-bit).
- Under Choose Storage, pick your microSD card.
- Click Write and wait for it to finish.
What this does
Copies a fresh, minimal 64-bit operating system onto the SD card. 64-bit is required — the Game binary won't run on a 32-bit OS.
For a regular PC, install a 64-bit Debian or Ubuntu server.
Step 3 — Boot and get online¶
- Boot the machine and log in (Pi defaults:
pi/raspberry). -
Connect to the internet. On a Pi:
What this does
Opens the Pi's settings menu. Go to System Options → Wireless LAN, pick your country, type your Wi-Fi name and password, then finish.
-
Check the connection:
What this does
Sends three test messages to the internet. Replies mean you're online.
Step 4 — Download the arcade project¶
sudo apt update && sudo apt install -y git
git clone -b single-native-arcade https://github.com/kikketer/CreationStationArcade /home/pi/CreationStationArcade
What this does
Updates the package list, installs git, then copies the Creation Station Arcade project (the native single-game branch) into /home/pi/CreationStationArcade. This flavor runs directly from that folder — there's no separate "runtime" folder like the other flavors.
Step 5 — Add your game to the project¶
Put the archive you downloaded in Step 1 onto the Pi (over the network, or by moving the SD card), then extract it into the games/ folder:
cd /home/pi/CreationStationArcade
mkdir -p games/MyGame
tar xzf MyGame-arm64.tar.gz -C games/MyGame
chmod +x games/MyGame/Game
What this does
Creates a folder for your game inside games/, extracts the archive into it (the archive contains Game and libpxt.so — both must stay in that folder together), and marks Game as runnable. Replace MyGame and the archive name with your actual file names.
Keep Game and libpxt.so together
The Game program needs libpxt.so sitting right next to it in the same folder. Don't move one without the other.
Step 6 — Run the one-shot setup script¶
This flavor has a single installer that does almost everything: installs the SDL and graphics libraries, adds your user to the right groups, enables the Pi's graphics overlay (if you're on a Pi), sets up auto-login, writes the auto-launch settings, and marks everything runnable.
cd /home/pi/CreationStationArcade
sudo bash install/single-native-arcade-setup.sh --game=MyGame
sudo reboot
What this does
The all-in-one installer for this flavor. Replace MyGame with the folder name you created in Step 5 (the name of the folder under games/, not the archive). The script installs the libraries the Game binary needs, enables the Pi's KMS graphics overlay, sets up auto-login so the cabinet boots straight into the game, and writes the launch settings. The reboot starts it.
No --game argument?
If you leave off --game, the installer picks the first valid game it finds under games/ (a folder containing both Game and libpxt.so). That's handy if you only have one game.
What the installer sets up behind the scenes
On a Raspberry Pi it enables the dtoverlay=vc4-kms-v3d,cma-128 line in your boot config so the Pi has a graphics device for SDL to use. It also sets the runtime environment variables SDL_VIDEODRIVER=kmsdrm and SDL_RENDER_DRIVER=opengles2 (on ARM64) so the Game binary draws to the screen correctly. You don't have to do any of this by hand.
Step 7 — (Optional) Wire the GPIO reset button¶
This flavor supports a dedicated cabinet reset button. When you press it, it triggers the game's own soft reset (the same as pressing the r key) — the Game process never exits, it just restarts inside itself.
Wiring (Raspberry Pi only):
- Connect one side of a momentary normally-open push button to GPIO 14 (physical pin 8).
- Connect the other side to GND (physical pin 6, two pins away from pin 8).
Because the button pulls the pin to ground, the launcher runs the reset helper in "active-low" mode by default. To use a different GPIO pin, set GPIO_RESET_PIN before rebooting. To use an active-high (3.3 V) button instead, set GPIO_RESET_ACTIVE_HIGH=1.
What this does
The reset button wires to GPIO 27 and ground. The launcher's reset helper watches that pin and, when it's pressed, injects an r keypress into the running Game, which the game turns into a soft reset. No external kill script runs — the game handles the reset itself.
Step 8 — (Optional) Wire real arcade buttons to the GPIO pins¶
If you're building a cabinet with real buttons and joysticks wired straight to the Pi's GPIO pins, the Game binary can read them directly — no USB encoder board needed. It uses the kernel's edge-triggered GPIO events, so there's no polling and no input lag to speak of.
-
Re-run the installer with
--gpio:What this does
Copies
install/arcade.cfgto/etc/arcade.cfg— the file theGamebinary reads at startup to learn which GPIO pin is which button. Any existing/etc/arcade.cfgis backed up first. -
Wire your buttons to the pins listed in
/etc/arcade.cfg— one wire of each button to the GPIO pin, the other to GND. The default map covers Player 1 plus menu/reset/exit, and matches the pin layout used by the ELF arcade flavors (so the same cabinet wiring works on both). Here's the full 40-pin map — the numbers on each row are the physical pin numbers on the header, not GPIO numbers:The Player 1 pins are blue; players 2–4 are green, orange, and purple. Cabinet controls (menu, exit, reset) are red. -
For players 2–4, uncomment the
BTN_UP2-style lines in/etc/arcade.cfgand wire those pins too.
Recommended wiring (keeps buttons reliable)¶
The simplest hookup — one wire from the pin to the button, the other side of the button to ground — works, because the Pi has a weak built-in pull-up on each pin. But a bare wire running a few inches acts like an antenna, and on long runs you'll see "ghost" presses and buttons that feel sluggish or sticky. For a cabinet you'll actually play on, wire each button like this instead:
- The 10kΩ pull-up goes from the GPIO pin to a 3.3V pin (physical pin 1 or 17 — one 3.3V pin can feed all your buttons). It holds the pin firmly at 3.3 volts until the button mashes it to ground, which kills the antenna noise.
- The 1kΩ series resistor sits inline on the signal wire between the pin and the button. It's optional insurance — it protects the pin if it's ever accidentally configured as an output.
- Your grounds stay the same: the other side of every button goes to any ground pin (they're all connected inside the Pi anyway, so sharing is fine).
No resistors on hand?
Don't let it stop you — wire the buttons straight to the pins and GND and everything still works. The software filters a lot of the noise on its own. Add the pull-ups later if a button ever feels flaky; keep the signal wires short or twisted with their ground return in the meantime.
What this does
The Game binary checks for /etc/arcade.cfg when it starts. If the file exists, each BTN_* pin becomes that button for that player — pressed edges go straight into the game's input, same as a keyboard press. If the file doesn't exist (on a regular PC, for example), GPIO input is simply off and keyboard/joystick work as normal. To keep the config somewhere else, set ARCADE_GPIO_CFG to a different path.
Needs a current build
GPIO input requires a Game binary built by the PNG to Desktop compiler after this feature landed. If your Game was compiled earlier, re-upload the PNG and rebuild it — older binaries just ignore /etc/arcade.cfg.
Step 9 — Play¶
After the reboot, the cabinet boots straight into ./Game -f (fullscreen) inside your games/MyGame/ folder. Plug in your USB gamepad — or use your GPIO buttons — and play. The reset button (if you wired one) restarts the game.
Changing the game later¶
This flavor has no menu. There are two ways to switch games:
Option A — USB stick (no keyboard, no SSH, no internet needed)
Once the installer has run, the cabinet watches for USB drives. To swap games:
-
Put the compiler's
.tar.gz(Step 1) at the root of a USB stick — the exact file you downloaded, no extraction needed (e.g.MyGame-arm64.tar.gz).In Chrome or Edge you can skip the copying entirely: check "Write to a USB cartridge" on the compiler page and after it builds, pick the stick's root folder — the archive is written straight onto it.
What this does
The cabinet picks the most recently modified
.tar.gzat the drive root (orarcade-game/if newer) and extracts it, checks it containsGameandlibpxt.so, names the game after the file (aname.txtinside the archive overrides it), and installs it intogames/. Other installed games are removed — only the new game andControllerTest(the built-in sanity check) are kept.Alternatively, an extracted
arcade-game/folder at the drive root works too — handy if you want to add anarcade.cfgfor GPIO buttons: -
Plug the stick into the arcade.
- Wait a few seconds — the cabinet checks the stick, installs the game, and swaps to it without rebooting. The screen may flicker as the old game exits and the new one starts (about 10–30 seconds total).
What this does
A udev rule on the cabinet starts a small install service whenever a USB filesystem is plugged in. It records the game's name in the active-game file and stops the running Game — the launcher notices and starts the new one. Drives without a game are ignored, a Game built for the wrong architecture is refused, and a stick carrying the same game that's already installed is skipped — so a random or bad stick can't break the cabinet, and leaving the stick in during a reboot won't loop.
Updating a game from the stick
Rebuild the .tar.gz, put it on the stick, and plug it back in — the cabinet compares the file's modified time against what it already installed and only swaps if the stick is newer. The same file again is ignored.
Didn't work?
Check the log on the cabinet (arcade.log, or journalctl -u 'arcade-usb-update@*' if you have a shell). Common misses: the tarball nested inside a folder instead of at the drive root, or a Game built for the wrong architecture (Step 1).
Option B — re-run the installer
- Add a new
games/<NewName>/folder (Step 5) with the newGameandlibpxt.so. -
Re-run the installer pointed at the new game:
cd /home/pi/CreationStationArcade sudo bash install/single-native-arcade-setup.sh --game=NewName sudo rebootWhat this does
Updates the auto-launch settings to boot into the new game, then reboots into it.
Putting a new game on the arcade¶
- Build the native binary with the PNG to Desktop compiler (Step 1), choosing the right architecture (
arm64for a Pi,x86-64for a PC). - Either drop the
.tar.gzonto a USB stick and plug it into the cabinet (Option A above — no reboot needed), or copy it onto the Pi and extract it intogames/<Name>/(Step 5). - If you installed manually, re-run the installer with
--game=<Name>and reboot (above).
Deleting a game
Remove a games/<Name>/ folder (and reboot, or let the current game exit) and the cabinet falls back to ControllerTest — which then becomes the recorded active game until a new one arrives.
You can also save the new game folder to the project and upload it through git to keep the source of truth in the repo.
If something goes wrong¶
The screen is black with a cursor, or "SDL Error: Invalid window"¶
This means the graphics setup is incomplete. The Game binary needs the OpenGL ES renderer explicitly. The installer sets SDL_RENDER_DRIVER=opengles2 for you, but if it's missing you'll see a black window. Make sure you ran the installer (it sets this), and on a Pi make sure the vc4-kms-v3d,cma-128 overlay is enabled in your boot config (the installer does this too). Re-run the installer to rewrite both.
Why this happens
Without SDL_RENDER_DRIVER=opengles2 the window opens but stays black. Without the EGL/GLES libraries (libegl1, libgles2) the window can fail to create at all. The installer installs all of these, so re-running it fixes both.
"no native game found" during setup¶
The installer looks for a folder under games/ that contains both Game and libpxt.so. If it can't find one, check:
What this does
Lists the files in your game folder. You should see both Game and libpxt.so. If Game isn't marked executable, run chmod +x games/MyGame/Game. If a file is missing, re-extract the archive (Step 5).
I'm on a 32-bit Pi (original Pi Zero / Pi 1)¶
The Game binary is 64-bit only (aarch64). It will not run on a 32-bit OS. Install a 64-bit OS, or pick a different flavor (the Pi 3 / Pi Zero Single-Game ELF works on the original Pi Zero).
No HDMI audio¶
This flavor includes the HDMI audio fix. Run it and reboot:
What this does
Copies in corrected HDMI audio settings and backs up your old ones. Reboot so they load.
I need to get back to a normal prompt¶
The auto-launch runs on TTY1. To disable it for debugging, use the toggle script that comes with this branch:
What this does
Turns off the auto-launch so the next time you log in on TTY1 you get a normal shell instead of the game. Re-enable it with toggle-arcade.sh enable when you're done. See the project's notes.md for more.
I need to kill a stuck game from another session¶
Log in as an admin user (or over SSH) and run:
What this does
Force-stops any running Game process and the launcher loop. Then see notes.md for restoring the launcher shell.