In spring 2023 my team built Shoutify for a software engineering class at Yale: Spotify's web player with comments, lyric annotations and a social feed. Three years later I gave the code to Claude Code. It came back as a TypeScript app on my home server where comments are pinned to moments in a song and pop up over a waveform as it plays, the way they do on SoundCloud. Here is how the revival went, and how Spotify's 2026 rules shaped it.
In spring 2023, my team built Shoutify for a software engineering class at Yale. It was Spotify's web player with a social layer on top: comments on songs, annotations on lyrics, and a Social tab with a feed. Our pitch was that streaming apps get one thing wrong: people talk about music everywhere except the app they listen in. We shipped it at the end of April, the class ended, and nobody touched the repository again.
Three years later I gave that code to Claude Code, Anthropic's coding agent (running Claude Opus 5.5). Over about thirty hours, from the evening of October 30 to the early hours of November 1, it became a TypeScript app that runs on my home server for me and a few friends. It looks like Spotify's current web player. It also finally does what Shoutify always hinted at: comments are pinned to moments in a song, and they pop up over a waveform when the song reaches them, the way they do on SoundCloud.
This post covers:
- what we built in 2023;
- what it took to make it run in 2026;
- how the SoundCloud-style comments work;
- how Spotify's 2026 rules shaped almost every decision;
- what it was like to revive a team project with an AI agent.
The idea: SoundCloud's comments, inside Spotify
SoundCloud has had timed comments for years. A comment is attached to the moment in the track you were listening to. Small avatars line the waveform, and when the song reaches a comment, it pops up above the playhead. Listening to a popular track feels like listening in a room with everyone who heard it before you: someone flags the drop a second before it lands, someone else quotes the line they love.
Spotify has nothing like it. You can't comment on a song. Friend Activity shows what your friends are playing, but you can't say anything about it. The conversation happens on TikTok, in group chats and on forums, and then you go back to Spotify to listen.
Shoutify brings SoundCloud's comments to Spotify's catalogue. You listen in a Spotify-style player, and the comments of the people you follow on Spotify play along with the song.
In 2023 we got halfway there without quite noticing. Every comment saved the song's position when it was written (Spotify's progress_ms), and the comment card printed it as "@1:43". Nothing else used it. The SoundCloud-style player of 2026 is built on that one field.
The 2023 version
We didn't start from scratch. Shoutify is built on JL978's open-source Spotify clone, a React copy of Spotify's 2020 web player with a small login server, both under the MIT license. Between February 8 and April 28, 2023, the team added Shoutify on top in 171 commits:
Area | What it did | Built by (GitHub) |
|---|---|---|
Comments | A Comment button in the player bar, a comment page for each song, comment counts in track lists | treylew01, scedanquah and me |
Annotations | The playing song's lyrics from Musixmatch; select a line to annotate it | UwemWilson4, with scedanquah on the server |
Social tab | A feed of everyone's comments | HarryShindika, scedanquah and treylew01 |
Server and deployment | Express routes for comments and annotations, MongoDB, Heroku and Netlify | scedanquah |
Spotify plumbing | Login and token refresh, private playlists, Liked Songs, infinite scroll, search fixes | me |

| 2023 |
|---|---|
Front end | React 16, Create React App, React Router 5, JavaScript |
Server | Express 4 on Heroku, with the front end on Netlify |
Data | MongoDB through Mongoose |
Lyrics | Musixmatch's API, with the team's key |
Code | 123 files: 5,493 lines of JavaScript and 2,115 of CSS |
Tests | None |
It worked for a class demo. The red boxes in the diagram are the parts that wouldn't survive contact with the internet, which I'll come back to.
The prompts
This is the whole brief I gave Claude on the first night:
I have a repository I worked in in which I cloned Spotify. The repository is the following: [the class repository], we also added things like commenting on [songs] and annotating [song] lyrics. I need you to do the following: Copy all of the code and use it as your starting ground. I don't want to contribute to this repo, rather I want to make a fresh one. Rewrite all of the code in TypeScript and update the API calls since this project was from 2023. For the time being, switch off the lyrics API and try and see if Spotify offers something native. Switch from MongoDB to something that we can self host here on this server, I'm going to want to use this among my friends so keep that in mind. Make small atomic commits
After that, each request came after I'd used what the last one built:
- Fix the bugs the 2023 version always had, and "touch up the CSS just slightly, I want it to look exactly like the Spotify webpage".
- Publish it to a new public repository on GitHub.
- A question: would an unofficial Musixmatch package be fine for lyrics, "solely for educational purposes"? The answer is further down.
- Six features, built one at a time, with a redeploy after each so I could try it. That's the round that made Shoutify SoundCloud for Spotify.
Getting a 2023 app to run in 2026
Claude imported the code into a new repository as one commit, without the class history (that stays in the class's private repository). It deleted the Heroku and Netlify configuration. Then it read the whole codebase, and Spotify's developer changelog, before changing anything.
Spotify moved the ground
Shoutify's biggest problem wasn't old code. Spotify's Web API in 2026 is much smaller than it was in 2023, especially for hobby apps.
In November 2024, Spotify stopped giving new apps related artists, recommendations, audio features, audio analysis, and category and featured playlists. In February 2026 it tightened Development Mode, the mode every app starts in and the only one open to individuals:
What changed | What it broke in Shoutify | What Shoutify does now |
|---|---|---|
An app serves at most five accounts, each added by hand, and its owner needs Premium | Logged-out browsing, through the server's proxy | Login is the first screen, and the proxy is gone |
Browse categories and their playlists are gone | Home's category rows, the Browse grid, genre pages | Home shows your top artists and tracks next to Liked Songs |
Related artists and artist top tracks are gone | The Related Artists tab and the Popular row | Artist pages show releases |
Other users' profiles and playlists are gone | Profile pages, and each comment's author name and picture | Comments store their author's name and picture; only your own profile shows |
Playlist contents moved from | Every playlist page | Other people's playlists show their banner, and still play |
Saving and following moved to | Hearts on playlists, following artists | |
Search and an artist's albums return at most 10 items per request | Long lists | Requests are split to fit |
Some of these still answer apps created before February 2026, but Spotify has said they'll go too.
The five-account cap changed what Shoutify is. It can't be a public social network any more. It's a private one for five friends, and that suits it. It also means every design choice can assume a tiny user base: a SQLite file is plenty, and asking Spotify about every other user is cheap when there are four of them.
Holes a class project could get away with
Reading the code with fresh eyes turned up problems we never noticed in 2023, because a class demo never meets an attacker:
In 2023 | Why it mattered | In 2026 |
|---|---|---|
Login redirected to | Both tokens ended up in browser history, and any script on the page could read the token | The refresh token lives in an httpOnly cookie, and the access token only in memory, renewed a minute before it expires |
| Every session died after an hour | Fixed, with tests |
The browser sent each comment's author ID | Anyone could post as anyone | The author comes from a signed cookie issued at login |
| An open proxy on our server | Removed, with logged-out browsing |
CORS echoed back any origin, with credentials allowed | Any website could read | One origin, so no CORS at all |
Comments could be read without logging in | Everyone's comments were public | Every API route needs a login |
The cookies use the __Host- prefix, so other sites under my domain can't plant them. The server checks with Spotify every hour that you're still on the app's list of five, so removing someone takes effect within about an hour.
New foundations
With the API sorted out, the rest of the stack was replaced one piece at a time, each in its own commit:
- MongoDB became SQLite. Comments live in one file through Node's built-in
node:sqlite. There's no database server and no driver, and a backup is one file. With at most five users, write-ahead logging handles a few people commenting at once. - Express 4 in JavaScript became Express 5 in TypeScript. Node 24 runs TypeScript files directly by stripping the types, so the server has no build step.
tsconly checks the types. Built-infetchreplaced axios, andnode --env-filereplaced dotenv. - Create React App became Vite. CRA's react-scripts 2 only built with
--openssl-legacy-provider. Vite 8, React 19 and React Router 8 replaced it. Then every component was converted to strict TypeScript, one area at a time. - One origin. The server serves the built app and keeps its own routes under
/api, so there's no second host and nothing to configure for CORS. - Docker. The image is built in two stages on Node 24 Alpine, and the runtime keeps about 4 MB of
node_modules. The container runs read-only, as a regular user, with every Linux capability dropped. It sits behind the Traefik proxy that serves everything on my home server, with the SQLite file on a named volume.
Making it look like Spotify
The first deploy worked, and I immediately hit the bugs we'd lived with in 2023. My next message listed them:
When we open the app, it should auto-connect to the Spotify Clone Player instead of us having to manually choose. When we play, we shouldn't get JSON error message. Back and forward buttons should work and settings on playlists should come up with actual settings. When a song is playing it should be highlighted and the play button should switch to something indicating music playing.
Claude split this into two passes.
Bug hunts. Separate agents went through the player, the music pages, the social features and the app shell. Each one looked for bugs, and other agents tried to disprove every finding before anything was fixed. Claude ran this twice: on the 2023 code, and later on its own fixes. Two of the bugs that survived show what it found:
- The Devices button couldn't open its own menu. React 19 runs a click's effects straight away, so the menu's "a click outside closes me" listener was attached while the same click was still bubbling. The menu closed as it opened. React 16 had run those effects later, so the bug arrived with the port.
- Repeat was backwards. The player read Spotify's repeat modes with 1 meaning "repeat one", following a comment in the TypeScript type definitions. Claude read the source of Spotify's Web Playback SDK, where 1 means "repeat all" and 2 means "repeat one", and fixed the mapping.
A redesign. "Exactly like the Spotify webpage" meant Spotify's current layout, not the 2020 one the clone copied:
- rounded panels on black;
- Your Library on the left;
- a right panel for Now playing, the queue and Friend Activity;
- Spotify's player bar.
Claude built the shared frame first. Then five agents built the areas in parallel, each in its own git worktree: the home page, playlists and albums, the player, search, and Your Library. Their work was merged at the end. Claude compared screenshots with a reference screenshot of Spotify's web player at the same window size, until the panel edges lined up within about a pixel. Spotify's own typeface isn't free to use, so Shoutify uses Figtree, which is close.
I published that version to GitHub and started using it.
Six features, one deploy each
Then came the request that made Shoutify what it was always meant to be. It's long, so here is the heart of it:
At the moment, when you click on the comment button I don't like how the comment modal appears. Instead it should be the comment page for the song should appear and it should look like a Twitter layout in a way, with the most recent comments at the top. However, there should also be a SoundCloud-like player at the top that shouts out comments when it gets to the section that they mention, just how SoundCloud currently does it. The comment it chooses should be based on popularity of the comment so be sure to implement liking on the comments and commenting on comments.
The rest of it asked for:
- a Social feed of my friends' comments;
- friends that are exactly my friends on Spotify, with "no new features" for friendship;
- Friend Activity in the right panel, and "Social" in the left menu;
- a green, editable timestamp in front of each comment, with tests for bounds checking;
- the Annotations button hidden;
- a notification bell that mixes comments with new releases;
- clicking a song further down the queue to play it.
It ended with "Build these features one at a time redeploying after each feature so I can see it and give feedback."
# | Feature | Live |
|---|---|---|
1 | The comment page, Twitter style, with the green time chip | October 31 |
2 | Likes and replies | October 31 |
3 | The SoundCloud-style player | November 1 |
4 | Friends from Spotify, Friend Activity and the Social feed | November 1 |
5 | What's New: notifications mixed with new releases | November 1 |
6 | Click a song in the queue to play it | November 1 |
A comment page that reads like Twitter
The Comment button used to open a small dialog over the player. Now it opens the song's own page and puts the cursor in the composer, which works like a tweet box: your picture, your text, and a Post button (or Ctrl/Cmd+Enter). Comments are listed newest first. Each one says how long ago it was posted, the way Twitter does: "now", "45s", "12m", "3h", "5d", then "Oct 31". The 2023 schema had no posting time, so the server now stamps each comment itself, and a browser can't backdate one.

The green chip at the start of the text is the moment in the song the comment is about. It follows the song while you listen, and stops as soon as you start typing or click the chip, so it marks the moment you reacted to. Click the chip to type a time; setting it jumps the song there, so you can check you've got the right moment before you post. The parser behind it is small and strict:
// A time someone typed, "1:05" or past an hour "1:02:05", in ms; null if it's anything else or after `duration`.
export function parseTime(text: string, duration: number) {
const match = /^(?:(\d+):([0-5]\d)|(\d+)):([0-5]\d)$/.exec(text.trim())
if (!match) return null
const [, h, mm, m, ss] = match
const ms = ((Number(h ?? 0) * 60 + Number(mm ?? m)) * 60 + Number(ss)) * 1000
return ms <= duration ? ms : null
}I asked for tests that check the bounds, and they read like a spec. On a 3:20 song, 3:20 is accepted and 3:21 isn't. 1:60, -0:01, 1:5, 1.05 and 1:05pm are turned down. Past an hour, 1:02:05 and 62:05 mean the same moment. A time that's turned down stays in the chip, marked, so you can fix it instead of losing it.
Likes and replies
The 2023 schema already had likes, replies and reshares fields on every comment. They were always zero: nothing ever changed them or showed them. Now likes and replies are real:
- Likes: one per person per comment, in a
comment_likestable, behind a heart that turns green. - Replies: a reply is a comment with a
parentID. Threads are one level deep, the way Twitter shows a conversation. Replies sit under their comment, oldest first, joined to it by a thread line, and a reply to a reply starts with "@name". A reply belongs to its comment's moment in the song, so it has no chip of its own.
Both changes only add to the schema (a new column and a new table), so the live database picked them up on the next start, with no migration step. Likes and replies matter beyond the page itself: they decide which comment the player shouts out.
The SoundCloud-style player
This is the part I'd been picturing since 2023. The top of every song's page is now laid out like SoundCloud's track header, in Spotify's colours.

- The waveform is a seek bar. It's green up to the playhead and grey after. Click or drag to move the song; the arrow keys move 5 seconds, and Home and End jump to either end. If this isn't the song that's playing, clicking starts it from there.
- Every comment has a marker along the bottom of the waveform: its author's picture or initial, at the comment's moment. Hover over one, or tab to it, to read it. Click it, and the page scrolls to that comment and lights it up.
- While the song plays, the waveform shouts out the comment for the moment you're at, in a bubble above the playhead, the way SoundCloud does.
The waveform is made up, on purpose
SoundCloud draws its waveform from the audio file. Shoutify can't. Spotify's player is DRM-protected, so apps never see the audio, and since November 2024 new apps don't get the audio analysis that described a song's loudness over time either. So the waveform is generated from the Spotify track id: the same song always gets the same shape, but the shape isn't its sound.
// FNV-1a hashes the id into the seed of a mulberry32 generator.
let seed = [...id].reduce((h, c) => Math.imul(h ^ c.charCodeAt(0), 16777619), 2166136261)From that seed, the code builds a shape that looks like a song rather than noise:
- It picks eight "sections" of loudness and eases between them along a cosine curve.
- It adds detail from bar to bar, smoothed over neighbouring bars.
- It fades the intro and the outro.
A comment in the code says exactly what this is: a shortcut, to be replaced with real loudness if Spotify ever provides it.
Choosing the shout-out by popularity
I asked for the shout-out to go to the most popular comment. This is the whole rule:
// The comment the waveform shouts out at `position` (ms) in the song: of the top-level comments about the 4 s up to
// it, the most liked, then the most replied to, then the newest (the highest id, as older comments have no date).
export const shoutOut = (comments: SongComment[], position: number) =>
comments.filter(({parentID, timestamp}) => parentID === null && timestamp <= position && timestamp > position - 4000)
.sort((a, b) => b.likes - a.likes || b.replies - a.replies || b.id - a.id)[0] ?? null
Each comment stays up for four seconds after its moment, long enough to read a line. Within that window:
- The most liked comment wins.
- If likes are tied, the comment with more replies wins, since starting a conversation counts as popular too.
- If that's tied too, the newest comment wins.
If ten people commented on the drop, you see the one your friends liked, not whichever was posted first.
Keeping time without redrawing everything
A waveform that follows the song needs to know where the song is several times a second. Spotify reports it only now and then:
- When the song plays in the browser, the Web Playback SDK sends an event each time something changes.
- When it plays on another device, such as your phone, Shoutify asks the Web API every 5 seconds.
Between reports, Shoutify works out the position from the time since the last one. Counting real time instead of timer ticks keeps it right even in a background tab, where browsers slow timers down.
The first draft kept that clock in the app's top-level React state, so every report redrew the whole app, track lists included, to move one playhead. Claude caught it before the feature went live. It moved the clock into a tiny store read with React's useSyncExternalStore, so only the parts that show the time redraw.
Friends, exactly as Spotify has them
My brief was strict: "The friends you have should be the exact friends that are your friends on Spotify, no new features should be included for this." So there are no friend requests and no follow button. Spotify makes this harder than it sounds:
- Friend Activity has never been part of Spotify's public Web API. Spotify's desktop app gets it from a private service.
- Since February 2026, an app in Development Mode can't even list whom you follow. It can only ask whether you follow particular users, 40 at a time, with
GET /me/library/contains?uris=spotify:user:….
Shoutify turns the second limit into the answer. It knows its own users, and there are at most five. So at every login, and again at every hourly token refresh, the server asks Spotify which of them you follow, and keeps the answer. Your friends on Shoutify are the people you follow on Spotify who also use Shoutify. If Spotify doesn't answer, the last list stays and your login still goes through.
Activity works from the inside. Each person's Shoutify player tells the server when the song changes, or when they press play or pause. When they open Shoutify, it also reports their latest play from Spotify's listening history. If reports arrive out of order, the newer one wins, and no report can be dated in the future, so a device whose clock runs fast can't block its own later reports.

- Friend Activity sits in the right panel, as in Spotify's desktop app. It shows each friend's picture, song, artist and album, with moving green bars while they're playing and "12m" or "3h" once they've stopped. It reloads every minute while it's open.
- Social is pinned in Your Library under Liked Songs. It shows the comments your friends post, newest first, whatever their likes, each with its song.
The catch: Friend Activity only knows what your friends play while Shoutify is open, plus their latest play when they open it. Spotify's own panel sees everything.
What's New: comments and releases in one list
I asked for the bell in the top bar to show every kind of news, with comments mixed in among new releases. Spotify doesn't give apps notifications or a new-releases feed, so Shoutify builds both:
- Shoutify's notifications come from my server: replies to your comments, likes on them, and new comments from the people you follow, over the last 30 days.
- New releases come from Spotify: what the artists you follow released in the last 30 days, from each artist's ten latest releases (the most Spotify returns at once). Everyone on a Shoutify server shares one Spotify request quota. So the browser asks about at most three artists at a time, and keeps each answer for 12 hours.
Both kinds go into one list, sorted by time, with All, Music and Comments filters:
- A release counts from midnight on its release day. Spotify dates some releases only by month or year; those can't be placed, so they count as old.
- A blue dot marks anything new since you last opened the page, and the bell keeps a dot until you do.

Clicking ahead in the queue
The last feature is the smallest. In Spotify you can click a song further down the queue to play it; in Shoutify you couldn't. The Web API has no "play this queue position" command, so Shoutify sends "next" once for every song before the one you clicked, then asks Spotify for the new state. A song far down the queue takes a moment.
The first version had a race. For about half a second after the skips, the panel still showed the old queue, so a quick second click counted from positions that had already moved. Now a queue that has been skipped from ignores clicks until it's been read again. If three seconds pass first, it takes clicks again: that covers Spotify playing the same song again, when the queue isn't reloaded.
Why the annotations are hidden
Annotations were the other half of Shoutify in 2023. The app showed the playing song's lyrics, from Musixmatch's API with our team's key, and you could select a line and write a note on it, as on Genius.
In 2026 there's nothing to annotate. Spotify's Web API has no lyrics at all. The lyrics in Spotify's own apps come from an internal service that needs a web-player session cookie, and Spotify's Developer Terms put it off-limits. So, as I'd asked, the lyrics are switched off. The annotations page still works if you type the line yourself, but its button stays hidden until there's a lyrics source.
I asked about one shortcut: musicxmatch-api, an unofficial Python package that pulls lyrics from Musixmatch for free. Would using it break Spotify's terms if it was "solely for educational purposes"? Claude read the package's code before answering. It said yes, for two reasons:
- Musixmatch's terms. The package decodes a hidden signing key from Musixmatch's website code and poses as their web app. Musixmatch's API Terms forbid harvesting data from their site "in any way other than by accessing the API with the User Authentication Key".
- Spotify's terms. Spotify's Developer Policy says not to build products "integrated with streams or content from another service". Its Developer Terms also require the rights to any content an app shows, and lyrics belong to the songwriters' publishers.
"Educational" changes neither. In practice, Spotify could revoke the app's access, which would end logins and playback for everyone. Claude's suggestion was better than the shortcut: comments are already pinned to a moment in the song, so you can quote the line you mean.
Testing without anyone's Spotify account
Testing a Spotify app is awkward. Every page needs a logged-in account, Spotify allows five, and I didn't want tests playing music on my real account. So Shoutify's browser tests run against a fake Spotify:
- The real Shoutify server runs in Docker, with a small preload that answers Spotify's login and identity endpoints and the follow check. Each refresh cookie logs in as a different fake person: Victor, Ana or Bo. On this fake Spotify, Victor follows Ana but not Bo, which is exactly what the friends feature needs to test.
- Headless Chromium, driven by Playwright, uses the app the way a person would. Fixtures answer every Web API call, and every album cover is drawn as a colour gradient. That's why the screenshots in this post have gradient covers and names like "Artist One".
- There are 136 checks, from logging in to liking a reply and skipping ahead in the queue. A check fails on any console error or failed request it didn't expect.
Next to them, 78 unit tests (Node's built-in test runner) cover the rules: the time parser, the shout-out choice, the waveform, the What's New mix, and the server's routes for comments, likes, friends and notifications.
What none of it can tell me is how Shoutify behaves with real accounts, in Firefox or Safari, or on a phone. That's what my friends are for.
Before and after

| 2023 | 2026 |
|---|---|---|
Front end | React 16, Create React App, React Router 5, JavaScript | React 19, Vite 8, React Router 8, strict TypeScript |
Server | Express 4 on Heroku, front end on Netlify | Express 5 on Node 24, TypeScript with no build step, one Docker container |
Data | MongoDB | One SQLite file |
Sign-in | Tokens in the URL and | Refresh token in an httpOnly cookie, access token in memory, a signed author cookie; login required |
Who can use it | Anyone with a Spotify account | Five accounts, Spotify's cap |
Comments | Text and the moment in the song | Plus posting time, likes, replies, and a waveform that plays them back |
Social | A feed of everyone's comments | Friends from Spotify follows: Friend Activity, a Social feed, What's New |
Lyrics | Musixmatch, with annotations | Off, with annotations hidden |
Tests | None | 78 unit tests, 136 browser checks |
What working with Claude was like
A few things stood out:
- It checked what it couldn't know. Spotify's February 2026 changes came after its training data, so it worked from Spotify's migration guide and changelog. The one change it couldn't confirm without a real account is flagged in its commit message. When a type definition and Spotify's SDK disagreed about repeat modes, it read the SDK's source.
- It used several agents when the work split cleanly. The bug hunt had finders and skeptics, and the redesign had one agent per area. When I said the review rounds were more than I needed ("I think I can test things myself by just using it"), it dropped them. From then on, it built a feature, ran the tests once, deployed, and let me use it.
- It kept me in the loop. Each feature was built on a
devbranch in a separate worktree, then moved tomainand deployed on its own. Each deploy came with a short note on what to try and what hadn't been checked. - It corrected itself. Its first fix for comment authors checked every new token with Spotify. A review then caught two problems: a token from any Spotify app would pass, and each page load fired a burst of those checks, which my server's fail2ban could mistake for an attack. The second version signs an author cookie at login. It caught the queue race in feature six itself.
- It told me when the bug wasn't in the app. When I pasted a wall of CORS and WebSocket errors from Firefox, it traced them to my browser blocking Spotify's player (tracking protection and DRM settings). Nothing in Shoutify needed to change.
- It said no when it should have. The lyrics question is the clearest example.
- It was upfront about limits. The waveform is decorative, Friend Activity only sees Shoutify, and the tests never touched a real account. Each of those is written down in the README or a code comment, not only in a chat.
By the numbers
| 2023 | 2026 |
|---|---|---|
Who | A team of students | Me and Claude Code |
Time | 11 weeks, February 8 to April 28, 2023 | About 30 hours, October 30 to November 1, 2026, breaks included |
Commits | 171, on top of the base project's 25 | 167 |
Application code | 5,493 lines of JavaScript, 2,115 of CSS | 6,471 lines of TypeScript (5,906 in the app, 565 on the server), 4,797 of CSS |
Tests | None | 78 unit tests and 136 browser checks, about 4,400 lines |
Database | MongoDB | One SQLite file |
Hosting | Heroku and Netlify | One Docker container on my home server |
Users | Anyone with a Spotify account | Five, Spotify's cap |
What I took away
- Old schemas hold old ideas. In 2023 we saved
progress_mswith every comment and barely used it. The SoundCloud-style player is mostly a new way to read data we'd been storing all along. - The platform's rules are the real spec. Five accounts made Shoutify a friends-only app. With no follows list, "friends" became the people you follow who use Shoutify. No audio data made the waveform decorative, and no lyrics hid the annotations. None of that was in my brief, and all of it shaped the result.
- A rewrite is also a security review. Tokens in URLs, authors taken from request bodies and an open proxy were fine for a class demo. They aren't fine for anything on the internet, even for five people.
- A fake service makes honest tests. A fake Spotify with fake friends let 136 checks run without anyone's password, and every screenshot here comes from it.
- Ship one thing at a time. Six small deploys meant six chances to say "not quite" before the next feature was built on top.
What's next
- Lyrics, if a source turns up that Spotify's terms allow. Then the annotations come back.
- Real accounts: getting my friends on it, and testing in Firefox, Safari and on phones.
- Replies to replies: today they only notify the author of the comment at the top of the thread.
- A real waveform, if Spotify ever gives apps loudness data again.
Thanks to my 2023 teammates, whose ideas this still runs on, and to JL978 for the clone we started from. The code is on GitHub. Spotify only lets an app in Development Mode serve five accounts, so my copy is for me and a few friends. You can run your own: create a Spotify app, fill in three settings, and start it with Docker Compose. The README walks through it.
