Build collaborative playlists with social features: comments, tips and curator tokens
This browser extension adds social features to Spotify. It allows users to:
How it’s made What it is A Chrome (Manifest V3) extension that layers a shared commenting system on top of Spotify’s Web Player. A small floating button opens a right‑side drawer for playlist/track comments; comment bubbles appear next to tracks that have discussion. Architecture at a glance Extension (Frontend): Vanilla JavaScript content script injects a Shadow DOM overlay so styles don’t clash with Spotify. It monitors Spotify’s single‑page app routing via a MutationObserver and a light history pushState/replaceState hook to detect playlist changes and update UI/state. API (Backend): Node.js + Express server with PostgreSQL. Endpoints for reading/writing comments, bulk comment counts, and playlist stats. Security via Helmet, CORS allowlist, input validation, and rate limiting. Infra/Dev: Docker Compose spins up Postgres, the Node server, and a Caddy reverse proxy that terminates HTTPS locally on https://localhost:8443. Health checks and sensible defaults are baked in. Key technologies Chrome Extension (MV3): content scripts, background service worker, web_accessible_resources, and declarativeNetRequest. UI/UX: Shadow DOM overlay, floating action button, slide‑in drawer, track‑level comment bubbles, keyboard shortcuts (Cmd/Ctrl+Enter to send), and responsive design aligned with Spotify’s dark theme. Backend: Express, pg Pool, structured validation (IDs, URI formats, length limits), and rate limiting to prevent spam. Database: PostgreSQL with a simple comments table keyed by playlist_id and optional track_uri. Dev runs via Docker; production can point to a managed Postgres (e.g., Supabase) using DATABASE_URL. HTTPS & CORS: Caddy provides local HTTPS (self‑signed, trusted with caddy trust) and handles Private Network Access preflights. Express sets a strict CORS origin allowlist (Spotify + proxy/API origins) configurable by CORS_ORIGINS. How the pieces fit The content script detects the current playlist ID from the URL and injects UI. When the drawer opens, it fetches comments from the API and polls every 30s for updates. Posting a comment calls POST /comments and re-renders. For track bubbles, the script checks if any comments exist for each track and adds a bubble to the row. The Node API exposes: GET /comments?playlist_id[&track_uri] POST /comments POST /comments/counts (bulk) GET /comments/stats/:playlist_id GET /health Caddy listens on :8443 with internal TLS and reverse‑proxies requests to the Node server on :5050, setting appropriate headers for preflight and CORS. Notable hacks and learnings SPA routing without official hooks: Spotify’s internal navigation doesn’t fire full page loads, so we combined a Title observer with monkey‑patched pushState/replaceState to reliably detect playlist changes and refresh UI. Shadow DOM everywhere: To avoid style collisions with Spotify’s complex CSS, all UI lives in a Shadow DOM root mounted at page level. This kept our styles predictable. Local HTTPS done right: Chrome MV3 + PNA can be finicky with localhost APIs. We added a Caddy reverse proxy with a trusted local CA, plus explicit preflight handling, to make local development behave like production TLS. Performance guards: Bulk counts endpoint, lightweight polling, and a small client‑side cache (in utils/api.js) are set up to scale track metadata checks. Input validation and rate limiting keep the API resilient. Why partner/infra choices helped Managed Postgres (e.g., Supabase): Lets us point DATABASE_URL at a production‑ready Postgres quickly while keeping the same schema used in local Docker. Caddy: Fastest path to dev‑grade TLS with minimal config, unblocking MV3 + CORS/PNA constraints. Docker Compose: One command brings up DB, API, and HTTPS proxy with health checks—great for contributors and demos. What’s next Optional user accounts, moderation tools (a DELETE endpoint scaffold exists), and richer real‑time updates (websocket/SSE) to replace polling. Security and privacy MVP has no auth; comments are public. Server sanitizes inputs, enforces length limits, sets security headers, and rate‑limits writes. All traffic runs over HTTPS in dev and production.