Radio you can watch, rebuilt in a day

· 6 min read · Tim Thomas

The old FM.video had died quietly. The scraper had stopped in February, the matcher's source code was gone, and the live site was calling a dev API. On 19 July I started again from an empty folder, with an AI agent, and by the evening it was scraping, matching and playing.

FM.video is a simple idea with an unreasonable amount of machinery under it. Radio stations publish what they are playing, right now. Every one of those songs has a music video somewhere. Stitch the two together and you get television that programmes itself: pick a station, watch the video for whatever it is playing, and the next one arrives when the song changes. Radio you can watch.

The idea had a first life a few years ago. By July 2026 it had died in the way small products die, one piece at a time, with nobody watching.

What I found on 19 July

I did an audit before touching anything, and it was bleak.

The scraper, a .NET service that listened to the stations, had stopped recording the MediaWorks stations in February and the NZME ones on 2 July. Not crashed loudly. Just stopped, because one transient failure had killed the whole loop and nothing was supervising it. The station feeds themselves were still alive; nobody had been listening.

The YouTube matcher, the part that finds the right video for each song, had last run in August 2024. Its source code no longer existed anywhere. Only its output survived, in the database, with about thirty percent of songs unmatched.

The live site was a single-page app on Netlify, calling the development API in production because the production one was stuck in a redirect loop. Playlists were dead because the database behind them had been deleted. And there were credentials committed in plain text in the old repositories.

So the choice was not "fix it or rebuild it". There was nothing to fix. The matcher did not exist to patch.

The decision

Rebuild everything, and own the whole stack this time.

A Python scraper with each station isolated from the others, so one bad feed cannot take down the rest, and a heartbeat that pages me when something goes quiet. A rebuilt matcher that discovers videos without burning API quota and enriches them with the official data. A read API that keeps the old response format byte for byte, so anything still pointing at it keeps working. A new web app, a proper progressive web app that installs to a phone and casts to a TV. And the song data in a Postgres database I run myself, on a box I can walk over to.

Off the free tiers. Own the always-on engine.

One day

Here is the part that would have been a month in my previous life.

I wrote the spec in the morning with Claude Code, then built through the day, one conversation at a time. The commit log for 19 July has 37 entries. The scraper with dual-write and a migration from the old database. The matcher with a rotating pool of API keys and a guard against picking the wrong artist's video. The read API. The web app: player, station guide, queue, search, install-to-phone, a channel flip. Then the things you only discover by using it: mobile fullscreen, swipe between songs, deep links to a specific video with a proper preview image, share buttons.

By the evening it was scraping 46 stations across four countries, matching in real time as songs came in, and playing video for whatever was on. The next day the admin area arrived, then a staleness monitor that messages me on WhatsApp within ten minutes if any station stops reporting. The thing that killed the old version silently cannot happen silently again.

None of this was typed by me in the usual sense. I described what I wanted, read what came back, tested it, said what was wrong, and repeated. The agent did the rest, including the parts I would have put off for weeks: the migration script, the CI, the documentation. The documentation was written the same day as the code, which I have never once managed to do by hand.

What it grew into

That first day set the shape of everything since. The scraper now listens to 224 stations in 17 countries. The library is past 150,000 songs with a video matched for nine in ten of them, and it has recorded six million plays. Charts come from real airplay, not from anyone's opinion. Every station has its own page, every country has a live chart, and there is a TV mode that turns any screen into a music channel.

The rest of this build log is the story of the things that came after: the admin area that watches it all, the deployment setup that lets one person ship to production several times a day with no downtime, the outage that taught me what failover actually means, and the marketing department that turned out to be a cron job.

I am writing it down because I think the interesting thing is not the product. It is that one person with an agent can now build and run something that used to need a team, and what that actually feels like day to day. The answer, so far, is that it feels like being the editor of a very fast newspaper.

Pick a station and try it: fm.video.

More from the build log