JavaScript + WebAssembly: Synergy for High-Performance XR Experiences

What has actually shifted in the browser XR stack since 2023 — from SIMD as the baseline to Wasm 3.0 — and where WebAssembly genuinely pays off in real projects.

JavaScript + WebAssembly: Synergy for High-Performance XR Experiences

In XR development, I intentionally rely on the duo of JavaScript and WebAssembly: WebAssembly brings computational power, JavaScript brings agility. Since this article was first published, the browser stack has shifted considerably — SIMD is now the baseline, Wasm 3.0 is the living standard, and WebGPU has shipped in all major browsers since late 2025. Time for a reality check: what has proven itself, and what was hot air?

Why two languages at all?

The division of labor is simple and has held up in practice:

  • JavaScript orchestrates: scene graph, UI, networking, DOM — wherever fast iteration and browser proximity matter.
  • WebAssembly computes: compiled bytecode with predictable performance, ideal for physics, tracking, mesh processing — and as a target for existing C/C++ and Rust libraries.

WASM doesn't replace JavaScript. It takes over the hot paths that would otherwise blow the 16 ms frame budget — at 90 Hz, more like 11 ms.

What has happened since 2023

WASM SIMD is the baseline

SIMD (Single Instruction, Multiple Data) processes multiple data points per instruction — exactly what the matrix and vector math in XR constantly needs. All major browsers have supported WASM SIMD for about three years, and the engines have followed suit:

  • Godot 4.5 ships web exports with SIMD enabled only. The project's own Jolt physics benchmarks paint a realistic picture: 1.5–2× performance in typical scenes — and in stress tests where the physics engine without SIMD tips into the "spiral of death" of single-digit frame rates, up to 10–14× more resilience. The latter isn't raw compute speed, but headroom against frame drops — for XR, exactly the case that matters.
  • Wonderland Engine has dropped non-SIMD exports entirely — SIMD is considered a given now.

Wasm 3.0 and WasmGC

Since September 2025, WebAssembly 3.0 is the living standard. The additions that matter for XR:

  • WasmGC (long since default in Chrome, Firefox and Safari): languages with their own garbage collection — Kotlin, Java, Dart, C# — compile to WASM without shipping a bundled GC runtime. That significantly widens language choice for the XR core's backend.
  • Memory64: address space beyond the 4 GB boundary (up to 16 GB on the web). Relevant for large point clouds, voxel data or CAD models.
  • JSPI (phase 4, shipping in Chrome 137+ and Firefox 139+): synchronous WASM code can call asynchronous web APIs — the Asyncify detour and its overhead become unnecessary.

WebGPU has arrived across the board

Late 2025 was the moment: WebGPU runs by default in Chrome, Edge, Firefox (Windows since 141, macOS since 145) and Safari 26 — including visionOS 26 on Vision Pro. For XR, what counts:

  • Native compute shaders — physics and image processing directly on the GPU
  • Multiview rendering — both eyes in a single pass instead of double draw-call overhead
  • Significantly lower CPU overhead per draw call than WebGL — noticeable in XR scenes with many objects

WebGL 2 remains the fallback baseline; the common engines and three.js handle the switch themselves.

The hardware side

  • Meta Quest: the in-headset browser is the most complete WebXR platform — including hand tracking and passthrough AR.
  • Apple Vision Pro: WebXR in Safari enabled by default since visionOS 2; visionOS 26 adds WebGPU.
  • Android: Chrome ships WebXR AR natively.
  • Desktop Chrome: WebXR still not enabled by default — for development and testing, the Immersive Web Emulator remains the tool of choice.

Who ships what: the framework landscape in 2026

  • De-Panther WebXR Export: the Unity path to the web is alive and maintained (latest release 0.25, May 2026; Unity 6 from 6000.0.23f1). Unity's WebGL build is WASM + JS glue anyway — the package adds the WebXR device API.
  • Needle Engine: actively developed (version 5 with MaterialX and OpenUSD support). Worth knowing: Needle does not compile C# to WASM — it's a TypeScript runtime on three.js with Unity and Blender integrations, shipping WebXR, multiplayer and AR for Quest, Vision Pro and mobile out of the box.
  • Rogue Engine: a Unity-like environment built on three.js — and a nice real-world WASM example: its physics integration runs on Rapier, a physics engine written in Rust and compiled to WASM.
  • Godot web export: WASM + SIMD by default since 4.5 — the most pragmatic route for small to mid-sized WebXR games.

And an honest reckoning: Mozilla Hubs — long the flagship project for social WebXR — was discontinued in May 2024. The code lives on as a Community Edition, but the project is a reminder: a solid WebXR stack is no guarantee of a viable product.

Proven combinations in practice

Two patterns have proven durable across my projects.

Pattern 1: three.js + WASM physics

Rendering stays in three.js (JavaScript), physics runs as a WASM module — shown here with the real-world example of Rapier (Rust → WASM; the -compat package loads the WASM module without bundler configuration):

import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import RAPIER from '@dimforge/rapier3d-compat';

await RAPIER.init(); // load the WASM module
const world = new RAPIER.World({ x: 0, y: -9.81, z: 0 });

const gltf = await new GLTFLoader().loadAsync('model.glb');
scene.add(gltf.scene);
// create rigid bodies and colliders for the meshes,
// then per frame: world.step() + read back mesh poses

Pattern 2: Babylon.js + WASM compute core

A classic: per-frame pathfinding or hand-tracking evaluation — too expensive for JS, trivial for WASM. wasmModule here stands for the instantiated exports of a WASM module (e.g. via WebAssembly.instantiateStreaming or wasm-bindgen):

import { TransformNode, Vector3 } from '@babylonjs/core';

const actor = new TransformNode("actor", scene);

scene.onBeforeRenderObservable.add(() => {
  // calculatePath() returns the next waypoints from the WASM core
  const path = wasmModule.calculatePath(actor.position, target);
  actor.position = Vector3.Lerp(actor.position, path[0], 0.1);
});

The Unity route looks similar — here's a condensed example using WebXR Export:

using UnityEngine;
using WebXR;

public class WebXRTest : MonoBehaviour
{
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            WebXRManager.Instance.ToggleVR();
        }
    }
}

When WASM pays off — and when it doesn't

Worth it for: physics simulations, mesh operations, tracking algorithms, decoders, CAD kernels — anything that measurably eats into the per-frame budget.

Not worth it for: UI logic, scene graph management, event handling. And watch the JS/WASM boundary: calls are cheap but not free. Crossing the boundary per object per frame often eats the gains right back — batch calls on typed arrays instead.

A rule of thumb from practice: prototype in JS first, measure the hot paths in the profiler, then port selectively. Anyone working the other way around is optimizing blind.

Practical notes

  • Threads need isolation: SharedArrayBuffer — and with it WASM threads — is only available with COOP/COEP headers set (cross-origin-isolated). Without them, everything runs single-threaded.
  • Think of memory as separate: WASM has explicit linear memory — allocation and freeing are the module's job. For large data blocks crossing between both sides: SharedArrayBuffer or passed typed arrays, not object marshalling.
  • Assets are the real bottleneck: in most WebXR projects, it's not the CPU that decides success but download size. Compression (Draco, KTX2) and streaming beat any micro-benchmark.

Frequently asked questions

Can WebAssembly and JavaScript be combined?

Not only can they — it's the recommended way of working: JS for UI/UX and fast iteration, WASM for physics, ML and CAD cores. Example: a three.js scene graph plus WASM physics.

Which frameworks support both sides?

  • three.js (WASM modules import directly; Rapier as the physics standard)
  • Babylon.js (native WASM plugins, built-in WebXR support)
  • Unity WebGL (via WebXR Export) and Godot (web export with SIMD)

What does memory management between JS and WASM look like?

  1. SharedArrayBuffer for large data blocks
  2. JS-side garbage collection for UI elements
  3. WASM memory allocated and freed explicitly

Two worlds — one goal

The future of XR development isn't about either/or decisions, but about intelligently combining both technologies. WebAssembly and JavaScript are like a developer's two hands — together they achieve more than either could alone. In my work, one thing has proven true: only their synergy enables truly immersive experiences. Anyone building WebXR today gets a foundation with Wasm 3.0 and WebGPU that was still a pipe dream three years ago.

Update September 2026: This article has been revised — updated to the current state of WASM SIMD, Wasm 3.0/WasmGC, JSPI and WebGPU; outdated references (including Mozilla Hubs) corrected and unverifiable benchmarks removed.

Sources


Planning a WebXR project or fighting a performance bottleneck in your existing stack? Write to ingmar@konnow.de or reach out via LinkedIn — a first look at the architecture costs nothing.

0%
6 min read