@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/xrQuick 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;