Skip to content

@web-engine-dev/xr

WebXR integration for immersive VR/AR experiences with hand tracking, controllers, and spatial anchors.

Features

  • VR/AR Support: Immersive and AR sessions
  • Controller Input: VR controller handling
  • Hand Tracking: Skeleton hand tracking
  • Spatial Anchors: World-anchored content
  • Hit Testing: AR surface detection
  • Reference Spaces: Room-scale and standing

Installation

bash
npm install @web-engine-dev/xr
# or
pnpm add @web-engine-dev/xr

Quick Start

typescript
import { XRManager, XRSession } from '@web-engine-dev/xr';

// Initialize
const xr = new XRManager(renderer);

// Check support
if (await xr.isSupported('immersive-vr')) {
  showVRButton();
}

// Start VR session
vrButton.onclick = async () => {
  await xr.startSession('immersive-vr', {
    requiredFeatures: ['local-floor'],
    optionalFeatures: ['hand-tracking'],
  });
};

// XR frame loop
xr.onFrame((frame, time) => {
  const pose = frame.getViewerPose(referenceSpace);
  renderXR(pose);
});

API Overview

Session Management

typescript
// Check support
await xr.isSupported('immersive-vr');
await xr.isSupported('immersive-ar');

// Start session
await xr.startSession('immersive-vr', {
  requiredFeatures: ['local-floor'],
  optionalFeatures: ['hand-tracking', 'bounded-floor'],
});

// End session
await xr.endSession();

// Session events
xr.onSessionStart(() => enterVRMode());
xr.onSessionEnd(() => exitVRMode());

Controllers

typescript
xr.onControllerConnected((controller, inputSource) => {
  // Add controller model
  controller.add(createControllerModel(inputSource));
});

// Get controller input
xr.onFrame((frame) => {
  for (const controller of xr.controllers) {
    const grip = controller.grip;
    const trigger = controller.getButton('trigger');
    const thumbstick = controller.getAxis('thumbstick');

    if (trigger.pressed) {
      fire();
    }
  }
});

Hand Tracking

typescript
xr.enableHandTracking();

xr.onHandUpdate((hand, jointPoses) => {
  // Get joint positions
  const indexTip = jointPoses.get('index-finger-tip');
  const thumbTip = jointPoses.get('thumb-tip');

  // Detect pinch
  const pinchDistance = indexTip.position.distanceTo(thumbTip.position);
  if (pinchDistance < 0.02) {
    onPinch();
  }
});

AR Features

typescript
// Start AR session
await xr.startSession('immersive-ar', {
  requiredFeatures: ['hit-test', 'anchors'],
});

// Hit testing
xr.onFrame((frame) => {
  const hitResults = frame.getHitTestResults(hitTestSource);

  if (hitResults.length > 0) {
    const pose = hitResults[0].getPose(referenceSpace);
    showPlacementIndicator(pose);
  }
});

// Create anchor
const anchor = await frame.createAnchor(pose, referenceSpace);
anchoredObject.matrix = anchor.anchorSpace.matrix;

Reference Spaces

typescript
// Get reference space
const space = await xr.session.requestReferenceSpace('local-floor');

// Bounded space for room-scale
const bounded = await xr.session.requestReferenceSpace('bounded-floor');
const bounds = bounded.boundsGeometry;

Proprietary software. All rights reserved.