Tempo Control
An editable BPM input with tap tempo, keyboard stepping and a rolling average.
Installation
$ pnpm dlx shadcn@latest add @audiocn/tempo-controlUsage
import { TempoControl } from "@/components/ui/tempo-control";<TempoControl defaultValue={120} onValueChange={setBpm} />Tap at least twice to measure a tempo. The control averages the last four intervals and starts a new sequence after more than two seconds without a tap. Typing or stepping the input clears the tap history.
BPM values are always whole numbers. Tapped, typed and externally supplied fractional values round to the nearest integer. The default range is 30-600 BPM; set min and max to suit your application.
Anatomy
The input and tap button render by default. Supply children to arrange the parts, change their labels, or omit the tap button.
<TempoControl>
<TempoControlInput />
<TempoControlTap>Tap tempo</TempoControlTap>
</TempoControl>Examples
Custom elements
Use TempoControlTap's render prop to replace the default trigger with a custom element. This example combines a sound pad with a tempo readout.
Controlled BPM
value remains owned by the parent. onValueChange reports changes with a reason of tap, keyboard or input. onValueCommitted fires after a measured tap, keyboard step, or text edit committed by blurring the input.
Sizes and disabled states
size and disabled inherit from AudioConfigProvider; explicit props win. readOnly keeps the input focusable but prevents editing and tapping.
Theming
| Variable or attribute | Meaning |
|---|---|
--tempo-height | Shared input and tap-button height, set by size |
data-size | sm, default or lg, on the root |
data-disabled | Disabled state, on the root |
data-readonly | Read-only state, on the root |
data-slot | tempo-control, tempo-control-input-group, tempo-control-input, tempo-control-unit, tempo-control-tap |
Use className on the root, input and tap button to customize the layout and appearance. Colours follow the standard shadcn theme tokens.
Accessibility
- The input is labelled "Tempo in beats per minute". Override
aria-label, supplyaria-labelledby, or pairidwith a visible label and remove the default witharia-label={undefined}. - Space and Enter activate the focused tap button.
- Disabled and read-only controls ignore taps. Holding a key does not generate repeated taps.
- Press feedback respects reduced-motion preferences.
<label htmlFor="delay-tempo">Delay tempo</label>
<TempoControl>
<TempoControlInput id="delay-tempo" aria-label={undefined} />
<TempoControlTap />
</TempoControl>| Focus | Key | Action |
|---|---|---|
| Input | Up / Down | Change by 1 BPM |
| Input | Shift + Up / Down | Change by largeStep |
| Input | Home / End | Set min / max |
| Tap button | Space / Enter | Tap once |
API reference
TempoControl
Prop
Type
TempoControlInput
Accepts Base UI NumberField.Input props, including id, aria-label, className and ref.
TempoControlTap
Accepts Base UI Button props, including render, children, className, ref and disabled. A disabled or read-only root always disables tapping. An onClick handler can cancel a tap with event.preventDefault().