Retrospect

Your Last.fm history against the actual sky. Every song you’ve played, set against the real planets, moons, solar storms and eclipses of that night, then held to a standard astrology never asks for.

Questions, the same for everyone
12corrected for asking 12
Fake histories given a false yes
9.4%1,000 with no sky in them
Plays in my history
500ksince October 2009, one account
Hosting cost
$0by design
The problem

Astrology never has to be right

TL;DR

The familiar astrology claims are never actually checked against anything. Retrospect makes them measurable, by testing each one against a real almanac of the sky and the music you actually played.

“Mercury retrograde makes you revisit the past” is unfalsifiable as usually stated. It is not unfalsifiable in principle, though. You need two things: a record of what someone actually did, and a real calendar of when the sky did the thing. A Last.fm history is the first. An ephemeris is the second.

Retrospect's landing page: a gold Saturn mark on deep navy, the headline 'Every song has a sky.', a field asking for a Last.fm username, and below it NASA's photo of Earth with the Moon's shadow over Texas during the April 2024 eclipse.
One field, and a real night underneath it: the 2024 eclipse, seen from a million miles out

Every planetary window is computed, not looked up. Retrogrades, full moons and the signs the planets sit in come from planetary positions via astronomy-engine. Solar storms, flares and photos come from NASA, and asteroid flybys from JPL. Below the field, the landing page shows the goods on a made-up listener’s history: a sample song’s sky, a sample night, tonight, and a sample set of answers.

Retrospect's landing page on a phone: the Saturn mark, the headline, and the username field stacked on a narrow screen.
The same front door on a phone
The hard thing

What if the answer is no?

TL;DR

Often the honest answer is that the cosmos didn’t move your listening in any meaningful way, which is a hard thing to hand someone who just waited for half a million songs to load.

The honest version of this app answers no most of the time. That is the whole premise, and it is also a product problem, because “we checked, nothing happened” is a terrible thing to show someone who just waited for half a million plays to sync.

So the first visit is paced like a story rather than dropped as a dashboard. Four screens: the scale of your history against the sky, your wildest night, the song with the strangest sky, and only then the answers. Every listening figure from here on is one real history, mine: 500,413 plays since October 2009, through 216 times Venus changed sign and 201 solar storms. Someone else’s numbers will be different, and that is the entire point of the thing.

Astrology is the question. Statistics is the answer. If the sky is innocent, the app has to be willing to say so, and still be worth the wait.
The design constraint

My wildest night, by the sky, was the total solar eclipse of April 8, 2024. I played 80 songs. A usual Monday is 79. The last screen sorts the answers to twelve fixed questions, explained below, into jars. Mine came back zero Yes, three Maybe, two Not clearly and seven No.

The last screen of the first visit on a phone: 'So, does the sky move you?' above five jars reading 0 Yes, 3 Maybe, 2 Not clearly, 7 No and 0 Too early, then 'Mostly no. Here's exactly how, and the three maybes.'
The payoff of the build-up is mostly no, said plainly
What it is

Tonight, every night, and the whole sky

TL;DR

Three views of the same history: tonight’s real sky and what it has meant for you before, a calendar of every night you’ve listened, and a wheel you can turn through all of it.

Tonight opens on the sky right now and lines it up with your own answers: Venus is retrograde until November 13, and here is what Venus retrograde has looked like in your listening so far. Every night is a calendar of every night you’ve listened, filterable down to the storm nights, the flare nights, the eclipses and the full moons. Sky is a wheel of the planets with a timeline of all your years under it.

Retrospect's Tonight view: 'Sunday night, Oct 4' over a wheel of tonight's planets, then 'Tonight, for you' with rows for Venus retrograde marked Maybe, Venus in detriment marked Not clearly, and a solar storm marked Maybe.
Tonight's real sky, and how this listener's listening went under skies like it
The Every night view: filter chips for all nights, 243 storm nights, 132 X-flare nights, 76 eclipses and full moons, above a calendar of September 2026 where each night carries its moon phase.
Every night since 2009, each one a door
The Sky view: a large zodiac wheel with tonight's planets and the date in its center, above a timeline of plays since 2009 with markers for events along it.
Sixteen years on one timeline, and a wheel that turns with it

Anything you open, whether a song, a night, a planet or one of the questions, opens as a sheet with its own address. A link to ?q=fullmoon opens on that answer. A link that lands in a chat unfurls into a share card drawn from the answers already stored, never recomputed, so the card can’t say something the page doesn’t.

Compare puts two listeners’ answers to the same twelve questions side by side, and stops there. The old version named one of them the more sky-ruled listener. That ranked two people on results that were mostly chance, so there is no winner and no score.

The Tonight view on a phone: the three view tabs, 'Sunday night, Oct 4', the wheel of tonight's sky, and the first row of 'Tonight, for you'.
Built for a phone first
The rule

Facts are free, claims are earned

TL;DR

The app can tell you anything that’s simply true about the sky the night you first played a song. Only the twelve questions are allowed to say the sky changed what you played.

One rule governs every line of copy. A pairing is a dated fact: I first played Twilight by Elliott Smith at 4:04 p.m. on August 1, 2012, under a full moon, with Mercury retrograde. I’ve played it 148 times since. That is all true, and none of it says the moon did anything.

Every pairing that touches a question links to it, in the same words every time: “Coincidence or pattern?” and which of the twelve has the answer, or needs more nights to give one. The fun stays, and the claim goes where it can be checked.

A song sheet on a phone for Twilight: the night's space weather, NASA's picture of that day, 'You've played it 148 times since.', and a link reading 'Coincidence or pattern? Question 1, on Mercury retrograde, has the answer.'
A fact, and a pointer to where the claim would be tested
The math

Twelve questions, asked the same way for everyone

TL;DR

Every result is re-run against two thousand fake skies, made by sliding the real calendar of events to random dates. There are twelve fixed questions, and the bar for a yes accounts for asking twelve at once.

Any large listening history shows some difference during retrograde, because any two arbitrary buckets of days differ. The question is whether the difference beats what chance produces on its own.

So every question is checked against fake skies, a circular permutation test. The real calendar of events is slid to 2,000 random start dates against the same listening, and the real result only counts when it beats most of them. Sliding matters more than it sounds: a plain reshuffle scrambles both the calendar and the listening, and hands back a version of chance that is easy to beat. Sliding keeps seasons, weekly rhythm and the streaks people listen in. The fake skies keep the shape of your actual life.

The random slides are fixed per listener, per question and per version of the engine, so the same history gets the same answer every time. They’re drawn as fractions of the history’s length, so a new day of plays shifts them only slightly instead of drawing a fresh set.

The twelve are fixed and the same for everyone. Each gets one word. Yes needs two things: it has to survive a correction for asking twelve questions at once (Benjamini-Hochberg, at 10%, for anyone checking), and chance alone has to produce a swing that big less than one time in twenty. Maybe is under one in ten, Not clearly is under about one in three, and anything weaker is No. Fewer than six events, or fewer than 500 plays inside them, is Too early. The size and direction of a swing go in the sentence, never in the word.

A grid of the twelve questions with their words: Mercury retrograde No, Full moon Maybe, New moon No, Venus at home No, Strong Moon No, Venus and Mars No, Solar storms Maybe, Big flares No, Mars in water Not clearly, Venus in detriment Not clearly, Venus retrograde Maybe, Mars retrograde No.
The same twelve for every listener. None of mine came back Yes.
The full moon

The one finding, asked again

TL;DR

The previous version of Retrospect told me full moons kept me up late. Asked again with the correction, the swing is still there, but chance makes one that big too often to call it real.

The previous version of Retrospect didn’t have twelve questions. It had a sweep: five skies against five measures, 25 combinations, each with its own scramble test. On my history exactly one cleared the usual bar, a full moon keeping me up past midnight, at +33%. The app called it an iron grip.

The old write-up of it named the catch: run 25 tests at the usual bar and about one false positive is expected from chance alone, and one came back. It chose to label results by strength rather than correct for that. The grip meter measured how big a swing was. Whether it was real rested on a single test that knew nothing about the other 24.

The redesign asks the full moon once, in a fixed question, with the correction. On 4 October 2026 it answers Maybe. Across 208 full moons, about a 40% bigger share of my plays came after midnight, so the swing held up. The evidence didn’t: chance alone produces a swing that big about one time in eleven, and once the other eleven questions are allowed for, it isn’t close to the bar for a yes. The likely range runs from 6% smaller to 108% bigger, which includes no effect at all.

The full moon question sheet: 'Does a full moon change how late you listen?' answered Maybe, with 'a 40% bigger share of your plays came after midnight, which could be chance', a bar sitting at 'Could be chance', a range from 6% smaller to 108% bigger, 208 full moons, and the math: p = 0.091 from 2,000 shuffled skies, adjusted p 0.37.
The swing survived. The evidence for it didn't.

The method got better, and the one striking finding didn’t survive it.

The same full moon question on a phone, reading Maybe with the chance bar and the range.
Plain words first. The p-value is behind a switch.
The proof

A thousand listeners with no sky in them

TL;DR

To check the new bar, the app is run on a thousand made-up histories where the sky does nothing. Fewer than one in ten get a false yes. Without the correction, about four in ten would have had a result that looked real.

The correction is only as good as its false alarms, so the repo tests it directly. npm run null-test builds 1,000 synthetic listening histories with no sky effect in them at all, runs all twelve questions on each, and counts how many get any Yes.

On the current code it was 94 of 1,000, or 9.4%, against a ceiling of 13%: the 10% the correction aims for, plus room for chance. Without the correction, 393 of those 1,000 histories, 39.3%, had at least one question that cleared the usual bar by chance. That is the failure the correction exists for: ask twelve questions of a sky that does nothing, and four histories in ten turn up something that looks real.

The full run is meant for any change to the engine or the answer rule. A quicker version, 100 smaller histories with a looser ceiling, runs in CI with every other test, and it also checks that the same histories would break that ceiling without the correction. Drop the correction and the build goes red, which is what “it can’t happen again” means here: not a promise, a check.

When the sky is innocent

Your habits leave fingerprints anyway

TL;DR

Even when the planets did nothing, your listening still says plenty: the hours you play most, the day of the week you lean on, your longest unbroken streak.

The listening profile needs no astronomy at all, and it says so in its own subtitle: “Not the sky.” Archetypes, golden hour, the day you lean on, loudest month, pace and best streak, all computed from the same timestamps, all there whether or not the planets did anything.

Your habits: Comfort Creature at 66 percent, Crate Digger at 13 percent, Daylight Listener at 2 percent after midnight, then golden hour 3 to 5 p.m., Fridays, December, 81 a day with a best streak of 715 days, above a bar chart of plays by hour.
Comfort Creature: 66% of my plays are songs I already knew
The same listening habits stacked on a phone screen.
Dense figures that still hold their shape on a narrow screen

Sleep sounds were the quiet threat to all of this. Rain, white noise and ASMR tracks run for hours unattended, and those plays add up to counts that can dwarf anything you actually chose that week. The previous version made filtering them a switch. Now they’re left out of every measure automatically, because a data-quality fix shouldn’t depend on someone finding the toggle.

The part nobody asked for

A loading screen worth waiting through

TL;DR

The first load can take quite some time, especially for Last.fm accounts that have many years of listening history, because the music service only hands over so much at a time. That wait got a small solar system to watch instead of a spinner.

The first sync of a large history takes minutes, because Last.fm is rate limited and the history is pulled page by page. That wait wasn’t going away, so it got a planetary system instead of a spinner. Every planet is a year of your history recovered so far: they spawn, orbit, occasionally collide and explode, while the count climbs underneath and the copy keeps you company (“Mercury has never once hurried.”).

The sync is built for short server calls. Each one pulls pages for eight seconds, four at a time with a quarter-second pause between batches, to stay polite to Last.fm. It saves once at the end, because every save rewrites the stored history. And it throws out duplicates when reading, because new plays arriving mid-download shift which songs land on which page.

The production screen, replayed with simulated progress so nothing had to re-sync
What it cost

Built to cost nothing, on purpose

TL;DR

It runs entirely on free tiers, and the storage was chosen so that a sudden rush of visitors can’t produce a surprise bill.

Retrospect runs on Vercel’s free tier with Cloudflare R2 for storage. R2 was chosen specifically because it has zero egress fees: it charges nothing to serve data out, so a rush of visitors can’t generate a surprise bill, and Vercel Hobby pauses rather than charges when limits are hit. Two daily jobs do the housekeeping: one fills and refreshes NASA’s data, the other deletes any history nobody has looked at in 90 days. None of the NASA or JPL sources need a key.

Birth charts are the one thing that never touches a server. The chart is a sheet on the Sky view: Sun, Moon, Mercury, Venus and Mars at the minute you were born, and a rising sign if you add a place, worked out in the browser and saved only on that device. Birth date, time and place are a more sensitive payload than anything else the app handles.

The birth chart sheet on a phone: fields for birth date, local time, the birthplace's UTC offset and optional coordinates, with a note that everything is computed in the browser and saved only on the device.
Computed in the browser and stored there. The server never sees it.