WebXR is a W3C standard API that enables web applications to render immersive experiences on VR headsets and AR-capable devices. It replaces the older WebVR API and works with devices from Meta Quest to mobile AR via ARCore and ARKit.

Key Concepts

  • XRSession — represents an active immersive session (inline, immersive-vr, or immersive-ar)
  • XRFrame — snapshot of tracking state at a given moment; provided each render loop tick
  • XRReferenceSpace — a coordinate system: local, local-floor, bounded-floor, or unbounded
  • XRInputSource — a tracked controller or hand
  • WebGL/WebGPU — the actual rendering layer; WebXR provides pose data, not rendering

Requesting a VR Session

webxr-session.jsjavascript
// Check for support
if (!navigator.xr) {
  console.log('WebXR not supported');
} else {
  const supported = await navigator.xr.isSessionSupported('immersive-vr');
  if (supported) {
    startButton.addEventListener('click', async () => {
      const session = await navigator.xr.requestSession('immersive-vr', {
        requiredFeatures: ['local-floor'],
      });
      onSessionStarted(session);
    });
  }
}

function onSessionStarted(session) {
  session.addEventListener('end', onSessionEnded);
  // Attach session to your WebGL renderer
  renderer.xr.setSession(session); // Three.js example
}

Three.js XR Render Loop

three-xr.jsjavascript
import * as THREE from 'three';

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.xr.enabled = true;
document.body.appendChild(renderer.domElement);

// Add VR button
import { VRButton } from 'three/addons/webxr/VRButton.js';
document.body.appendChild(VRButton.createButton(renderer));

// The render loop runs at headset refresh rate (72-120 Hz)
renderer.setAnimationLoop((timestamp, frame) => {
  // frame is an XRFrame when in XR, null otherwise
  renderer.render(scene, camera);
});