Highlands Ranch Champ
Consumer-facing artist platform with a persistent music player that survives client-side route transitions and an in-browser book reader with word-level synced narration.

Overview
Signature site for an independent musical artist publishing original music, parody tracks, and a companion series of illustrated children's books. The brief was to build a media platform — not a brochure — with first-class audio playback and a real reading experience for the books.
Two custom media systems carry the project:
-
Persistent music player. The naive approach mounts an
<audio>element on the page that hosts the player; the moment a visitor clicks any other link, the route component unmounts and playback dies. The fix lifts the audio subsystem above the routing layer — the<audio>element mounts once in the root layout, player state lives in a Zustand store, and a floating mini-player stays visible on every route. Start an album on Albums, browse to Books, open a chapter — same DOM audio node, no interruption. -
In-browser book reader with synced narration. Built around
react-pageflipfor a two-page hardcover layout with dynamic pagination that re-measures on resize. Each chapter ships with multiple ElevenLabs TTS voice variants and a per-word timing manifest; a binary-search active-word lookup drives karaoke-style highlighting of the current word as audio plays. Narration and the global music player are separate<audio>instances but coordinate through the same store — only one is active at a time.
Content model & deployment:
- All content lives in MDX under
content/, read at build time. Album and book routes are statically generated viagenerateStaticParams— no CMS, no client-side fetches for catalog content. - A companion import pipeline pulls highest-version lyric files from an external studio repository using slug-normalized matching with Levenshtein fallback.
- Self-hosted on AWS EC2 — Apache reverse proxy fronts a PM2-managed Node process; the build script rsyncs the Next.js
output: "standalone"bundle to the host and reloads the worker. Not Vercel.
The Challenge
Music sites typically lose playback the moment a user navigates, and "book" sections are usually PDF downloads or external embeds. Neither matches what a consumer media platform should feel like.
The Solution
Lifted the audio subsystem above the routing layer so playback survives client-side navigation on the same DOM audio node, and built a real in-browser book reader with two-page hardcover layout and word-level synced narration — two audio contexts coordinated through a single global store.
Results
Persistent
<audio>element mounted once at the root layout — playback survives every client-side navigationTwo-page hardcover book reader (react-pageflip) with dynamic pagination that re-measures on resize
Word-level narration sync via per-word timing manifests and a binary-search active-word lookup
Multiple TTS voice variants per chapter with localStorage-saved voice preference
Coordinated audio contexts — narration and music never compete for the user's attention
MDX-driven content model: albums, singles, books, chapters, lyrics, videos, merch all in repo
Build-time static generation for every album and book page — no CMS, no runtime catalog fetches
Self-hosted on EC2 with rsync + PM2 deploy pipeline
Gallery

