use-gamepad
Button indices of the W3C "standard" gamepad mapping.
| Package | @vizejs/composable/use-gamepad |
| Own the source | vize lib pull composable:use-gamepad |
| Runtime exports | useGamepad, mapGamepad, snapshotGamepad, standardGamepadButtons, standardGamepadAxes |
| Gzip budget | 4096 B |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/composable
Import the entry directly. Call lifecycle-bound utilities inside <script setup> so cleanup follows the component scope; consult the runtime contract for this entry's SSR and hydration behavior.
import { useGamepad, mapGamepad, snapshotGamepad, standardGamepadButtons, standardGamepadAxes } from "@vizejs/composable/use-gamepad";
Try it
Show connected controllers
Poll connected game controllers and show which buttons are pressed.
What to observe: Connect a controller and press a button; it appears with the number of pressed buttons.
Browsers only report a gamepad after a button press. Polling runs on animation frames while the component is mounted. Every example renders a neutral message until it mounts, so server and client markup match, and then explains what to do when the browser lacks the API.
Compare the initial and interacted states


Copy the example
Save this complete SFC in your Vue project. Its public imports and source are the same ones executed in the preview.
<script setup lang="ts">
import { useGamepad } from "@vizejs/composable/use-gamepad";
import { useMounted } from "@vizejs/composable/use-mounted";
const mounted = useMounted();
const { supported, gamepads } = useGamepad();
</script>
<template>
<div class="composable-example">
<p>Show connected game controllers and which buttons are pressed.</p>
<ul aria-label="Controllers">
<li v-for="pad in gamepads" :key="pad.index">
{{ pad.id }}: {{ pad.buttons.filter((button) => button.pressed).length }} buttons pressed
</li>
</ul>
<output aria-live="polite">{{
!mounted
? "Checking browser support…"
: !supported
? "The Gamepad API is not available in this browser."
: gamepads.length === 0
? "Connect a controller and press any button."
: `${gamepads.length} controllers connected.`
}}</output>
</div>
</template>
Runtime contract
| Utility | Category | Stability | SSR | Hydration | Cleanup | Targets | Host globals | Uses |
|---|---|---|---|---|---|---|---|---|
useGamepad |
input | experimental | deterministic-fallback | caller-managed | reactive-scope, returned-value | web, desktop | window |
tryOnScopeDispose, useRafFn |
mapGamepad |
input | experimental | safe | not-applicable | none | web, server, worker, native, desktop, terminal | — | — |
snapshotGamepad |
input | experimental | safe | not-applicable | none | web, server, worker, native, desktop, terminal | — | — |
API
standardGamepadButtons
Button indices of the W3C "standard" gamepad mapping.
const standardGamepadButtons
standardGamepadAxes
Axis indices of the W3C "standard" gamepad mapping.
const standardGamepadAxes
snapshotGamepad
Copy a live pad into an immutable GamepadSnapshot.
function snapshotGamepad(pad: GamepadLike): GamepadSnapshot
mapGamepad
Derive typed named state from a snapshot. Missing buttons read as released and missing axes as 0; axis dead zones are applied and rescaled.
function mapGamepad<const Mapping extends GamepadMapping>( snapshot: Pick<GamepadSnapshot, "buttons" | "axes">, mapping: Mapping, ): MappedGamepad<Mapping>
const state = mapGamepad(pad, { jump: { button: 0 }, moveX: { axis: 0, deadzone: 0.1 } });
state.jump.pressed; state.moveX; // boolean, number
useGamepad
Track connected gamepads as immutable snapshots, polled on animation frames, with optional typed named bindings. Pads are re-read every frame while active and on gamepadconnected / gamepaddisconnected; gamepads is replaced only when a pad's timestamp or connection changed. Entries are plain copies, so they are safe to keep or serialize. The frame loop and listeners stop with the owning reactive scope; outside a scope call pause(). Server rendering: no frames or listeners, supported is false and gamepads is empty. Inside a component the host is resolved after mounting, so hydration renders this server state first.
function useGamepad<const Mapping extends GamepadMapping = Record<never, never>>( options: UseGamepadOptions<Mapping> = {}, ): GamepadControls<Mapping>
const { gamepads } = useGamepad({ mapping: { jump: { button: 0 }, moveX: { axis: 0, deadzone: 0.1 } } });
const player = computed(() => gamepads.value[0]?.mapped);
Types
GamepadButtonLike
Minimal GamepadButton read by useGamepad.
| Member | Type | Description |
|---|---|---|
pressed |
boolean |
Whether the button is pressed. |
touched? |
boolean |
Whether the button is touched (defaults to pressed when absent). |
value |
number |
Analog value in [0, 1]. |
GamepadVibrationActuatorLike
Minimal GamepadHapticActuator used for vibration.
| Member | Type | Description |
|---|---|---|
playEffect |
(type: string, params: GamepadVibrationParams) => Promise<unknown> |
Play a haptic effect. |
GamepadLike
Minimal live Gamepad object read by useGamepad.
| Member | Type | Description |
|---|---|---|
id |
string |
Device identifier string. |
index |
number |
Slot index in navigator.getGamepads(). |
connected |
boolean |
Whether the pad is still connected. |
mapping |
string |
Layout mapping ("standard" or ""). |
timestamp |
number |
Last update timestamp. |
buttons |
ArrayLike<GamepadButtonLike> |
Button states. |
axes |
ArrayLike<number> |
Axis values in [-1, 1]. |
vibrationActuator? |
GamepadVibrationActuatorLike | null |
Haptic actuator, when the pad supports vibration. |
GamepadHost
Window-like capability used by useGamepad.
| Member | Type | Description |
|---|---|---|
navigator |
{ /** Read the current pads (live objects, |
Navigator exposing getGamepads(). |
addEventListener |
(type: string, listener: () => void) => void |
Subscribe to gamepadconnected / gamepaddisconnected. |
removeEventListener |
(type: string, listener: () => void) => void |
Unsubscribe from gamepadconnected / gamepaddisconnected. |
GamepadVibrationParams
Parameters of a dual-rumble vibration effect.
| Member | Type | Description |
|---|---|---|
duration |
number |
Effect duration in milliseconds. |
startDelay? |
number |
Delay before the effect starts, in milliseconds. |
strongMagnitude? |
number |
Low-frequency motor magnitude in [0, 1]. |
weakMagnitude? |
number |
High-frequency motor magnitude in [0, 1]. |
GamepadButtonState
Plain, copied state of one button.
| Member | Type | Description |
|---|---|---|
pressed |
boolean |
Whether the button is pressed. |
touched |
boolean |
Whether the button is touched. |
value |
number |
Analog value in [0, 1]. |
GamepadSnapshot
Immutable copy of a pad taken at one poll; never a live Gamepad.
| Member | Type | Description |
|---|---|---|
id |
string |
Device identifier string. |
index |
number |
Slot index. |
connected |
boolean |
Whether the pad was connected when copied. |
mapping |
string |
Layout mapping ("standard" or ""). |
timestamp |
number |
Update timestamp of the copied state. |
buttons |
readonly GamepadButtonState[] |
Copied button states. |
axes |
readonly number[] |
Copied axis values. |
vibration |
boolean |
Whether the pad exposes a vibration actuator. |
GamepadButtonBinding
Binds a mapping key to a button; the mapped value is a GamepadButtonState.
| Member | Type | Description |
|---|---|---|
button |
number |
Button index (see standardGamepadButtons). |
GamepadAxisBinding
Binds a mapping key to an axis; the mapped value is a number.
| Member | Type | Description |
|---|---|---|
axis |
number |
Axis index (see standardGamepadAxes). |
deadzone? |
number |
Values with a magnitude below this become 0; the rest is rescaled to [-1, 1]. |
invert? |
boolean |
Negate the axis value. |
ConnectedGamepad
One connected pad: its snapshot plus the mapped state.
| Member | Type | Description |
|---|---|---|
id |
string |
Device identifier string. |
index |
number |
Slot index. |
connected |
boolean |
Whether the pad was connected when copied. |
mapping |
string |
Layout mapping ("standard" or ""). |
timestamp |
number |
Update timestamp of the copied state. |
buttons |
readonly GamepadButtonState[] |
Copied button states. |
axes |
readonly number[] |
Copied axis values. |
vibration |
boolean |
Whether the pad exposes a vibration actuator. |
mapped |
MappedGamepad<Mapping> |
State derived from the mapping option. |
UseGamepadOptions
Options for useGamepad.
| Member | Type | Description |
|---|---|---|
host? |
MaybeRefOrGetter<GamepadHost | null | undefined> |
Window-like host providing navigator.getGamepads() and connection events. |
mapping? |
Mapping |
Named bindings applied to every pad; the keys and value kinds type mapped. |
scheduler? |
FrameScheduler |
Frame host driving the polling loop. |
immediate? |
boolean |
Start polling immediately. |
GamepadControls
Reactive state and actions returned by useGamepad.
| Member | Type | Description |
|---|---|---|
supported |
ComputedRef<boolean> |
Whether the Gamepad API is available. |
gamepads |
Readonly<ShallowRef<readonly ConnectedGamepad<Mapping>[]>> |
Connected pads, copied on every poll that saw a change. |
isActive |
Readonly<Ref<boolean>> |
Whether polling is running. |
error |
Readonly<ShallowRef<unknown>> |
Most recent vibration failure. |
pause |
() => void |
Stop polling. Idempotent. |
resume |
() => void |
Resume polling. Idempotent. |
refresh |
() => void |
Read the pads once, outside the frame loop. |
vibrate |
( index: number, params: GamepadVibrationParams, type?: string, ) => Promise<boolean> |
Play a vibration effect on the pad at index. |