← Writing

The display fought back

Claude Code’s usage limits are invisible right up until you hit one. There is a dashboard, but a dashboard is a tab you have to remember to open, which means you check it after the limit interrupts you, not before. I wanted the signal in peripheral vision instead: a small object on the desk that shows the burn at a glance and is otherwise quiet. So I built one.

The object is a round 2.8” ESP32-S3 touchscreen, 480×480, with an arc bar around the bezel that fills as the session fills. Tap it and it flips to a detail view. It updates every thirty seconds. When you are rate-limited it shows a live countdown to the reset, because if a device’s entire job is delivering one number, its error states deserve design too.

The rule that shaped the architecture

The data sits behind an OAuth token, and I did not want that token anywhere near a hobby microcontroller on WiFi. So the system is two halves. A small daemon (Bun and TypeScript, compiled to a single binary, auto-starts at logon) runs on the dev machine, polls Anthropic’s usage endpoint with the local token, and re-serves the result as plain JSON on the local network. The ESP32 polls the daemon. It never sees a credential, only numbers. The trust boundary stays on the machine that already holds the token, and the worst case on a hostile LAN is that someone learns how fast I burn tokens.

The display fought back

The screen produced, in order: magenta flashes, rainbow noise, and pinstripes. Three symptoms, three different bugs. The magenta traced to RGB timing. The rainbow was a wrong color mode. The pinstripes were cache/DMA tearing, the CPU writing the framebuffer while the panel was reading it. The fixes were explicit cache syncs, double framebuffering, and a lower pixel clock.

Then I threw away the hand-rolled display code and moved to the official panel library. That is the classic arc: write it yourself, collect the scars, and arrive at an exact understanding of why the library exists. I would do it in the same order again. The scars are what make the library legible.

Small dignities

The rest of the firmware is small dignities. Time syncs over NTP so the rate-limit countdown is honest. Brightness persists across power cycles. Updates can arrive over the air on the LAN, though my WiFi is slow enough that the cable usually wins anyway.

Where it is

Working prototype, on the desk now, with the enclosure in its second CAD iteration. Firmware, daemon, UI, and enclosure are one pair of hands end to end, which was the point of the exercise: take an idea all the way to a physical object and see what that teaches. Whether it becomes more than a personal tool is a question for after v1 ships.

The limit still comes. It just stopped being a surprise.