How JSAR Enables Immersive Web Content Creation: Architecture and Implementation Guide

JSAR enables immersive web content creation by integrating a full-featured browser engine with native XR pipelines, allowing HTML/CSS/JavaScript content to be spatially positioned and rendered as 3D objects within VR/AR environments through the TrEmbedder API and WebXR session management.

JSAR (JavaScript-AR) is a specialized browser runtime built specifically for the Spatial Web, bridging standard web technologies with immersive 3D engines. This article examines how the m-creativelab/jsar-runtime repository implements immersive web content creation through its embedder architecture, XR device management, and spatial rendering pipeline.

Architectural Foundation for Immersive Web Content

JSAR operates through a layered architecture that transforms ordinary web pages into first-class 3D objects. The system combines an embedded V8 JavaScript engine with native XR device management to render spatialized DOM elements inside host engines like Unity or Unreal.

The Embedder API Interface

At the C++ level, JSAR exposes the TrEmbedder virtual class defined in docs/api/embedder.md. This interface allows host engines to initialize, control, and shut down the runtime through standardized lifecycle hooks. Embedder implementations override methods including onStart, onBeforeRendering, and onOpaquesRenderPass to drive the rendering loop and manage resource allocation.

XR Device and Session Management

The XR subsystem centers on two primary components. The Device class in src/xr/device.hpp manages WebXR sessions, input sources, and stereo rendering configurations. When a session initiates, the runtime creates a TrXRSession object implemented in src/xr/session.cpp, which tracks per-eye view matrices, projection data, and frustum culling for spatial rendering.

Content Runtime and Spatial Rendering

Standard web content loads through the runtime layer (runtime/content.hpp) and renders via the content renderer (renderer/content_renderer.hpp). The engine treats the DOM as a scene graph where every HTML element receives 3D coordinates. To maintain performance on mobile XR hardware, the renderer batches all spatialized DOM elements into ≤10 GPU draw calls per frame, minimizing overhead while preserving visual fidelity.

How JSAR Achieves Spatial Immersion

JSAR creates immersive experiences through five core technical mechanisms that bridge web standards with native 3D pipelines:

  1. Spatialized DOM Processing – The renderer assigns 3D coordinates to every HTML element and applies the session’s baseMatrix to position the entire web page in world space, effectively converting CSS layouts into spatial scene graphs.

  2. WebXR Integration – The Device class exposes the complete WebXR API, creating TrXRSession objects that drive per-eye view and projection matrices for stereoscopic rendering.

  3. Stereo Rendering Modes – JSAR supports Multi-Pass, Single-Pass, Instanced, and Multiview pipelines. Host engines select the optimal mode for their graphics API (OpenGL, Metal, or D3D11) through the StereoRenderingMode enumeration.

  4. Low Draw-Call Overhead – By batching DOM elements into a minimal set of GPU draw calls (≤10 per frame), JSAR maintains high frame rates critical for comfortable VR/AR experiences on resource-constrained hardware.

  5. Cross-Platform Host Integration – Through inheritance from TrEmbedder, any 3D engine can embed JSAR, start the runtime with JSON configuration, and synchronize rendering via per-frame hooks like onBeforeRendering.

Implementation Examples

Creating a Custom C++ Embedder

Host engines integrate JSAR by implementing the TrEmbedder interface. The following example demonstrates a Unity-compatible embedder that configures XR for single-pass multiview rendering:

#include "embedder/TrEmbedder.h"
#include "xr/device.hpp"

class MyUnityEmbedder : public TrEmbedder {
public:
  MyUnityEmbedder() : TrEmbedder(TrHostEngine::Unity) {}

  void onStart(std::string argJson) override {
    xr::TrDeviceInit xrInit;
    xrInit.active = true;
    xrInit.stereoRenderingMode = xr::StereoRenderingMode::SinglePassMultiview;
    configureXrDevice(true, xrInit);
  }

  void onBeforeRendering() override {
    if (!contentLoaded) {
      loadUrl("https://example.com/immersive.html");
      contentLoaded = true;
    }
  }
};

Key references: TrEmbedder definition in docs/api/embedder.md; XR configuration in src/xr/device.hpp.

Requesting XR Sessions from JavaScript

Web developers request immersive sessions using standard WebXR APIs. The JSAR runtime intercepts these calls through the native bridge:

if (navigator.xr) {
  navigator.xr.requestSession('immersive-vr', { 
    requiredFeatures: ['local-floor'] 
  })
  .then(session => {
    console.log('JSAR XR session started', session);
  })
  .catch(err => console.error('Failed to start XR session', err));
}

The native side receives the request via Device::requestSession (declared in src/xr/device.hpp) and instantiates a TrXRSession object (implemented in src/xr/session.cpp) to manage the rendering pipeline.

Positioning Web Content in 3D Space

After establishing a session, developers position the web page within the 3D environment by updating the session’s base transformation matrix:

auto xrSession = device->requestSession(
  xr::TrXRSessionMode::ImmersiveVR, 
  contentRuntime
);
xrSession->setLocalBaseMatrix(
  glm::translate(glm::mat4(1.0f), glm::vec3(0, 1.5f, -2))
);

This positions the web page 2 meters in front of the user at eye level. Internally, setLocalBaseMatrix updates the zone system and synchronizes spatial audio source matrices (see implementation details in src/xr/session.cpp).

Core Source Files for Immersive Development

Understanding these key files is essential for developers extending JSAR’s immersive capabilities:

  • docs/api/embedder.md – Defines the TrEmbedder virtual class, lifecycle hooks (onStart, onBeforeRendering), and JSON startup configuration schema.

  • src/xr/device.hpp – Public API for XR device configuration, session management, and handling of view/projection matrices for stereo rendering.

  • src/xr/session.cpp – Implements TrXRSession including session state management, stereo-id generation, frustum culling, and base-matrix updates that position content in world space.

  • src/xr/input_source.cpp – Handles XR input devices including controllers, gaze tracking, and hand tracking systems.

  • runtime/content.hpp and renderer/content_renderer.hpp – Manage loading of HTML/CSS/JS content and batch DOM elements into optimized GPU draw calls.

  • common/zone.hpp – Persists per-session state including transformation matrices and spatial coordinates for debugging and tooling integration.

  • runtime/media_manager.hpp – Provides Web Audio API support and spatial sound sources that follow the session’s base matrix transformations.

Summary

  • JSAR bridges web technologies with native XR engines through the TrEmbedder C++ interface, enabling HTML content to function as interactive 3D objects.

  • The architecture batches DOM rendering into ≤10 draw calls per frame while supporting multiple stereo rendering modes (Multi-Pass, Single-Pass, Instanced, Multiview).

  • Web developers use standard WebXR JavaScript APIs (navigator.xr.requestSession) while C++ host engines control positioning via TrXRSession::setLocalBaseMatrix and the zone system.

  • Core implementation files include src/xr/device.hpp for device management and src/xr/session.cpp for session lifecycle and spatial transforms.

Frequently Asked Questions

What is the TrEmbedder class in JSAR?

The TrEmbedder class is a C++ virtual interface defined in docs/api/embedder.md that allows host engines (Unity, Unreal, or custom) to embed and control the JSAR runtime. It provides lifecycle hooks including onStart for initialization, onBeforeRendering for per-frame updates, and shutdown handlers for resource cleanup.

How does JSAR handle WebXR session management?

JSAR implements the WebXR specification through the Device class in src/xr/device.hpp, which creates and manages TrXRSession objects. When JavaScript calls navigator.xr.requestSession(), the native Device::requestSession method instantiates a session that handles per-eye view matrices, projection calculations, and frustum culling as implemented in src/xr/session.cpp.

What stereo rendering modes does JSAR support?

JSAR supports four stereo rendering modes defined in the StereoRenderingMode enumeration: Multi-Pass, Single-Pass, Instanced, and SinglePassMultiview. Host engines select the appropriate mode during initialization via xr::TrDeviceInit to optimize for their target graphics API (OpenGL, Metal, or Direct3D).

How many draw calls does JSAR use per frame?

JSAR batches all spatialized DOM elements into ≤10 GPU draw calls per frame, as implemented in the content renderer (renderer/content_renderer.hpp). This optimization ensures high frame rates and stable performance on mobile XR hardware while maintaining visual quality for immersive web content.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →