longplay
Hyvinkää, Finland · 31 October to 15 November 2026

From mirror to map

What happens when a curious systems thinker1 gets an interesting problem, two AI assistants and free hands? — Sixteen days later there is a place to find new music again. This is that story as product development: one listener, one problem, and a lot of listening to both.

What happens when a curious systems thinker1 gets an interesting problem, two AI assistants and free hands? — Sixteen days later there is a data platform, an agent control plane and eleven architecture decision records. It serves one user. This is that story as engineering, told with love and a little shame.

228,033scrobbles since 14 February 2005, by 19,814 artists
16 daysfrom the first commit to the version on this page
437commits: 402 by Kiro Web, 33 by me, 2 by Claude
101plays of a band I had never heard, the day the tool pointed at them
1user in production. Five planned, all within shouting distance
Sunday, 8 November exhibit a

The second-busiest day in twenty-one years

the acceptance test was a sunday

On Sunday the eighth of November I played 290 tracks. In a listening history that starts in February 2005, only one day has been louder: an October Tuesday in 2005 with 306. A hundred and one of those 290 plays belonged to one band, Novulent3, shoegaze from the United States with a little grunge in its shoes. From lunchtime on I worked through record after record.

That morning I did not know they existed.

A week earlier, neither did the thing that introduced us.

Every system needs one test that matters. Mine passed on Sunday the eighth of November, when a ranking function I did not write surfaced a band I had never heard, and I played them 101 times before bed. 290 plays that day in total, the second-highest daily count in a dataset that starts in February 2005.

I can prove the order of events, which is the embarrassing part. The app's data export is checked into git4. Yes, Parquet5 files, in git, on purpose. It means every commit is a snapshot of what the system believed at that moment, and git show is my audit log.

3 Nov, 22:13
The first export with a list of artists I have never played is committed. Novulent3 is in it.unheard_neighbours.parquet @ 5f68220 · agreement score 2.21 · because I listen to Trxy!, quannnic and FlyingFish
8 Nov, 12:11
I commit a fresh export. Novulent is still in the unheard list.d10ed09 “chore: update data”
8 Nov, 12:45
First scrobble. Thirty-four minutes after that commit.scrobbles.parquet · played_local 2026-11-08 12:45:27
8 Nov, by night
101 plays of Novulent, 290 plays in all.Calendar view, “Busiest Days”: 11 Oct 2005 (306), 8 Nov 2026 (290)
9 Nov, 00:30
The next export no longer lists them. They are not unheard anymore.unheard_neighbours.parquet @ 3ebbe23 · 0 rows for Novulent
The receipt. The app's data export is committed to the repository, so git4 remembers what the tool knew and when it knew it.*

* A habit of the early days, and not one to copy. The platform work replaces the committed export with a proper API, and the receipts move from git history to an event log.

This is the story of that week, told the way a product gets made: a listener with a problem, a hero to borrow from, a handful of decisions about what to build first, and a lot of pressing play. Flip the switch at the top and the same days are told as engineering instead.

This is the story of that week, told the way a system gets built: a load profile, a stack, a pair of agents, and an honest accounting of how much machinery one man pointed at his own record collection. Flip the switch at the top and the same days are told as a product story instead.

The problem the incident

Finding new music got harder

a regression nobody filed

I have kept a diary since 2005 without writing a word of it. Last.fm6 wrote it for me, one line per song. Read as a diary it says something I had felt for a while and could not prove.

I have twenty-one years of telemetry on one user. Plot the right metric and there is a regression in it: throughput keeps climbing while the rate of new entities falls off a cliff.

Artists I heard for the first time, per year

Bars: artists whose first play falls in that year. Line: all plays that year. 2026 runs to 13 November.

first-heard artists2026, partial yearplays
Source: my own scrobbles.parquet, 228,033 rows. In 2022 I met 4,172 new artists, two hundred for every thousand plays. By 2025 it was 1,746, or 74 per thousand, while I listened more than ever.

The listening went up. The finding went down. Somewhere after 2022 my music stopped surprising me at the rate it used to, and I do not think my curiosity retired.

200 first-heard artists per thousand plays in 2022. 74 in 2025. Same user, same client, more traffic. Something upstream changed.

What the public record says

the upstream changelog

On 4 December 2023 Spotify laid off about 1,500 people, seventeen percent of the company.7 One of them was Glenn McDonald, who had spent a decade there mapping music into 6,291 genres and publishing the result as a website called Every Noise at Once.8 His genre data sat underneath features like Daily Mix and Fans Also Like.8 When he lost his job he lost the data feed, and the site froze in place. The Friday ritual of browsing every new release by genre simply stopped.9

McDonald says the system he helped build held more than 6,500 human-guided genres by the end, and that the automated one that replaced it has 708 tags.10 That is his account, and I have no way to audit it.

Then the doors closed for the rest of us. On 27 November 2024 Spotify cut new apps off from Related Artists, Recommendations, Audio Features and Audio Analysis, the exact tools a hobbyist would use to build their own discovery.1112 In February 2026 the hobbyist tier shrank again.13

Then came the breaking changes. On 27 November 2024 Spotify closed Related Artists, Recommendations, Audio Features and Audio Analysis to new apps, with no deprecation window to speak of.1112 In February 2026 development mode shrank again: Premium required, one client ID, five users.13 Five users. Remember that number; it comes back.

There is also research, some of it Spotify's own. A large 2020 study of Spotify listeners found that algorithm-driven listening goes together with narrower listening, and that people who broaden their taste do it by leaving the algorithm and exploring by hand.14 A field experiment the same year measured personalised recommendations cutting the diversity of what individuals played by 11.51 percent.15 MIDiA Research's consumer surveys describe passive discovery pushing streams up and artist discovery down.16

What it does not say

root cause: not established

I would love to tell you that recommendations went downhill the Monday Glenn McDonald was let go. I believe something like it. The evidence will not carry the sentence. He built the genre system, not the recommender. Nobody has published a before and after. And my own curve peaks in 2022 and is already falling through 2023, a year before that December.

So the villain here has no face. It is a habit — the habit of serving people more of what they already played — and it was never one person's fault. What I can say plainly is this: the shovels I used to dig with were taken away one by one, and the curve never came back up.

I would love to write “recommendations degraded when Glenn McDonald was let go” in the postmortem. I believe something like it. It would not survive review. He built the genre system, not the recommender. Nobody has published a before and after. And my own curve peaks in 2022 and is already falling through 2023, a year before that December. Correlation, a small sample, one very biased observer.

What is established: the endpoints I would have built on are gone, the best map of the territory is frozen, and my metric never recovered. So the design constraint writes itself. No dependency on Spotify for discovery. Open data, my own storage, my own ranking.

If nobody is going to hand me a shovel, I am going to have to make one.
If the upstream will not fix it, vendor it.
The user capacity planning

A persona of one

the load profile, such as it is

Product people warn you against building for yourself. I did it anyway, with one advantage most teams never get: twenty-one years of honest usage data on the only user. The diary is unsentimental about me.

The spine

Guitars with weather in them

Radiohead 4,265 plays. Muse 3,747. Manic Street Preachers 3,102. Sigur Rós 3,041. Then Kent, The National, Mew, Mogwai, Interpol. Indie, shoegaze, post-punk, dream pop and post-rock carry most of the weight.

Home

Finnish, stubbornly

1,112 Finnish artists and 36,203 plays. Rubik, Pariisin Kevät, Antti Autio, PMMP, Lapko, The Holy. My most played album of all time is Antti Autio's Täällä sinua kaivataan, 687 plays, ahead of The Bends.

The tail

Curious to a fault

The ten biggest names are only 11 percent of everything. 10,137 artists, more than half of all I have ever heard, got exactly one play. A single play is not a failure — it is a door I opened.

The job to be done

Surprise me, and show your work

I do not want a feed. I want somewhere to go digging, a reason for every suggestion, and a play button next to it. Weekends and late afternoons, mostly; five o'clock is the loudest hour of my week.

I play guitar, which mostly teaches respect for the people in that list. By day I am a Principal Engineer17 in the Sales and Store Operations unit at SOK, the Finnish retail co-operative, where system and software architecture take most of my working hours. Evenings, apparently, I am a product manager with one very opinionated customer.

Let us size this honestly, because everything after this section will pretend it is bigger.

dimension
measured
users
One. Growth plan: my family, five people in total.
data
228,033 rows in the fact table, growing by about thirty a day.
peak traffic
Me, on a Sunday. 290 writes in a day; the all-time record is 306, set in 2005.
availability target
“Is it up?” “Hang on, I'll check.”

By day I am a Principal Engineer17 in the Sales and Store Operations unit at SOK, the Finnish retail co-operative, where the systems are large and the bugs I like best are the ones that turn out to be a whole class of bug. I know what scale looks like. This is not it. Hold that thought while I show you what I built.

The inspiration prior art

The man who drew the map

the reference implementation

Glenn McDonald called himself a data alchemist, and Every Noise at Once was the proof: every genre a word on a page, placed by how it sounds, each one a doorway to a hundred artists you had never heard of.18 I admire him the way you admire people who made a serious thing playful and a playful thing serious.

Three of his ideas became my product principles.

A genre is a community. He defines it as a community of artists, practice and listeners, found in how people actually listen and then checked by a human ear.19

Distinctive beats popular. The interesting artist in a genre is rarely the biggest one. It is the one who belongs there more than anywhere else.

The listener should own the ranking. A list you can read, question and edit is a tool. A list you can only accept is a feed. He still writes about all of it on his own site.20

His book is called You Have Not Yet Heard Your Favourite Song.19 I read the title as a promise. Hold on to it; it comes back at the end.

I had a small tool in mind already, a mirror — load my Last.fm history into a database and look at it. On the second of November I asked Claude to study everything McDonald has published and tell me which of his ideas could live inside it. The answer came back with sixteen candidates, ranked, and one sentence on top that renamed the whole project: turn longplay from mirror into map.

Glenn McDonald spent a decade at Spotify building a genre system and published its output as Every Noise at Once: every genre a word on a page, placed by how it sounds.18 He is the reason this project exists, and I admire him the way you admire people who made a serious thing playful and a playful thing serious. His book is called You Have Not Yet Heard Your Favourite Song.19 The title comes back at the end.

Read as engineering, his work gives three requirements.

Genres are clusters of people, not attributes of audio. A genre is a community of artists, practice and listeners.19 So the model is a graph and a set of weights, and tags are evidence.

Rank by distinctiveness, never by popularity. Share within the group divided by share in the world. Popularity is the null hypothesis.

Every ranking is a short query the user can read and edit. He still argues for this on his own site.20 It rules out a black-box recommender on day one, and I was happy to see it go.

On the second of November I asked Claude to study everything he has published and map it onto my codebase. The report came back with sixteen candidates and one finding I would have paid for: longplay was already caching Last.fm's similar-artist lists in every response and nothing was reading them. The first discovery feature needed zero new API calls. docs/turn-longplay-from-mirror-into-map.md.

The product the system

A place to go digging

what one user apparently requires

longplay keeps my entire listening history in one small database, enriches it from open music data, and puts two dozen views and pages on top. Some show where I have been. The best ones point at where I have not. Every feature below started as a sentence about something I missed.

longplay is a pipeline, a query engine in the browser, a small API and two dozen views. Here is the tour, then the wiring.

Half a minute through eleven views, cut from screenshots of commit 2e7f1be. Cover art is switched off, so the sleeves you see are generated from each record's genre colour.
Unheard Neighbours

“Who would my favourites vouch for?”

a graph join I already had the data for

Every artist I play points at similar artists. The ones I have never played are candidates, and a candidate rises when several of my favourites agree on it. There are 8,557 that at least two agree on. This is the list Novulent was sitting in.

A favourite counts by how often I come back, not by how much I binged. Twenty plays in one evening are a crush. One play a month for five years is a relationship. This shipped first because the data was already there.

Score = the sum, over my artists pointing at the candidate, of attention × Last.fm similarity weight. Attention is the square root of plays per day, summed over days, so recurrence beats bingeing. 8,557 candidates clear the two-source bar. README.md, “Unheard-neighbour candidates”.

Edge

“Take me to the far end of shoegaze.”

a percentile and a floor

After McDonald's three lists. Sound is a genre's core. Pulse is where my taste differs most from the world's. Edge is the point: artists deep in the genre, little known, that I have never played. When a list is weak the page says so and sends me somewhere better.

Edge = tag-rank weight × (1 − percentile of log10(listeners)) within the genre's list, floor of 1,000 listeners. Pulse = my attention share ÷ the artist's share of global listeners. The page prints its own coverage: “listener counts known for 150 of 150”. README.md, “Edge of my genres”.

Every Noise and Footprint

“Where have I never been?”

umap, and a crosswalk that refuses to guess

Every Noise lays out my own genres the way his site did. Footprint draws McDonald's frozen map of 6,291 genres and lights the 1,124 I have reached. The dark words next to the lit ones are tomorrow's listening.

A UMAP21 embedding of the genre × artist matrix for my map; exact or spelling-key matches only against the frozen one, no fuzzy matching, 1,124 of 6,291 lit. src/longplay/every_noise_view.py.

New Releases

“Give me my Fridays back.”

the first job that needs a clock

Every Noise's most loved feature was new releases by genre. This is that, shaped to me: a week of records by artists I play, artists my neighbours point at, and strangers in genres I care about. One button saves the week as a playlist.

ListenBrainz fresh releases22 joined to MusicBrainz release groups.23 The week of 7 November held 1,392 releases, 1,330 after dropping compilations, live and remix records. It is also the feature that made a continuous backend unavoidable, which is how the trouble started.

Nine views at commit 2e7f1be. Select a tab; select the picture to enlarge it. Genre map data on the Footprint: Every Noise at Once by Glenn McDonald.18

What I decided it should feel like

A record shop, on a slow afternoon, with a clerk who knows you. That sentence did more design work than any wireframe. The home screen is a crate, with this week's records on top and one thing I have not played yet. Every list ends with somewhere to go next. Nothing is a dashboard for its own sake.

Principle

A list to explore, not a report

That line is printed on the discovery pages. A report tells you what happened and lets you leave. A list to explore has a play button on every row.

Principle

One genre, one colour, his colour

Shoegaze is the same hue on a chip, a sleeve, a chart and a page. The hue is the one McDonald gave it on his map: red for energy, green for dynamic variation, blue for instrumentalness.

Principle

Say what you do not know

When data is thin the page says so and shows the numbers behind it. A tool that admits its gaps earns the right to be believed about the rest.

Principle

Every name is a door

Artist, album, track, genre, country, day: each has a page, and any mention anywhere links to it. Curiosity should never hit a dead end.

longplay home on a phone: the total play count and this week's new releases.
The shoegaze genre page on a phone.
Unheard Neighbours on a phone.
It fits in a pocket, because discoveries rarely happen at a desk.

how it is wired today

The current shape is almost embarrassingly sensible. A command line fills a database, an export turns it into files, and a static page queries the files in the browser. No application server. Enjoy it; it does not last.

collect
longplay sync / enrichPython 3.12 on uv.24 Resumable, paced, cached calls to Last.fm,25 MusicBrainz23 and ListenBrainz.22
duckdb 2.0.0 fileOne local database. 2.0.0 shipped on 21 October;26 my first commit landed ten days later. Restraint was never the plan.
longplay export30 Parquet tables and a manifest with row counts and enrichment coverage.
serve
cloudfront + private s3One shared-password gate. CDK v2 in TypeScript,27 run by Node's own type stripping, no build step.28
post /askOne Lambda. Claude Haiku on an EU inference profile in Amazon Bedrock29 sees the schema, never the data.
last.fm proxyAllow-listed, cached in S3, daily budget, key in SSM Parameter Store.
explore
duckdb-wasmEvery query runs in the browser over the Parquet files, on the same engine version as the CLI.
react 19 · vite 8 · typescript 7Rolldown,30 the native compiler,31 Oxlint,32 Observable Plot and d3.33
spotify, from the pageAuthorization Code with PKCE,34 playback on Connect devices, playlists written from track lists.

decisions I would defend in a review

The model writes SQL35; the browser runs it. Ask sends only the question. The Lambda returns one statement that has passed a validator: single statement, SELECT only, an allow-list of 26 views, a row cap. The same validator guards the SQL I type myself. api/src/validator.ts.

One DuckDB36 everywhere. Python pins duckdb==2.0.0; the browser pins the matching DuckDB-WASM build exactly, because it is the first with working OPFS storage,37 and database files move between the two unchanged. scripts/check-duckdb-versions.mjs fails the build if they drift.

New packages wait a day. pnpm's release-age cooldown against supply-chain attacks,38 with one written exception for that DuckDB-WASM build. pnpm-workspace.yaml.

Motion is native. Page changes use the View Transition API39 with a shared cover element and one reduced-motion block. Covers fall back from Spotify to the Cover Art Archive40 to a sleeve generated from the genre's colour. docs/design/longplay-design-spec.md.

The process delivery

Sixteen days, three pairs of hands

402 commits I did not type

I did not type this application. I want to be exact about that, because the honest version is more interesting than either the boast or the apology.

The repository has 437 commits. Kiro Web41, the cloud-hosted version of the Kiro coding agent, made 402 of them, each session working in its own sandbox. I made 33. Claude made two. Each of us commits under our own name, so the history tells you who did what without my having to.

The repository has 437 commits. Kiro Web,41 the cloud-hosted version of the Kiro coding agent, made 402, each session in its own sandbox.42 I made 33. Claude made two. Every agent commits under its own git identity, never mine, so git shortlog is an honest org chart.

Commits per day, by author

31 October to 15 November 2026. The quiet stretch in the middle is a Monday to a Wednesday.

Kiro Agent · 402Niko · 33Claude · 2
Source: git log. Saturday the 14th has 113 commits: the day eleven architecture decisions were recorded and the whole interface was redesigned.

What I did was think, choose, listen and say no. The loop looked like this.

An itch

Mine. “Friday releases are gone and I miss them.” It starts as a feeling and has to stay one long enough to be worth solving.

Thinking, with Claude

Research, the trade-offs said out loud, then a brief for the builder, written to remove every reason to stop and ask.

Building, with Kiro Web

A cloud session takes the brief, reads the repository, plans, writes code and tests, and commits in small steps while my laptop is closed.42

Listening

I refresh the data, open the view and use it for what it is for. Either I find a band or I find the next itch. Usually both.

The tools we made to make the tool

Two things came out of the process that were not longplay at all. They exist because the loop above had friction in it, and friction is just an itch with a stopwatch.

A skill for the brief. Step two kept producing the same kind of document, so it became a reusable skill: how to choose the next feature and write the prompt an autonomous agent can finish without me.43

Kiro Remote. My two assistants could not talk to each other. One thinks in a chat window, the other builds in the cloud, and I was the courier carrying prompts between them. So we built the telephone: a small service of my own that lets the chat launch and steer the builder directly.44 Its landing page tells it as a family feud, and I am fond of it.45

A product that grows its own tools on the way is, I think, a sign the work is alive.

the delivery pipeline is also over-engineered

The prompt is a product. Every feature starts as a brief written by a skill of mine: pick the next feature, write the prompt an autonomous agent can finish without me. The rules are strict. Check the latest versions before you start. No spike the agent cannot run alone. Unblock, never ask.43

Kiro Remote. My chat assistant could not command my coding agent, so I was copying prompts between two windows like an animal. The proportionate fix was a text file. The fix I shipped is a control plane in my own AWS46 account: API Gateway, Lambda, SQS, DynamoDB, S3 and Cognito, sixteen tools over both REST and MCP, OAuth 2.1 with dynamic client registration, invite-only sign-up, passkeys.4445 It has exactly one invited user. He is very secure.

Skills in the repo. Two files in .kiro/skills/ teach every session the house rules, in the open Agent Skills format47 that Kiro loads on demand.48 When a skill and the spec disagree, the spec wins and the skill is fixed in the same change.

Least privilege. The agent has no write access to my AWS account. It can read, synthesise and test; I deploy.

Tests as the contract. About 2,400 test cases in 177 files, counted by grep across Vitest and pytest, for roughly 88,000 lines of Python and TypeScript. For a music player with one listener. I regret nothing — it is the reason 402 unattended commits did not end in tears.

Drag the slider and watch it grow. Each frame is the home screen of a real commit, checked out and run against the data that was committed with it.

1 / 9
Nine commits from the git history of nikovirtala/longplay, rebuilt for this page on 15 November.

The afternoon it went brown

My favourite frames are the sixth and seventh. The fourteenth brought a warmer, more analogue dark. It arrived at 13:19 and it was brown. Not vinyl-sleeve brown. Wet-cardboard brown, and it turned every genre colour to mud. By 15:24 the palette was pure black with a single hot pink, and a new rule had been written into the project's own instructions so that no future session would walk into the same puddle:

Frames six and seven are a design incident with a two-hour time to recovery. 13:19, a warmer dark palette ships. It is brown, and it muddies every genre colour. 15:24, pure black, one accent, and the postmortem is committed as a rule in the skill file where the next session will read it:

No tinted brown surfaces: a warm neutral hue on near-black reads as brown and muddies genre colours.

That is what a mistake costs now. Two hours, and it leaves a sentence behind that makes the next one less likely. A wrong turn used to be expensive enough that we argued for weeks to avoid taking one. Now the cheapest way to find out is to look.

What stayed mine is the taste. Nobody else can tell that a list of well-known bands is a failed Edge, or that a page full of correct numbers is still not somewhere you want to be on a Sunday. Every one of the 33 commits with my name on it is either fresh data from my own listening or a decision. The machines brought speed. I brought the ears.

Detect, fix, write the rule down where the agent will find it. That is the whole incident process, and it is the same one I would want at work, minus four meetings.

the roast

web-scale practices for five users

Here is where the sensible little pipeline goes to die. The family wants in. “The family” is five people. The correct architecture for five people is a shared password and a cron job. I know this. I wrote the opposite down in docs/platform/design.md and then recorded eleven architecture decisions about it, using an ADR tool I also wrote myself.

what five people need
what I designed
a loginfive people, one surname
Cognito with Sign in with Apple, and Cedar policies49 so that only discovery views are shareable. A policy engine, to stop my family seeing my Year in Review.
a place for the rowsabout thirty new ones a day, each
Apache Iceberg on Amazon S3 Tables,50 written by DuckDB.51 A table format built for petabytes, holding a CSV's worth of my feelings.
to know it workstry it and see
A storage spike: 390,000 seeded plays, 1,881 poll commits, 672 back-to-back cycles. I load-tested my family. They do not know.
fresh numbersrun the script again
SQLMesh incremental models on a Graviton Lambda, backfill in 15.7 seconds, with 260 lines of adapter glue because it did not support DuckDB 2.0 yet.52
the same answer everywhereone app, one screen
A Malloy53 semantic layer serving REST, MCP and an iOS app that runs precompiled SQL offline. Three API types. One consumer. Me.
to not switch windowsalt-tab
Kiro Remote. See above. Passkeys.

in my defence

The spikes were at least honest. The storage report's own script printed NOT DECIDED, and the write-up keeps that verdict and says the choice “rests on reasoning, not on a measurement that passed”. Zero plays lost or duplicated, poll commit p50 1.8 s and p9554 4.3 s, and late reads that slowed to 85 to 114 seconds as snapshots piled up. I trust the other numbers more because that sentence is there. docs/spikes/0.2-storage.md, 0.3-transformations.md, 0.4-malloy-web.md.

And here is the real defence. Over-engineering was a vice when engineering was expensive. Every one of those rows used to cost weeks, so building them for five users was indefensible, and we were right to mock it. The price collapsed. An afternoon and a good brief now buys what a sprint did. When doing it properly costs about the same as doing it badly, “properly” stops being a luxury and starts being a hobby.

It is also a gym. I advise on systems that serve millions. I would rather learn what Iceberg does under a streaming writer, or where a semantic layer leaks, on my own record collection than on yours.

Some of it is still too much. The Cedar policies can wait. My family will not read the ADRs. The control plane has a better security posture than my front door. I am at peace with all of it.

What it means

Playing with serious matters

Here is the part I keep turning over.

Three years ago this was not a matter of effort. It was a matter of impossibility. A product like this needs research, design, data, an interface with its own language and somebody to build all of it. That is a team and a year — or it is a daydream. I have carried a version of this daydream for years.

It took sixteen days. The strange thing is how quickly that stopped feeling strange. By the second weekend, asking for a new way to look at twenty-one years of my own life before lunch and listening to its results after lunch had become ordinary. Things that were out of reach have turned normal, and normal is the most radical thing that can happen to a miracle.

I understand why that frightens people who build things for a living. If the making was the job, the job is leaving. But the making was never the job. The job was caring about the right problem, knowing what good feels like, and being willing to say “brown” out loud. Those got more valuable, because now they are the only bottleneck left. The tools did not remove the thinking. They got me to the thinking faster and kept a record of it.

There is an old idea that play is what you do when nothing is at stake. I think it is closer to the opposite. Play is how you approach a thing when you take it seriously enough to try it a second way. Glenn McDonald played with the entire recorded output of humanity and built an atlas. I played with one listener's diary and got a Sunday with a hundred and one songs in it. The scale differs. The posture is the same.

So I would rather not be afraid. I would rather be the person who notices that it has never been this much fun to make things, or this easy to keep growing, and who acts accordingly.

Here is the part I keep turning over.

Three years ago none of this was a matter of effort. A pipeline against four music APIs, a query engine in the browser, a projection of my own taste, a guarded natural-language endpoint, a platform design with spikes behind it. That is a team and a year — or it is a daydream. It took sixteen days, and by the second weekend it felt ordinary. Things that were out of reach have turned normal, and normal is the most radical thing that can happen to a miracle.

I understand why that frightens people in my line of work. If the typing was the job, the job is leaving. But the typing was never the job. The job was choosing the problem, knowing what good looks like, and being able to say “brown” or “not decided” out loud. Those got more valuable, because they are the only bottleneck left. The tools did not remove the thinking. They got me to the thinking faster and kept a record of it.

Which brings me back to the roast. I laughed at the Iceberg tables, and I meant it. But look at what the laughter assumes: that rigour is rationed, and spending it on five people is waste. That assumption just expired. When care is cheap, you get to be careful about things simply because you love them. I think that is what play is. It is seriousness with nothing to prove.

So I would rather not be afraid. It has never been this much fun to be a developer, or this easy to keep growing. I intend to behave accordingly, lower-case headings and all.

The new ordinary

You have not yet built your favourite tool

McDonald's promise was that your favourite song is still out there, unheard. Mine found me on a Sunday in November, thirty-four minutes after a commit.

The same promise now holds one level up. Somewhere in you there is an itch you have carried for years because scratching it used to cost a team. Check the price again. Then give it one weekend, and take the result seriously enough to play with it.

McDonald's promise was that your favourite song is still out there, unheard. Mine arrived thirty-four minutes after a commit, on infrastructure sized for a small nation.

The same promise holds one level up. Somewhere you have an itch you never scratched because it would have taken a team. Check the price again. Then give it a weekend, over-engineer it without apology, and write down what you learn.

For years I have had a mantra: if I can do this, you can do it too. Today it is truer than ever. I do not consider myself special by any means, and none of this took an exceptional imagination — I do not have one — or anything else that cannot be learned.

So ping me, and let's get your journey started. I say journey on purpose — it matters more than the destination.

I am going back to the crate. There are 8,556 neighbours left.

I am going back to the crate. 8,556 neighbours left, and a p95 to bring down.

Thank you

If these evenings turned me into a product manager with one very opinionated customer, I know where I learned the job: on the other side of the table. I have been the customer of my friend and colleague Massimo Re Ferrè55 since AWS Fargate launched in November 2017. For nine years he has done what this whole story is about. He listened, patiently, one product after another: to my feature requests, my bug reports, my big ideas, and a great deal of venting he never asked for. Today he works on Kiro, which wrote 402 of the commits in this story. So the opinionated customer is still talking, and Massimo is still listening, now from inside the tool that built this. Thank you, Massimo. This one is for you.

Colophon

How this page was made

The words are mine in voice and Claude's in first draft, built from the repository, its git history and our working conversations; I edit what is not true to me. The numbers come from the data export at commit 8942c35 (13 November 2026) and from git log at 2e7f1be. The app pictures come from nine commits checked out and run on 15 November, with cover art switched off. The tech cut wears Wisteria Lily, my editor theme: wisteria purple on Day Lily cream, set in one monospace face.56 The music cut wears longplay's own neon black, and the liquid colour on both is painted by a short shader each time the page loads. Genre map data in the Footprint view is Every Noise at Once by Glenn McDonald, used non-commercially with credit. longplay is a private project built for one listener, and soon for five.

References
  1. “Systems thinking”, Wikipedia. en.wikipedia.org
  2. Claude. claude.ai
  3. Novulent on Spotify. open.spotify.com
  4. “Git”, Wikipedia. en.wikipedia.org
  5. “Apache Parquet”, Wikipedia. en.wikipedia.org
  6. Last.fm. last.fm
  7. Amanda Silberling, “Spotify's layoffs put an end to a musical encyclopedia, and fans are pissed”, TechCrunch, 12 Feb 2024. techcrunch.com
  8. “Every Noise at Once”, Wikipedia. en.wikipedia.org
  9. “Spotify's Former Data Alchemist Gives Every Song a Genre”, Billboard Canada. ca.billboard.com
  10. Every Noise at Once on Bluesky, on the genre system and its replacement. bsky.app
  11. Spotify for Developers, “Introducing some changes to our Web API”, 27 Nov 2024. developer.spotify.com
  12. “Spotify cuts developer access to several of its recommendation features”, TechCrunch, 27 Nov 2024. techcrunch.com
  13. Spotify for Developers, “Update on Developer Access and Platform Security”, 6 Feb 2026. developer.spotify.com
  14. Anderson, Maystre, Anderson, Mehrotra, Lalmas, “Algorithmic Effects on the Diversity of Consumption on Spotify”, WWW 2020. doi.org
  15. Holtz, Carterette, Chandar, Nazari, Cramer, Aral, “The Engagement-Diversity Connection: Evidence from a Field Experiment on Spotify”, 2020. The experiment concerned podcast recommendations. arxiv.org
  16. MIDiA Research, “Music discovery is not dead, just evolving”. midiaresearch.com
  17. Niko Virtala on LinkedIn. linkedin.com/in/nikovirtala
  18. Every Noise at Once. everynoise.com
  19. Review of Glenn McDonald, You Have Not Yet Heard Your Favourite Song, The Quietus. thequietus.com
  20. Glenn McDonald's own site. furia.com
  21. McInnes, Healy, Melville, “UMAP: Uniform Manifold Approximation and Projection for Dimension Reduction”. arxiv.org
  22. ListenBrainz API, Explore: fresh releases. listenbrainz.readthedocs.io
  23. MusicBrainz API. musicbrainz.org
  24. uv. docs.astral.sh
  25. Last.fm API. last.fm
  26. “Announcing DuckDB 2.0.0 LTS”, 21 Oct 2026. duckdb.org
  27. AWS CDK v2 developer guide. docs.aws.amazon.com
  28. Node.js documentation, “Modules: TypeScript”. nodejs.org
  29. Amazon Bedrock, cross-region inference. docs.aws.amazon.com
  30. “Vite 8.0 is out!”, 12 Mar 2026. vite.dev
  31. “Announcing TypeScript 7.0”, 8 Jul 2026. devblogs.microsoft.com
  32. Oxlint. oxc.rs
  33. Observable Plot. observablehq.com
  34. Spotify Web API, Authorization Code with PKCE. developer.spotify.com
  35. “SQL”, Wikipedia. en.wikipedia.org
  36. DuckDB. duckdb.org
  37. “Persistent Databases in the Browser with DuckDB-Wasm and OPFS”, 18 Sep 2026. duckdb.org
  38. pnpm, “Mitigating supply chain attacks”. pnpm.io
  39. MDN, “View Transition API”. developer.mozilla.org
  40. Cover Art Archive. coverartarchive.org
  41. Kiro Web, the cloud-hosted version of Kiro. kiro.dev/web
  42. Kiro documentation, “Cloud sessions”. kiro.dev
  43. Niko Virtala, coding-agent-feature-prompts, a skill for choosing the next feature and briefing an autonomous coding agent. github.com/nikovirtala/skills
  44. Niko Virtala, Kiro Remote, a personal AWS control plane that launches and drives Kiro Web sessions over MCP and REST. github.com/nikovirtala/kiro-remote
  45. “Same parents. Not on speaking terms.”, the Kiro Remote landing page. chat-cant-code.lovable.app
  46. Amazon Web Services. aws.amazon.com
  47. Agent Skills specification. agentskills.io
  48. Kiro documentation, “Agent Skills”. kiro.dev
  49. Cedar policy language. cedarpolicy.com
  50. Amazon S3 Tables. aws.amazon.com
  51. DuckDB documentation, “Writing to Iceberg”. duckdb.org
  52. SQLMesh pull request 6158, DuckDB 2.0 support. github.com/SQLMesh/sqlmesh
  53. Malloy. github.com/malloydata/malloy
  54. “Percentile”, Wikipedia. en.wikipedia.org
  55. Massimo Re Ferrè on LinkedIn. linkedin.com/in/massimoreferre
  56. Niko Virtala, Wisteria Lily, a warm light colour theme for Kiro and VS Code. github.com/nikovirtala/wisteria-lily