Skip to main content

Head Gestures

XR Blocks recognizes completed head nods and shakes from the same camera pose used by WebXR and the desktop simulator. Head gestures do not require hand or controller tracking.

Quick Start

import * as xb from 'xrblocks';

const options = new xb.Options();
options.enableHeadGestures();

await xb.init(options);

xb.input.headGestures?.addEventListener('gesture', (event) => {
const {name, confidence, data} = event.detail;
console.log(name, confidence, data);
});

The built-in gesture names are nod, shake, nod-up, nod-down, shake-left, and shake-right. All are enabled by default. A completed motion emits both its generic and directional gesture; for example, an upward-first nod emits nod and nod-up. Each event represents one completed motion rather than a held pose.

Configuration

const options = new xb.Options();
options.enableHeadGestures();

options.headGestures.minimumConfidence = 0.65;
options.headGestures.releaseConfidence = 0.4;
options.headGestures.updateIntervalMs = 16;
options.headGestures.historyDurationMs = 1500;

options.headGestures.setGestureEnabled('shake', false);
options.headGestures.setGestureConfig('nod', {
enabled: true,
threshold: THREE.MathUtils.degToRad(10),
});

For built-in heuristics, threshold is the minimum angular amplitude in radians. Defaults are approximately 12 degrees for nods and 10 degrees for shakes.

Recognition Pipeline

camera pose -> HeadGestureContext -> HeadGestureRecognizer -> gesture event

HeadGestureRecognition samples the full world-space camera pose every frame and retains a rolling history. The configured recognizer evaluates that history at updateIntervalMs and returns confidence scores by name. A per-gesture latch emits once when confidence crosses minimumConfidence and rearms below releaseConfidence.

The default HeuristicHeadGestureRecognizer measures motion in axes local to a recent resting orientation. A nod is a pitch excursion and return. A shake is a yaw excursion and return. The directional variants classify the initial excursion as up, down, left, or right. The detectors accept either starting direction and reject excessive off-axis motion.

Built-in gestures are intentionally quick: a completed excursion must take roughly 200–750 ms and reach a minimum peak angular speed. Slow looks and slow nods are not recognized as gestures.

Large tracking jumps and timestamp gaps clear recognition history without emitting. This prevents session entry, tab suspension, or simulator pose resets from looking like gestures.

Event Data

interface HeadGestureEventDetail {
name: string;
confidence: number;
data?: Record<string, unknown>;
}

Built-in heuristic events include best-effort diagnostics in data:

amplitudeRadians
durationMs
peakAngularSpeed
initialDirection

Custom Recognizers

A recognizer may be synchronous or asynchronous, leaving room for future ML implementations without changing event handling:

interface HeadGestureRecognizer {
recognize(
context: HeadGestureContext
): HeadGestureScoreMap | Promise<HeadGestureScoreMap>;
}

For a custom heuristic, register a detector function:

const recognizer = new xb.HeuristicHeadGestureRecognizer(false);

recognizer.registerGesture(
'tilt',
(context, config) => {
// Inspect context.samples and return a normalized confidence.
return {confidence: 0.8};
},
{enabled: true}
);

options.headGestures.setGestureRecognizer(recognizer);

Call enableHeadGestures() and select the recognizer before xb.init(). The runtime detector is an optional child, so check xb.input.headGestures before subscribing.