# Get Started
---
title: Get Started
description: A quick guide to adding audio/ui components to your application.
---
This guide walks you through the essential steps to add audio/ui to your shadcn project.
## Prerequisites
audio/ui is built on **React 19** and [Tailwind CSS v4](https://tailwindcss.com). Ensure your project is set up with these technologies before proceeding. We recommend following the [Official shadcn/ui Installation Guide](https://ui.shadcn.com/docs/installation) to prepare your environment.
## Setup the audio/ui Registry
Add the audio/ui registry namespace to your `components.json` and set your component library and style. Learn more about registry config from [shadcn registry docs](https://ui.shadcn.com/docs/registry).
```json
{
"registries": {
"@audio": "https://audio-ui.xyz/r/{style}/{name}.json"
}
}
```
## Components
You can install audio/ui using the [**shadcn CLI**](https://ui.shadcn.com/docs/cli) for automation or by manual installation for full control. Both methods apply to everything in [Docs](/docs/) and the [component catalog](/components), including the `block-*` blocks and demos you install with `@audio/…`.
## Styling
Components are styled with a design token system defined by CSS variables and implemented with Tailwind CSS, following the same conventions as shadcn/ui so they're fully customizable. See the [Styling guide](/docs/styling) for details.
## AI-Enhanced Workflow
audio/ui is designed from the ground up to be **AI-friendly**, making it easier for language models to understand and modify your UI.
- **llms.txt**: Access our [llms.txt](/llms.txt) file for a structured map of our documentation and architecture optimized for AI agents.
- **Copy Markdown**: Every page includes a **Copy Markdown** feature, allowing you to instantly feed documentation or code into your AI-driven development workflows.
## Next Steps
Head over to the [Audio Player](/docs/components/base/player) docs to install your first component, or browse the [component catalog](/components) for ready-to-use blocks.
# Introduction
---
title: Introduction
description: A set of accessible and composable Audio UI components. Built on top of shadcn/ui, designed to be copied, pasted, and owned.
---
**audio/ui** is a set of accessible and composable Audio UI components. Built on top of [shadcn/ui](https://ui.shadcn.com/), it's designed for you to copy, paste, and own.
**This is not a component library. It is how you build your audio component library.**
You know how most traditional component libraries work: you install a package from NPM, import the components, and use them in your app. This works well until you need to customize a component to fit your design system, or need one that isn't included. Often you end up wrapping library components, overriding styles, or mixing components from different libraries with incompatible APIs.
## Open Code
audio/ui hands you the actual component code. You have full control to customize and extend the components to your needs:
- **Full Transparency:** You see exactly how each component is built.
- **Easy Customization:** Modify any part of a component to fit your design and functionality requirements.
- **AI Integration:** Access to the code makes it straightforward for LLMs to read, understand, and even improve your components.
## Composition
Every component in audio/ui shares a common, composable interface. Browse the [component catalog](/components) for composed examples by category, or install primitives and blocks straight from the registry — copy-ready layouts and lower-level pieces so you can pick the right level of abstraction for each screen.
## Beautiful Defaults
audio/ui comes with a collection of components that have carefully chosen default styles, designed to look good on their own and to work well together as a consistent system.
## AI-Ready
The design of audio/ui makes it easy for AI tools to work with your code. Its open code and consistent API allow AI models to read, understand, and even generate new components.
## Open Source
This project is open source. We welcome contributions, feedback, or improvements. Check out our [repository](https://github.com/ouestlabs/audio-ui) on GitHub.
## Next Steps
Ready to get started? Head over to the [Get started](/docs/get-started) guide to add audio/ui to your project.
# MCP
---
title: MCP
description: Learn how to use the shadcn/ui MCP (Model Context Protocol) with audio/ui.
---
MCP is an open protocol that standardizes how applications provide context to LLMs.
## Configure MCP
Run the following command to configure the mcp server:
```bash
npx shadcn@latest mcp init
```
Select your MCP client as prompted, then enable the MCP server in your client to finish setup.
## Usage
You can now ask your IDE to use any audio/ui component. Here are some prompt examples:
- Build an audio player with queue management, shuffle, and repeat modes using the Audio Player.
- Create a synth control panel with Knob, XY Pad, and Channel Strip components.
- Add a mixer-style channel strip with Fader and Transport controls for a DAW-like interface.
# Registry
---
title: Registry
description: Learn how to use the audio/ui Registry with shadcn/ui.
---
The audio/ui Registry gives you instant access to production-ready audio components. Add it once, then pull in flexible, composable blocks on demand.
## Setup the audio/ui Registry
Add the audio/ui registry namespace to your `components.json`. Learn more about registry config from [shadcn registry docs](https://ui.shadcn.com/docs/registry).
```json
{
"registries": {
"@audio": "https://audio-ui.xyz/r/{style}/{name}.json"
}
}
```
## Usage
Install blocks via the shadcn CLI using the `@audio/{name}` syntax.
```bash
npx shadcn@latest add @audio/block-pocket-synth
```
Install components via the shadcn CLI using the `@audio/{name}` syntax.
```bash
npx shadcn@latest add @audio/knob
```
# Roadmap
---
title: Roadmap
description: Current state of audio/ui and where we're headed next.
---
**audio/ui** is a component library for building audio interfaces in React applications. Built on top of [shadcn/ui](https://ui.shadcn.com/), we provide accessible, composable components that you can copy, paste, and customize to fit your needs.
## Current Status
We're actively building and improving `audio/ui`. Here's what's available today:
### Components
- **Audio Player**: A fully-featured, composable audio player with queue management, shuffle, repeat modes, and volume control
- **Controls**: Knob, Fader, XY Pad, and Transport primitives for building custom audio interfaces
- **Channel Strip**: Layout patterns for mixer-style columns combining controls, labels, and readouts
### Library
- **Audio Store**: A Zustand-based state management solution for audio playback with localStorage persistence
- **HTML Audio**: Core singleton for loading and controlling HTML5 audio with retry logic and event handling
### Blocks
- **Pre-assembled Patterns**: Ready-to-use audio player and channel strip patterns that combine multiple components — browse the [component catalog](/components)
## What's Next
- **More Audio Components**: Additional audio-specific UI patterns and controls
- **Enhanced Player Features**: Advanced playback controls, playlist management, and audio visualization
- **Visualization Components**: Waveform displays, spectrum analyzers, and other audio visualizations
- **Radix UI Support**: A Radix-based variant of the component set, alongside the current Base UI implementation
## Contributing
We're building this in the open and welcome contributions! Whether it's bug reports, feature requests, or code contributions, we'd love to have you involved.
Check out our [GitHub repository](https://github.com/ouestlabs/audio-ui) to get started.
# Styling
---
title: Styling
description: A guide for extended styling options with our color system.
---
## Overview
audio/ui extends the official shadcn/ui theming system with additional semantic tokens that enable precise, context-aware UI states.
While you can use standard shadcn/ui themes, we recommend adopting these custom color tokens to ensure consistent contrast across component states such as alerts and badges.
## Extended Tokens
To support advanced state communication, we extend the base palette with additional semantic tokens:
- `--destructive-foreground`: Foreground color specifically for destructive actions.
- `--info` & `--info-foreground`: Background and text colors for informational states.
- `--success` & `--success-foreground`: Background and text colors for success/positive outcomes.
- `--warning` & `--warning-foreground`: Background and text colors for cautionary states.
- `--invert` & `--invert-foreground`: Background and text colors for invert states.
```css title="app/globals.css"
@theme inline {
--color-destructive-foreground: var(--destructive-foreground);
--color-info: var(--info);
--color-info-foreground: var(--info-foreground);
--color-success: var(--success);
--color-success-foreground: var(--success-foreground);
--color-warning: var(--warning);
--color-warning-foreground: var(--warning-foreground);
--color-invert: var(--invert);
--color-invert-foreground: var(--invert-foreground);
}
:root {
--destructive-foreground: var(--color-red-800);
--info: var(--color-violet-500);
--info-foreground: var(--color-violet-900);
--success: var(--color-emerald-500);
--success-foreground: var(--color-emerald-900);
--warning: var(--color-yellow-500);
--warning-foreground: var(--color-yellow-900);
--invert: var(--color-zinc-900);
--invert-foreground: var(--color-zinc-50);
}
.dark {
--destructive-foreground: var(--color-red-600);
--info: var(--color-violet-500);
--info-foreground: var(--color-violet-600);
--success: var(--color-emerald-500);
--success-foreground: var(--color-emerald-600);
--warning: var(--color-yellow-500);
--warning-foreground: var(--color-yellow-600);
--invert: var(--color-zinc-700);
--invert-foreground: var(--color-zinc-50);
}
```
For general customization guidelines such as adding your own custom colors or switching between utility classes and variables, please refer to the [official shadcn/ui documentation](https://ui.shadcn.com/docs/theming).
# Audio Store
---
title: Audio Store
description: Zustand-based store and helper actions for queue and playback management.
---
## Installation
CLIManual
```bash component
npx shadcn@latest add @audio/store
```
Install the runtime dependencies:
```bash
npm install zustand
```
Copy and paste the following code into your project.Update the import paths to match your project setup.
### Import
Import the store hook and types:
```tsx
import {
useAudioStore,
calculateNextIndex,
calculatePreviousIndex,
canUseDOM,
type AudioStore,
type RepeatMode,
type InsertMode,
} from "@/lib/audio-store";
```
## Core Concepts
### useAudioStore Hook
Performance Best Practices: Use granular selectors for better
performance. Subscribe only to the specific slices of state you need. This
prevents unnecessary re-renders when unrelated state changes.
Access the store with granular selectors for better performance:
```tsx
const currentTime = useAudioStore((s) => s.currentTime);
const isPlaying = useAudioStore((s) => s.isPlaying);
```
For multiple values, extract them inside your component:
```tsx
function PlayerStatus() {
const currentTrack = useAudioStore((s) => s.currentTrack);
const duration = useAudioStore((s) => s.duration);
const isPlaying = useAudioStore((s) => s.isPlaying);
return (
);
}
```
### Direct Store Access
```tsx
import { useAudioStore } from "@/lib/audio-store";
// Read state without React subscriptions
const state = useAudioStore.getState();
console.log(state.isPlaying);
// Call actions directly
await useAudioStore.getState().play();
// Subscribe to changes (returns unsubscribe function)
const unsubscribe = useAudioStore.subscribe(
(s) => s.isPlaying,
(isPlaying) => console.log("Playing:", isPlaying)
);
```
## Persistence
The store automatically persists a subset of state to localStorage using Zustand's `persist` middleware:
| Category | Properties |
| ------------ | --------------------------------------------------------------------- |
| **Playback** | `currentTrack`, `currentTime`, `currentQueueIndex` |
| **Queue** | `queue` |
| **Settings** | `volume`, `isMuted`, `repeatMode`, `shuffleEnabled`, `insertMode` |
**Storage Key:** `audio:ui:store`
This allows users to resume playback and maintain queue state across page refreshes.
## Related
- [HTML Audio](/docs/lib/html-audio) - Core audio playback singleton
- [Audio Player](/docs/components/base/player) - Player UI components
## Notes
Best Practices:
- Prefer selector subscriptions for performance (subscribe to specific slices
of state)
- Async actions (play, next,{" "}
setCurrentTrack) wait for audio loading
- addToQueue supports "first", "last", and "after" insert modes
- Queue shuffling randomizes order but preserves track identity
- setQueueAndPlay and similar actions trigger audio loading via the
[HTML Audio](/docs/lib/html-audio)
- Persistence is automatic; no manual save required
- Direct store access via getState() is useful for non-React code
or imperative operations
# HTML Audio
---
title: HTML Audio
description: Core audio helper and singleton for loading and controlling HTML5 audio.
---
The `$htmlAudio` singleton manages playback of HTML5 audio with automatic retry logic, event handling, and volume fading. Use it alongside the [Audio Store](/docs/lib/audio-store) for full player functionality.
## Installation
CLIManual
```bash component
npx shadcn@latest add @audio/html
```
Copy and paste the following code into your project.Update the import paths to match your project setup.
### Import
Import the singleton and helpers from the audio library:
```tsx
import { $htmlAudio, formatDuration, type Track } from "@/lib/html-audio";
```
Or use the React hook for better integration:
```tsx
import { useAudio } from "@/hooks/use-audio";
function MyComponent() {
const { htmlAudio } = useAudio();
// Use htmlAudio instead of $audio
}
```
## Core API
### $htmlAudio (Singleton)
Manages the underlying `HTMLAudioElement`, playback state, retries, and events. Initialize on client start - the instance is server-safe.
```tsx
import { $htmlAudio } from "@/lib/html-audio";
// Initialize on the client
$htmlAudio.init();
// Load and play
await $htmlAudio.load("https://example.com/audio.mp3", 0);
await $htmlAudio.play();
```
Client initialization: The $htmlAudio singleton must
be initialized on the client. Call $htmlAudio.init() from a client
component or inside useEffect() so the underlying{" "}
HTMLAudioElement is created only in the browser environment.
#### Lifecycle
| Method | Description |
| ----------- | --------------------------------------------------------- |
| `init()` | Initialize on the client. Safe to call multiple times. |
| `cleanup()` | Reset and release the audio element (pause, clear src). |
```mermaid
sequenceDiagram
participant App
participant AudioLib
participant HTMLAudio
App->>AudioLib: init()
AudioLib->>HTMLAudio: new Audio()
AudioLib->>HTMLAudio: setupEventListeners()
Note over AudioLib,HTMLAudio: Ready for playback
App->>AudioLib: load(url, startTime)
AudioLib->>HTMLAudio: src = url
AudioLib->>HTMLAudio: load()
HTMLAudio-->>AudioLib: loadedmetadata
AudioLib-->>App: Promise resolved
App->>AudioLib: play()
AudioLib->>HTMLAudio: play()
HTMLAudio-->>AudioLib: playing event
AudioLib-->>App: Promise resolved
App->>AudioLib: cleanup()
AudioLib->>HTMLAudio: pause()
AudioLib->>HTMLAudio: src = ""
```
#### Playback
| Method | Description |
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `load(url, startTime?, isLive?)` | Load an audio source and wait for readiness. Pass `isLive` for live streams (longer timeout, no seek). Returns `Promise`. |
| `play()` | Start or resume playback. Returns promise that resolves when browser allows playback. |
| `pause()` | Pause playback immediately. |
| `seek(time)` | Seek when metadata is available. Ignored for live streams. |
Browser autoplay restrictions: The play() call
returns a promise which may be rejected by browser autoplay policies if there
was no user gesture. Wrap calls to play() in a try/catch and
provide a fallback UI so your app handles playback interruptions gracefully.
Live streams: For live streams (when isLive is
true) seeking is disabled and long timeouts are used. Live streams are best
handled with a separate code-path because the playback semantics differ from
on-demand audio files.
#### Volume
| Method | Description |
| --------------------------------- | ------------------------------------------------------------------ |
| `setVolume(volume, fadeTime?)` | Set or fade volume (0–1). If `fadeTime` > 0, animates smoothly. |
| `getVolume()` | Return current volume (0–1). |
| `setMuted(muted)` | Mute or unmute. Restores previous volume when unmuting. |
#### State
| Method | Description |
| ------------------------ | ------------------------------------------------------------------ |
| `getDuration()` | Return loaded source duration (seconds) or `0` if unavailable. |
| `getCurrentTime()` | Return current playback position (seconds). |
| `isPaused()` | Return boolean - is playback paused. |
| `getBufferedRanges()` | Return underlying `TimeRanges` or `null`. |
| `getSource()` | Return current source URL string. |
| `getAudioElement()` | Return raw `HTMLAudioElement` or `null` on server. |
#### Events
The library emits custom events via an internal `EventTarget`:
```tsx
$htmlAudio.addEventListener("bufferingStart", () => console.log("Buffering..."));
$htmlAudio.addEventListener("bufferingEnd", () => console.log("Ready to play"));
$htmlAudio.addEventListener("playbackStarted", () => console.log("Playing"));
$htmlAudio.addEventListener("audioError", () => console.error("Error"));
$htmlAudio.addEventListener("bufferUpdate", (e) => {
if (e instanceof CustomEvent) console.log("Buffered:", e.detail.bufferedTime);
});
```
## Utilities
### formatDuration(seconds)
Format seconds to `MM:SS` string. Handles invalid input by returning `"0:00"`.
```tsx
import { formatDuration } from "@/lib/html-audio";
formatDuration(125); // "2:05"
formatDuration(3661); // "61:01"
```
### isLive(duration)
Check if a duration value indicates a live stream. This is a method on the `HtmlAudio` class.
```tsx
import { useAudio } from "@/hooks/use-audio";
function MyComponent() {
const { htmlAudio } = useAudio();
const duration = htmlAudio.getDuration();
if (htmlAudio.isLive(duration)) {
// Handle live stream (no seeking)
}
}
```
Live Stream Detection: The isLive() method checks
if a duration is NaN, Infinity, or{" "}
-Infinity. A duration of 0 is not considered a live
stream (it just means the duration hasn't loaded yet).
## Types
### Track
Common audio track object with optional fields:
| Prop | Type | Default | Description |
| ------------------ | -------------------- | --------- | ----------------------------------- |
| `id` | `string \| number` | - | Unique identifier for the track. |
| `url` | `string` | - | URL of the audio file or stream. |
| `title` | `string` | - | Track title. |
| `artist` | `string` | - | Artist name. |
| `artwork` | `string` | - | Album artwork URL. |
| `images` | `string[]` | - | Array of image URLs. |
| `duration` | `number` | - | Track duration in seconds. |
| `album` | `string` | - | Album name. |
| `genre` | `string` | - | Genre. |
| `live` | `boolean` | - | Whether this is a live stream. |
| `[key: string]` | `unknown` | - | Additional properties. |
## Examples
### Basic Playback
```tsx
import { $htmlAudio } from "@/lib/html-audio";
$htmlAudio.init();
async function playTrack(url: string) {
try {
await $htmlAudio.load(url, 0);
await $htmlAudio.play();
} catch (error) {
console.error("Playback failed:", error);
}
}
```
### Volume Management
```tsx
import { $htmlAudio } from "@/lib/html-audio";
// Immediate change
$htmlAudio.setVolume(0.5);
// Smooth fade over 1 second
$htmlAudio.setVolume(0.8, 1000);
// Mute with memory
$htmlAudio.setMuted(true);
$htmlAudio.setMuted(false); // Restores previous volume
```
### React Component
```tsx
import { $htmlAudio, formatDuration } from "@/lib/html-audio";
import { useEffect, useState } from "react";
function TimeDisplay() {
const [time, setTime] = useState(0);
useEffect(() => {
const updateTime = () => setTime($htmlAudio.getCurrentTime());
const interval = setInterval(updateTime, 100);
return () => clearInterval(interval);
}, []);
return {formatDuration(time)};
}
```
### Event Monitoring
```tsx
import { $htmlAudio } from "@/lib/html-audio";
$htmlAudio.addEventListener("bufferingStart", () => {
console.log("Loading audio...");
});
$htmlAudio.addEventListener("bufferingEnd", () => {
console.log("Ready to play");
});
$htmlAudio.addEventListener("audioError", (event) => {
console.error("Audio error:", event);
});
```
## Related
- [Audio Store](/docs/lib/audio-store) - Zustand store for queue and playback state management
- [Audio Player](/docs/components/base/player) - Composable player UI components
## Notes
- Singleton pattern: All methods access the same `$htmlAudio` instance
- Server-safe: Methods check for client-side availability before executing
- Automatic retries: Handles load/play errors with exponential backoff (max 3 attempts)
- Volume fading: Animations use `requestAnimationFrame` for smooth transitions
- Live streams: Disable seeking and use extended timeout for reliability
- `formatDuration` handles edge cases (NaN, Infinity, negative values)
# Channel Strip
---
title: Channel Strip
description: Layout shell for channel-style columns—header, sections with label, control, and readout, and footer—with vertical or horizontal orientation.
component: true
base: base
---
Channel strip layout for grouping labels, controls, and value readouts in a mixer-style column. The registry component wraps `ChannelStrip` primitives from `@audio-ui/react` with card chrome, orientation-aware spacing, and header/footer layout presets.
## Overview
- **Interaction model:** The strip wrapper does not handle pointer, wheel, or keyboard input; those behaviors come from the controls you render inside each `ChannelStripSection`.
- **Layout:** Vertical (default) or horizontal `orientation`; optional `ChannelStripHeader` and `ChannelStripFooter`. With a footer, the styled root uses a responsive grid on wide screens so header and footer stay in distinct grid areas.
- **Composition:** One `ChannelStripSection` per control line (label, control slot, readout). On a vertical strip, `ChannelStripContent` supports `layout="row"` so multiple sections sit side by side from the `md` breakpoint (flex row + wrap), and stack in one column on smaller viewports.
- **Accessibility:** The root is a `section` with header/footer landmarks where used. `ChannelStripValue` is an `output` with `aria-live="polite"`. Name the strip with `aria-label` or `aria-labelledby`; wire controls with `id` / `aria-label` / `aria-labelledby` as usual.
## Installation
CLIManual
```bash component
npx shadcn@latest add @audio/channel-strip
```
Install the following dependencies:
```bash
npm install @audio-ui/react
```
Copy and paste the following code into your project.Update the import paths to match your project setup.
## Usage
```tsx
import {
ChannelStrip,
ChannelStripContent,
ChannelStripFooter,
ChannelStripHeader,
ChannelStripLabel,
ChannelStripSection,
ChannelStripValue,
} from "@/components/channel-strip";
```
**Vertical (default)** - header, one section, footer:
```tsx
Channel 1Level
{/* your control */}
—Metadata
```
**Horizontal** - set `orientation` on the root:
```tsx
Channel 1{/* label, control, readout */}Metadata
```
**Sections in a row (vertical strip)** — responsive flex, not CSS Grid; narrow screens stack, from `md` up sections share a row and wrap if needed:
```tsx
Channel 1{/* A */}{/* B */}Metadata
```
## Interaction
### Pointer
| Behavior | Details |
| -------- | ------- |
| **Strip root** | Not an interactive target; pointer gestures are handled by child controls (`Fader`, `Knob`, etc.). |
| **Sections** | Layout only; hit testing applies to controls you place inside each section. |
### Keyboard
| Key | Action |
| --- | ------ |
| `Tab` / `Shift+Tab` | Moves focus among focusable elements inside the strip in DOM order. |
The strip root is not focused as a control; there is no strip-level shortcut table beyond focus navigation.
### Wheel
Wheel deltas are handled by whatever control currently has focus (if that control implements wheel), not by the strip wrapper.
## API reference
`ChannelStrip` and its subcomponents accept `className` plus primitive props from the matching `ChannelStripPrimitive.*` typings in `@audio-ui/react`.
### Subcomponents
| Export | Element | Role |
| --- | --- | --- |
| `ChannelStrip` | `section` | Root; orientation and card chrome. |
| `ChannelStripHeader` | `header` | Optional title region. |
| `ChannelStripContent` | `div` | Required wrapper for sections. |
| `ChannelStripSection` | `div` | One label / control / value group. |
| `ChannelStripLabel` | `span` | Section label. |
| `ChannelStripValue` | `output` | Readout. |
| `ChannelStripFooter` | `footer` | Optional footer region. |
### Root props
| Prop | Type | Default | Description |
| ---- | ---- | ------- | ----------- |
| `orientation` | `"vertical" \| "horizontal"` | `"vertical"` | Layout direction. |
| `className` | `string` | - | Classes on the root `section`. |
### `ChannelStripContent`
| Prop | Type | Default | Description |
| ---- | ---- | ------- | ----------- |
| `layout` | `"stack" \| "row"` | `"stack"` | `stack`: sections in a column. `row` (vertical strip only in the styled registry): sections in a row from `md` with `flex-wrap`, stacked on small viewports. |
| `className` | `string` | - | Classes on the content wrapper. |
### Section props
| Prop | Type | Default | Description |
| ---- | ---- | ------- | ----------- |
| `orientation` | `"vertical" \| "horizontal"` | inherited | Optional per-section layout override. |
| `className` | `string` | - | Classes on the section wrapper. |
Additional valid HTML attributes on each primitive part are forwarded where applicable.
## Examples
### Vertical (default)
### Horizontal
### Pan knob
### Pan / width
### XY filter pad
# Fader
---
title: Fader
description: Linear slider control for gain and parameter adjustment with pointer, wheel, keyboard, orientation, and thumb mark variants.
component: true
base: base
---
Linear fader for continuous parameters (volume, gain, send level, etc.). The registry component wraps `Fader` primitives from `@audio-ui/react` with size variants and thumb mark styling.
## Overview
- **Interaction model:** Pointer drag on track or thumb, mouse wheel adjustment, keyboard nudging, and commit callbacks on release.
- **Layout:** Vertical (default) or horizontal orientation, with `sm`, `default`, and `lg` size presets.
- **Feedback:** Filled range + thumb with optional internal marks (`thumbMarks`).
- **Accessibility:** The thumb is exposed as `role="slider"` with `aria-valuenow`, `aria-valuemin`, and `aria-valuemax`.
## Installation
CLIManual
```bash component
npx shadcn@latest add @audio/fader
```
Install the following dependencies:
```bash
npm install @audio-ui/react class-variance-authority
```
Copy and paste the following code into your project.Update the import paths to match your project setup.
## Usage
```tsx
import { Fader } from "@/components/audio/fader";
```
**Uncontrolled** - initial value only:
```tsx
```
**Controlled** - drive value from state:
```tsx
const [gain, setGain] = useState(0);
console.log("Committed:", value)}
step={1}
value={gain}
/>
```
## Interaction
### Pointer
| Behavior | Details |
| -------- | ------- |
| **Track click / drag** | Clicking or dragging on the track updates the value from pointer position. |
| **Thumb drag** | Dragging the thumb adjusts continuously, clamped to `min...max`. |
| **Commit event** | `onValueCommit` fires when drag interaction ends. |
### Keyboard
Focused thumb supports:
| Key | Action |
| --- | ------ |
| `ArrowUp` / `ArrowRight` | Increase by `step` |
| `ArrowDown` / `ArrowLeft` | Decrease by `step` |
| `PageUp` | Increase by `step × 10` |
| `PageDown` | Decrease by `step × 10` |
| `Home` | Set to `min` |
| `End` | Set to `max` |
### Wheel
When focused, wheel input nudges the value by `step`.
## API reference
`Fader` accepts style props (`size`, `thumbMarks`, `className`) plus primitive props from `FaderPrimitive.RootProps`.
### Value and state
| Prop | Type | Default | Description |
| ---- | ---- | ------- | ----------- |
| `value` | `number` | - | Controlled value. |
| `defaultValue` | `number` | midpoint | Uncontrolled initial value. Midpoint is `(min + max) / 2`. |
| `min` | `number` | `-60` | Minimum value. |
| `max` | `number` | `6` | Maximum value. |
| `step` | `number` | `1` | Quantization step for drag, wheel, and keyboard. |
| `disabled` | `boolean` | `false` | Disables interaction. |
| `onValueChange` | `(value: number) => void` | - | Live updates while adjusting. |
| `onValueCommit` | `(value: number) => void` | - | Fires when interaction is committed. |
| `aria-label` | `string` | - | Accessible name for the slider. |
| `aria-labelledby` | `string` | - | ID of a labeling element. |
### Layout and visuals
| Prop | Type | Default | Description |
| ---- | ---- | ------- | ----------- |
| `orientation` | `"horizontal" \| "vertical"` | `"vertical"` | Direction of travel. |
| `size` | `"sm" \| "default" \| "lg"` | `"default"` | Track and thumb size preset. |
| `thumbMarks` | `number \| false` | `3` | Number of marks in the thumb or `false` to hide them. |
| `className` | `string` | - | Root wrapper class name. |
Additional valid HTML attributes passed to `Fader.Root` are forwarded.
## Examples
### Vertical (default)
### Horizontal
### Size variants
### Thumb marks variants
### Channel strip
#### Gain range in dB (`-60..+6`)
#### Normalized range (`0..100`)
#### Multi-channel strip
# Knob
---
title: Knob
description: Rotary control for parameters - drag, wheel, keyboard, optional fader-style vertical pan, and reset via double-tap.
component: true
base: base
---
Rotary knob for continuous parameters (level, cutoff, pan, etc.). The registry component wraps `Knob` primitives from `@audio-ui/react` with size variants and theme styles; behavior and geometry are documented below.
## Overview
- **Interaction model:** Circular drag by default, optional vertical "fader" pan, mouse wheel, keyboard nudging, and **double-tap / double-click** to snap back to `defaultValue`.
- **Feedback:** Track rail, highlighted arc between `anchor` and value when useful, and a needle aligned to the current value.
- **Accessibility:** The draggable surface is a `role="slider"` with `aria-valuenow` / `min` / `max`; label via `aria-label` or `aria-labelledby`.
## Installation
CLIManual
```bash component
npx shadcn@latest add @audio/knob
```
Install the following dependencies:
```bash
npm install @audio-ui/react class-variance-authority
```
Copy and paste the following code into your project.Update the import paths to match your project setup.
## Usage
```tsx
import { Knob } from "@/components/audio/knob";
```
**Uncontrolled** - initial value only:
```tsx
```
**Controlled** - drive the value from state (typical in audio UIs):
```tsx
const [cutoff, setCutoff] = useState(1000);
```
Pass **`defaultValue`** even when controlled if you use **double-tap reset**; it defines the value the knob jumps back to (still clamped and stepped).
## Interaction
### Pointer
| Behavior | Details |
| -------- | ------- |
| **Circular drag (default)** | Value tracks accumulated rotation around the center from the value at **pointer down**. Uses `dragSensitivity` to choose the angular reference (see [Pointer mapping](#pointer-mapping)). |
| **Vertical fader drag** | Set `dragOptions={{ verticalPanEnabled: true }}`. After a small movement threshold, a mostly-vertical drag nudges the value like a fader; circular drag still works when the gesture is more rotational. |
| **Double-tap / double-click** | Two quick releases with **≤ ~12px** movement from each **pointer down**, within **~320ms**, resets to `defaultValue`, or to the midpoint of `min…max` if `defaultValue` is omitted. Fires `onValueCommit` like a normal commit. |
Default geometry uses a large sweep (~288° from `angleGapRad`), so **`arc` and `revolution` can feel similar**; use a smaller `angleRange` if you want `arc` to span the full range in less than one physical turn. Values always **clamp** to `min…max` (no wrap).
### Keyboard
Focused knob:
| Key | Action |
| --- | ------ |
| `ArrowUp` / `ArrowRight` | Increase by `step` |
| `ArrowDown` / `ArrowLeft` | Decrease by `step` |
| `PageUp` | Increase by `step × 10` |
| `PageDown` | Decrease by `step × 10` |
| `Home` | `min` |
| `End` | `max` |
### Wheel
When the knob is focused, vertical wheel deltas step the value (same idea as key nudging).
## API reference
`Knob` accepts **`size`** and **`className`** plus primitive props from `KnobPrimitive.RootProps`.
### Value and state
| Prop | Type | Default | Description |
| ---- | ---- | ------- | ----------- |
| `value` | `number` | - | Controlled value. |
| `defaultValue` | `number` | - | Uncontrolled starting value; also the **double-tap reset** target when provided. |
| `min` | `number` | `0` | Minimum. |
| `max` | `number` | `100` | Maximum. |
| `step` | `number` | `1` | Quantization step (drag, wheel, keyboard, reset). |
| `disabled` | `boolean` | `false` | Disables pointer, wheel, and keyboard. |
| `onValueChange` | `(value: number) => void` | - | Live updates while dragging or adjusting. |
| `onValueCommit` | `(value: number) => void` | - | When the gesture ends (pointer up, wheel tick, reset, etc.). |
| `aria-label` | `string` | - | Accessible name for the slider. |
| `aria-labelledby` | `string` | - | ID of a labeling element. |
### Size and rail
| Prop | Type | Default | Description |
| ---- | ---- | ------- | ----------- |
| `size` | `"sm" \| "default" \| "lg" \| "xl"` | `"default"` | Diameter variant. |
| `className` | `string` | - | On the root wrapper. |
| `arcRadius` | `number` | `23` | Arc radius in the 48×48 viewBox. |
| `arcStrokeWidth` | `number` | `3` | Track / arc stroke width. |
| `angleGapRad` | `number` | `π/5` | Bottom gap in radians; drives default `angleOffset` / `angleRange`. |
| `angleOffset` | `number` | from gap | Dial start angle in **degrees** (optional override). |
| `angleRange` | `number` | from gap | Total sweep in **degrees** (optional override). |
| `anchor` | `number` | - | Secondary value for the highlighted arc segment. |
| `indicatorSpan` | `[number, number]` | `[0.24, 0.58]` | Needle inner / outer radius as fractions of track radius. |
| `indicatorWidth` | `number` | `2.75` | Needle stroke width. |
### Pointer and drag
| Prop | Type | Default | Description |
| ---- | ---- | ------- | ----------- |
| `dragSensitivity` | `"arc" \| "revolution"` | `"arc"` | `arc`: one full span matches **`angleRange`** (as radians). `revolution`: one full span matches **360°** of pointer travel. Both are relative to the value at pointer down and clamp to `min…max`. |
| `dragOptions` | see below | - | Pan vs rotate, dead zone, vertical sensitivity. |
#### `dragOptions` (`Knob.DragOptions`)
| Field | Default | Purpose |
| ----- | ------- | ------- |
| `verticalPanEnabled` | `false` | Allow vertical fader-style drag when `true`. |
| `panSensitivityDivisor` | `150` | Vertical pan scale: `(max - min) / divisor` per unit delta. |
| `centerDeadZoneRel` / `centerDeadZoneMinPx` | `0.08` / `2` | Hub radius where angle follows pointer without accumulating huge jumps. |
| `modeLockMinPx` / `modeLockRel` | `6` / `0.055` | Movement before locking rotate vs pan mode. |
| `panMinVerticalPx` | `10` | Minimum vertical movement to consider pan. |
| `panDominanceRatio` | `2.5` | Pan wins when `movedY > movedX × ratio`. |
Additional HTML attributes on the root are forwarded where valid.
## Examples
### Disabled state
### Size variants
### Rail, anchor, and range highlight
Use `anchor` to show a reference point on the rail (for example **center** on a bipolar control). The arc highlights the interval between `anchor` and the current value.
### Pointer mapping
Each preview shows one mapping in isolation (live value under the control).
#### Default arc (circular)
#### Vertical fader-style drag
#### Revolution (360° reference)
### Reset with double-tap
### Fine step precision (`step={0.01}`)
### Live value vs committed value callbacks
### Filter cutoff
### Channel strip
# Audio Player
---
title: Audio Player
description: A composable audio player. One component, one install, fully owned by you.
component: true
base: base
audioProvider: true
---
Everything you need to build an audio player is in a single file: the engine, the UI controls, the queue, tracks, and playback speed. No separate installs, no cross-file wiring. Just one component you copy, paste, and own.
## Installation
CLIManual
```bash component
npx shadcn@latest add @audio/player
```
The CLI resolves every dependency automatically: the store, the HTML audio engine, `Fader`, `Transport`, `SortableList`, and the shadcn/ui primitives the player uses.
Install the runtime dependencies:
```bash
npm install @phosphor-icons/react class-variance-authority @base-ui/react
```
Install the required shadcn/ui primitives:
```bash
npx shadcn@latest add avatar badge button command dialog dropdown-menu empty item spinner toggle tooltip
```
Copy the control primitives too — [Fader](/docs/components/base/fader),
[Transport](/docs/components/base/transport), and
[Sortable List](/docs/components/base/sortable-list) — plus the
`store` and `html` lib files and the `use-audio` / `use-audio-provider`
hooks (see [Audio Store](/docs/lib/audio-store) and
[HTML Audio](/docs/lib/html-audio)).
Copy and paste the following code into your project.Update the import paths to match your project setup.
## Philosophy
**Composable by default.** `AudioPlayer` is just a container. Every control (seek bar, volume, queue, playback speed) is an independent component you drop in where you need it. No black-box layout, no hidden markup.
**State lives in the store, not in components.** All playback state (current track, progress, queue, shuffle, repeat) is held in a Zustand store. Components read and write that store directly. No prop drilling, no context threading.
**The engine is separate from the UI.** `AudioProvider` wires up the HTML audio element to the store: event listeners, retries, preloading, state restoration. UI components just read state and call store actions.
## Setup
Pass `tracks` directly to `AudioPlayer` for a self-contained widget:
```tsx
import { AudioPlayer, AudioPlayerControlBar, AudioPlayerPlay } from "@/components/player";
```
For shared state across multiple players or app-wide persistence, mount `AudioProvider` once at the root:
```tsx
// app/layout.tsx
import { AudioProvider } from "@/components/player";
export default function RootLayout({ children }) {
return (
{children}
);
}
```
tracks vs AudioProvider: Use tracks on AudioPlayer for self-contained widgets. Use AudioProvider at the root when multiple components share the same playback session.
## Player
### Compact
### With queue
### Variants
### Sizes
### AudioPlayer
Container for all player controls.
| Prop | Type | Default | Description |
| ----------- | --------------------------------- | ----------- | ------------------------------------------------------------------- |
| `tracks` | `Track[]` | - | When provided, wraps children with `AudioProvider` automatically. |
| `variant` | `"default" \| "ghost"` | `"default"` | Visual style. `ghost` is transparent with a hover background. |
| `size` | `"sm" \| "default"` | `"default"` | Controls padding and border radius. |
| `className` | `string` | - | Additional CSS classes. |
Inherits all other props from `HTMLDivElement`. The underlying CVA definition is exported as `audioPlayerVariants` for extension.
### AudioProvider
Connects the HTML audio element to the Zustand store. Handles event listeners, retries, preloading, and state restoration.
| Prop | Type | Default | Description |
| ---------- | ----------- | ------- | ------------------------------------------- |
| `tracks` | `Track[]` | `[]` | Initial tracks to populate the queue. |
| `children` | `ReactNode` | - | Components that share this audio session. |
### AudioPlayerButton
Shared button primitive used by all player controls. Wraps a `Button` with an optional tooltip.
| Prop | Type | Default | Description |
| -------------- | -------- | ------- | ------------------------------------------------ |
| `tooltipLabel` | `string` | - | When provided, wraps the button in a tooltip. |
Inherits all other props from `Button`.
### AudioPlayerControlBar
Groups controls in a single row or stacked column layout.
| Prop | Type | Default | Description |
| --------- | ------------------------ | ----------- | ---------------------- |
| `variant` | `"compact" \| "stacked"` | `"compact"` | Controls the layout. |
### AudioPlayerControlGroup
Flexible row wrapper for arranging control clusters. Use `className` to adjust alignment.
### AudioPlayerPlay
Play/pause button. Shows a spinner during loading/buffering. Spacebar shortcut is enabled automatically.
Inherits all props from `AudioPlayerButton`.
### AudioPlayerSkipBack / AudioPlayerSkipForward
Navigate between tracks.
Inherits all props from `AudioPlayerButton`.
### AudioPlayerRewind / AudioPlayerFastForward
Seek backward or forward by 10 seconds. Disabled for live streams.
Inherits all props from `AudioPlayerButton`.
### AudioPlayerSeekBar
Seek timeline showing playback and buffered progress. Locked to 100% for live streams.
Inherits all props from [Transport](/docs/components/base/transport) (except `value`, `onSeek`, `bufferedValue`).
### AudioPlayerTimeDisplay
Displays current time or remaining time. Shows a live indicator for streams.
| Prop | Type | Default | Description |
| ----------- | --------- | ------- | ----------------------------------------------- |
| `remaining` | `boolean` | `false` | Show remaining time instead of elapsed time. |
### AudioPlayerVolume
Dropdown with a horizontal fader to control volume and mute.
Inherits all props from [Fader](/docs/components/base/fader) (except `value`, `onValueChange`, `min`, `max`, `orientation`, `size`).
## Tracks
`AudioTrackList` renders a list of tracks. Omit `tracks` to read from the global queue; pass `tracks` for controlled mode.
### Single track
### Default
### Grid
### Sortable
### Sortable Grid
### AudioTrack
Renders a single track row. `media` and `actions` are composed, not configured — pass the pieces you want.
| Prop | Type | Default | Description |
| ---------- | ---------------------------- | ------- | ------------------------------------------------- |
| `trackId` | `string \| number` | - | Look up a track from the queue by id (store mode). |
| `track` | `Track` | - | Render a provided track object (controlled mode). |
| `index` | `number` | - | Display index, exposed to `AudioTrackIndex`. |
| `onClick` | `() => void` | - | Called when the row is clicked. |
| `onRemove` | `(trackId: string) => void` | - | Enables `AudioTrackRemoveAction` when provided. |
| `media` | `React.ReactNode` | - | Left-side content, e.g. ``. |
| `actions` | `React.ReactNode` | - | Right-side content, e.g. ``. |
```tsx
}
actions={}
/>
// Sortable row with remove support
>
}
actions={
<>
>
}
onRemove={handleRemove}
/>
```
`AudioTrackCover`, `AudioTrackIndex`, `AudioTrackPlayPauseAction`, and `AudioTrackRemoveAction` read track state from context — they only work inside `AudioTrack`. `AudioTrackRemoveAction` renders nothing for the currently playing track or when `onRemove` isn't set.
Mode constraint: Use either trackId or track, not both. trackId requires an AudioProvider in the tree.
### AudioTrackList
| Prop | Type | Default | Description |
| ------------------ | ----------------------------------------------------------------- | --------------------------- | ---------------------------------------------------------------------------------------- |
| `tracks` | `Track[]` | - | Controlled list. Omit to read from the global queue. |
| `onTrackSelect` | `(index: number, track?: Track) => void` | - | Called when a track is selected. |
| `onTrackRemove` | `(trackId: string) => void` | - | Called when a track is removed. Enables remove actions automatically. |
| `mode` | `"static" \| "sortable"` | `"static"` | Enable drag-and-drop reordering. |
| `media` | `"cover" \| "index"` | `"cover"` | Media style. In sortable mode a drag handle is added automatically. |
| `actions` | `"none" \| "play-pause" \| "remove" \| "play-pause-with-remove"` | Auto | Action variant per row. Auto: `"play-pause-with-remove"` when `onTrackRemove` is set. |
| `variant` | `"default" \| "grid"` | `"default"` | Stacked list or responsive grid. |
| `filterQuery` | `string` | - | Text filter (matches title or artist). |
| `filterFn` | `(track: Track) => boolean` | - | Custom filter. Overrides `filterQuery` when both are set. |
| `emptyLabel` | `string` | `"No tracks found"` | Empty state label. |
| `emptyDescription` | `string` | `"Try adding some tracks"` | Empty state description. |
Sortable constraint: Drag-and-drop reordering only updates the store when the list is unfiltered and in store mode.
## Queue
All queue controls work together or independently.
### Simple
### All controls
### Shuffle and repeat
### Preferences
### AudioQueue
A dialog with the full queue: search, selection, remove, and drag-and-drop reordering.
| Prop | Type | Default | Description |
| ------------------- | ------------------------- | ------------------------------------------ | ---------------------------------------------- |
| `onTrackSelect` | `(index: number) => void` | - | Called when a track is selected from the queue. |
| `searchPlaceholder` | `string` | `"Search for a track..."` | Search input placeholder. |
| `emptyLabel` | `string` | `"No tracks found"` | Empty state label. |
| `emptyDescription` | `string` | `"Try searching for a different track"` | Empty state description. |
Search and reorder: Drag-and-drop is disabled while a search filter is active.
### AudioQueueShuffle
Toggle that enables/disables shuffle. Persisted to localStorage.
Inherits all props from `Toggle` (except `onPressedChange`).
### AudioQueueRepeatMode
Toggle that cycles through repeat modes: `none` → `all` → `one`. Persisted to localStorage.
Inherits all props from `Toggle`.
### AudioQueuePreferences
Dropdown combining repeat mode and insert mode (`first`, `last`, `after current`) in one compact menu.
Inherits all props from `AudioPlayerButton`.
## Playback Speed
### AudioPlaybackSpeed
Dropdown to change playback speed. Disabled automatically for live streams.
| Prop | Type | Default | Description |
| --------- | ---------------------------------------------- | ------------------ | ------------------------------------------------------------ |
| `speeds` | `readonly { value: number; label: string }[]` | `PLAYBACK_SPEEDS` | Speed options. Default: 0.5×, 0.75×, 1×, 1.25×, 1.5×, 2×. |
| `size` | `ButtonSize` | - | `"icon"` hides the gauge icon, shows only the label. |
| `variant` | `ButtonVariant` | `"outline"` | Button variant. |
## Notes
Live streams: Seeking, rewind, fast-forward, and playback speed are disabled automatically. The seek bar locks to 100%.
Spacebar shortcut:AudioPlayerPlay registers a global keydown listener for Space when the document body is focused.
# Sortable List
---
title: Sortable List
description: A drag-and-drop sortable list component built with dnd-kit.
component: true
base: base
---
## Installation
CLIManual
```bash component
npx shadcn@latest add @audio/sortable-list
```
Install the following dependencies:
```bash
npm install @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities @phosphor-icons/react
```
Install the required shadcn/ui primitives:
```bash
npx shadcn@latest add button
```
Copy and paste the following code into your project.Update the import paths to match your project setup.
## Usage
```tsx
import {
SortableList,
SortableItem,
SortableDragHandle,
} from "@/components/audio/sortable-list";
```
```tsx showLineNumbers
"use client";
import { useState } from "react";
import {
Item,
ItemActions,
ItemContent,
ItemDescription,
ItemTitle,
} from "@/components/ui/item";
import {
SortableList,
SortableItem,
SortableDragHandle,
} from "@/components/audio/sortable-list";
const initialItems = [
{
id: "1",
title: "Item 1",
description: "Description for item 1",
},
{
id: "2",
title: "Item 2",
description: "Description for item 2",
},
{
id: "3",
title: "Item 3",
description: "Description for item 3",
},
];
export function SortableListDemo() {
const [items, setItems] = useState(initialItems);
return (
(
{item.title}{item.description}
)}
/>
);
}
```
## API Reference
### SortableList
The main container component for sortable items. Built with `@dnd-kit` for accessibility and smooth drag-and-drop interactions.
#### Props
| Prop | Type | Description |
| ------------ | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `items` | `TItem[]` | Array of items to display. Each item must have an `id` property. |
| `onChange` | `(items: TItem[]) => void` | Callback fired when items are reordered. |
| `renderItem` | `(item: TItem, index: number, isOverlay?: boolean) => React.ReactNode` | Function to render each item. The `isOverlay` parameter indicates when an item is being dragged. |
| `className` | `string` | Additional CSS classes for the list container. |
### SortableItem
A wrapper component for individual sortable items.
#### Props
| Prop | Type | Description |
| ----------- | ------------------ | ----------------------------------------------------- |
| `id` | `string \| number` | Unique identifier for the item (must match item.id). |
| `className` | `string` | Additional CSS classes. |
| `children` | `React.ReactNode` | Content of the sortable item. |
### SortableDragHandle
A pre-built drag handle button that uses the sortable context. Extends shadcn/ui's `Button` component.
#### Props
Inherits all props from `Button` component.
## Notes
Important Information:
- **Custom Component:** This component is not part of the default shadcn/ui
registry. It is custom to this project for drag-and-drop audio queue reordering.
- **Built with dnd-kit:** This component is built on top of @dnd-kit,
which includes keyboard-friendly drag-and-drop behavior. See the{" "}
dnd-kit documentation
for details.
- **Drag Handle:** SortableDragHandle uses the project Button{" "}
primitive and ships pre-wired accessibility attributes.
- **Item IDs:** Each item must have a unique id property. The
`id` type must match between the item object and the SortableItem{" "}
component.
- **Usage in Audio Components:** This component is used by{" "}
AudioTrackList for queue reordering.
# Transport
---
title: Transport
description: Timeline transport control for playback progress and scrubbing, with buffered range visualization, keyboard, wheel, and pointer interactions.
component: true
base: base
---
Timeline control for playback progress (current value + buffered range). The registry component wraps `Transport` primitives from `@audio-ui/react` with audio-focused defaults for player seek bars and scrub interactions.
## Overview
- **Interaction model:** Pointer drag on track/thumb, wheel stepping, keyboard nudging, and commit callbacks.
- **Visual model:** Base track + buffered range + played range + thumb.
- **Playback UX:** Supports `freezeValuesWhileDragging` to avoid jitter/flicker while scrubbing.
- **Accessibility:** Thumb is exposed as `role="slider"` with `aria-valuenow`, `aria-valuemin`, and `aria-valuemax`.
## Installation
CLIManual
```bash component
npx shadcn@latest add @audio/transport
```
Install the following dependencies:
```bash
npm install @audio-ui/react
```
Copy and paste the following code into your project.Update the import paths to match your project setup.
## Usage
```tsx
import { Transport } from "@/components/audio/transport";
```
**Controlled** (typical in player UIs):
```tsx
const [progress, setProgress] = useState(42);
```
## Interaction
### Pointer
| Behavior | Details |
| -------- | ------- |
| **Track click / drag** | Updates value from pointer position along the rail. |
| **Thumb drag** | Continuous scrubbing, clamped to `min...max`. |
| **Commit event** | `onValueCommit` fires when drag interaction ends (primitive-level callback). |
### Keyboard
Focused thumb supports:
| Key | Action |
| --- | ------ |
| `ArrowUp` / `ArrowRight` | Increase by `step` |
| `ArrowDown` / `ArrowLeft` | Decrease by `step` |
| `PageUp` | Increase by `step × 10` |
| `PageDown` | Decrease by `step × 10` |
| `Home` | Set to `min` |
| `End` | Set to `max` |
### Wheel
When focused, wheel input nudges the value by `step`.
## API reference
`Transport` keeps an audio-focused API (`value`, `bufferedValue`, `onSeek`) and forwards remaining root props to `TransportPrimitive.Root`.
### Value and state
| Prop | Type | Default | Description |
| ---- | ---- | ------- | ----------- |
| `value` | `number` | - | Current transport value (typically `0..100`). |
| `bufferedValue` | `number` | `0` | Buffered position (same scale as `value`). |
| `onSeek` | `(nextValue: number) => void` | - | Called when scrubbing or keyboard/wheel changes value. |
| `freezeValuesWhileDragging` | `boolean` | `false` | Freezes rendered values during active drag for smoother visual feedback. |
| `disabled` | `boolean` | `false` | Disables interaction. |
| `aria-label` | `string` | - | Accessible name for the slider. |
| `aria-labelledby` | `string` | - | ID of the associated label element. |
For production usage, pass either `aria-label` or `aria-labelledby` explicitly.
### Layout and behavior
| Prop | Type | Default | Description |
| ---- | ---- | ------- | ----------- |
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` (registry wrapper) | Direction of travel. |
| `size` | `"sm" \| "default" \| "lg"` | `"default"` | Thumb/rail density preset. |
| `min` | `number` | `0` | Minimum value. |
| `max` | `number` | `100` | Maximum value. |
| `step` | `number` | `1` | Quantization step for drag/wheel/keyboard. |
| `className` | `string` | - | Wrapper class name. |
## Examples
### Basic transport timeline
### Channel strip
#### Horizontal
#### Vertical
# XY Pad
---
title: XY Pad
description: Two-dimensional control surface for mapping and automating X/Y parameters with pointer, wheel, and keyboard input.
component: true
base: base
---
XY pad for simultaneous control of two values (for example filter cutoff/resonance, pan/mix, or macro controls). The registry component wraps `XYPad` primitives from `@audio-ui/react` and provides ready-to-use visuals (grid, crosshair, cursor, value display).
## Overview
- **Interaction model:** Pointer drag, wheel nudging, and keyboard nudging over both axes.
- **Axes:** Independent ranges and steps for X (`minX`, `maxX`, `stepX`) and Y (`minY`, `maxY`, `stepY`).
- **Feedback:** Grid lines, crosshair, animated cursor, and optional value display overlay.
- **Accessibility:** Focusable interactive surface with ARIA labeling (`aria-label` / `aria-labelledby`).
## Installation
CLIManual
```bash component
npx shadcn@latest add @audio/xypad
```
Install the following dependencies:
```bash
npm install @audio-ui/react class-variance-authority
```
Copy and paste the following code into your project.Update the import paths to match your project setup.
## Usage
```tsx
import { XYPad } from "@/components/audio/xypad";
```
**Uncontrolled** - initial value only:
```tsx
```
**Controlled** - drive value from state:
```tsx
const [position, setPosition] = useState({ x: 50, y: 50 });
console.log("Committed:", value)}
value={position}
/>
```
## Interaction
### Pointer
| Behavior | Details |
| -------- | ------- |
| **Click / drag on pad** | Pointer position maps directly to X/Y in bounds. |
| **Clamping** | Values always clamp to configured axis ranges. |
| **Commit event** | `onValueCommit` fires when drag interaction ends. |
### Keyboard
Focused XY pad:
| Key | Action |
| --- | ------ |
| `ArrowLeft` / `ArrowRight` | Decrease / increase X by `stepX` |
| `ArrowUp` / `ArrowDown` | Increase / decrease Y by `stepY` |
| `PageUp` / `PageDown` | Increase / decrease Y by `stepY × 10` |
| `Home` | Jump to top-left logical corner (`x = minX`, `y = maxY`) |
| `End` | Jump to bottom-right logical corner (`x = maxX`, `y = minY`) |
### Wheel
When focused, wheel deltas nudge both axes according to pad dimensions and axis ranges, then quantize with `stepX` and `stepY`.
## API reference
`XYPad` accepts style props (`size`, `formatValue`, `valueDisplay`, `className`) plus primitive props from `XYPadPrimitive.RootProps`.
### Value and state
| Prop | Type | Default | Description |
| --------------- | --------------------------------------- | ------------- | ------------------------------------------------------------------ |
| `value` | `{ x: number; y: number }` | - | Controlled value. |
| `defaultValue` | `{ x: number; y: number }` | `{ x: 0, y: 0 }` | Uncontrolled default value. |
| `minX` | `number` | `0` | Minimum X value. |
| `maxX` | `number` | `100` | Maximum X value. |
| `minY` | `number` | `0` | Minimum Y value. |
| `maxY` | `number` | `100` | Maximum Y value. |
| `stepX` | `number` | `1` | Step increment for X axis. |
| `stepY` | `number` | `1` | Step increment for Y axis. |
| `disabled` | `boolean` | `false` | Whether the pad is disabled. |
| `onValueChange` | `(value: { x: number; y: number }) => void` | - | Callback when value changes during interaction. |
| `onValueCommit` | `(value: { x: number; y: number }) => void` | - | Callback when value is committed (on release). |
| `aria-label` | `string` | `"XY Pad"` | Accessible name for the interactive surface. |
| `aria-labelledby` | `string` | - | ID of a labeling element. |
### Layout and display
| Prop | Type | Default | Description |
| --------------- | --------------------------------------- | ------------- | ------------------------------------------------------------------ |
| `size` | `"sm" \| "default" \| "lg" \| "xl"` | `"default"` | Size variant (affects height). |
| `formatValue` | `(value: { x: number; y: number }) => React.ReactNode` | - | Custom formatter for value display. |
| `valueDisplay` | `"visible" \| "hidden"` | `"visible"` | Controls value display visibility. Use `"hidden"` to hide overlay. |
| `className` | `string` | - | Additional CSS classes. |
Additional valid HTML attributes passed to `XYPad.Root` are forwarded.
## Examples
### Live value readout
### Size variants
#### `sm`
#### `default`
#### `lg`
#### `xl`
### Disabled state
### Custom formatted display
### Hidden value display
### Bipolar ranges (pan and mix style)
### Live value vs committed value callbacks
### Channel strip
#### Filter pad
#### Reverb pad