Skip to main content

Getting started

A dark, touch-first analogue joystick for React.

Drag it and it emits a direction and a speed, every frame, until you let go. Drag past the ring and it keeps accelerating while the thumb stays inside — so the same control does a 2 mm nudge and a 300 mm shove without a modifier key or a mode switch.

Drag slowly near the centre for fine control, then push past the ring and hold — it keeps accelerating.

Install

npm install @jugaaadi/joystick

react and react-dom (>= 17) are peer dependencies. Zero runtime dependencies otherwise.

import { Joystick } from '@jugaaadi/joystick';
import '@jugaaadi/joystick/styles.css';

Nothing renders until you ask for it

The default is one operation, no switcher, no clutter. A furniture app has no use for extrude or fillet, and a dead mode is worse than a hidden one.

Switch operations with the chevrons. Tap an axis label for a single step.

Strip it down instead — one axis, no switcher, no Z:

Props

PropTypeDefault
operationsJoystickOperation[]['move']Offered modes. One = no switcher rendered.
operation / onOperationChangeControlled mode.
axesAxis[]['x','y']Which axes the stick drives.
zToggle / zMode / onZModeChangebooleantrueZ as a toggle rather than a third axis.
sizenumber140Diameter in px.
accelExponentnumber2.2Curve shape past the ring.
maxSpeedMultipliernumber6Ceiling on the acceleration.
deadzonenumber0.060–1, ignored deflection near centre.
rotateSnapDegnumber15Rotation snap. 0 = free.
keyStep / keyStepMultipliernumber1 / 10Arrow-key step, and with Shift.
tapMaxMsnumber250Longest motionless press still a tap.
disabledbooleanfalse
collapsible / defaultCollapsedbooleantrue / false
labelstringAccessible label.
className / style / id

Events are covered in Events.

Touch and keyboard

Pointer capture, so a drag survives your finger leaving the stick. touch-action: none, so dragging never scrolls the page. pointercancel is handled, so a system gesture releases the stick cleanly rather than leaving it stuck.

Focus it and the arrow keys step by keyStep, Shift multiplies by keyStepMultiplier. A motionless press under tapMaxMs counts as a tap rather than a drag.