XY Pad

xy-padControls

Two parameters under one finger, on XYPadHeadless. Grid, crosshair, and a dot that leaves a short trail behind the pointer. Each axis carries its own range, step and formatter, and the Y axis reads bottom-up the way a filter/resonance pad is expected to. It wears the kit's five shared skins, so a pad, a knob and a fader on one panel read as the same instrument.

2.20 kHz · 40%

Install

Copies the source into your project. Anything it depends on comes with it.

$ npx shadcn add @syntho-ui/xy-pad

Pulls in

xy-pad-headlesscontrol-mathcontrol-formatcontrol-themecontrol-skinuse-controlcc-dnd

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 { XYPad } from "@/components/ui/xy-pad"

<XYPad
  x={{ min: 20, max: 20000, label: "Cutoff", unit: "Hz" }}
  y={{ min: 0, max: 1, label: "Reso" }}
  defaultValue={{ x: 2200, y: 0.4 }}
  onChange={({ x, y }) => patch.set({ cutoff: x, reso: y })}
/>

Props

PropTypeDefaultDescription
value{ x: number; y: number }Controlled position. Omit for uncontrolled operation.
defaultValue{ x: number; y: number }{ x: 0.5, y: 0.5 }Starting position, and the double-click reset target.
onChange(value: { x: number; y: number }) => voidFires on every move.
onChangeEnd(value: { x: number; y: number }) => voidFires once when the gesture ends.
x / yXYAxisConfigPer-axis min, max, step, label, unit and format. Defaults to a 0..1 continuous axis.
heightnumber180Pad height in pixels; width defaults to 100%.
maxWidthnumber | string | null300Upper bound on the rendered width, so a full-width pad fills its column only up to a sane size. Pass null to remove the cap.
variant"default" | "flat" | "vintage" | "minimal" | "neon""default"Visual preset, shared with every other control in the kit: flat drops the accent wash for a matte dot, vintage puts a cream bead on the plane, minimal keeps only the crosshair and the dot, and neon keeps the dot's halo lit at rest. Omitted, the pad takes whatever a surrounding ControlSkinProvider declares.
gridnumber4Grid divisions per axis. 0 hides the grid.
trailLengthnumber10Trail samples kept behind the dot. 0 disables the trail.
showReadoutbooleantrueShow the formatted x/y values in the corner.
midi{ x?: ControlMidiProps; y?: ControlMidiProps }Makes both halves of the plane CC drop targets and shows one assignment badge per axis.

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 steps finely, 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.