threlte logo
@threlte/extras

useGamepad

A hook that will provide a reference to an object that maps Gamepad values using the Standard Gamepad layout when connected.

<script lang="ts">
  import { useGamepad } from '@threlte/extras'

  const gamepad = useGamepad()
</script>

More than one gamepad can be connected at any given time, so an optional index can be specified.

<script lang="ts">
  import { useGamepad } from '@threlte/extras'

  const gamepad1 = useGamepad({ index: 0 })
  const gamepad2 = useGamepad({ index: 1 })
</script>

The raw unmapped Gamepad can be accessed via the raw prop. This prop will be null unless a gamepad is connected.

<script lang="ts">
  import { useFrame } from '@threlte/core'
  import { useGamepad } from '@threlte/extras'

  const gamepad = useGamepad()

  useFrame(() => {
    if (gamepad.raw) {
      // A gamepad is connected!
    }
  })
</script>

Gamepad data is fetched as an immutable snapshot on every frame, so any variable that caches gamepad.raw will become stale on the next frame.

Connection status

The connected property provides a currentWritable that will update when a gamepad connects.

<script lang="ts">
  import { useGamepad } from '@threlte/extras'

  const gamepad = useGamepad()
  const { connected } = gamepad

  $: console.log(`A gamepad ${$connected ? 'connected' : 'disconnected'}!`)
</script>

Standard gamepad layout

If the button and axis layout of the gamepad corresponds with the Standard Gamepad layout, the gamepad object may be used to read and subscribe to input values.

Properties are given to more easily access button or joystick values.

<script lang="ts">
  import { useFrame } from '@threlte/core'
  import { useGamepad } from '@threlte/extras'

  const gamepad = useGamepad()

  useFrame(() => {
    console.log(gamepad.leftStick.x, gamepad.leftStick.y)
    console.log(gamepad.leftBumper.value)
    console.log(gamepad.rightTrigger.value)
  })
</script>

Event listeners can also be attached to buttons or joysticks, or the gamepad itself.

<script lang="ts">
  import { useFrame } from '@threlte/core'
  import { useGamepad } from '@threlte/extras'

  const gamepad = useGamepad()

  const off = gamepad.leftTrigger.on('down', (event) => {
    console.log('The left trigger has just been pressed!')
    // Unsubscribe from the event after it has fired once.
    off()
  })

  gamepad.leftStick.on('change', (event) => {
    console.log(`Left stick moved: ${event.value.x}, ${event.value.y}`)
  })

  gamepad.on('press', (event) => {
    console.log(`A ${event.type} event on ${event.target} occurred: ${event.value}`)
  })

  const onPress = () => {
    console.log('A button was pressed!')
  }
  gamepad.on('press', onPress)
  // Some time later...
  gamepad.off('press', onPress)
</script>

XR-Standard gamepad layout

To create a mapped object for a gamepad representing WebXR controller with the xr-standard gamepad layout, set the xr flag to true when calling the hook:

const leftGamepad = useGamepad({ xr: true, hand: 'left' })
const rightGamepad = useGamepad({ xr: true, hand: 'right' })

leftGamepad.trigger.on('change', (event) => console.log(event))
rightGamepad.trigger.on('change', (event) => console.log(event))

This will create a similar mapped object once XR controllers are connected.

Button mapping and events

The gamepad object maps the 17 standard gamepad buttons and 4 axes to:

  • The four right cluster buttons: clusterBottom, clusterRight, clusterLeft, clusterTop.
  • The four top buttons: leftBumper, rightBumper, leftTrigger, rightTrigger.
  • The center buttons: select, start, center.
  • The joystick axes and buttons: leftStickButton, leftStick, rightStickButton, rightStick.
  • The directional pad: directionalTop, directionalBottom, directionalLeft, directionalRight.

In a WebXR context, the following buttons and axes are mapped:

  • Two cluster buttons per hand: clusterBottom, clusterTop.
  • The trigger and squeeze buttons.
  • The touchpad and thumbstick axes.

The available events for mapped buttons are the following:

  • change will fire whenever the value of the stick or button changes. This is helpful for buttons or joysticks that can have continuous values.
  • press will fire if a button is pressed.
  • down will fire when a button press begins.
  • up will fire when a button press ends.
  • touch is supported by some gamepads, and will fire if a small amount of pressure on a button is detected.
  • touchstart will fire when a touch begins,
  • and touchend will fire when a touch ends.