software project ·

PICO-8 on Rabbit R1

A packager that turns any PICO-8 cart into a Rabbit R1 Creation, with touch controls mapped to the R1's scroll wheel and side button.

PICO-8Rabbit R1PythonJavaScript

pico8-r1 packages a PICO-8 cartridge as a Rabbit R1 “Creation”: a folder of static web files, plus the QR code you scan on the R1 to install it.

The Rabbit R1 is a cute little orange brick, but honestly not a very useful one. It has a 240-pixel-wide touch screen, a scroll wheel and a side button. Creations are the web-based mini-apps you can make for it, running in its built-in WebView. PICO-8 can export any game as an HTML + JavaScript web player, so the two looked like a good match: a tiny fantasy console on a tiny device.

The Creation is just a repackaging of PICO-8’s own web export, with the player itself left unchanged. It installs on a stock R1, with no custom firmware or rooting. In the games I tested, it runs at a steady ~60 fps.

One command does the whole job:

python3 pico8_to_r1.py cart.p8 --title "My Game" --base-url https://example.com/games/mygame/

The cart can be a .p8 source file or a .p8.png as distributed on the PICO-8 BBS. You then upload the contents of dist/ to that HTTPS URL, and scan dist/qr.png with the R1’s Creations scanner.

SMBP8 fan game running on a Rabbit R1
SMBP8 by JadeLombax, running on the R1 at 59 fps (with the --fps readout on).

How it works

The packager is a single Python script, using only the standard library:

  • It runs your own licensed copy of PICO-8 headless (pico8 -export) with a custom HTML template, so the exported player comes out already fitted for the R1 screen.
  • It cuts the R1 Creation icon from the cart’s label.
  • It writes the Creation manifest (title, URL, description, icon, theme color) and encodes it as the install QR code.

Pillow and qrcode are optional: without them, the script skips the icon and the QR code.

Controls

The custom template adds touch controls and maps the R1 hardware to PICO-8 buttons:

  • touch d-pad and O/X buttons, with multi-touch
  • scroll wheel = up/down (or left/right in landscape)
  • side button = O
  • hold the side button = pause menu

Under the hood, the controls write directly into pico8_buttons[0], the bitfield the PICO-8 web player reads for btn() and btnp(). I first tried synthesized keyboard events, but they did not register, and the bitfield turned out to be simpler and more reliable.

Two layouts are available: by default the controls sit in their own strip under the game, and --layout overlay draws them over the game for a bigger picture.

Default layout, controls under the game Overlay layout, controls over the game
Default layout (left) and --layout overlay (right).

There is also a landscape mode (--orientation landscape), for holding the R1 sideways with the wheel at the bottom: the page is rotated a quarter turn, with the d-pad in the bottom-left corner and O/X in the bottom-right, over a full-height game.

Scopa! in landscape mode on the R1
Scopa! by Damobmafia, in landscape with the overlay layout.

Challenges

The R1’s input events are not really documented, so I wrote a small probe page (tools/probe.html) that logs every event it receives, hosted it, and installed it on the R1 like any other Creation. That gave me the real names: scrollUp / scrollDown for the wheel, sideClick for the side button, and longPressStart / longPressEnd for a hold.

If the wheel scrolls in the opposite direction to what you expect for a particular game, you can add --invert-wheel to swap the direction.

Everything verified on the real device is written down in docs/r1-findings.md.

After playing with a few games, I found it hard to be accurate with the tiny touch controls, so this works best for slow, casual games (card games, puzzles, etc.). A physical keyboard would probably help with action games and platformers.

The other constraint is distribution: as far as I know, Creations cannot be side-loaded, so the files have to be served over HTTPS from somewhere the R1 can reach, and the QR code has to point to that URL.

Reception and what’s next

I first shared it on r/pico8 as a screenshot only, with the script still on my machine, saying I would publish it under the MIT license if there was interest. There was, so I cleaned up the README, added the landscape mode, and put it on GitHub. It is still very alpha.

My favorite comment summed up the R1 pretty well: “That’s the best use of that hardware ever. Still a terrible product. But you made it better.”

A possible next step is a game designed around the R1’s scroll wheel: it is a fairly unique controller, and PICO-8 games currently only see it as up/down.

Tech Stack

  • Python 3.8+ (standard library), with optional Pillow and qrcode
  • PICO-8 0.2.x headless web export, with a custom HTML template
  • Vanilla JavaScript for the touch, wheel and side button input bridge
  • Rabbit R1 Creations (WebView, JSON manifest in a QR code)

Only tested on macOS, but since it is plain Python, it should work wherever the PICO-8 app works.

License

pico8-r1 is open source, under the MIT license. PICO-8 itself is a commercial product by Lexaloffle; you need your own licensed copy to export games.