Develop

Pointers (Canvas, Ray, Grab & Touch)

Updated: Sep 4, 2026
IWSDK uses pointer events as a common interaction path for browser canvas input and WebXR input. Each XR hand has a MultiPointer that coordinates ray, grab, and touch pointers.

Pointer kinds and priority

PointerKind has three values:
type PointerKind = 'ray' | 'grab' | 'touch';
When more than one pointer has a candidate, IWSDK applies this priority:
Touch > Grab > Ray
Once a pointer starts selecting, it remains active until release. The touch pointer uses separate enter and exit distances to prevent rapid hover-state changes near the boundary.

Make an entity interactive

Use the interaction tag that matches the input path.
import { PokeInteractable, RayInteractable } from '@iwsdk/core';

const entity = world.createTransformEntity(buttonMesh);
entity.addComponent(RayInteractable);
entity.addComponent(PokeInteractable);

entity.object3D!.addEventListener('pointerenter', showHover);
entity.object3D!.addEventListener('pointerleave', hideHover);
entity.object3D!.addEventListener('click', activateButton);
RayInteractable is used by XR rays and browser canvas pointer forwarding. PokeInteractable makes the object available to touch interaction. Grab components configure manipulation behavior.

Inspect and control a MultiPointer

Obtain the instances from the World’s XR input manager.
const left = world.input.xr.multiPointers.left;
const right = world.input.xr.multiPointers.right;

left.toggleSubPointer('ray', true);
left.toggleSubPointer('grab', true);
left.toggleSubPointer('touch', true);

console.log(right.getActiveKind());
console.log(right.getSubPointerState('touch').registered);
console.log(right.getRayBusy());
Public methods used here are:
  • toggleSubPointer(kind, enabled): boolean
  • getSubPointerState(kind): { registered: boolean }
  • getActiveKind(): PointerKind | null
  • getRayBusy(): boolean
The World manages pointer updates. Application code does not need to call a MultiPointer update method.
The listed methods are the supported public control surface. Do not access the internal combined pointer or register custom pointers through private fields.

Browser canvas pointers

Canvas forwarding is enabled by default.
const world = await World.create(container, {
  input: {
    canvasPointerEvents: true,
  },
});
Use the object form to keep browser input active on the mirror canvas during immersive XR.
const world = await World.create(container, {
  input: {
    canvasPointerEvents: { activeDuringXR: true },
  },
});
Leave this disabled during XR unless the application intentionally accepts simultaneous mirror-canvas input.

Pointer state

The internal interaction state values are disabled, normal, hover, and select. Use pointer events and the public MultiPointer inspection methods instead of depending on private state storage.