How-to
Test without hardware
Render your app's examples, see the cards on a full screen, run unit tests, try the board's image, and run a job straight from your editor.
You can build and check a slowapp completely on your own computer. The commands below assume the tools from the quickstart: the SDK in /tmp/slowapp and a Python with Pillow in ~/.slowapp-venv.
Render the examples
docker build -t slowapp-myapp:dev .
PYTHONPATH=/tmp/slowapp ~/.slowapp-venv/bin/python -m slowapp_sdk fixture --image slowapp-myapp:dev --repeat
This is a test render (the files call it fixture mode). For each [[example]] in your manifest, it runs your image the way a hub would, with these differences:
SLOWAPP_MODEisfixture.- There is no network, whatever the manifest asks for.
- There are no secrets. The Python SDK’s
ctx.secret(name)returnsfixture-<name>. /datais a read-only copy of the example’sfixturefolder.- The time is fixed to the example’s
now, and the time zone is the example’stz. - The feeds are the ones the example lists, at the sizes it gives.
It then checks everything the job wrote, as the hub would: the deck, card ids and kinds, and that every card is an exact-size PNG in the four inks.
--repeat runs each example twice and fails unless the pictures are identical. A test render must be deterministic: don’t read the clock (use ctx.for_), and don’t use random numbers.
Use Podman or Docker; the SDK picks Podman if it is installed. SLOWAPP_ENGINE=docker forces one.
Look at the results
Everything lands in out/previews/ (--out changes it):
<app>-<example>-<feed>-<card>.png: each card.<app>-<example>-frame.png: the whole 1360 × 480 screen with your home cards placed in the Slowboard 1085’s default three-slot layout. The strips are left blank; slowd draws them.report.md: a table of every card with its title and id.summary.json: the same as data, with timings and memory use.
Make a fixture from a real run
If your app fetches its inputs through ctx.fetch (see the Weather tutorial), one live run produces a fixture: copy its /data/snapshots/ folder into fixtures/<name>/. Add an example for each state worth seeing: a busy day, an empty one, an error from the API.
Run unit tests
Run your tests inside the base image, where the SDK is installed, the same way CI does:
docker run --rm -v "$PWD":/src -w /src -e PYTHONPATH=/src:/opt/slowapp \
--entrypoint python3 ghcr.io/mike-anderson/slowapp-python:1.2.0 -m unittest discover -s tests
Try the board’s image
The Slowboard 1085 runs 32-bit ARM images. To run your app’s examples on that platform under emulation:
SLOWAPP_PLATFORM=linux/arm/v7 SLOWAPP_TIMEOUT_SCALE=4 \
PYTHONPATH=/tmp/slowapp ~/.slowapp-venv/bin/python -m slowapp_sdk fixture --image slowapp-myapp:dev
Build the image for that platform first (docker buildx build --platform linux/arm/v7 …). Emulation is slow, so SLOWAPP_TIMEOUT_SCALE multiplies your manifest’s timeout_s.
Run a job without a container
For quick changes, you can run a Python job directly, without building an image. The SDK reads its paths from environment variables, so point them at local folders:
SLOWAPP_FONTS=/tmp/slowapp/fonts \
SLOWAPP_MANIFEST=slowapp.toml \
SLOWAPP_STATE=./data SLOWAPP_OUT=./out SLOWAPP_IN=./in SLOWAPP_JOB_LOCK=./job.lock \
SLOWAPP_FEEDS='[{"feed":"hello","w":452,"h":444}]' \
PYTHONPATH=/tmp/slowapp ~/.slowapp-venv/bin/python hello.py job render
The cards land in ./out/feeds/<feed>/. Without SLOWAPP_MODE=fixture this is a live run: it can use the network, and SLOWAPP_NOW defaults to the current time. Add SLOWAPP_MODE=fixture SLOWAPP_NOW=2026-10-07T09:00:00-07:00 to read from ./data like a test render.
This skips the container’s limits, so always finish with a real test render.
See it on a simulated screen
slowd has a simulator, the sim profile: the real display program with no panel, which writes each frame to a PNG and takes key presses from the command line. It shows the strips, decks and key behaviour that the frame preview leaves out.
TODO(owner): document how to run a hub and
slowd --profile simtogether on a developer’s machine (which OS, which binaries, the commands, and where the frame PNG appears), or say that this isn’t supported yet.