The short version: five 360° panoramas a day, drawn from a curated pool of 374 historically anchored scenes. You drop a pin on Earth and pick a year. Both get scored on a curve calibrated to the scene's era.
The longer version is below — the math, the pools, the pipeline, the rules.
Each daily session is five rounds. Each round:
Each round scores two dimensions independently: distance and year. Both use Gaussian decay — the score falls off smoothly with distance from the right answer, not via hard cutoffs.
The interesting design problem was the year score. A 50-year miss in 2000 BCE shouldn't score the same as a 50-year miss in 1980. The historical record for the Modern era is dense — 50 years off is a generation, three regimes, and the Internet. The Ancient era's record is sparse — 50 years off is dynastic noise.
So the year score is scaled per era. Each era pool has its own sigma (the curve's standard deviation), tuned to the era's typical date-resolution:
| Era | Year range | Year sigma (≈) | What that means |
|---|---|---|---|
| Ancient | ≤ 500 CE | ~500 yr | Loose. A century off can still score well. |
| Medieval | 501–1500 | ~200 yr | Medium. Decades count, but not severely. |
| Renaissance | 1501–1900 | ~80 yr | Tighter. The historical record is denser. |
| Modern | 1901+ | ~25 yr | Tight. A decade matters. |
Sigma values are approximate and may shift as the calibration loop runs against player data.
Scene: Hammurabi's Babylon, ~1792 BCE (Ancient pool, sigma ≈ 500 yr).
Your guess: 1850 BCE. Miss: 58 years.
58 ÷ 500 ≈ 0.12σ off. The Gaussian score is exp(-(0.12²)/2) ≈ 0.993 — you keep ~99% of the max year-points.
Now imagine the same 58-year miss in the Modern pool (sigma ≈ 25): 58 ÷ 25 ≈ 2.32σ off. The Gaussian score is exp(-(2.32²)/2) ≈ 0.07 — you keep ~7% of the max year-points.
Same numerical miss. Wildly different scores. That's per-era calibration.
Distance scoring uses a similar Gaussian decay, calibrated to give meaningful credit even when you pin the right continent but the wrong country. The exact distance sigma is the same across eras — Earth hasn't moved.
Scenes are bucketed into four non-overlapping era pools. Daily mode draws one scene per pool plus a fifth from the full set; Casual mode (coming soon) lets you pick a pool and play only from it.
As of 2026-05-13 the library has 374 scenes:
| Pool | Year range | Scenes |
|---|---|---|
| Ancient | ≤ 500 CE | 89 |
| Medieval | 501–1500 | 89 |
| Renaissance | 1501–1900 | 98 |
| Modern | 1901+ | 98 |
| Total | 374 |
The pools are deliberately non-overlapping. A scene tagged 500 CE is Ancient; 501 CE is Medieval. The transitions aren't historically perfect — no four-bucket split is — but they map roughly to broad-strokes Western periodization and keep the era-sigma calibration sane.
Four pools give enough granularity that "Ancient" doesn't mean both Sumer and Charlemagne, while staying few enough that Casual mode is a quick mental decision ("am I in a Medieval mood?"). Three was too coarse; five tested as analysis-paralysis.
Each panorama is an AI-generated cinematic stitched into a 360° viewable sphere. The pipeline runs from scene draft to live game in about two minutes per scene, at roughly $0.36 in API costs:
gpt-image-2 generates the panoramic still from the prompt. About 90 seconds.scenes.json; the panorama gets uploaded to Cloudflare R2./sphere-demo.html on localhost to spot-check before it ships.Every player who opens the game on the same calendar day gets the same five scenes. This is the Wordle move — the spoiler-risk is the social hook. If you and your group chat are playing on 2026-05-12, you're guessing the same Spartans-at-Thermopylae panorama at the same moment.
The daily seed is a deterministic hash of the date (UTC). Five scenes are drawn: one each from Ancient / Medieval / Renaissance / Modern, plus a fifth from the full pool. Recent scenes (the rolling 10 most-recently-used) are excluded to avoid same-week repeats.
The seed is cached server-side in Upstash Redis for the day, so every request to /api/round?mode=daily resolves to the same scenes without recomputing the draw.
Effective 2026-05-07: every scene must occur on Earth (surface, atmosphere, or oceans).
This rule was added after a few off-planet scenes (Apollo 11 on the lunar surface, for example) shipped during the early library expansion. They were cool. They were unguessable. There was no pin you could drop on a flat map of Earth that scored well, because the scene wasn't on Earth.
The scoring math assumes Earth as the substrate. Breaking that assumption breaks the game. So: Earth-only, no exceptions, refused at draft time.
Effective 2026-05-08: every panorama must render in full color, even when the source reference photo is famously monochrome.
D-Day's Robert Capa photos are black-and-white. The first Moon landing's TV broadcast was grainy monochrome. Famous Civil War daguerreotypes are sepia. The instinct of an image model trained on those references is to reproduce the monochrome look. But Where in Time isn't a documentary recreation — it's a cinematic placement of you, the player, inside the scene as if you were there. You wouldn't have seen Omaha Beach in black and white. You'd have seen the slate gray of the Channel, the green of grass on the bluffs, the red of the blood in the sand.
The skill enforces this via an explicit color directive in the visual-style block and a B&W ban in the negative prompt.
Daily play is fully anonymous — no signup, no account. But leaderboard placement is a different problem. If you can submit a perfect-score result without identifying yourself, the leaderboard fills with anonymous "5000/5000" entries that are either lucky guesses, screenshot reads, or bots.
So once a session score crosses a threshold, the game prompts a soft sign-in via Google OAuth. You can decline — the score still records anonymously — but the leaderboard column is gated on the OAuth-verified identity. A pseudonym filter blocks the obvious impersonation attempts at submission time.
It's a low-friction filter that keeps the leaderboard meaningful without making the daily a signup wall.
index.html, one main.js, one styles.css. No framework, no build step, no bundler.whereintime.ai in production.main.The whole thing is intentionally boring infrastructure. No SPA framework, no GraphQL, no microservices, no Kafka. A game like this doesn't need them, and the "no build step" rule keeps iteration fast.
Casual+ (era-pick, unlimited daily rounds) is the next ship. Mastery mode and multiplayer ("Versus") are on the roadmap but not v1. See the FAQ for the public roadmap and press kit for assets.