# XrHand

> Talos next · Since next

One hand this tick: its controller and, where the headset tracks hands, its joints.

Plain data.

## Signature

```ts
export interface XrHand {
  /** A controller is bound to this hand and tracked. */
  readonly active: boolean
  /** The controller's OpenXR interaction profile, or null. */
  readonly profile: string | null
  /** Where the hand holds the controller, in the world; null while untracked. */
  readonly grip: XrPose | null
  /** The controller's pointing ray, in the world. */
  readonly aim: XrPose | null
  /** `grip` in the XR origin's frame: what a cockpit aligns against. */
  readonly gripLocal: XrPose | null
  /** `aim` in the XR origin's frame. */
  readonly aimLocal: XrPose | null
  /** The trigger, 0 to 1. */
  readonly trigger: number
  /** 0 to 1 (a click grip is 0 or 1). */
  readonly squeeze: number
  /** -1 to 1 each. */
  readonly thumbstick: [number, number]
  /** Held this tick (the analog ones from 0.6 until below 0.4). */
  readonly buttons: Record<XrButton, boolean>
  /** Pressed this tick. */
  readonly pressed: Record<XrButton, boolean>
  /** Released this tick. */
  readonly released: Record<XrButton, boolean>
  /** The hand itself is tracked (hand tracking): `joints` and `palm` are set. */
  readonly tracked: boolean
  /** The palm of a tracked hand, in the world; null unless `tracked`. */
  readonly palm: XrPose | null
  /** The palm in the XR origin's frame. */
  readonly palmLocal: XrPose | null
  /** The 26 joints of OpenXR hand tracking (wrist, palm, then thumb to little finger), or null. */
  readonly joints: XrJoint[] | null
}
```

Import: `import type { XrHand } from "@argus/api"`

## Members

| Member | Signature | Description |
|---|---|---|
| `active` | `readonly active: boolean` | A controller is bound to this hand and tracked. |
| `profile` | `readonly profile: string \| null` | The controller's OpenXR interaction profile, or null. |
| `grip` | `readonly grip: XrPose \| null` | Where the hand holds the controller, in the world; null while untracked. |
| `aim` | `readonly aim: XrPose \| null` | The controller's pointing ray, in the world. |
| `gripLocal` | `readonly gripLocal: XrPose \| null` | `grip` in the XR origin's frame: what a cockpit aligns against. |
| `aimLocal` | `readonly aimLocal: XrPose \| null` | `aim` in the XR origin's frame. |
| `trigger` | `readonly trigger: number` | The trigger, 0 to 1. |
| `squeeze` | `readonly squeeze: number` | 0 to 1 (a click grip is 0 or 1). |
| `thumbstick` | `readonly thumbstick: [number, number]` | -1 to 1 each. |
| `buttons` | `readonly buttons: Record<XrButton, boolean>` | Held this tick (the analog ones from 0.6 until below 0.4). |
| `pressed` | `readonly pressed: Record<XrButton, boolean>` | Pressed this tick. |
| `released` | `readonly released: Record<XrButton, boolean>` | Released this tick. |
| `tracked` | `readonly tracked: boolean` | The hand itself is tracked (hand tracking): `joints` and `palm` are set. |
| `palm` | `readonly palm: XrPose \| null` | The palm of a tracked hand, in the world; null unless `tracked`. |
| `palmLocal` | `readonly palmLocal: XrPose \| null` | The palm in the XR origin's frame. |
| `joints` | `readonly joints: XrJoint[] \| null` | The 26 joints of OpenXR hand tracking (wrist, palm, then thumb to little finger), or null. |

## Example

The tested fixture `fixtures/recipes/jump-pad-and-elevator/scenes/behaviors/camera-zone.ts`.

```ts
import type { Behavior } from "@argus/api"

/**
 * A trigger volume that retunes the camera while the player is inside it.
 *
 * The engine has no camera-zone concept and needs none: a volume, a `trigger` component and this.
 * Here the player's behaviour owns the camera (it re-sends its params when the view is turned), so
 * the zone tells it what to change rather than calling `ctx.camera.set` itself. In a scene where
 * nothing else writes the camera, the zone can call `ctx.camera.set(kind, params)` directly.
 */
interface Props {
  /** `ctx.camera.set` params to merge in on the way in, and on the way out. */
  inside?: Record<string, unknown>
  outside?: Record<string, unknown>
}

export default {
  onTrigger(ctx, trigger) {
    const params = trigger.entering ? ctx.props.inside : ctx.props.outside
    if (params) ctx.events.emit("camera-zone", { target: trigger.other.id, params })
  },
} satisfies Behavior<Record<string, never>, Props>
```
