KQ-1126 · GESTURE/PLAYER

Gesture Player

A player whose picture is the control: a sideways swipe seeks 1:1 with a time bubble riding the bar that shows where you are and how far you have jumped, and its release is projected and glides on the glide spring with the release velocity. Swipes up and down set volume on the right half and brightness on the left, a side meter filling and stretching a little past either end before it snaps back, and a double tap on a side skips with a ripple that adds up as you keep tapping. The host draws the picture from a motion value of the time, so a scrub moves the scene at frame rate without a render, and the focusable picture takes Space, the arrows and Shift with them along the same paths, with a tick per second scrubbed and a click per skip.

KQ-1126 · GESTURE/PLAYER
0:42 / 3:40

Swipe sideways to seek; swipe up or down on the right for volume and on the left for brightness. Tap to play or pause, double-tap a side to skip 10 seconds. Keys: Space plays and pauses, Left and Right skip 10 seconds, Shift with Left or Right seeks one second, Home and End jump to the start and end, Up and Down set the volume, and Shift with Up or Down sets the brightness.

paused · 0:42 / 3:40 · vol 70% · bright 60%

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/gesture-player

Props

children
(time: MotionValue<number>) => ReactNode
default —
The picture, drawn from the playback time in seconds. Bind it with useTransform so a scrub moves the scene every frame without a render.
label
string
default —
The clip's title: the player's accessible name.
duration
number
default —
Length of the clip, in seconds.
value / defaultValue
number
default 0
Position in seconds. A new value from the host seeks there; the player reports every seek and skip, and each whole second as it plays.
onValueChange
(seconds: number) => void
default —
Fires with the landing of every seek and skip, and with each whole second passed while playing.
playing / defaultPlaying
boolean
default false
Whether it plays. Playback runs only while the page is visible and nothing is being scrubbed, and stops at the end.
onPlayingChange
(playing: boolean) => void
default —
Fires from the tap, key or button that played or paused, and with false at the end.
volume / defaultVolume
number
default 0.7
Volume, 0 to 1. Reported as a swipe moves it; after the release the meter settles on the host's value, so a refused change springs back.
onVolumeChange
(volume: number) => void
default —
Fires as a vertical swipe or an arrow key changes the volume.
brightness / defaultBrightness
number
default 0.6
Brightness, 0 to 1, applied as a CSS brightness from 0.4 to 1.4: 0.6 shows the picture as drawn.
onBrightnessChange
(brightness: number) => void
default —
Fires as a vertical swipe on the left half or Shift with an arrow key changes the brightness.
sensitivity
number
default 1
How far a swipe goes, 0.5 to 2: a swipe across the picture seeks sensitivity × 90 seconds, and one of 80% of its height sweeps a level from end to end at 1.
zones
boolean
default true
A brightness half on the left and a volume half on the right, the active half washed while it is set. Off, a vertical swipe anywhere sets the volume.
skip
number
default 10
Seconds a double tap or an arrow key skips, 5 to 15. Further taps on the same side while the ripple is up add another skip each.
hints
boolean
default true
Faint glyphs over the paused, idle picture showing what it answers to: the skips on the sides and the levels at the top.
sound
boolean
default false
A tick for every whole second a seek crosses (a ratchet at most every 35 ms, pitched and panned by position), a tick per tenth of a level, and a click per skip. Playback is silent. Nothing plays, and no audio context exists, unless this is on.
disabled
boolean
default false
Dims the player, holds playback, and ignores taps, swipes and keys.
className
string
default —
Merged onto the player's classes; it is w-full at 16:9 unless an aspect class here says otherwise.