← Back to landing

Case study

Building streambeats.live

A focused music player + OBS widget platform for streamers — designed and built in iterations across May 2026.

Timeline
May 2026 · ~4 weeks
Role
Design + frontend
Stack
Next.js 16 · React 19 · Tailwind v4

The problem

Spotify isn't allowed. Existing widgets are generic.

Streamers can't play Spotify on Twitch or YouTube — the ToS forbids public performance and the DMCA strikes that followed in 2020–2024 made it a non-option. The fallback is StreamElements / StreamLabs, which give you a generic widget cube and call it a day. Add to that the tedium of manually typing artist + title for every MP3 you drop in, and the streamer workflow becomes friction first, music second.

Discovery

What streamers actually want.

Talked through the workflow with streaming friends: they want to drop their own legally-owned MP3s, see covers + titles auto-populate, and stream the now-playing card to OBS without 30 minutes of CSS tweaking. The platform should disappear into the show — not be its own production task.

Design system

Yellow bloom, frosted glass, anti-generic guardrails.

Token-driven via Tokens Studio (Figma) — eleven-step Primary / Secondary / Neutral scales, plus glow + glass + radius + shadow tokens that exist solely because the Figma reference demanded them. Typography is Satoshi for display + Roboto for body. Hardcoded anti-generic rules in a Visual Identity Commitments document: no shadcn defaults, no flat fills, no sticky-bar nav, no off-the-shelf Google Font substitutes. Every primitive (BloomBackground, GlassCard, PillNav) was hand-built before any feature code shipped.

Technical decisions

Four problems worth showing my work on.

The notch saga is the truthful messy-middle. The persistent-audio refactor is the architectural inflection point. The metadata pipeline and the two-player model frame the product.

Two players, not one

The /app/player browser surface is the DJ control panel. The /w/[widgetId] route is the visual rendered inside an OBS Browser Source. They share state via BroadcastChannel (NowPlayingSnapshot), but they're different products with different design budgets — one optimized for control, one for legibility from across a streamer's bedroom.

The hero notch — three failed attempts

First try: CSS clip-path: path() with @supports gate for corner-shape: notch. Chrome rendered the panel as invisible because the experimental property was recognized but unimplemented. Second try: SVG mask with mask-composite — fragile across Chromium builds. Third try: SVG element with a `clipPath` applied to `image` — the clipPath URL ref wasn't being honored after hydration. Final: pure CSS clip-path: path() on a div with background-image. Real boolean cutout, smooth Q-curve inner corners, guaranteed in every browser. The lesson: bleeding-edge CSS feels like progress until it isn't.

Metadata pipeline — ID3, Last.fm, iTunes, fallbacks

Test corpus was 16 yt-dlp-downloaded MP3s with zero ID3 tags. Filename was the only signal — but my first parser took the leading 'NN - ' track number prefix as the artist, returning artist='14' for one file. The fix: always strip 'NN - ', then split on ' - ', then strip YouTube noise like '(Official Video)'. Last.fm gives indie corrections; iTunes fallback catches mainstream releases with sharp 600×600 cover art.

Persistent audio across navigation

Initially the `audio` element lived inside /app/player — navigating away unmounted it and killed playback. The refactor: lift it to /app/layout as an `AudioEngine` component. Music now survives every route change. A Spotify-style MiniPlayer appears on every /app page except the full player itself. As a bonus, the OBS widget gets cleaner BroadcastChannel state because there's a single emitter.

Outcomes

Shipped a real thing, not a Figma deck.

Working end-to-end pipeline: drop MP3s → auto-metadata → DJ surface → OBS widget rendering on stream. View Transitions API for navigation. Real Web Audio AnalyserNode driving the Equalizer template. Custom HSL color picker generating an entire 50–950 scale from one hue. Bilingual PL/EN. Anti-generic visual identity that's specifically streambeats — not interchangeable with any other shadcn site.

What's next

v0.2 — accounts, widget builder, cloud sync.

The /app/widget-builder canvas (already designed in Figma) becomes a true builder: drag-drop elements, custom CSS, save & share. Accounts + Supabase for cloud-synced widget configs and cross-device player ↔ widget sync. StreamBeats / Pretzel API integration for ready-made DMCA-safe libraries.

Tech stack

Built with

Next.js 16 App Router · React 19 · Tailwind v4 · Zustand · Dexie (IndexedDB) · Framer Motion · next-intl · music-metadata · Last.fm + iTunes APIs · BroadcastChannel + Web Audio API