WebXR Bridge

Bring WebXR apps to glasses-free 3D displays. A browser page gets the same display geometry, rendering modes, and tracked eye poses a native app does — and falls back to standard WebXR when the extension isn't there.

WebXR, but display-aware

Chrome's built-in WebXR is display-agnostic — generic stereo, compromise-scaled framebuffers, no concept of a physical 3D display. The DisplayXR WebXR Bridge fills that gap: a browser page acquires session.displayXR and gets display geometry in meters, vendor rendering modes, tracked eye poses, window metadata, and a compositor HUD — reaching parity with a native handle app, but from JavaScript.

It's a stopgap, by design. The bridge gives web apps the DisplayXR surface today, until browsers support these extensions natively. Apps written against it stay standard WebXR: when the extension isn't installed, session.displayXR is undefined and the page runs as ordinary WebXR. One codebase, everywhere.

How it works

A Chrome extension

An MV3 extension wraps navigator.xr and adds a session.displayXR surface to every WebXR session — present only when the extension is installed.

A local bridge

displayxr-webxr-bridge.exe runs a headless OpenXR session against the DisplayXR service, reads XR_EXT_display_info, rendering-mode events, and eye poses, and relays them over a 127.0.0.1:9014 WebSocket.

Frames stay untouched

Chrome's WebXR still renders your frames through OpenXR. The bridge only carries metadata and control on a side channel — no extra copy in the render path.

The session.displayXR API

Feature-detect, await the bridge handshake, then read display info, rendering modes, and eye poses. Always keep the standard-WebXR fallback path.

// A bridge-aware WebXR app: full DisplayXR features when the extension
// is installed, standard WebXR everywhere else.
const session = await navigator.xr.requestSession("immersive-vr", {
  optionalFeatures: ["local"],
});

let displayXR = session.displayXR;          // undefined without the extension
if (displayXR?.ready) {
  try {
    await displayXR.ready;                   // bridge handshake (~10ms warm)
    displayXR = session.displayXR;           // re-read: now populated
    const { displaySizeMeters } = displayXR.displayInfo;
    displayXR.configureEyePoses("raw");      // stream tracked eye poses
    // → off-axis (Kooima) projection, rendering-mode switching, HUD …
  } catch {
    // bridge unavailable → fall through to standard WebXR
  }
}
// else: standard WebXR — Chrome's compromise-scaled stereo.

Full surface, events, and the per-frame loop are in the developer guide; the wire protocol is in PROTOCOL.md.

Try it

The bridge needs the DisplayXR runtime running locally, so there's no zero-install web demo — but the extension and a full three.js reference sample ship in the runtime repo. Windows today.

  1. 1

    Install DisplayXR (runtime + WebXR bridge) — see Download / Get Started. Start the service and displayxr-webxr-bridge.exe.

  2. 2

    Load the extension (not on the Chrome Web Store yet): open chrome://extensions, enable Developer mode, click Load unpacked, and select webxr-bridge/extension/.

  3. 3

    Open a sample — serve webxr-bridge/sample/ over localhost (python -m http.server 8080) and open it in Chrome. Full setup is in the README.

The WebXR Bridge ships in the webxr-bridge/ folder of the runtime repo — extension, samples, and docs.