Overview

Past Time.

Past Time is a configurable, glanceable desk timer built on a high-contrast e-ink display that tells time through art. It ships with 101 distinct animated story-scene faces, each one a full-screen 1-bit scene whose subject embodies the countdown: a tower crane building a high-rise floor by floor, a piggy bank filling with coins, a kite climbing its string, a retro boss whose HP bar drains, a wrecking ball levelling a tower.

The subject itself reaches its climax exactly as the timer hits zero, with many faces playing a bespoke "time's-up" finale. Its stark 1-bit aesthetic keeps it distraction-free on the desk. The firmware runs four timer modes (Countdown, Pomodoro, Stopwatch, Interval/HIIT), an on-device settings menu, a soft power button, an aquarium screensaver, optional Wi-Fi clock and weather, and deep-sleep standby. It is at v1.0, shipped.

At A Glance

Scaling Embedded Content

Past Time runs on the Elecrow CrowPanel ESP32‑S3 2.13" E‑Paper HMI (122×250, SSD1680). The interesting problem was scale: 101 distinct full-screen C++ animations. The project builds a desktop preview of the e-ink screen and a pipeline of software agents that design and check faces so that each face can be drawn, rendered, critiqued, and integrated without flashing hardware on every iteration.

Visuals

101 Animated Stories

Each face is one orientation-aware function that lays out proportional to the panel size and draws a 1-bit scene: a crane raising a high-rise, a kite climbing, a submarine diving to a treasure chest, a lighthouse beam guiding a boat to dock. The work is iterated on a desktop render mirror first and then ported, since the edit→compile→flash loop is impractical at 101 faces.

Pipeline

1-Bit Render Mirror

A Python/Pillow shim re-implements the ESP32 GFX primitives so a face's draw code renders pixel-faithfully on the desktop at 250×122, 1-bit, anti-aliasing off. The pipeline writes C++, renders a verification strip, and tests the output against a strict rendering contract.

Quality Gate

Per-Face Audit Fleet

One reviewer agent per face grades its strip against the contract (recognizable, embodied, sensible, dynamic, legible) and returns a ranked fix list. The first pass scored 84/101; the 17 flagged faces were redesigned and re-audited until no face scored below 4/5.

Animated Aquarium Timer rendering
Reliable Workflows

Safe, Repeatable Integration.

Giving the pipeline the ability to render and critique its own output is what makes the scale tractable. Each face is built by an autonomous agent that renders a 3-row verification strip (Row A is the scene swept across the countdown in landscape, Row B the same sweep in portrait, and Row C samples per-second motion to check the scene keeps moving), then critiques it against the contract and iterates.

Once a scene passes, Python integration scripts splice the verified C++ into the firmware: adding the enum id, name, scene function, finale, and dispatcher case. They validate before applying and are safe to re-run on partial batches. Hand-splicing dozens of functions into a 38k-line codebase is where silent errors creep in.

Past Time animated story-scene countdown face
Animation Model

One Face, 30 s to 90 min

Every face is driven by three independent inputs, so the same scene reads well across the whole range. A single progress value would look frozen on a 90-minute timer and starved on a one-minute one.

frac

What finishes at zero

The remaining fraction (1→0) drives the one thing that must complete exactly at zero: the crane's roof flag, the kite reaching the top, the boat docking.

elapsedSec

What keeps moving

Free-running elapsed seconds drive continuous motion at a constant, watchable pace regardless of the total duration, so something is always moving between the things that matter.

totalSec

How much content

The total duration scales the amount of stuff in the scene. Splitting "what finishes" from "what moves" from "how much" is what lets one face work from 30 s to 90 min.

Finales

61 Bespoke Time's-Up Animations

"Time's up" deserves a payoff. 61 of the 101 faces have a custom finale, dispatched per-face from Finale.cpp via runFaceFinale(); faces without one fall back to a chosen generic alarm animation (Flash, Rings, Checker, Text, Wipe, or Border).

Scale

Over-the-Air Updates, With Rollback

The 101-face scene library and its finales make up the bulk of the firmware. The whole app fits inside a single over-the-air update slot with room to spare, on a two-slot layout so a failed update can roll back to the last working version. Panel is 250×122, 1-bit.

Power Engineering

The E-Ink Lifecycle

Graceful Sleep

Deep-Sleep Park

When idle, paused, finished, or soft-powered-off (hold EXIT ~1.5 s), the ESP32-S3 enters true deep sleep and any of the 5 buttons wakes it via EXT1. The bistable e-paper holds its last frame with no power, so aggressive sleep is free. The board has no battery-sense calibration, so no measured standby current is claimed. Soft-off standby is weeks, not months, because the USB-serial chip and regulator still draw; a hardware switch on the battery lead is the path to true zero-drain.

Hardware Quirks

Driver Profiling

Identical part numbers don't guarantee identical glass. On a replacement board the SSD1680 panel ghosted on the original GxEPD2 profile. The fix was a one-line controller-class swap to GxEPD2_213_GDEY0213B74 (same 250×122 SSD1680 controller, different waveform), a reminder to confirm driver waveforms against the actual glass batch.