17 years old. Vanilla JS. No framework. No backend server. Just shipped.
The Problem Nobody Was Solving
Every week, thousands of people ask ChatGPT, Claude, or Gemini for a playlist. The AI happily gives them a beautiful list of 10 songs. And then... nothing. They're stuck copy-pasting each song into Spotify or YouTube one by one.
That's annoying. So I fixed it.
PlaylistBridge (playlistbridge.netlify.app) converts any AI-generated song list into instant playable links. No login. No app install. Paste and play.
I started with a simple idea. Over the past few months it became something I didn't expect — a full in-browser music player with video mode, community playlists, shareable cards, and a Firestore-backed song cache. All in vanilla JS.
This is the story of how I built it and what I learned along the way.
The Stack (And Why I Chose It)
- Frontend: Vanilla HTML, CSS, JavaScript — no React, no Vue, nothing
- Backend: Netlify Functions (serverless)
- Database: Firebase Firestore
- Hosting: Netlify free tier
- Metadata: iTunes Search API (free, no key needed)
- Playback: YouTube IFrame API
I chose vanilla JS deliberately. No build step, no dependency hell, no framework overhead. Every file is directly editable and deployable. When something breaks, I know exactly where to look.
The tradeoff is more verbose DOM manipulation — but I handled this by creating a ui.js rendering layer that separates all visual state from business logic. script.js never touches the DOM directly. window.UI.* handles everything visual.
The Core Feature: Metadata Matching
When a user pastes a song list, here's what happens:
- Parser strips numbering, dashes, and whitespace
- Each song query hits my Netlify function which proxies the iTunes Search API
- iTunes returns the real track name, artist, and album art URL
- Cards render progressively as each song resolves — no waiting for all songs
The progressive rendering was important. If I waited for all 20 songs to resolve before showing anything, users would think the app was broken. Progressive rendering makes it feel instant.
One tricky edge case: the iTunes API sometimes returns the wrong song if the query is ambiguous. I solved this by constructing the query as "Artist Title" from the parsed input, which dramatically improves match accuracy.
InstantPlay: The Feature That Changed Everything
The original app just gave links. Users had to click 20 times to open 20 songs. That's still better than nothing, but it's not great.
So I built InstantPlay — an in-browser music player using the YouTube IFrame API.
The flow:
User clicks Play
→ Check in-memory cache
→ Check Firestore songs collection
→ Call /.netlify/functions/ytsearch
→ Try Invidious API (3 instances, 3s timeout each)
→ Fallback: YouTube HTML scrape
→ Load YouTube IFrame player
→ Auto-advance on song end
→ Prefetch next song ID in background
The Caching Strategy
I built three layers of caching for video IDs:
Layer 1 — In-memory JS object: Zero latency, lost on page refresh.
Layer 2 — sessionStorage: Survives page refresh within the same browser session.
Layer 3 — Firestore songs collection: Cross-user, permanent. Document ID is the normalized query (query.toLowerCase().replace(/[^a-z0-9 ]/g, '').trim()). First user to play a song pays the API cost. Every user after gets it from Firestore instantly.
This meant that as the app grew, the Netlify function calls for video IDs would actually decrease over time as the cache filled up.
The YouTube ToS Problem
Here's something I had to think carefully about: the YouTube IFrame API Terms of Service explicitly prohibit creating an "audio-only" experience. My player was hiding the iframe with height: 0.
My solution: an expandable full-screen player with an Audio/Video toggle. In Audio mode, users see the album art. In Video mode, they see the actual YouTube video. The hidden iframe is always present for audio — but users can switch to video mode to see the visual content, keeping the experience ToS-compliant.
The Expanded Player
This was the most complex UI piece.
When users tap the mini bar at the bottom, it slides up into a full-screen player — exactly like YouTube Music's expanded view. Behind the scenes:
- The background becomes a blurred version of the current album art
- Album art is displayed large and centered with a subtle glow
- Progress bar, controls, and queue are all visible
- Switching to Video mode injects a standard YouTube embed iframe at the current timestamp
The trickiest bug: when Video mode injected its own iframe, the hidden YT API player was still running — two audio sources playing simultaneously. The fix was to mute() and pauseVideo() on the API player when switching to video mode, then unMute() and playVideo() when switching back to audio.
Community Playlists
I wanted users to share what they'd built. The community page shows playlists published by other users — each card has a 2×2 album art collage, song preview, play count, and an "Open & Play" button.
A key optimisation: instead of fetching album art from iTunes on every community page load (which would be 48 API calls for 12 cards), I save the artUrls array to Firestore at publish time. The community page reads them directly — zero API calls.
For playlist titles, I auto-generate them: "The Weeknd, Taylor Swift + 3 more". No user input required — frictionless publishing means more people actually publish.
Share Cards
After generating a playlist, users can share a beautiful image card — like Spotify Wrapped for their playlist.
Built entirely with HTML5 Canvas:
- First album art becomes a blurred background
- 2×2 collage of album arts at the top
- Song list with thumbnails below
- PlaylistBridge branding + CTA button at the bottom
The CORS challenge: Canvas taints when drawing cross-origin images. The fix was adding crossorigin="anonymous" to the <img> elements at card render time, then reading those DOM elements directly in the Canvas — zero re-fetch, zero additional API calls.
On mobile, it triggers the native share sheet. On desktop, it downloads as PNG.
SEO That Actually Worked
I'm getting 100% foreign traffic (US, UK, Australia) with zero paid promotion.
What worked:
- Multiple dedicated landing pages targeting long-tail keywords (
/chatgpt-to-spotify,/text-to-youtube-music, etc.) - JSON-LD schema for
WebApplicationandFAQPage -
llms.txt— a plain text file explaining the tool to AI models. This is why ChatGPT and Gemini have been recommending PlaylistBridge to users organically. -
alternateName: "Playlist Bridge"in schema to capture the two-word search variant
What didn't work: Reddit. Every post got removed regardless of account age or content quality. Moved on.
The Numbers
- 319+ playlists generated (tracked via Firestore)
- 100% foreign traffic — US, UK, Australia, Europe
- 10 days to reach 100 visitors after the redesign (vs 2 months before)
- 777 Netlify function calls in a single day peak
- 0 paid promotion
What I'd Do Differently
Use a design system from day one. I spent hours fixing inconsistent spacing because I didn't establish CSS variables properly at the start. Now I have a full design system — but retrofitting it was painful.
Build the caching layer earlier. I only added the Firestore song cache after noticing Netlify function calls spiking. It should have been there from day one.
Test on real devices sooner. So many bugs only appeared on mobile — the expanded player layout, volume controls getting cropped, background play limitations. Emulators lie.
What's Next
- Spotify OAuth — create real playlists in user accounts. This is the feature users actually want.
- Mood/Vibes page — browse community playlists by genre (Gym, Romance, Sad, Late Night)
- AdSense — the foreign traffic makes this viable now
Try It
Paste any AI playlist. Hit play. It just works.
If you've built something similar or have thoughts on the architecture decisions, I'd love to hear them in the comments. Especially interested in how others have handled the YouTube IFrame ToS compliance question.
Built with vanilla JS, Firebase, and Netlify. No VC funding. No team. Just me, 17, figuring it out.
Top comments (4)
Nice site. Everything is working, and ad free music also works well. Keep it up
Thank you sir
Does this create actual playlist? Bcoz I tried doing it but it required api and it adds complexity. But I went through your site it good. Just improve you ui a bit
No currently it dont create playlist. Bcoz it will require oauth and our goal is no account login. We will add you hybrid approach