Knob

knobControls

The 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.

2.20 kHz
35%
C
60%
60%
60%
60%

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/knob

Pulls in

knob-headlessknob-corecontrol-skinuse-knob-keyboarduse-first-interactioncontrol-labelcontrol-mathcontrol-formatcontrol-themeuse-control

First time? Add the registry to your components.json once:

components.json
{
  "registries": {
    "@syntho-ui": "https://engine.syntho.app/r/{name}.json"
  }
}

Usage

Controlled or uncontrolled, whichever suits the surrounding state.

example.tsx
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

PropTypeDefaultDescription
valuenumberControlled value. Omit to let the control own its state.
defaultValuenumber0Starting value when uncontrolled, and the double-click reset target.
onChange(value: number) => voidFires on every value change, in both controlled and uncontrolled modes.
onChangeEnd(value: number) => voidFires once when a drag gesture finishes — the hook for undo coalescing.
minnumber0Lower bound of the range.
maxnumber1Upper bound of the range.
stepnumber0Quantisation grid. 0 means continuous.
labelstringCaption above the control. Doubles as the accessible name.
unitstringSuffix appended by the default formatter, e.g. "Hz".
format(value: number) => stringCustom readout formatter. Overrides `unit`.
hideValuebooleanfalseHide the numeric readout.
disabledbooleanfalseBlock 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 ownHow 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.
showTrackbooleantrueDraw the unlit arc track behind the value arc. Turn it off to show only the lit arc or the wedge.
showCenterbooleanfalseDraw 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.
activeColorstringColour 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.
displaystringExplicit readout text, useful for instrument panels with custom units.
accentClassstringOptional utility class for the lit arc and pointer.
glowbooleanfalseKeep 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.
showLabelOnHoverbooleanfalseSwap the caption for the live value while hovering or turning, instead of showing a separate readout.
rotationRangenumber270Total arc travel in degrees. 270 is the classic hardware throw.
rotationOffsetnumber-135Where the sweep begins, in degrees from twelve o'clock.
bipolarbooleanfalseLight 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.
dragDistancenumber126 at m sizePixels of drag needed to cross the whole range. Smaller knobs scale this down; pass an explicit value to override it.
axisTolerancenumber20Degrees 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.
wheelSensitivitynumber1/70 ≈ 0.0143Normalised 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) => voidFires 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 slider and switch roles with live aria-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.