webdevpuneet.com

Rotary Knob Input — Drag Dial HTML CSS JS Snippet

Rotary Knob Input · Forms · Plain HTML, CSS & JS · Live preview

CategoryForms

Copy & paste

Source Code

<div class="knob-panel">
  <div class="knob-unit">
    <div class="knob" data-val="65" tabindex="0" role="slider" aria-label="Volume" aria-valuemin="0" aria-valuemax="100" aria-valuenow="65">
      <div class="knob-face">
        <div class="knob-dial"><span class="knob-tick"></span></div>
        <span class="knob-num">65</span>
      </div>
    </div>
    <div class="knob-label">Volume</div>
  </div>

  <div class="knob-unit">
    <div class="knob" data-val="30" tabindex="0" role="slider" aria-label="Reverb" aria-valuemin="0" aria-valuemax="100" aria-valuenow="30">
      <div class="knob-face">
        <div class="knob-dial"><span class="knob-tick"></span></div>
        <span class="knob-num">30</span>
      </div>
    </div>
    <div class="knob-label">Reverb</div>
  </div>

  <div class="knob-unit">
    <div class="knob alt" data-val="80" tabindex="0" role="slider" aria-label="Drive" aria-valuemin="0" aria-valuemax="100" aria-valuenow="80">
      <div class="knob-face">
        <div class="knob-dial"><span class="knob-tick"></span></div>
        <span class="knob-num">80</span>
      </div>
    </div>
    <div class="knob-label">Drive</div>
  </div>
</div>
<p class="knob-hint">Drag up / down, scroll, or focus and use arrow keys</p>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

270° knob sweep
Value maps to −135°…+135° with a bottom dead zone, matching real hardware knobs rather than a confusing full-circle rotation.
Conic-gradient arc
One conic-gradient with a --deg custom property renders the fill — updated with a single property write, no SVG or dash math.
Upright value readout
The number sits in a non-rotating layer while the tick spins, so the readout stays legible at every value.
Vertical-drag control
Press-and-drag up/down adjusts the value with 2px-per-unit damping — the same ergonomic pattern pro audio plugins use.
Window-level drag tracking
Move and release listeners on window keep the drag alive even when the cursor leaves the knob mid-adjust.
Scroll-wheel fine-tuning
onWheel steps the value ±2 per notch with preventDefault, enabling quick precise tweaks without dragging.
Accessible slider role
Each knob is role="slider" with arrow-key support and live aria-valuenow, so it is keyboard-operable and screen-reader friendly.
Multi-knob from one initialiser
A single forEach wires every .knob, reading and writing each one's data-val — adding a knob needs no new JavaScript.

About this UI Snippet

Rotary Knob Input — Conic-Gradient Arc, Vertical-Drag Control & 270° Value Sweep

Screenshot of the Rotary Knob Input snippet rendered live

Rotary knobs are the signature control of audio software, synth plugins, and hardware-style interfaces. They pack a continuous value into a compact, tactile dial that users can fling quickly or nudge precisely. This snippet implements a polished, multi-knob rotary control in plain HTML, CSS, and vanilla JavaScript: a conic-gradient arc that fills as the value rises, a rotating indicator tick, a live numeric readout, and three input methods — vertical drag, scroll wheel, and keyboard — all over the conventional 270° knob sweep.

The 270° sweep

Real knobs do not rotate a full circle; they sweep about 270 degrees with a "dead zone" gap at the bottom. This snippet maps value 0 to −135° and value 100 to +135°, leaving the bottom 90° empty. The indicator tick lives inside a .knob-dial that is rotated by -135 + value × 2.7 degrees (since 100 × 2.7 = 270). The numeric readout sits in a separate, non-rotating layer so it always stays upright and legible while the tick spins around it.

Conic-gradient arc fill

The coloured progress arc is a single conic-gradient on the knob element, started from 225deg to align with the bottom-left minimum. The accent colour fills from 0 to var(--deg) × 1deg, the track colour fills the rest up to 270°, and everything past 270° is transparent to create the dead-zone gap. setKnob updates the --deg custom property, so the arc redraws with a single property write — no SVG paths, no stroke-dashoffset math. The inner .knob-face is a radial-gradient circle layered on top to leave only the arc visible as a ring.

Vertical-drag control with damping

Dragging a knob in a circle is fiddly and error-prone, so professional audio software uses vertical drag instead: press and move up to increase, down to decrease. onDown records the start Y and the knob's current value; onMove applies the delta divided by 2 (so two pixels equal one unit) for a comfortable sensitivity. touch-action: none and preventDefault keep the page from scrolling mid-drag. The move and up listeners live on window, so the drag continues even if the cursor leaves the knob.

Scroll and keyboard input

onWheel nudges the value by ±2 per notch for quick fine-tuning, and onKey makes each knob a real role="slider": arrow keys adjust the value and aria-valuenow updates so screen readers announce it. A single forEach wires all three knobs from one set of handlers, reading and writing each knob's value on its own data-val attribute — so adding a fourth knob is just markup.

Pair these knobs with a range slider for linear controls, a multi-range slider for ranges, or a music player interface.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSThree knobs — Volume, Reverb, and Drive — appear on a dark panel, each showing its value and a coloured arc indicating its level.
  2. 2
    Drag a knob verticallyPress a knob and drag up to raise the value or down to lower it; the arc fills and the indicator tick rotates as the number changes.
  3. 3
    Scroll to fine-tuneHover a knob and scroll the wheel — each notch nudges the value by 2 for precise adjustment.
  4. 4
    Use the keyboardTab to a knob (it shows a focus ring) and press the arrow keys; the value steps and screen readers announce it via aria-valuenow.
  5. 5
    Watch the dead zoneNotice the arc sweeps 270° with a gap at the bottom — the conventional knob range, not a full circle.
  6. 6
    Add another knobCopy a .knob block, set its data-val and label, and it is wired automatically by the shared forEach initialiser.

Real-world uses

Common Use Cases

Audio mixers and synth UIs
The native habitat — volume, reverb, drive, and EQ knobs. Combine with a music player for a full playback interface.
Image and video editors
Brightness, contrast, and saturation dials; pair with an image filter editor for live adjustments.
Smart-home and IoT dashboards
Thermostat temperature, light dimming, and fan speed controls that map a value to a tactile dial.
Game and creative tool settings
Difficulty, sensitivity, and effect-intensity controls where a knob feels more expressive than a slider.
Configurator and pricing dials
Adjust a quantity or budget on a dial; combine with a usage calculator to drive a live cost.
Compact value inputs
Anywhere a range slider would take too much horizontal space, a knob packs the same 0–100 control into a small circle.

Got questions?

Frequently Asked Questions

Angular drag requires the cursor to follow a circular path and breaks down near the centre where small movements swing the angle wildly. Professional audio software solved this long ago with vertical drag: move up to increase, down to decrease. It is precise, predictable, and works the same on touch and mouse — which is why this snippet uses it.

The knob is normalised to 0–100. To use a different range, map the displayed number in setKnob (e.g. Math.round(v / 100 * (max - min) + min)) while keeping the internal value 0–100 for the arc math. Change the drag sensitivity by editing the / 2 divisor in onMove, and the wheel/arrow step by editing the 2 in onWheel/onKey.

Each knob stores its current value on data-val. Read them with [...document.querySelectorAll('.knob')].map(k => +k.dataset.val), or call your audio/parameter API from inside setKnob so every change pushes live (e.g. gainNode.gain.value = v / 100). The single update point makes wiring to a real engine straightforward.

Yes — each knob is a focusable role="slider" with aria-valuemin, aria-valuemax, and a live aria-valuenow that setKnob updates. Arrow keys adjust the value, and a visible focus ring shows keyboard focus. Give each knob a meaningful aria-label (Volume, Reverb) so screen-reader users know which control they are on.

In React, hold each knob's value in state, set the arc via the --deg style and the tick via a rotate transform, and attach pointer listeners in a useEffect (store drag start in a ref). In Vue, bind :style for --deg and use @mousedown/@wheel handlers. In Angular, bind [style.--deg] and (mousedown). The conic-gradient CSS and sweep math port unchanged.