Knob
knobControlsThe workhorse of a plugin panel, built on the headless knob primitive. Drag, scroll or type to turn it, hold Shift while dragging for a fine pass, or use Shift+arrow for five normal steps. Double-click snaps back to the default. A conic value wedge fades in while you turn, log and custom response curves keep non-linear parameters honest, and bipolar mode lights the arc out from twelve o'clock for pan-style controls.
Drag or scroll to turn — a diagonal drag counts, so long as it isn't running across the knob's axis. Shift-drag is fine; Shift+arrow advances five steps. Double-click to reset.
Install
Copies the source into your project. Anything it depends on comes with it.
$ npx shadcn add @syntho-ui/knobPulls in
First time? Add the registry to your components.json once:
{
"registries": {
"@syntho-ui": "https://engine.syntho.app/r/{name}.json"
}
}Usage
Controlled or uncontrolled, whichever suits the surrounding state.
import { Knob } from "@/components/ui/knob"
<Knob
label="Cutoff"
size="m"
min={20}
max={20000}
defaultValue={2200}
curve="log"
valueDisplay="hz"
pizza="interaction"
onChange={(hz) => param.set("cutoff", hz)}
onInteraction={() => audioContext.resume()}
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| value | number | — | Controlled value. Omit to let the control own its state. |
| defaultValue | number | 0 | Starting value when uncontrolled, and the double-click reset target. |
| onChange | (value: number) => void | — | Fires on every value change, in both controlled and uncontrolled modes. |
| onChangeEnd | (value: number) => void | — | Fires once when a drag gesture finishes — the hook for undo coalescing. |
| min | number | 0 | Lower bound of the range. |
| max | number | 1 | Upper bound of the range. |
| step | number | 0 | Quantisation grid. 0 means continuous. |
| label | string | — | Caption above the control. Doubles as the accessible name. |
| unit | string | — | Suffix appended by the default formatter, e.g. "Hz". |
| format | (value: number) => string | — | Custom readout formatter. Overrides `unit`. |
| hideValue | boolean | false | Hide the numeric readout. |
| disabled | boolean | false | Block interaction and dim the control. |
| variant | "default" | "small" | "mini" | "large" | "minimal" | "detailed" | "flat" | "ring" | "vintage" | "stepped" | "neon" | "instrument" | "molten" | "default" | Visual preset. flat is a matte software knob, ring drops the cap entirely, vintage and stepped engrave a ticked skirt that lights up to the value, and neon keeps the arc glowing at rest. Prefer size for semantic layout sizing; instrument and molten provide shared themed panel skins. An explicit variant wins over an inherited skin. |
| skin | "default" | "flat" | "vintage" | "minimal" | "neon" | — | Finish, shared with every other control in the kit. Omitted, the knob takes whatever a surrounding ControlSkinProvider declares — one panel, one finish, said once. |
| size | "xs" | "s" | "m" | "l" | "xl" | number | "m" | Semantic size preset from extra-small to extra-large, or an explicit diameter in pixels. m is the current/default size. |
| indicator | "line" | "dot" | variant's own | How the position is marked on the cap: a radial line, or the classic potentiometer dot near the rim. Defaults to whatever the variant asks for — a line for most, a dot for flat, ring and stepped. |
| showTrack | boolean | true | Draw the unlit arc track behind the value arc. Turn it off to show only the lit arc or the wedge. |
| showCenter | boolean | false | Draw a centre detent tick. Bipolar knobs always show one. |
| pizza | "interaction" | "always" | "never" | "interaction" | When the conic value wedge is drawn. "interaction" fades it in only while you are turning the knob. |
| activeColor | string | — | Colour of the wedge, arc and marker. Defaults to the --uikit-accent token. |
| curve | "linear" | "log" | { mapTo01, mapFrom01 } | "linear" | Response curve for both the gesture and the visual. Log spaces a frequency range by octave; a custom pair maps raw values however you like. |
| valueDisplay | "raw" | "percent" | "db" | "hz" | "ms" | — | Preset readout format. Ignored when `format` is given. |
| display | string | — | Explicit readout text, useful for instrument panels with custom units. |
| accentClass | string | — | Optional utility class for the lit arc and pointer. |
| glow | boolean | false | Keep the instrument-style cap glow active while the host reports live playback. |
| labelPosition | "top" | "bottom" | "left" | "right" | "top" | Where the caption sits relative to the knob. |
| showLabelOnHover | boolean | false | Swap the caption for the live value while hovering or turning, instead of showing a separate readout. |
| rotationRange | number | 270 | Total arc travel in degrees. 270 is the classic hardware throw. |
| rotationOffset | number | -135 | Where the sweep begins, in degrees from twelve o'clock. |
| bipolar | boolean | false | Light the arc out from the centre and draw a detent tick, for pan-style parameters. |
| interaction | "vertical" | "horizontal" | "xy" | "circular" | "vertical" | Which drag axis turns the knob. A single-axis drag is judged by angle, not locked to the axis, so a diagonal drag still counts. Circular follows the pointer's angle around the knob instead — honouring rotationOffset, holding rather than flipping when the pointer crosses the gap below the sweep, and ignoring the few pixels around the spindle. |
| dragDistance | number | 126 at m size | Pixels of drag needed to cross the whole range. Smaller knobs scale this down; pass an explicit value to override it. |
| axisTolerance | number | 20 | Degrees from the cross axis inside which a single-axis drag is ignored. The default only refuses drags running within 20° of the wrong axis, so ordinary diagonal drags still turn the knob; 0 accepts any drag with a component along the axis. |
| wheelSensitivity | number | 1/70 ≈ 0.0143 | Normalised units per wheel notch. The default crosses a continuous range in about 70 notches and guarantees one declared step per notch for stepped ranges; 0 disables the wheel. |
| onInteraction | (event) => void | — | Fires once, on the first gesture, synchronously inside the event — the place to resume a suspended AudioContext. |
Interaction
- Drag with pointer capture, so the gesture keeps tracking past the edge of the control.
- Shift while dragging for a fine pass; release it mid-gesture and coarse tracking resumes.
- Double-click resets to
defaultValue. - Arrow keys step normally, Shift+arrow advances five normal steps, PageUp/PageDown jump by ten, Home/End go to the extremes.
- Proper
sliderandswitchroles with livearia-valuetext, so a screen reader announces the formatted value rather than the raw number. - The scroll wheel adjusts the value while the pointer is over the control; stepped ranges move at least one declared step per notch by default.