# How JSAR Manages XR Sessions Using WebXR: A Deep Dive into the JavaScript AR Runtime

> Discover how JSAR manages XR sessions with WebXR. Learn about its abstraction layer, deferred-composition, and extended XRSession features for coordinate handedness, collision, and rendering.

- Repository: [M Creative Lab/jsar-runtime](https://github.com/m-creativelab/jsar-runtime)
- Tags: deep-dive
- Published: 2026-03-06

---

**JSAR manages XR sessions by wrapping the standard WebXR API with a thin abstraction layer that delegates framebuffer creation to the host engine while extending XRSession with deferred-composition helpers for coordinate handedness, collision boxes, and multiview rendering.**

The `m-creativelab/jsar-runtime` repository implements a JavaScript Augmented-Reality runtime that simplifies immersive AR development. By building on top of the browser's native WebXR implementation, JSAR allows developers to launch XR sessions with minimal boilerplate while providing host engines like Unity or Unreal with low-level control over rendering and hit-testing.

## The WebXR Session Lifecycle in JSAR

JSAR concentrates its XR session management logic in the *splinedesign* viewer, which demonstrates the complete lifecycle from request to render loop.

### Requesting an Immersive-AR Session

The process begins with a standard WebXR session request. In [`lib/runtime2/viewers/splinedesign.ts`](https://github.com/m-creativelab/jsar-runtime/blob/main/lib/runtime2/viewers/splinedesign.ts), JSAR calls the browser's XR system to initiate an AR session:

```typescript
navigator.xr.requestSession('immersive-ar', {})
  .then(session => {
    // Session initialization continues...
  })
  .catch(err => console.warn('Failed to start XR session:', err));

```

This returns a standard `XRSession` object that JSAR subsequently extends with its own runtime capabilities.

### Binding the Host GL Context to XRWebGLLayer

Unlike typical web-based WebXR applications that create their own WebGL contexts, JSAR receives a pre-configured GL context from the host engine through `navigator.gl`. The runtime wraps this context in an `XRWebGLLayer` and attaches it to the session:

```typescript
const baseLayer = new XRWebGLLayer(session, navigator.gl);
session.updateRenderState({ baseLayer });

```

This delegation pattern allows the host engine to maintain control over framebuffer allocation while the JavaScript client handles the rendering commands.

### Configuring Three.js for XR Rendering

Once the WebXR layer is established, JSAR configures Three.js to use the host-provided canvas and context. The renderer is set up with XR enabled and attached to the session:

```typescript
const renderer = new THREE.WebGLRenderer({
  canvas: navigator.gl._screenCanvas,
  context: navigator.gl,
});
renderer.preserveDrawingBuffer = true;
renderer.autoClear = false;
renderer.xr.enabled = true;
renderer.xr.setReferenceSpaceType('local');
renderer.xr.setSession(session);

function animate() {
  renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);

```

The `setAnimationLoop` method drives the render loop, automatically handling the XR pose updates each frame.

## JSAR's Extended WebXR API for Deferred Composition

Behind the scenes, JSAR extends standard WebXR interfaces with **deferred-composition** helpers. These extensions, declared in [`types/transmute-webapis.d.ts`](https://github.com/m-creativelab/jsar-runtime/blob/main/types/transmute-webapis.d.ts), allow the host engine to optimize rendering and hit-testing without breaking the standard WebXR contract.

### Coordinate Handedness Management

JSAR adds `setDefaultCoordHandedness` to `XRSession`, allowing the host to specify whether the rendering pipeline uses left- or right-handed coordinates:

```typescript
interface XRSession {
  setDefaultCoordHandedness?(handedness: 'left' | 'right'): void;
}

```

This ensures correct view-projection matrix construction when the host engine uses a different coordinate system than standard WebGL.

### Host-Side Hit-Testing with Collision Boxes

To reduce per-frame latency, JSAR allows the client to send collision boundaries to the host via `updateCollisionBox`:

```typescript
interface XRSession {
  updateCollisionBox?(min: number[], max: number[]): void;
}

```

The host performs hit-testing against these bounds natively, returning results without requiring the JavaScript runtime to process every frame.

### Multiview Rendering Support

For stereoscopic rendering optimization, `XRWebGLLayer` exposes a `multiviewRequired` property:

```typescript
interface XRWebGLLayer {
  readonly multiviewRequired: boolean;
}

```

When true, the host framebuffer expects multiview rendering (e.g., `glFramebufferTextureMultiviewOVR`), allowing the client to adjust shaders accordingly for single-pass stereo rendering.

## Architecture Overview: Client-Host Delegation

JSAR's architecture delegates heavy graphics work to the host engine while maintaining a standard WebXR surface for developers:

```

+-------------------+      navigator.xr.requestSession('immersive-ar')
|   Web Browser     | ------------------------------> XRSession (JSAR wrapper)
+-------------------+                                 |
                         +------------------------+------------------------+
                         |                         |                        |
               XRWebGLLayer (host GL)   XRSession extensions (handedness, hit-test)   Three.js Renderer (xr.enabled)
                         |                         |                        |
                renderer.xr.setSession(session)      |                render loop
                         +------------------------+------------------------+
                                            |
                                  JSAR host (Unity / Unreal / custom engine)

```

This delegation allows JSAR to **keep the XR session lightweight** while the host manages framebuffer creation, hit-testing, and coordinate system transformations.

## Implementation Examples

### Minimal XR-Enabled Viewer

The [`splinedesign.ts`](https://github.com/m-creativelab/jsar-runtime/blob/main/splinedesign.ts) viewer demonstrates the complete setup:

```typescript
// Request an immersive-AR session and bind it to the host's GL context
navigator.xr.requestSession('immersive-ar', {})
  .then(session => {
    const baseLayer = new XRWebGLLayer(session, navigator.gl);
    session.updateRenderState({ baseLayer });

    const renderer = new THREE.WebGLRenderer({
      canvas: navigator.gl._screenCanvas,
      context: navigator.gl,
    });
    renderer.preserveDrawingBuffer = true;
    renderer.autoClear = false;
    renderer.xr.enabled = true;
    renderer.xr.setReferenceSpaceType('local');
    renderer.xr.setSession(session);

    function animate() {
      renderer.render(scene, camera);
    }
    renderer.setAnimationLoop(animate);
  })
  .catch(err => console.warn('Failed to start XR session:', err));

```

*Source*: [[`lib/runtime2/viewers/splinedesign.ts`](https://github.com/m-creativelab/jsar-runtime/blob/main/lib/runtime2/viewers/splinedesign.ts)](https://github.com/m-creativelab/jsar-runtime/blob/main/lib/runtime2/viewers/splinedesign.ts) – lines 54‑67.

### Extended WebXR Type Definitions

The type declarations reveal the extended API surface:

```typescript
// These methods are declared in the JSAR type layer
declare global {
  interface XRSession {
    /** Set coordinate handedness (left/right) for deferred composition */
    setDefaultCoordHandedness?(handedness: 'left' | 'right'): void;

    /** Send a collision box to the host for hit-testing */
    updateCollisionBox?(min: number[], max: number[]): void;
  }

  interface XRWebGLLayer {
    /** True when the host framebuffer expects multiview rendering */
    readonly multiviewRequired: boolean;
  }
}

```

*Source*: [[`types/transmute-webapis.d.ts`](https://github.com/m-creativelab/jsar-runtime/blob/main/types/transmute-webapis.d.ts)](https://github.com/m-creativelab/jsar-runtime/blob/main/types/transmute-webapis.d.ts) – XRSession (lines 55‑68) and XRWebGLLayer (lines 74‑96).

### Native Session Types

The private types reveal the host-side implementation:

```typescript
// Native side of the XR session, used by the host engine
type XRNativeSession = {
  // opaque id used to reference the session on the host
  sessionId: number;
  // ... other host-specific fields
};

```

*Source*: [[`types/transmute-private.d.ts`](https://github.com/m-creativelab/jsar-runtime/blob/main/types/transmute-private.d.ts)](https://github.com/m-creativelab/jsar-runtime/blob/main/types/transmute-private.d.ts) – line 145.

## Key Files and Their Roles

| File | Role |
|------|------|
| [[`lib/runtime2/viewers/splinedesign.ts`](https://github.com/m-creativelab/jsar-runtime/blob/main/lib/runtime2/viewers/splinedesign.ts)](https://github.com/m-creativelab/jsar-runtime/blob/main/lib/runtime2/viewers/splinedesign.ts) | Demonstrates the full XR session creation, WebGL layer binding, and Three.js XR setup. |
| [[`types/transmute-webapis.d.ts`](https://github.com/m-creativelab/jsar-runtime/blob/main/types/transmute-webapis.d.ts)](https://github.com/m-creativelab/jsar-runtime/blob/main/types/transmute-webapis.d.ts) | Declares the extended `XRSession` and `XRWebGLLayer` interfaces that JSAR adds to the standard WebXR API. |
| [[`types/transmute-private.d.ts`](https://github.com/m-creativelab/jsar-runtime/blob/main/types/transmute-private.d.ts)](https://github.com/m-creativelab/jsar-runtime/blob/main/types/transmute-private.d.ts) | Contains internal native-session types (`XRNativeSession`, `XRNativeInputSource`) used by the host engine. |
| [[`lib/main.ts`](https://github.com/m-creativelab/jsar-runtime/blob/main/lib/main.ts)](https://github.com/m-creativelab/jsar-runtime/blob/main/lib/main.ts) | Entry point that sets up the JSAR environment, patches global objects (e.g., `navigator.gl`), and loads the appropriate viewer. |
| [[`lib/bindings/renderer.ts`](https://github.com/m-creativelab/jsar-runtime/blob/main/lib/bindings/renderer.ts)](https://github.com/m-creativelab/jsar-runtime/blob/main/lib/bindings/renderer.ts) | Provides the bridge between the host’s GL context and the client-side Three.js renderer. |

## Summary

- **JSAR wraps the standard WebXR API** to provide a lightweight abstraction for AR sessions, implemented primarily in [`lib/runtime2/viewers/splinedesign.ts`](https://github.com/m-creativelab/jsar-runtime/blob/main/lib/runtime2/viewers/splinedesign.ts).
- **Host delegation** allows JSAR to use a pre-existing GL context via `navigator.gl`, creating an `XRWebGLLayer` that renders into a host-managed framebuffer.
- **Extended WebXR interfaces** in [`types/transmute-webapis.d.ts`](https://github.com/m-creativelab/jsar-runtime/blob/main/types/transmute-webapis.d.ts) add deferred-composition helpers for coordinate handedness, collision-box hit-testing, and multiview rendering.
- **Three.js integration** requires enabling `renderer.xr.enabled`, setting the reference space to `'local'`, and attaching the session via `renderer.xr.setSession(session)`.

## Frequently Asked Questions

### How does JSAR differ from standard browser WebXR implementations?

JSAR acts as a thin runtime that delegates heavy graphics operations to a native host engine (such as Unity or Unreal) while exposing the standard WebXR API to JavaScript developers. Unlike browsers that manage their own framebuffers and hit-testing, JSAR uses `navigator.gl` to bind to a host-provided GL context and extends `XRSession` with methods like `updateCollisionBox` to offload computations to the host.

### What is the purpose of the XRWebGLLayer extensions in JSAR?

The `XRWebGLLayer` extensions, specifically the `multiviewRequired` property declared in [`types/transmute-webapis.d.ts`](https://github.com/m-creativelab/jsar-runtime/blob/main/types/transmute-webapis.d.ts), allow the host engine to signal whether it expects multiview rendering (such as `glFramebufferTextureMultiviewOVR`). This enables the JavaScript client to adjust its shaders for single-pass stereo rendering when running on host engines that support multiview framebuffers, optimizing performance for AR headsets.

### How does JSAR handle coordinate system differences between the host engine and WebGL?

JSAR addresses coordinate system mismatches through the `setDefaultCoordHandedness` method on `XRSession`. This extension allows the host to inform the JavaScript runtime whether it uses left-handed or right-handed coordinates. According to the type definitions in [`types/transmute-webapis.d.ts`](https://github.com/m-creativelab/jsar-runtime/blob/main/types/transmute-webapis.d.ts), this ensures that view-projection matrices are constructed correctly for the host's rendering pipeline, preventing mirroring or orientation errors in the final AR output.

### Can JSAR run without a host engine providing navigator.gl?

No, JSAR requires a host engine to function. The runtime depends on `navigator.gl` being injected by the host environment, as seen in [`lib/main.ts`](https://github.com/m-creativelab/jsar-runtime/blob/main/lib/main.ts) and utilized in [`lib/runtime2/viewers/splinedesign.ts`](https://github.com/m-creativelab/jsar-runtime/blob/main/lib/runtime2/viewers/splinedesign.ts). Without this host-provided GL context, JSAR cannot create the `XRWebGLLayer` or bind to the framebuffer, as the runtime is specifically designed as a client-host architecture where heavy graphics operations and session management are delegated to native engines like Unity or Unreal.