← All work
Case study · 2024

Musix

A neobrutalist music web app where a CopilotKit assistant can play, pause and queue songs, using YouTube search as the music source.

Next.jsTypeScriptCopilotKitFirebase AuthYouTube Data APITailwind CSSFramer MotionLive site ↗Source ↗

Overview

Musix is a music player you can control by chatting. After signing in with Google and picking genres and artists, you land on a player backed by YouTube videos. A CopilotKit popup is wired to the player through actions, so typing "play something by Linkin Park" or "queue Espresso" changes the playback state.

What I built

  • Firebase Google sign-in, an auth guard on the home page that redirects to /login, and a questionnaire for choosing genres and artists after first login.
  • A /api/youtube route that calls the YouTube Data API v3 search endpoint (type=video, maxResults=1) so the browser does not call YouTube's API itself.
  • A react-youtube based player with a playlist held in component state.
  • Three CopilotKit actions registered with useCopilotAction: playSong(query) searches and plays the top result, togglePlayPause controls the player, and queueSong(query) adds the result to the playlist.
  • A neobrutalist UI with themed CopilotKit CSS variables, a velocity-scroll hero, music cards and a favourite artists grid.

Architecture

The assistant runs in the browser and calls actions that change the same player state as the buttons do.

flowchart LR
  subgraph client["Browser"]
    pop["CopilotPopup"]
    player["MusicPlayer with react-youtube"]
    land["Landing sections"]
  end
  subgraph server["Next.js"]
    yt["/api/youtube route"]
  end
  subgraph ext["External"]
    ck["CopilotKit cloud"]
    ytapi["YouTube Data API v3"]
    fb["Firebase Auth"]
  end
  pop -->|"chat"| ck
  ck -->|"invoke action"| player
  player -->|"search query"| yt
  yt --> ytapi
  land --> fb

System design

How a chat request becomes a song playing.

sequenceDiagram
  participant U as User
  participant C as CopilotPopup
  participant K as CopilotKit
  participant P as MusicPlayer
  participant A as api youtube
  participant Y as YouTube API
  U->>C: Queue Numb by Linkin Park
  C->>K: Message with registered actions
  K-->>P: Call queueSong with query
  P->>A: GET query
  A->>Y: search part snippet maxResults 1
  Y-->>A: Top video
  A-->>P: videoId, title, thumbnail
  P->>P: Append to playlist state
  P-->>C: Action complete
  C-->>U: Confirmation

Learnings

  • Exposing a few well-named actions was enough for natural-language control. The model chooses the action and fills in the query.
  • Using only the top search result is fast but sometimes picks a cover or a live version. Returning a few results to choose from would be more accurate.
  • Preferences from the questionnaire are not saved yet. Storing them in Firestore and using them to seed the playlist is the next step.