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.
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/youtuberoute 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-youtubebased player with a playlist held in component state. - Three CopilotKit actions registered with
useCopilotAction:playSong(query)searches and plays the top result,togglePlayPausecontrols the player, andqueueSong(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 --> fbSystem 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: ConfirmationLearnings
- 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.