Case study
Skoncentrowany odtwarzacz muzyki i widget OBS dla streamerów — zaprojektowany i zbudowany iteracyjnie w maju 2026.
Problem
Streamerzy nie mogą puszczać Spotify na Twitchu ani YouTubie — ToS zabrania publicznego odtwarzania, a fala DMCA strike'ów z 2020–2024 ostatecznie to wyrżnęła. Fallback to StreamElements / StreamLabs — dają generyczny widget i tyle. Dodaj do tego ręczne wpisywanie artysty + tytułu dla każdego MP3 i workflow streamera robi się jednym wielkim tarciem.
Discovery
Porozmawiałem z kilkoma streamerami: chcą wrzucać własne MP3 (legalnie posiadane), widzieć automatycznie wczytane okładki i tytuły, i streamować widget na OBS bez 30 minut grzebania w CSS. Platforma powinna znikać w tle — nie być kolejnym zadaniem produkcyjnym.
Design system
Token-driven przez Tokens Studio (Figma) — 11-stopniowe skale Primary / Secondary / Neutral, plus tokeny glow + glass + radius + shadow które istnieją bo Figma ich wymagała. Typografia Satoshi (display) + Roboto (body). Hardcoded anty-generyczne reguły w Visual Identity Commitments: zero shadcn defaults, zero płaskich fillów, zero sticky-bar navów, zero zamienników Google Fonts. Każdy primitive (BloomBackground, GlassCard, PillNav) zbudowany ręcznie przed jakimkolwiek feature kodem.
Decyzje techniczne
Notch saga to prawdziwy bałagan środka. Refactor persistent audio to punkt zwrotny architektury. Pipeline metadanych i model two-player oprawiają produkt.
Powierzchnia /app/player w przeglądarce to panel DJ-a. Route /w/[widgetId] to wizual renderowany w OBS Browser Source. Dzielą stan przez BroadcastChannel (NowPlayingSnapshot), ale to różne produkty z różnymi budżetami designu — jeden zoptymalizowany pod kontrolę, drugi pod czytelność z drugiego końca pokoju streamera.
Pierwsza próba: CSS clip-path: path() z @supports gate dla corner-shape: notch. Chrome renderował panel jako niewidoczny bo eksperymentalna property była rozpoznana ale niezaimplementowana. Druga: SVG mask z mask-composite — kruchy między buildami Chromium. Trzecia: element SVG z `clipPath` aplikowanym na `image` — referencja clipPath URL nie była honorowana po hydration. Finalna: czysty CSS clip-path: path() na divie z background-image. Prawdziwy boolean cutout, smooth Q-curve inner corners, działa w każdej przeglądarce. Lekcja: bleeding-edge CSS czuje się jak postęp aż przestaje.
Test corpus to 16 MP3 z yt-dlp, zero ID3 tagów. Filename był jedynym sygnałem — ale mój pierwszy parser brał prefix 'NN - ' jako artystę i zwracał artist='14' dla jednego pliku. Fix: zawsze strip 'NN - ', potem split na ' - ', potem strip YouTube noise typu '(Official Video)'. Last.fm daje indie korekty; iTunes fallback łapie mainstream releases z ostrymi okładkami 600×600.
Pierwotnie element `audio` żył w /app/player — nawigacja go unmounta i zatrzymywała muzykę. Refactor: wyniesienie do /app/layout jako komponent `AudioEngine`. Muzyka teraz przeżywa każdą zmianę route. Spotify-style MiniPlayer pojawia się na każdej stronie /app poza pełnym playerem. Bonus: widget OBS dostaje czystszy BroadcastChannel state bo jest jeden emitter.
Wyniki
Działający end-to-end pipeline: drop MP3 → auto-metadane → DJ surface → widget OBS renderujący się na streamie. View Transitions API do nawigacji. Real Web Audio AnalyserNode napędzający szablon Equalizer. Custom HSL color picker generujący całą skalę 50–950 z jednego odcienia. Dwujęzyczne PL/EN. Anty-generyczna identyfikacja wizualna — specyficznie streambeats, nie wymienna z żadną inną stroną shadcn.
Co dalej
Canvas /app/widget-builder (już zaprojektowany w Figmie) staje się prawdziwym builderem: drag-drop elementy, własny CSS, save & share. Konta + Supabase dla cloud-syncowanych configów widgetów i cross-device sync player ↔ widget. Integracja StreamBeats / Pretzel API dla gotowych DMCA-safe bibliotek.
Tech stack
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