Frontend Development 25 min read

Understanding WebXR: API Overview, Device Interaction, and VR/AR Development

This article explains the WebXR standard for rendering 3D scenes in browsers, covers its history, browser support, step‑by‑step usage with WebGL, details the WebXR Device API objects such as XRSystem, XRSession, XRReferenceSpace, and demonstrates a panoramic VR image viewer with full code examples.

ByteFE
ByteFE
ByteFE
Understanding WebXR: API Overview, Device Interaction, and VR/AR Development

Overview

WebXR is a set of web standards that enable rendering 3D scenes for Virtual Reality (VR) or adding graphics to the real world for Augmented Reality (AR). It replaces the deprecated WebVR API and provides access to VR/AR devices and user pose tracking.

While Khronos' OpenXR covers similar functionality, WebXR is defined by the W3C and works together with WebGL for rendering.

Terminology

AR (Augmented Reality) blends virtual objects with the real world (e.g., Pokémon GO). VR (Virtual Reality) creates a fully immersive 3D world (e.g., the movie "Ready Player One"). MR (Mixed Reality) combines both, allowing users to see the real environment and interact with virtual objects. XR (Extended Reality) is the umbrella term for all these technologies. In WebXR the "X" is not an acronym but a placeholder that can stand for "Extended" or "Cross".

History

In 2014 Mozilla introduced the WebVR concept. Chrome joined in 2016, and by 2018 the W3C Immersive Web Working Group standardized WebXR to replace WebVR, integrating AR, VR, and future reality devices.

Browser Compatibility

WebXR is not yet officially released; only a few browsers support it, often behind experimental flags. VR‑focused browsers such as Firefox Reality (now continued by Igalia as Wolvic) provide native support.

Quick Experience

To render to a VR device you need both WebXR and WebGL. The minimal steps are:

Check if the environment supports WebXR.

Create an XRSession.

Create a WebGL context that is XR‑compatible.

Render a new frame inside the XRSession's requestAnimationFrame loop.

End the session when the user exits.

The following code demonstrates the simplest VR scene:

if (navigator.xr) {
  // 1. Check support for immersive‑vr mode
  navigator.xr.isSessionSupported('immersive-vr').then((supported) => {
    if (supported) {
      const btn = document.createElement('button');
      btn.textContent = 'Enter VR';
      btn.onclick = onBtnClick;
      document.body.appendChild(btn);
    }
  });
}

let gl;
function onBtnClick() {
  navigator.xr.requestSession('immersive-vr').then((session) => {
    // 2. Request VR session
    const canvas = document.createElement('canvas');
    gl = canvas.getContext('webgl', { xrCompatible: true });
    // 3. Create XR‑compatible WebGL context
    session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) });
    // 4. Start rendering loop
    session.requestAnimationFrame(onXRFrame);
  });
}

function onXRFrame(time, frame) {
  const session = frame.session;
  session.requestAnimationFrame(onXRFrame);
  const glLayer = session.renderState.baseLayer;
  gl.bindFramebuffer(gl.FRAMEBUFFER, glLayer.framebuffer);
  gl.clearColor(Math.cos(time/2000), Math.cos(time/4000), Math.cos(time/6000), 1.0);
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
}

The navigator.xr object (XRSystem) provides two methods: isSessionSupported to query mode support and requestSession to start a session. Sessions can be of type ar or vr and must be initiated from a user‑generated event.

WebXR Device API

XRSystem

Accessible via navigator.xr , XRSystem defines:

interface XRSystem : EventTarget {
  Promise
isSessionSupported(XRSessionMode mode);
  Promise
requestSession(XRSessionMode mode, optional XRSessionInit options = {});
  attribute EventHandler ondevicechange;
}

Session modes are inline , immersive-vr , and immersive-ar .

XRSession

Represents an active XR session. Key attributes include visibilityState , frameRate , renderState , and inputSources . Important methods are requestReferenceSpace , requestAnimationFrame , and updateRenderState . Events such as onvisibilitychange and onframeratechange notify about state changes.

Visibility States

visible : normal rendering.

visible-blurred : reduced frame rate, possible blur.

hidden : rendering callbacks are paused.

Reference Spaces

Created via session.requestReferenceSpace(type) . Types include viewer , local , local-floor , bounded-floor , and unbounded , each defining different tracking capabilities.

XRWebGLLayer

Provides the framebuffer for XR rendering. It can be constructed with an optional XRWebGLLayerInit object to control antialiasing, depth, stencil, alpha, and framebuffer scaling.

const glLayer = new XRWebGLLayer(session, gl, { framebufferScaleFactor: 1.0 });
session.updateRenderState({ baseLayer: glLayer });

Framebuffer scaling can be queried with XRWebGLLayer.getNativeFramebufferScaleFactor(session) and adjusted accordingly.

XRFrame

Available only inside the requestAnimationFrame callback. It provides getViewerPose(referenceSpace) and getPose(space, baseSpace) to obtain pose information for rendering.

XRInputSource

Describes input devices such as VR controllers. Important properties are handedness , targetRayMode , targetRaySpace , and gripSpace .

Panoramic VR Image Viewer

The article then shows how to build a simple equirectangular panoramic viewer that runs inside a VR headset. The demo omits detailed WebGL shader code for brevity.

function main() {
  const xr = navigator.xr;
  let refSpace;
  if (xr) {
    xr.isSessionSupported('immersive-vr').then((supported) => {
      if (supported) {
        const btn = document.createElement('button');
        btn.textContent = 'Enter VR';
        btn.onclick = onBtnClick;
        document.body.appendChild(btn);
      } else {
        document.body.innerHTML = 'Current device does not support VR';
      }
    }).catch(() => { document.body.innerHTML = 'Detection failed'; });
  } else {
    document.body.innerHTML = 'Browser does not support WebXR';
  }

  function onBtnClick() {
    xr.requestSession('immersive-vr').then((session) => {
      initWebGL();
      session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) });
      session.requestReferenceSpace('local').then((space) => {
        refSpace = space;
        session.requestAnimationFrame(onXRFrame);
      });
    });
  }

  function onXRFrame(time, frame) {
    const session = frame.session;
    session.requestAnimationFrame(onXRFrame);
    const glLayer = session.renderState.baseLayer;
    gl.bindFramebuffer(gl.FRAMEBUFFER, glLayer.framebuffer);
    const pose = frame.getViewerPose(refSpace);
    if (pose) {
      pose.views.forEach((view) => {
        const vp = glLayer.getViewport(view);
        gl.viewport(vp.x, vp.y, vp.width, vp.height);
        // set shader uniforms for eye, matrices, etc.
        gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
      });
    }
  }
}

The viewer checks environment support, creates an XRSession on user interaction, obtains a local reference space, and renders each eye's view inside the XRWebGLLayer framebuffer.

Conclusion

WebXR Device API gives web applications access to XR hardware through three session modes: inline (rendered in the page), immersive-vr (rendered to a VR headset), and immersive-ar (rendered to an AR device). Rendering follows the same principles as ordinary WebGL, with the addition of rendering to an XRWebGLLayer framebuffer and handling separate eye views.

References

MR headset video: https://www.youtube.com/watch?v=tgJ7m0Phd64

WebXR emulator extension: https://github.com/MozillaReality/WebXR-emulator-extension

WebXR Augmented Reality Module: https://www.w3.org/TR/webxr-ar-module-1/

WebXR Device API specification: https://immersive-web.github.io/webxr/

GitHub immersive‑web/webxr repository: https://github.com/immersive-web/webxr

MDN WebXR Device API reference: https://developer.mozilla.org/zh-CN/docs/Web/API/WebXR_Device_API

Article on WebXR's potential for VR social scenes: https://www.sohu.com/a/525476997_395737

LiveVideoStack interview about immersive web: https://www.livevideostack.cn/news/webxr/

JavaScriptARWebGLXRVRWebXR
ByteFE
Written by

ByteFE

Cutting‑edge tech, article sharing, and practical insights from the ByteDance frontend team.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.