DreamKnob/packages/dreamknob/README.md
Dreamodus 60a02cb1db Initial release: dreamknob library + showcase
React + TypeScript knob/fader library with a headless interaction core
(rotary, relative-drag and track modes, wheel, keyboard, a11y), SVG
primitives, nine prebuilt skins, seven-segment display engine, and a
Vite showcase with gallery, live playground and API docs.
2026-07-12 14:26:52 -07:00

3.4 KiB
Raw Blame History

dreamknob

Studio-grade React knobs & faders. Rotary, linear, 2D, 3D, digital and analog — with a headless core, any number range, any decimal precision, and colors you fully control. Zero runtime dependencies.

npm install dreamknob

Quick start

import { FlatKnob } from 'dreamknob'

function Volume() {
  const [value, setValue] = useState(64)
  return (
    <FlatKnob
      size={90}
      value={value}
      onChange={setValue}
      min={0}
      max={100}
      step={0.5}
      label="Volume"
      unit="%"
      aria-label="Volume"
    />
  )
}

Uncontrolled works too: pass defaultValue instead of value/onChange (it also becomes the double-click reset target).

Interaction model

Every component shares one core:

Gesture Behaviour
Drag rotary (grab & turn, follows the pointer angle), vertical, horizontal, both, or absolute track-* (faders)
Scroll wheel one step per notch, Shift = fine
↑ → / ↓ ← ± one step (Shift = 10×)
PageUp/PageDown ± 10 % of range
Home/End min / max
Double-click reset to defaultValue
Touch / pen pointer capture, touch-action: none

All knobs render role="slider" with aria-valuemin/max/now/text.

Value handling

  • min / max — any numbers (negative, fractional, huge).
  • step — decimal snap increments (0.5, 0.001, 1e-7…) with float-drift-safe rounding; display precision inferred automatically, overridable via decimals.
  • values={[0.25, 0.5, 1, 2, 4]} — discrete allowed values.
  • steps={5} — evenly spaced detents (selector knobs).
  • taperlinearTaper (default), logTaper (frequencies), powTaper(n) (gain), or your own { toNormalized, fromNormalized }.
  • onChange / onChangeStart / onChangeEnd — gesture-aware callbacks.

Prebuilt skins

import {
  FlatKnob,     // clean 2D arc knob (arcFrom="center" for pan knobs)
  MetalKnob,    // 3D brushed aluminum, knurled rim (tone="silver" | "dark")
  RubberKnob,   // 3D soft-touch synth knob with glow halo
  VintageKnob,  // chicken-head bakelite over a printed scale
  LEDKnob,      // segmented LED ring + true seven-segment readout
  NeonKnob,     // glowing arc for dark UIs
  SteppedKnob,  // detented selector (positions={['LP','BP','HP']})
  Fader,        // linear channel fader, vertical or horizontal
  LEDFader,     // segmented LED meter-fader with color zones
  SegmentDisplay, // standalone seven-segment numeric display
} from 'dreamknob'

Every skin takes the core props plus size, label, showValue, unit, format, and per-part color props (color, trackColor, faceColor, bodyColor, …).

Compose your own

import { Knob, Arc, Pointer, Ticks, KnobValue } from 'dreamknob'

<Knob size={96} min={-24} max={24} step={0.5} defaultValue={0} aria-label="Trim">
  <Ticks count={25} activeColor="#ffd23e" />
  <Arc radius={36} thickness={3} color="#ffd23e" from="center" />
  <Pointer type="triangle" radius={33} length={9} width={8} color="#ffd23e" />
  <KnobValue unit=" dB" />
</Knob>

<Knob> also accepts a render function: {(ctx) => <YourSVG angle={ctx.angle} />}.

Fully headless

import { useKnob } from 'dreamknob'

const { value, normalized, angle, isDragging, bind, setValue } = useKnob({
  min: 0, max: 11, step: 0.1, defaultValue: 11, 'aria-label': 'Volume',
})
return <div {...bind}>your own rendering</div>

MIT