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.
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/
ByteFE
Cutting‑edge tech, article sharing, and practical insights from the ByteDance frontend team.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.