
Replay
A local-first timeline that turns a camera roll back into trips and days.
- Year
- 2025
- Role
- Design and engineering
- Stack
- TypeScript · Electron · SQLite
The problem
A camera roll is chronological and nothing else. The structure you actually remember — the trip, the day, the afternoon something happened — exists in your head and nowhere in the file system.
The approach
Replay reads a photo and video library on Windows and reconstructs that structure locally. Nothing is uploaded; the library never leaves the machine.
- Clusters captures into trips, days and moments from time and location.
- Local-first by design — no account, no sync, no cloud dependency.
What was hard
Clustering is easy to make work on your own library and hard to make work on someone else's. Threshold-tuning that felt correct on a dense London camera roll fell apart on a sparse one.
What I'd change
The clustering is tuned, not learned, and tuned against my own library — which is dense, urban and heavily photographed. On a sparse library the same thresholds produce either one enormous bundle or a hundred singletons. The fix is not better constants; it is deriving the thresholds from the library's own density before clustering anything, so the parameters are a property of the collection rather than of mine.
Local-first was the right constraint and it cost more than I expected. No sync means no second device, and the moment someone has a phone and a laptop the model quietly stops matching how people actually keep photographs. I would keep the default and add optional, explicit sync rather than pretending the need is not there.
I never measured it against a genuinely large library. Electron and SQLite are fine at the sizes I tested, and "fine at the sizes I tested" is exactly the sentence that precedes a performance problem.