Skip to content

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 Game binary.

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

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.

  1. In your browser, open https://www.makecode.games/compilers/desktop.
  2. Upload your MakeCode Arcade PNG export.
  3. Choose the architecture that matches your cabinet:
    • arm64 for a Raspberry Pi / ARM arcade cabinet.
    • x86-64 for a desktop Linux PC.
  4. Download the resulting .tar.gz archive (for example SafeName-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.

  1. On your regular computer, download Raspberry Pi Imager from https://www.raspberrypi.com/software/.
  2. Put the microSD card into your computer.
  3. Open Raspberry Pi Imager.
  4. Under Choose OS, pick Raspberry Pi OS (Other) → Raspberry Pi OS Lite (64-bit).
  5. Under Choose Storage, pick your microSD card.
  6. 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

  1. Boot the machine and log in (Pi defaults: pi / raspberry).
  2. Connect to the internet. On a Pi:

    sudo raspi-config
    

    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.

  3. Check the connection:

    ping -c 3 google.com
    

    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).
Pin 8  GPIO 14  -> one switch wire
Pin 6  GND      -> other switch wire

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.

  1. Re-run the installer with --gpio:

    sudo bash install/single-native-arcade-setup.sh --game=MyGame --gpio
    

    What this does

    Copies install/arcade.cfg to /etc/arcade.cfg — the file the Game binary reads at startup to learn which GPIO pin is which button. Any existing /etc/arcade.cfg is backed up first.

  2. 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:

    Raspberry Pi 40-pin GPIO header — arcade button wiring
    The Player 1 pins are blue; players 2–4 are green, orange, and purple. Cabinet controls (menu, exit, reset) are red.

  3. For players 2–4, uncomment the BTN_UP2-style lines in /etc/arcade.cfg and wire those pins too.

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:

Recommended button wiring: a 10k pull-up resistor to 3.3V plus a 1k series resistor

Each button gets a 10kΩ pull-up to 3.3V and a 1kΩ resistor in series with the switch.
  • 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:

  1. 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.gz at the drive root (or arcade-game/ if newer) and extracts it, checks it contains Game and libpxt.so, names the game after the file (a name.txt inside the archive overrides it), and installs it into games/. Other installed games are removed — only the new game and ControllerTest (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 an arcade.cfg for GPIO buttons:

    arcade-game/
      Game         # the native binary (arm64 for a Pi, x86-64 for a PC)
      libpxt.so
      name.txt     # optional — the name for the games/ folder on the cabinet
      arcade.cfg   # optional — copied to /etc/arcade.cfg (GPIO button map)
    
  2. Plug the stick into the arcade.

  3. 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

  1. Add a new games/<NewName>/ folder (Step 5) with the new Game and libpxt.so.
  2. Re-run the installer pointed at the new game:

    cd /home/pi/CreationStationArcade
    sudo bash install/single-native-arcade-setup.sh --game=NewName
    sudo reboot
    

    What this does

    Updates the auto-launch settings to boot into the new game, then reboots into it.

Putting a new game on the arcade

  1. Build the native binary with the PNG to Desktop compiler (Step 1), choosing the right architecture (arm64 for a Pi, x86-64 for a PC).
  2. Either drop the .tar.gz onto a USB stick and plug it into the cabinet (Option A above — no reboot needed), or copy it onto the Pi and extract it into games/<Name>/ (Step 5).
  3. 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:

ls -l /home/pi/CreationStationArcade/games/MyGame/

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:

sudo /home/pi/CreationStationArcade/install/hdmi-audio-fix.sh
sudo 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:

bash /home/pi/CreationStationArcade/toggle-arcade.sh disable

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:

sudo pkill -9 -f "games/[^/]+/Game"
sudo pkill -9 -f launcher.sh

What this does

Force-stops any running Game process and the launcher loop. Then see notes.md for restoring the launcher shell.