A daily history game that opens into a browsable web of the moments that changed history — and the people, places, and ideas around them.
▶ Play the daily at momentsofthepast.com
A personal side project. It started 2026-05-30 as a WhenTaken-style guessing game and has grown into something bigger since. This README describes the current vision and state.
The main menu — play today, replay a past day, or browse the library.
Guess the year by dragging the card along a continuous timeline: ease it for a single year, pull hard to fly across millennia, then lock in.
Lock in for a precision score — then the moment opens into the reader: a written page, full of links, over the real painting.
Browse the whole library, era by era.
Or replay any past day from the archive.
Every page threads to others through links in the prose — click one and keep digging.
On a phone, panels become full-screen drill-downs:
The hook is a game. The soul is a network.
Under the daily game sits a growing web of nodes — each one an image and a few paragraphs of text, linked to the others. Most nodes are moments (a single datable, locatable event). Many are not: the people, places, and ideas those moments touch. Every page links onward through its prose, so you can keep digging — Marathon → Darius the Great → Thermopylae → and on.
The ambition is thousands of these nodes, added to over time, forming one connected graph of history you can wander. You come for the daily game; you stay for the network.
The ground rules:
- Real images. Every node carries an actual painting or photograph — no synthetic art — shown large. Most are public domain; some are Creative Commons, credited on the card; a few copyrighted works appear at reduced resolution for commentary under fair use. The authenticity is the appeal.
- Text written for the game. Every page is written here (facts aren't copyrightable), drawing on Wikipedia and other sources, and tuned to invite the next click. A single app-wide acknowledgment credits Wikipedia, rather than a footer on every page.
- Curated links. A few threads per page, chosen because they're worth following — curation over completeness, the same principle as the deck itself.
It's a slow-build, evergreen library. LLMs help maintain it: checking that links resolve and facts hold up, and suggesting new connections as it grows.
A daily set of moments. For each one:
- See an image and its title — e.g. "Caesar is assassinated in the Senate." The title names the depicted moment in plain language.
- Guess the year. Drag the moment card along a continuous timeline — a velocity "shuttle": creep for a precise year, pull harder to fly across millennia. Arrow keys nudge ±1.
- Lock in → the reveal. The true year slides in, the gap closes, and you're scored on precision (see below). The moment then opens into the reader.
- Dig. The reader's prose is full of links. Click an entity to read its page; click another moment to read about it (and, eventually, to play it). Every page leads onward.
Scoring is about precision, not recognition. Everyone knows Cortés is "1500s-ish"; almost nobody knows 1519, not 1521. So even a giveaway moment stays competitive on the exact year. Tolerance is era-scaled — each card carries a half-life suited to its era — so a 50-year miss in antiquity costs about what a 5-year miss in 1969 does.
Nodes. Two kinds:
- Moments — a single event you can pin to a year and a place (a battle, a signing, a sighting, a launch). These can be cards you play.
- Topics — the people, places, objects, and ideas a moment touches (Darius the Great, the hoplite, the Bayeux Tapestry).
Each node is a high-resolution image (public domain or freely/fair-used, credited on the card), several paragraphs of prose, and a handful of inline links.
Links. Woven into the text, two kinds, styled distinctly:
- Topic links — brass. People, places, ideas to read about.
- Moment links — steel-blue. Other moments in (or bound for) the deck — things you could also play, not just read.
The reader. Two panels at most: the moment's own page, plus a single reader panel that any link replaces in place. You browse a web of topics without panels piling up. It floats over the painting, which stays put.
Stored locally, on purpose. Everything lives in the app rather than being fetched live, which keeps control over image quality, voice, length, and which threads to follow — none of the chrome of an embedded encyclopedia. (An early live-Wikipedia version worked; local is the better product.)
What earns a node — point-like (one thing to picture), depictable, and worth the reader's time. Moments also need to be datable, locatable, and consequential ("if the why it mattered doesn't write itself, cut it").
Editorial care:
- Show history honestly — including its violence. The moments that changed the world are often battles, executions, assassinations, sackings, sieges. We don't sanitise them: a fallen city has bodies in it, and the image can show that. The point is the real event, not shock for its own sake — but we never cut a pivotal moment because it's bloody.
- Sacred-figure care — sacred subjects shown only through a tradition's own art: Muhammad appears solely as the veiled figure of the historic Ottoman miniatures, the Buddha through devotional sculpture and painting, the Crucifixion handled with care. One consistent policy.
- Accuracy is the brand. For a history project, getting a fact or an image wrong is a credibility-killer — every node gets a human (and LLM) accuracy check.
Global balance — deliberately not all-European, and spread across the whole timeline (Antiquity → Contemporary). The scarcest resource is recognizable non-Western moments.
A static, client-side app — React + Vite + TypeScript, no backend.
- The timeline shuttle — the make-or-break interaction. Drag the card; pull distance sets scrub velocity (a linear floor blended with a power curve, so small leans are responsive and big pulls fly across millennia); release snaps to a whole year; arrow keys nudge ±1. Tuned by feel. (
src/useShuttle.ts) - The card art — the painting is shown still, cover-cropped to the 3:2 frame, no effects on the image itself (its authenticity is the appeal). The card grows to fill the available space. All the lock-in satisfaction lives in the mechanism — the pin seating onto the timeline rule, not a filter on the art. (
src/MomentCard.tsx) - The reveal — a gap-closing animation between your guess and the truth, a precision score shown as
N / 5,000, then a frosted overlay reader. (src/scoring.ts) - One content graph — every page, moments and topics alike, is a node in a single registry, linked by inline
[label](id)references whose colour derives from the target's kind. A dev-time integrity check flags links that point nowhere and nodes nothing points to. (src/data/content.ts) - The deck — moments that carry art are dealt as playable cards. The daily draws five, date-seeded so everyone gets the same set on a given day, and an archive lets you replay any past day. There's no login and no backend — each day's progress is saved in the browser (localStorage), so a half-played day resumes and a finished one reopens at its summary. The rest of the graph — topics, artworks, moments not yet arted — is woven through the prose. The data in
src/data/content.tsis the source of truth: every moment carries astatus(published/ready/draft) and tags, and the whole library is browsable in the app's Browse mode. One guide supports it,EDITORIAL.md: the curation principles, the image-licensing policy, the spines, the rejected list, and the prose voice — how we choose a card and how we write it. - Mobile — a working first pass: the card scales, and panels become full-screen drill-downs.
Known-good checkpoints are git-tagged; the loved feel/layout baseline is loved-feel-v1.
Requires Node 20 (see .nvmrc). No backend — it's a static client-side app.
npm install
npm run dev # Vite dev server at http://localhost:5173
npm run build # type-check + production build → dist/
npm run preview # serve the production build
npm run check # content-integrity gate + tsc --noEmit
npm run thumbs # regenerate Browse thumbnails (macOS sips)
npm run credits # regenerate CREDITS.md from the content graphIn a dev build, open http://localhost:5173/?review to walk the whole deck in chronological order — every playable card end to end, for proof-reading. It's dev-only and can't ship in a production build.
src/
App.tsx routing + views (menu / play / archive / browse), reveal + reader panels
Landing.tsx the main menu
Archive.tsx play any past day
Timeline.tsx the era bands, ticks, and reveal markers
MomentCard.tsx the card / shuttle handle
useShuttle.ts the velocity "shuttle" — the make-or-break drag interaction
scoring.ts era-scaled precision scoring (a half-life per card)
Browse.tsx the read-only library timeline
Summary.tsx the session-complete results + share card
share.ts the Wordle-style share text
data/content.ts the single content graph — every node and its links (source of truth)
config.ts the playable span, era bands, year formatting
scripts/ content-integrity check, thumbnail + CREDITS generation
public/art/ the images (each individually sourced — see CREDITS.md)
Hash routing (#/ menu · #/play · #/archive · #/browse · #/n/<id>) keeps every node linkable and the back button working, with no router dependency. Deployed on Cloudflare Pages.
Made by Kim Frithiof.
Every image is individually credited in CREDITS.md. The code is MIT; the prose is CC BY 4.0; the images are each individually sourced (mostly public-domain, some Creative Commons, a few reduced-resolution fair-use) and are not relicensed — see LICENSE-CONTENT.md. The full editorial and image-sourcing policy lives in EDITORIAL.md.
The point was never volume for its own sake. A deeply-linked, well-curated few hundred nodes beats a shallow thousand.