Skip to main content
Back to Portfolio

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.

TypeScriptNext.js 15React 19Tailwind CSS 4ZustandMDXMotionreact-pageflipElevenLabsSharpAWS EC2PM2
Highlands Ranch Champ preview

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-pageflip for 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 via generateStaticParams — 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 navigation

  • Two-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

Highlands Ranch Champ screenshot 2
Highlands Ranch Champ screenshot 3

Interested in working together?

Let's discuss how I can help with your project

Get in Touch