CD Player
A few albums from the user's music app as CDs. Drop one in and it spins and plays.
Experience · Shares patterns with Music Player, Entity Picker
≈ 1,101 tokens per use (tool 501 · guidance 305 · typical payload 295)How token costs are estimated
Example: CD Player
Put some music on for tonight. Actually, no, let me pick.
Pick a disc
When to use
- The user wants you to put something on, or asks what to play for a moment or a mood (dinner, a party, winding down), and you have a few albums from their music app to offer.
- The user would rather pick by feel from a handful of albums than read a list.
When not to use
- The user asks what's playing now, or wants to control playback (skip, shuffle, seek). Use the Music Player.
- You'd offer single tracks or build a playlist. Use the Music Player.
- You don't have the albums from the user's music provider. Never invent albums, artwork or preview URLs.
- There's only one album to offer. Say it in text, or play it.
How the agent should use it
- Pass 2–8 albums exactly as the provider returns them, the one you'd suggest first. Artwork and previews are optional but make the choice: the cover is printed on the disc, and the preview plays while it spins.
- Each disc the user puts in returns
playwith itsalbumId. The preview plays in the chat; start the full album on the user's device only when they asked you to play it there. pausemeans the user stopped the disc. Pause the device if you started it there.- Set
nowPlayingIdonly when the provider reports that album playing. It starts in the player, spinning. - Leave
titleout when your reply already asks the question.
Design
- One player, one rack. A square player with a blank silver disc sits above a rack of discs. Picking a disc flies it into the player, where it spins; the disc it replaces flies back to its slot, and the empty slot is drawn as a dashed ring so the rack never reflows.
- Click, drag or keyboard. A disc goes in on a click, when it's dropped on the player, or with Enter or Space. A drag let go anywhere else snaps back.
- The cover is the disc. Artwork fills the disc, with a still sheen and fine rings over it and a clear hub in the middle, so the face turns under the light. A disc without artwork is plain silver.
- Written on in marker. Every disc carries the artist's name in a handwritten face (Gochi Hand), above and below the hub, turning with the disc. The writing scales with the disc, so the small discs carry the same name in miniature. It is near-black on artwork and the text colour on a silver disc. Load the font and expose it as
--font-gochi-hand(the GenUI theme maps it to--font-marker); without it the browser's cursive face is used. - Spinning means playing. A disc with a preview spins while the preview plays (on repeat); one without spins until the user stops it, since it plays on their device. Pressing the disc in the player pauses or resumes it.
- The album's title and artist sit under the player; before a pick, a muted "Pick a disc".
Accessibility
Each disc in the rack is a button named "Play title by artist", with the same name in a tooltip. After a pick, focus moves to the disc in the player, a button named "Pause title by artist" (or "Play" once paused). A polite live region announces each change ("Playing Gold Hour by Velvet Static"). Drag is never the only way in. With reduced motion, discs move without flying and the disc in the player holds still; the title under it still says what's on.
Installation
npx shadcn@latest add https://your-site.com/r/cd-player.json
Usage
Each pick is a result, so carry it out (or just note it) and reply:
import { tool } from "ai"
import { cdPlayerTool } from "@/components/genui/cd-player.schema"
const tools = {
[cdPlayerTool.name]: tool({
description: cdPlayerTool.description,
inputSchema: cdPlayerTool.inputSchema,
// No execute: the user's pick comes back as the tool result.
}),
}Render it through the Renderer under show_cd_player, or directly:
<CdPlayer
{...part.input}
status={part.state === "input-streaming" ? "streaming" : "ready"}
onResult={(result) => addToolResult({ toolCallId: part.toolCallId, output: result })}
/>Discs appear as they stream in and can be picked once the call is complete.
Schema
| Prop | Type | Notes |
|---|---|---|
title | string? | A short heading, 60 characters at most. Usually left out |
albums | { id, title, artist, artwork?, previewUrl? }[] | Data-bound. 2–8 albums from the provider. artwork is { src, alt }; previewUrl is http(s), blob: or data:audio |
nowPlayingId | string? | Data-bound. The album the provider reports playing; starts in the player |
Host props: status (streaming or ready), onResult, result (the last play or pause from history: puts that disc back without calling onResult) and className.
Result: { action: "play", albumId } or { action: "pause", albumId }.