# Aaron Gotwalt

> [Blog](/blog/) / [Projects](/projects/) / [Work](/work/) | [llms.txt](/llms.txt)

---

## Screeny

Date: 2026-10-05

A Tidbyt turned into a 30 fps window for generative art.

---

<video autoplay loop muted playsinline poster="/blog/screeny/screeny-loop-poster.jpg" width="1280" height="720" style="width: 100%; height: auto;">
  <source src="/blog/screeny/screeny-loop.mp4" type="video/mp4" />
</video>

Screeny is open-source firmware and software that turns a [Tidbyt](https://tidbyt.com) Gen 1 into a 30 fps display for generative art. Instead of rendering frames in the cloud, Screeny renders them on a computer on the local network and streams them straight to the panel. No cloud, no account, no subscription.

```
patches ──> art pipeline ──> encoder (best of 5 codecs, ≤ 1464 B) ──UDP/WiFi──> firmware ──> HUB75 panel
```

## Firmware

- `no_std` Rust on [embassy](https://embassy.dev) and esp-hal, with the display on one ESP32 core and the network on the other.
- Refreshes the 64×32 panel at 154 Hz with six bit planes, and dithers temporally to about a thousand levels per channel.
- Caps brightness so a laptop USB port can power it.
- Advertises itself over DNS-SD, sets up WiFi from the browser or a phone QR code, and takes over-the-air updates that roll back on their own if they fail.

## Protocol

- One frame, one UDP datagram: an 8-byte header and at most 1,464 bytes of pixels.
- Nothing is retransmitted, every frame decodes on its own, and the newest frame wins.
- The sender tries five codecs on every frame — mostly adaptive palettes that shrink until the frame fits — scores each against a model of the panel, and sends the best. A clock frame is about 280 bytes.
- `screeny pipe` takes raw RGB frames on stdin, so any program in any language can drive a panel.

## Studio

- A single Rust binary: an axum server with the web UI compiled in, and an MQTT bridge to Home Assistant.
- Runs as a Home Assistant app, in Docker (amd64 or arm64), or from source. On a Raspberry Pi 4 it uses about half a core and 85 MB of memory.
- Each patch renders on its own thread, and a patch that stalls is replaced without taking the process down.
- Patches include clocks inspired by [ClockClock 24](https://www.humanssince1982.com/products/clockclock-24-white) and the [Vestaboard](https://www.vestaboard.com), a slow-motion flock, metaballs, raymarched solids, and a GPU-rendered procedural world.

The [design docs](https://github.com/gotwalt/screeny/tree/main/docs) cover the protocol, the architecture, and the research behind each decision. Read the story behind it in [the blog post](/blog/2026-10-05-screeny).
