Rotary control for parameters - drag, wheel, keyboard, optional fader-style vertical pan, and reset via double-tap.
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
anchorand value when useful, and a needle aligned to the current value. - Accessibility: The draggable surface is a
role="slider"witharia-valuenow/min/max; label viaaria-labeloraria-labelledby.
Installation
pnpm dlx shadcn@latest add @audio/knobUsage
import { Knob } from "@/components/audio/knob";Uncontrolled - initial value only:
<Knob defaultValue={50} max={100} min={0} step={1} />Controlled - drive the value from state (typical in audio UIs):
const [cutoff, setCutoff] = useState(1000);
<Knob
max={20_000}
min={20}
onValueChange={setCutoff}
step={1}
value={cutoff}
/>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). |
| 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
Disabled
Enabled
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.
Value only
With anchor range
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
Double-tap the control to jump back to 30 (defaultValue).
Fine step precision (step={0.01})
Live value vs committed value callbacks
Filter cutoff
Channel strip
Last updated 4/15/2026
On This Page
dragOptions (Knob.DragOptions)ExamplesDisabled stateSize variantsRail, anchor, and range highlightPointer mappingDefault arc (circular)Vertical fader-style dragRevolution (360° reference)Reset with double-tapFine step precision (step={0.01})Live value vs committed value callbacksFilter cutoffChannel strip