---
title: "A family e-ink photo frame in a day (Pigment)"
description: "I built Pigment, a self-hosted photo curation app for a color e-ink frame, and scored 7 Byonk dither settings by Oklab ΔE to land on sierra-lite."
url: "https://michael.zazon.us/notes/a-family-e-ink-photo-frame-in-a-day-pigment"
published: "2026-08-10T12:00:00-04:00"
kind: "build-log"
subject: "home-automation"
tags: ["media"]
series: "E-ink frames"
part: 1
---
I got the [Seeed reTerminal E1004](https://wiki.seeedstudio.com/reterminal_e10xx_main_page/) with the color e-ink panel and battery. I wanted to be able to pick (and crop) the photos myself, from my own server, rather than use Seeed's stock app or set up Immich like other owners have done. The frame wakes up and polls the server for the next picture, so nothing needs to push anything.. the build only needs to be able to answer on request.

Before I started coding, I looked around for what others had done before me. The r/trmnl thread "Byonk driving a E1004" and the r/immich thread "ReTerminal e1004 working with Immich" (which includes an eleven-step flashing walkthrough and how to hook up Byonk) got me most of the way on the map. [Byonk](https://github.com/oetiker/byonk) is oetiker's self-hosted TRMNL device server, so it already speaks the device protocol, and it comes with community-tuned dithering for this exact panel.

I decided against writing a standalone server because it would mean reimplementing the tuned dithering, and the built-in screens in Byonk don't support photo cropping. So Pigment only does the curation.. it stores photos and crops, and Byonk handles all the communication with the frame and turning a photo into six ink colors. Suddenly, the hard part was already solved by someone else, and I was left with just picking and cropping photos.

I decided to call it Pigment because e-ink physically moves pigment particles to draw things, and "pigment print" is the archival standard for color photo prints, so it fits both the screen and the photos.

I wrote the spec in the afternoon and had a working app by the evening. It lets you upload a photo from your phone, crop it in the browser, and render it at the panel's 1200x1600 portrait resolution. I designed it to store the original photo alongside its crop rectangle, and the render is derived from both, so you don't have to re-upload if you want to change the crop. The release tool tagged the first cut 1.0.0, and I was like "we aren't even close to 1.0", so I retagged it 0.1.0. From design to five releases in a day.

Here's a diagram of the path the photo takes from the phone to the glass, including the button presses that reach Pigment before the frame requests a picture from Byonk.

[Diagram: Photo path from phone through Pigment and Byonk to the e-ink frame](https://michael.zazon.us/notes/a-family-e-ink-photo-frame-in-a-day-pigment)

## Flashing and first bugs

That same evening, I flashed the community firmware from [TRMNL firmware PR #445](https://github.com/usetrmnl/firmware/pull/445), which was closed without being merged. I created my own fork, which maps three buttons to next, previous, and refresh, using the button pin map from [Frans-Willem/epd-photoframe](https://github.com/Frans-Willem/epd-photoframe). When a button is pressed, the firmware communicates with Pigment first (step back or hold current photo) and then asks Byonk for a picture as usual, so Byonk doesn't need to be changed at all.

I ran into two problems: First, the frame's Wi-Fi setup page never saved the network join, failing the same way twice. I worked around it by writing the Wi-Fi settings directly into device storage over USB, where they survived later firmware flashes. Second, a crop bug squashed photos taken sideways because the server validated the crop against the image's dimensions before EXIF rotation. I hadn't thought about rotation until I saw one photo get distorted, but fixed it the same night.

## The dither shootout

The next day, I turned to the picture itself. I scored seven Byonk dither settings on five real photos, rendering each with Byonk and measuring the color distance from the pre-dither original (average Oklab color difference). The chart below shows the mean score for each setting.

[Diagram: Mean color error across seven dither settings](https://michael.zazon.us/notes/a-family-e-ink-photo-frame-in-a-day-pigment)

Unexpectedly, the live frame performed worst of the seven settings. This was because I'd hardcoded jarvis-judice-ninke in the script, overriding the config, and because I'd spelled "sierra-light" in the panel tuning block instead of "sierra-lite", which Byonk expects, so the tuning was silently ignored (no error). I wondered how long the worst setting would have gone unnoticed had I not started measuring. A one-word rename in the panel config fixed it.

```diff
     colors: "#000000,#FFFFFF,#FF0000,#FFFF00,#0000FF,#00FF00"
     colors_actual: "#000000,#FFFFFF,#C20000,#FFE042,#2160C4,#00994D"
     dither:
-      sierra-light:
+      sierra-lite:
         error_clamp: 0.11
         noise_scale: 5
```

Numbers and eyes disagreed. Atkinson scored best but its output nudged mid-tones green, a hue drift the average hid. I trusted my eyes more than a single number for photos. I ranked them visually and picked sierra-lite tuned. It beat runner-up floyd-steinberg with a lower error clamp on all five photos, on average and at their worst pixels. Days later, on real glass, sierra-lite still looked good.

[Image: The e-ink frame on the wall next to a window, showing a family photo](https://michael.zazon.us/notes/a-family-e-ink-photo-frame-in-a-day-pigment)

It's now running on battery, changing either when I press a button or once a day by itself. It pages me when the battery gets low. It uses very little power: I thought the battery reading was flat and asked why, it turned out to have dropped from 98 to 95 percent over about four days off USB. I estimate it'll run for about four months per charge, but that's a ballpark figure from the first few days of readings, not measured. Since then I've added the ability to show a rendered image from another service in a Pigment album, such as the bird collage from [BirdNET listening in the backyard](https://michael.zazon.us/notes/birdnet-listening-in-the-backyard).