Tabletop Forge
Kiosk image for the game table (Raspberry Pi Zero 2 W)
A lean, read-only Raspberry Pi OS image that shows the game table display in full screen right after booting. Everything is controlled from the game master app under Screens → Kiosk devices.
Suitable for Raspberry Pi Zero 2 W, 3, 4 and 5 (64-bit). The Zero 2 W outputs at most 1080p – enough for the game table, and the 1″ calibration calculates correctly by itself.
Two variants
| Buildroot (recommended for Pi Zero 2 W) | Raspberry Pi OS Lite | |
|---|---|---|
| Contents | only kernel, graphics driver, WPE WebKit/cog, NetworkManager, Python for the agent | complete Debian system, plus Chromium |
| Image | tabletop- | tabletop- |
| Start | read-only from the first boot, no initial setup | one-time initial setup with a reboot |
| Build | build- (WSL on D:, first run several hours) | build. (Docker, about 30 min) |
| Devices | Pi Zero 2 W only | Pi Zero 2 W, 3, 4, 5 |
Both use the same kiosk agent and the same kiosk. and are controlled the same way from the game master app.
The Buildroot variant only has cog as its browser.
How it works
| Area | Behaviour |
|---|---|
| Root file system | runs as an overlay in RAM – the SD card is never written during operation, a power cut is no problem |
| Boot partition | mounted read-only; holds kiosk. with the device settings |
| Changing settings | the kiosk agent briefly remounts the boot partition writable, saves kiosk. and immediately mounts it read-only again |
| Display | cog (WPE WebKit) directly via DRM/KMS in full screen, without a desktop – Chromium under cage as an alternative |
| Control | the agent checks in with the server every 15 s and applies changed settings and commands |
| Boot image | (Buildroot) a few seconds after power-on kiosk- writes the boot image straight into the frame buffer; start page, kiosk page and the table's loading screen look the same (dice in the middle, text facing all four sides of the table) – until the map or the setup instructions appear. Regenerate: DEJAVU_ |
| Boot order | (Buildroot) kiosk- creates browser settings and start page without network, the browser starts in parallel with Wi-Fi |
| System image | (Buildroot) root file system as SquashFS (zstd) – only about a third has to be read from the SD card; two system slots A/B for updates over the network |
| Graphics | (Buildroot) graphics driver built into the kernel, graphics chip at 400 instead of 300 MHz (config.), font cache already in the image |
| Watchdog | (Buildroot) if the system hangs, the Pi restarts by itself after 14 s |
| Power supply | the agent reports undervoltage and temperature – warning in the game master app at the device |
| Sign-in | (from kiosk software 1.7) own secret device key in kiosk. (deviceKey); if sign-in is set up on the server, a new device shows a QR code for approval once. After that the agent fetches a one-time sign-in for the browser on every start (/, via the local start page) |
What the device shows (campaign, game table or player display) is stored only on the server – nothing has to be written on the Pi for that.
Setup mode (own hotspot)
If the Pi cannot find its Wi-Fi or the server, it opens its own hotspot – the instructions appear large on the TV:
| Situation | Hotspot after |
|---|---|
| no Wi-Fi entered | about 20 seconds |
| Wi-Fi entered but not connected | 90 seconds |
| Wi-Fi connected, server not reachable | 3 minutes |
- Connect your phone to the Wi-Fi DnD-Kiosk-xxxx (the password is shown on the TV).
- The setup page opens automatically (otherwise open
http:/)./ 10. 42. 0. 1 - Choose the Wi-Fi from the list, enter password and server address, save.
As always, the settings are saved briefly writable → stored → read-only and reported to the server at the next contact. If nobody enters anything, the Pi tries again with the previous data after 5 minutes.
Backup of the settings
Every change is written to kiosk. and then kiosk. – if the power fails exactly while saving, at least
one file is always complete. If kiosk. is damaged, the agent loads the backup and repairs the main file
automatically.
Setup
- Flash the image: write
dist/to a microSD card with the Raspberry Pi Imager (“Use custom”) or balenaEtcher. Do not set any customisation (user/Wi-Fi) in the Imager –tabletop- forge- kiosk- - . img. xz kiosk.takes care of that.json - Adjust
kiosk.: the card shows up on the PC as the drive “bootfs”. Openjson kiosk.there and enter:json { "rev": 0, "serverUrl": "http://192.168.1.10:3000", "hostname": "dnd-table", "country": "DE", "wifi": [{ "ssid": "MY-WIFI", "psk": "MY-PASSWORD" }], "sshKeys": [] } - Start: card into the Pi, HDMI to the TV, power on. The first start takes a few minutes: grow the file system → enable read-only operation → automatic reboot.
- Assign: the device appears in the game master app under Screens → Kiosk devices. Choose campaign and display (game table / player display) there – the TV switches immediately.
Controllable from the game master app
- Name, campaign, display type
- Reload (page), restart browser, restart device
- Settings: server address, host name, Wi-Fi country and Wi-Fi networks. Passwords are only transmitted, never shown again; an empty password field keeps the stored one.
Monitor data (calibration per device)
Under Screens → Kiosk devices → Settings → Monitor you can store individual monitor data for each device:
resolution, aspect ratio, diagonal or width/height in mm, rotation, fine correction and the HDMI output mode. The
values are stored on the device in kiosk. (briefly rw → ro); the game table display of this device uses them to
calculate the 1″ squares.
- Without its own monitor data the global TV calibration applies.
- Show test pattern displays the 10-square line on exactly this device; enter the measured value → Calculate correction → save.
- A changed HDMI mode is written to
cmdline.and the device reboots once.txt - If the device checks in with a newly set up server, the server takes over the monitor data from the device.
- Preset while building:
"monitor": { … }inkiosk.(see the example file).local. json
Browser: cog or Chromium
The default is cog (WPE WebKit): a lean browser for embedded devices that draws directly on the screen without a compositor. On the Pi Zero 2 W (512 MB RAM) it starts much faster and needs only a fraction of Chromium's memory. Chromium is still in the image and can be switched on per device (Kiosk devices → Settings → Browser on the device).
Further savings in the image: graphics memory (CMA) 128 instead of 256 MB, Bluetooth and maintenance timers (apt, man-db, fstrim …) switched off, journal in RAM only, swap via zram only.
White or flickering picture? – Graphics acceleration
The graphics chip of the Pi Zero 2 W and Pi 3 (VideoCore IV) only supports OpenGL ES 2.0. Current Chromium often draws
just a white area with it. That is why graphics acceleration in the browser is automatically off there (software
rendering) and on for Pi 4/5. It can be changed in the game master app (Kiosk devices → Settings → Graphics
acceleration) or in kiosk.:
"browser": { "engine": "cog", "gpu": "auto", "flags": "" }
engine: cog · chromium. gpu: auto · on · off (with cog: accelerated canvas).
flags can hold additional switches (-) for the chosen browser.
Updates over the network (Buildroot)
The SD card has two system slots: kernel etc. in a/ and b/ on the boot partition, the root file system on
partition 2 and 3. Which slot boots is set in config. (os_). Process:
- The build creates
dist/next to the image – without private data.tabletop- forge- kiosk- . kup - Upload it in the game master app under Screens → Kiosk devices → Kiosk updates.
- At the device, install update: the agent downloads the package straight into the free slot, checks the
checksums and boots once on trial into the new system (
tryboot.).txt - If the new system reaches the server, it becomes permanent (
config.switched). If it does not start, hangs (watchdog) or does not check in within 10 minutes (txt kiosk-), the Pi automatically falls back to the previous system on restart – the game master app shows this as an error.trial- guard
kiosk. (Wi-Fi, server, monitor data) stays unchanged during updates. Devices with an older image (without
system slots) have to be flashed once more.
Hotspot password without a screen
When the Pi opens its setup hotspot, it also writes name and password to kiosk- on the SD card (drive
“bootfs” on the PC).
Troubleshooting without screen access
Because the system runs in RAM, logs are gone after a restart. For troubleshooting:
- Open the SD card on the PC (drive “bootfs”) and create an empty file
kiosk-there.debug. txt - Put the card back into the Pi, start it and let it run for a few minutes.
- Open the card on the PC again:
kiosk-contains a detailed log (services, network, memory, power supply, graphics, browser error messages). It is updated every minute – as always briefly writable → read-only.log. txt - To switch it off, delete
kiosk-again.debug. txt
Maintenance
- SSH is switched off. For maintenance enter public SSH keys in
sshKeys– then SSH starts with key login (userkiosk, no password login). sudo kiosk-makes the boot partition and the underlying root file system (rw /) writable,media/ root- ro sudo kiosk-switches back.ro - Permanent changes to the system:
sudo overlayroot-, or switch the overlay off withchroot sudo raspi-(then reboot).config nonint do_ overlayfs 1 - Buildroot variant: the overlay is set up by
/. The root file system is SquashFS and always read-only – changes to the system only via an update package. Theresbin/ overlay- init kiosk-only makes the boot partition writable.rw
Tests
python kiosk/tests/test_agent.py
python kiosk/tests/test_ota.py # update over the network (A/B, trial boot, fallback)
python kiosk/tests/test_monitor.py # needs a running server (npm start)
Checks backup/repair, the setup page (including captive portal redirect) and – if a server runs locally at
http:/ – the synchronisation of local changes.
Building the image yourself
Buildroot variant
cd kiosk
powershell -ExecutionPolicy Bypass -File .\build-buildroot.ps1
The build runs in its own WSL distribution dnd- under D:\WSL\dnd- (another location: environment
variable KIOSK_). If it is missing, the script creates it from the Docker image debian:trixie-. Sources,
intermediate results and compiler cache (around 30 GB) live there – not on C:. The first run compiles WebKit and takes
several hours, later runs only a few minutes.
| File | Purpose |
|---|---|
buildroot/ | package selection (Buildroot 2026.02 LTS) |
buildroot/, cmdline. | firmware and kernel parameters |
buildroot/ | overlay init, browser, zram and NetworkManager settings |
buildroot/ | enable services, fstab, permissions |
buildroot/, genimage. | SD card image incl. kiosk., system slots A/B, update package . |
buildroot/ | kernel: SquashFS, graphics driver built in (build. detects changes and rebuilds the kernel) |
Agent, start page and kiosk services come from files/ – shared with the Raspberry Pi OS variant.
Raspberry Pi OS variant
Needs Docker (on Windows: Docker Desktop).
Presetting Wi-Fi and server address
So that the Pi joins the Wi-Fi without further work, the build reads the file kiosk. (template:
kiosk.) and lays it over the defaults:
{
"serverUrl": "http://192.168.1.10:3000",
"hostname": "dnd-table",
"country": "DE",
"wifi": [{ "ssid": "MY-WIFI", "psk": "MY-WIFI-PASSWORD" }]
}
If the file is missing, build. or build. asks for the values (password hidden) and creates it.
kiosk. is in . and never checked in. Images with a preset Wi-Fi are called …- –
they contain the Wi-Fi password and should not be passed on.
cd kiosk
powershell -ExecutionPolicy Bypass -File .\build.ps1
cd kiosk && ./build.sh
The build downloads the current Raspberry Pi OS Lite (64-bit), enlarges it, installs cog, cage, Chromium,
overlayroot and zram- via ARM emulation, adds the files from files/ and writes the result to dist/.
Duration 20–60 minutes depending on the machine.
| Path | Contents |
|---|---|
files/ | template of the device settings |
files/ | kiosk agent (Python, standard library only) |
files/ | local start page (waits for the server) |
files/ | starts cog or Chromium under cage |
files/ | initial setup: overlay + read-only boot partition |
files/ | services kiosk-, kiosk-, kiosk-, kiosk- |
build/ | Docker build environment |
Passing on images (licenses)
The images contain free software under GPL, LGPL and other licenses (Linux kernel, BusyBox, WPE WebKit, cog …). Anyone who passes on or publishes a finished image must fulfil their obligations – above all offer the source code of the GPL components.
- Buildroot image:
build.automatically putssh dist/next to the image (Buildroottabletop- forge- kiosk- - legal- info. tar make legal-: license texts, sources and patches of all packages). Offer this archive together with the image. Switch it off for pure test builds:info KIOSK_.LEGAL_ INFO=0 - Raspberry Pi OS image: based on the official Raspberry Pi OS Lite; the sources of the included packages are provided by Raspberry Pi and Debian (https://archive.raspberrypi.com/debian/, https://sources.debian.org/). When passing it on, refer to these sources or offer them as well.
- Private data: never pass on images with a preset Wi-Fi (
kiosk., file name ends inlocal. json -).privat