Readable in full sun
Reflective panel, no backlight. The brighter the day, the better it looks. Visor down, glance, done.
Heavy development · nothing here is finished
TrailInk turns a pocket e-ink reader into an offline map display for motorcycles and trails. No backlight to wash out, no battery gone by lunch. One still picture: where you are, where the route goes, what is around you.
Built on CrossPoint Reader. Map data © OpenStreetMap contributors.
01 — why e-ink
The screen washes out in direct sun. It cooks in a handlebar mount. The battery does not last a day of riding. E-ink has the opposite properties — and one hard cost you have to design around.
Reflective panel, no backlight. The brighter the day, the better it looks. Visor down, glance, done.
Holding an image costs nothing. Power goes only into changing the picture, so the X4 runs for tens of hours.
A full refresh takes about 1.7 s; four grey levels cost an extra waveform pass, and lose their grey to the next one. That rules out a moving map.
Position, route, surroundings — redrawn rarely, readable at a glance at speed. No rerouting, no voice, no traffic. The route is planned before you leave.
02 — the screen
The ones tagged device came off the panel over USB — send a coordinate and the device draws what is around it from tiles on its SD card. The one tagged preview is the same renderer running on a laptop against the same tiles, which is how styling gets tuned without reflashing. We keep the two labelled apart on purpose.
Honest caveat: the device renderer follows most of the style spec now — road widths, casings, buildings, forest, built-up and water areas all draw on the panel. Place labels, the route line and junction dots exist in the spec and in the laptop preview, not yet on the glass.
03 — how it is built
Fetches an area from OpenStreetMap, projects and clips it, routes along real
roads, and writes binary .tib tiles at three zoom levels. A browser
tuning tool renders a pixel-exact 480×800 preview, so the styling gets judged
as e-ink instead of as a pretty vector map.
A fork of CrossPoint Reader. Map screen, BLE position receiver, tiles off the SD card, zoom and marker height on the physical buttons, ride / hike / cycle filter, and a command console over USB serial and BLE. The e-reader stack comes out; maps go in.
Sends the phone's GPS fix to the device over BLE every few seconds and records every fix and packet for replay. One window, no settings screen, foreground service — it keeps sending with the phone locked in a bag. A GPS liveness gate throws away network fixes that would jump you across town.
04 — status
Formats, the render spec and the BLE protocol all still change without notice. Do not depend on any of it yet.
.tib tiles, three zoom levelsworks05 — development log
The map used to swing round every time you turned far enough, because it kept your direction pointing up. Through hairpins that is constant: over one 3.4 km pass it redrew the whole screen eleven times into nine different orientations, twice ending up back where it started.
With a route loaded it now stays the way the route runs, and only the marker moves — its arrow turns instead of the map. The same pass measured zero redraws in one orientation, and a recorded 7.4 km morning commute went from twelve down to two, both of those because the rider genuinely rode off the edge of the picture. On a screen that blanks to redraw, the picture holding still is the feature.
It works now, all the way through. The device knows which map squares it never had, asks the phone for them, and the phone brings them down from a public tile server and hands them over Bluetooth. Five squares, 322 kB, 43 seconds — with a progress bar per square on the panel and a plain “not available” for anything the server has not been given yet.
The phone keeps nothing. A map square belongs on the device or on the server, so the phone holds one only for the seconds it takes to pass it along. Nothing quietly fills up.
It also got three times faster than the first attempt, and not by sending more at once. Bluetooth waits for an answer after every small piece, so the time between answers was the whole limit — the phone now asks for a faster rhythm while a transfer is running, and gives it back the moment it finishes, because holding it costs battery for nothing.
You plan a route on the laptop — from two clicked points routed along real roads, from a GPX file out of a planner, or from whatever the browser tool has drawn — and push it to the reader over Bluetooth. Opening the map now asks which route to load, or lets you skip and just have a map.
Pick one and the first thing you see is the whole route on one screen. The device works out which zoom step and which direction fit it, by measuring the route itself rather than the box around it: a route running north-east occupies a thin diagonal strip, and turning the map to match it fits far more on the panel than keeping north up would. It also turns the route so your direction of travel runs up the screen. If the route is longer than the widest zoom can hold, it shows the middle and says so, rather than pretending.
After that the route stays drawn over the map for the rest of the ride. It costs no memory at all, however long it is — the file is read a point at a time and each leg drawn as it arrives, so nothing is ever held. Measured on the panel: the whole-route screen takes 2537 ms, and a map redraw with a route loaded allocates nothing.
One thing the laptop found before the hardware did. Choosing the direction purely by what fits drew a straight north-south route tilted 22.5° — mathematically the best fit, because a slight tilt trades screen height for width, and useless in practice since the zoom only comes in five fixed steps and both angles land on the same one. A map askew for a gain nobody can see. Direction of travel wins ties now.
A position update used to redraw the whole map. It no longer does. The new fix is placed inside the picture already on the panel, and only the marker is redrawn — the map is rebuilt when the marker nears an edge, when the rider has turned far enough that the map faces the wrong way, or after enough small moves to keep the panel clean. Over a real ride replayed onto the device, that turned 117 full redraws into 14, with 31 updates costing nothing at all.
The map is also drawn track-up now: the direction of travel is up the screen and the north indicator turns to keep pointing north. That is what makes the look-ahead setting mean anything — with north pinned upward, the road ahead only landed in the empty half of the screen when you happened to be riding north.
Two things we believed turned out to be wrong, and the panel said so. Refreshing a small rectangle costs exactly what refreshing the whole screen costs — 500 ms either way, the waveform does not care about area. So the saving is not the screen at all; it is the eight to fourteen seconds of reading tiles and drawing them that a redraw no longer spends. And the fix we expected to stop the map spinning in corners measured slightly worse than doing nothing. Corners are still the expensive case, and still open.
Buildings, forest and built-up areas now draw on the panel, filled with a dither pattern rather than hatch lines — at map scale a house is a handful of pixels, and lines across it read as dirt. Roads take a width per class, and the panel confirmed both.
Which of the two a village gets is decided when the tiles are built: individual buildings up close, one built-up area from 6 m/px out. The device never reads what it would not draw, and that shows in the numbers — a viewport reset takes 2506 ms reading 484 KB at 3 m/px, and 1088 ms reading 198 KB at 6 m/px.
All four grey levels render and are distinguishable by eye. A full grey frame measures ~2.1 s, and it has to be the last thing that touches the panel: a refresh issued in the same breath breaks the picture. A repeated nudge lightens the same pixel and adds grain, so grey cannot be topped up in place.
Judged side by side on the glass, the existing 2×2 dither simply read better for area fills — and it survives any refresh, because it is ordinary black and white. So fills stay dithered and four-level grey stays out of the map. The support layer and its test screen remain, behind a build flag, for the next panel question.
The device's own renderer now compiles as a host binary and runs against the style being edited, so the webapp preview shows real device output. Two seconds per edit instead of a build-and-flash cycle.
The full dated log lives in the repo, alongside one topic document per hardware finding — how the panel actually behaves, why the obvious approach fails. Read it on GitHub →
06 — the hardware
A pocket e-ink reader, bought off the shelf. Not a dev board, not Android, no ADB — an ESP32-C3 with a display driver and physical buttons. Everything TrailInk does has to fit in that.
07 — get in touch
I ride, and I wanted a display that survives a sunny day on a handlebar. If you are doing something similar on Xteink hardware, or you want to try it, or you just want to tell me the map style is wrong — write to me.