cd /news/developer-tools/tesserae-e-ink-dashboard-companion-c… Β· home β€Ί topics β€Ί developer-tools β€Ί article
[ARTICLE Β· art-168208] src=github.com β†— pub= topic=developer-tools verified=true sentiment=↑ positive

Tesserae: E-ink dashboard companion. Compose dashboards and push to e-ink panels

Developer Kayden released Tesserae, an open-source, self-hosted dashboard companion for e-ink displays that renders tile-based dashboards in headless Chromium (Playwright) at panel resolution and pushes frames to panels over MQTT or HTTP. Tesserae, licensed AGPL-3.0-or-later, applies Floyd-Steinberg dithering against each panel's measured color palette, using calibrated measurements from epdoptimize, then packs frames into the panel's native byte format β€” packed 4-bit Spectra 6, 1-bit mono, or 4-bit grayscale. The server requires no account and contacts api.tesserae.ink only for update checks, an anonymous install count and an opt-in daily heartbeat, while a separate hosted Tesserae Cloud is offered for users who do not want to run a server.

read12 min views1 publishedNov 12, 2026
Tesserae: E-ink dashboard companion. Compose dashboards and push to e-ink panels
Image: Michielbdejong (auto-discovered)

Nine panels, one Tesserae server.

Self-hosted dashboard companion for e-ink displays. Compose tile-based dashboards in a browser, render the frame headless, push it to one or more panels over MQTT or HTTP.

Note

Available for contract work. I'm Kayden. I created Tesserae and maintain it, with help from a growing group of contributors. I take on contracts building with AI: custom agents, MCP servers that connect AI models to your tools and data, and AI-assisted development workflows for teams. I also take on full-stack, embedded and Home Assistant projects, the same work Tesserae is built from.

If Tesserae is useful to you, sponsoring helps keep it independent and actively maintained.

Email me about a contract Β· Sponsor on GitHub Β· Support on Ko-fi

Open source under AGPL-3.0-or-later. The self-hosted server needs no account and never has to talk to us: the only outbound contact is api.tesserae.ink for update checks, an anonymous install count, and a daily aggregate heartbeat, off by default and opt-in (you're asked once at first-run setup; toggle in Settings β†’ System β†’ Online features).

Tesserae Cloud is a separate hosted Tesserae for people who would rather not run a server: sign in, create a dashboard, and connect your display. This self-hosted server stays the full product and stays free, and a supported panel is not tied to the cloud: it can be pointed at a server of your own at any time, though dashboards made in the cloud are specific to it and would need recreating here.

πŸ“– Full documentation: install guides, hardware quickstarts, widget gallery, community catalog, architecture deep dive, how to build a widget, how to add hardware support.

mkdir tesserae && cd tesserae
curl -fsSLO https://raw.githubusercontent.com/dmellok/tesserae/main/docker-compose.yml
docker compose up -d

Open http://localhost:8765. First request walks through password setup and the onboarding wizard.

Other install paths: Home Assistant App, Proxmox VE helper script, LXC / MicroCloud, macOS / Linux / Pi shell installer, Windows PowerShell, manual venv.

Server is running? Pick a hardware quickstart to get a panel on the wall.

Dashboards are plain HTML. The editor preview and the production render load the same URL: a headless Chromium (Playwright) screenshots the composition at panel resolution, so "looked fine in the editor, broke on the panel" is impossible by construction.

The frame then goes through a per-device renderer: Floyd-Steinberg dithering against the panel's measured color palette (Spectra 6 and ACeP panels don't actually produce sRGB primaries; calibrated measurements come from epdoptimize), then packing into the exact byte format the panel wants: packed 4-bit Spectra 6, 1-bit mono, or 4-bit grayscale.

Devices are deliberately thin clients with no on-device image decoding. Always-on clients (a Pi driving an Inky) get frames pushed over MQTT the moment a page changes. Battery clients (ESP32) wake, GET their frame over REST with If-None-Match (a 304 skips both the download and the slow e-ink refresh), POST battery + RSSI telemetry, and deep-sleep on a server-driven interval. Wall-clock time comes from the HTTP Date header, so there's no SNTP on-device.

browser editor          Tesserae server                    panels
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   HTTP   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   MQTT   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ composer │─────────▢│ headless render      │─────────▢│ Pi + Inky   β”‚
β”‚ /compose β”‚          β”‚ dither (measured     β”‚          β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β”‚   palettes)          β”‚   REST   β”‚ ESP32,      β”‚
                      β”‚ pack panel-native    │◀────────▢│ Kindle,     β”‚
                      β”‚ schedule / rotate    β”‚          β”‚ TRMNL       β”‚
                      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Every layer is a drop-a-folder plugin: widgets, themes, renderers, transports, and device kinds. The seams are real directories: plugins/ Β· renderers/ Β· transports/ Β· devices/.

Build dashboards with AI (MCP). Tesserae exposes an MCP API so an agent can lay out a freeform dashboard, render a preview to check its work, and push it to a panel. It's on by default; run the tesserae-mcp bridge on your agent's machine (a remote agent needs the token from Settings β†’ System β†’ MCP). See the docs.

Server-side rendering, dumb firmware. The server pre-renders each frame into the exact bytes the panel controller wants; the device firmware streams them straight to the panel. Adding a new panel is a board header plus one driver. The network, power, and provisioning stack never changes.

Widget network egress is allowlisted. Third-party widgets declare the network hosts they need in a requires: block in plugin.json, and the host enforces the allowlist at the socket layer. An undeclared connection raises CapabilityDenied instead of phoning home. That is the extent of the containment: widgets otherwise run in-process, and the trust model is PR review of the community catalog, not a sandbox. The threat model is documented honestly, including what it doesn't catch.

One server, a fleet of panels. Tesserae splits rendering from transport from hardware, so a single instance drives every panel in the house, each with its own dashboards, schedules, and rotations.

Community widgets install from an audited catalog. Pinned release tarballs, sha256-verified, schema-validated, PR-reviewed: tesserae-widgets.

Tesserae renders to one of several panel families through drop-a-folder device plugins. In the tables below, βœ… means the panel has been confirmed on real hardware (on my bench or via a community report); TBD means a client and hardware profile ship but nobody has confirmed a frame on the physical panel yet. The per-renderer bench matrix lives at Screens & compatibility.

Some links below are affiliate links. Seeed product links and the Pimoroni Inky Frame and Inky Impression links carry a Tesserae affiliate tag: buying through one pays a small commission that goes back into the project, and a Seeed link also gets you a discount at checkout. Nothing is listed here because of it, and a panel's βœ… / TBD status means exactly what it says above. Links without the tag are plain product links.

The reTerminal E-Series and XIAO ePaper family run the Tesserae-native firmware; flash from the browser in one click at tesserae.ink/flash (Chrome / Edge, Web Serial, no toolchain). Battery-powered, no assembly required.

Model Panel Client Status
reTerminal E1001 7.5" mono, 800Γ—480 tesserae-device-firmware βœ…
reTerminal E1002 7.3" Spectra 6, 800Γ—480 tesserae-device-firmware βœ…
reTerminal E1003 10.3" mono, 16-level grey, 1872Γ—1404 tesserae-device-firmware βœ…
reTerminal E1004 13.3" Spectra 6, 1200Γ—1600 tesserae-device-firmware βœ…
reTerminal Sticky 3.97" mono, 4-level grey, 480Γ—800 portrait tesserae-device-firmware βœ…
XIAO ePaper EE02 13.3" Spectra 6, 1200Γ—1600 tesserae-device-firmware βœ…
XIAO ePaper EE03 10.3" mono, 16-level grey, 1872Γ—1404 tesserae-device-firmware TBD
XIAO 7.5" ePaper Panel 7.5" mono, 800Γ—480 tesserae-device-firmware TBD
TRMNL 7.5" OG DIY Kit 7.5" mono, 800Γ—480 TRMNL firmware βœ… via BYOS

The PaperS3's screen has no controller chip: the ESP32-S3 drives the glass directly and the 16 grey levels come from a waveform table in the firmware rather than from the panel. Greyscale tuning is therefore a firmware change. Partial refresh is not implemented yet, so this panel has no tap echo or on-device touch controls, and its touch digitiser is not wired up.

The link goes to M5's documentation rather than their store: M5 have marked the PaperS3 end of life and it is sold out, so this entry is for people who already own one.

The original M5Paper (pre-S3, classic ESP32-D0WDQ6-V3) drives the same 4.7" glass through an IT8951 controller over SPI, so it reuses the reTerminal E1003's greyscale driver. Also discontinued; this entry is for existing owners.

The PaperMono (2026, Pro and Lite) is a 3.97" 480x800 4-level greyscale handheld on the reTerminal Sticky's controller family, so it reuses that driver and wire format; its panel rails sit behind an I2C IO expander and its battery is read from the on-board PMIC. Confirmed on hardware; touch, microSD and the frontlight are not wired yet.

Model Panel Client Status
M5Paper 4.7" mono, 16-level grey, 960Γ—540 (IT8951) tesserae-device-firmware βœ…
PaperS3 4.7" mono, 16-level grey, 960Γ—540 tesserae-device-firmware TBD
PaperMono / PaperMono Lite 3.97" mono, 4-level grey, 480Γ—800 portrait (SSD1677) tesserae-device-firmware βœ…
Panel Resolution Client Status
Inky Impression 4" (6-colour Spectra 6) 600Γ—400 tesserae-device-pi βœ…
Inky Impression 4" (legacy 7-colour ACeP) 640Γ—400 tesserae-device-pi βœ…
Inky Impression 5.7" (7-colour ACeP) 600Γ—448 tesserae-device-pi βœ…
Inky Impression 7.3" (6-colour Spectra 6) 800Γ—480 tesserae-device-pi βœ…
Inky Impression 13.3" (6-colour Spectra 6) 1600Γ—1200 tesserae-device-pi βœ…
Inky pHAT /wHAT various tesserae-device-pi works via pi_png

The Inky Frame and the Badgers carry their own microcontroller, so they need no Raspberry Pi. The Inky Frame runs tesserae-device-inkyframe (Pico SDK firmware, the Tesserae fork of Carl Partridge's client: setup from a phone, button letters, signed over-the-air updates); flash it from the browser at tesserae.ink/flash/inky-frame (Chrome / Edge, WebUSB, no toolchain). The Badgers run tesserae-device-badger, an app inside the badge's own launcher, so the badge keeps its other apps.

Panel Resolution Client Status
Inky Frame 7.3" (6-colour Spectra 6, Pico 2 W) 800Γ—480 tesserae-device-inkyframe βœ…
Inky Frame 7.3" (legacy 7-colour ACeP) 800Γ—480 tesserae-device-inkyframe TBD
Badger 2350 264Γ—176, 4-level grey tesserae-device-badger βœ…
Badger 2040 W 296Γ—128, mono tesserae-device-badger TBD
Panel Resolution Client Status
Waveshare 13.3" Spectra 6 (ESP32-S3) 1200Γ—1600 esp32-bin βœ…
Waveshare 7.3" PhotoPainter (ESP32-S3) 800Γ—480 photopainter-7.3-bin βœ…
Waveshare 4.2" B/W (ESP32) 400Γ—300 esp32-bw TBD

Xteink's X-series e-readers run CrossInk firmware with Tesserae client support: the dashboard becomes the reader's sleep screen, painted on the sleep transition rather than on a timer.

Model Panel Client Status
Xteink X3 (4-level grayscale) 792Γ—528, gray CrossInk βœ…
Xteink X3 (mono) 792Γ—528, mono CrossInk TBD
Xteink X4 800Γ—480, mono CrossInk βœ…
Xteink X4 (4-level grayscale) 800Γ—480, gray CrossInk βœ…
Xteink X4 Pro 800Γ—480, mono CrossInk TBD

Community-authored clients, contributed and maintained by their authors (not in the Tesserae org). Thanks to everyone building these.

Panel Resolution Client Status
PicPak 4.2" BWRY 400Γ—300 (4-colour BWRY) picpak-tesserae-client by@varanu5 βœ…
ZecTrix NOTE4C Devkit 400Γ—300 (4-colour BWRY) note4c-tesserae-client by@varanu5 βœ…
Soldered Inkplate 10 1200Γ—825 (9.7" 16-level grey) tesserae.inkplate by@partridgeworks βœ…
Soldered Inkplate 6COLOR 600Γ—448 (5.85" 7-colour ACeP) inkplate-tesserae-driver by@ripking βœ…
Soldered Inkplate 13SPECTRA 1600Γ—1200 (13.3" Spectra 6) inkplate-tesserae-driver by@ripking βœ…
Pimoroni Inky Frame 7.3" (7-colour ACeP) 800Γ—480 tesserae.inkyframe by@partridgeworks βœ…
Pimoroni Inky Frame 7.3" (6-colour Spectra 6) 800Γ—480 tesserae.inkyframe by@partridgeworks TBD
reMarkable 2 1404Γ—1872 (10.3" 16-level grey) tesserae.remarkable by@partridgeworks βœ…
reMarkable 1 1404Γ—1872 (10.3" 16-level grey) tesserae.remarkable by@partridgeworks TBD
reMarkable Paper Pro 1620Γ—2160 (11.8" colour) tesserae.remarkable by@partridgeworks TBD

E-readers running KOReader with the Tesserae plugin. The plugin pairs with a claim code, decodes 16-level greyscale frames on the reader, and sleeps between refreshes where KOReader exposes the hardware alarm. Kindles need a jailbreak to run KOReader; Kobo does not.

Panel Resolution Client Status
Kindle Paperwhite 2 (jailbroken) 758Γ—1024 tesserae-koreader TBD
Kindle Paperwhite 3/4, Voyage, Basic 4 1072Γ—1448 tesserae-koreader TBD
Kindle Paperwhite 5, Oasis 3, Scribe 1236Γ—1648, 1264Γ—1680, 1860Γ—2480 tesserae-koreader TBD
Kobo Clara HD, Libra 2, Sage, Aura various tesserae-koreader TBD

Any client implementing the TRMNL BYOS spec works against the trmnl_png renderer.

Panel Resolution Client Status
TRMNL OG 800Γ—480 TRMNL firmware TBD via BYOS
TRMNL X 1872Γ—1404 (10.3") TRMNL firmware TBD
Amazon Kindle Paperwhite 2 (jailbroken) 758Γ—1024 KOReader trmnl-display plugin (1-bit; see the KOReader section above for the Tesserae plugin) βœ…

Anything else: pick custom in Settings β†’ Panel and set the dimensions. If a client drives your panel, Tesserae can push frames to it. A generic CircuitPython client (400+ boards) is in development; follow discussion #24.

Per-client setup walkthroughs (the firmware repos in the Client column): Install a client. Full renderer / device-kind compatibility matrix: Screens & compatibility.

Pre-1.0 and moving fast. Every release is documented in the CHANGELOG. CI runs pytest (7,700+ tests), the Lineups browser tests in Chromium, ruff, and mypy (strict on the render/push pipeline modules) on every push; coverage (86% at last count) is measured weekly, report only.

Built with AI assistance (Claude Code). Architecture decisions, code review, hardware testing, and what ships vs. what doesn't are mine. The changelogs document the debugging in the open.

Tesserae contacts one first-party endpoint, api.tesserae.ink, for update checks, an anonymous marketplace install count, and a daily aggregate heartbeat (version, platform, deployment kind, device kinds; no exact counts). No accounts, no personal data, no IP addresses stored, no third-party analytics. It is off by default and opt-in: a fresh install asks once at first-run setup, and it's a single switch at Settings β†’ System β†’ Online features. Per-device diagnostics (battery, RSSI, sleep cadence) stay on the box. Privacy.

The server stays free even when someone runs it as a service: improvements have to stay public. And a dashboard for your home shouldn't report home. No analytics, no accounts, nothing phones out. Don't take the README's word for it; read the source.

Tesserae stands on generously-licensed open source: Phosphor Icons, Chart.js, 20 typefaces under SIL OFL / Apache 2.0, the TRMNL BYOS protocol, the KOReader trmnl-display plugin, and paperlesspaper/epdoptimize's Spectra 6 + ACeP calibrated palette measurements (also shipped as selectable presets in the Calibration tab under Apache 2.0). Full attribution: Credits, NOTICES.md.

AGPL-3.0-or-later, see LICENSE.

── more in #developer-tools 4 stories Β· sorted by recency
── more on @tesserae 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/tesserae-e-ink-dashb…] indexed:0 read:12min 2026-11-12 Β· β€”