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.
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?
- SharedArrayBuffer for large data blocks
- JS-side garbage collection for UI elements
- 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
- Upcoming (serious) Web performance boost — Adam Scott, Godot Engine (2025)
- Wasm 3.0 Completed — WebAssembly Community Group (2025)
- WebGPU is now supported in major browsers — web.dev / Chrome for Developers (2025)
- Introducing the WebAssembly JavaScript Promise Integration API — V8 Team (2025)
- Mozilla Hubs Is Shutting Down, Will Be Handed To Community — David Heaney, UploadVR (2024)
- Is Memory64 actually worth using? — SpiderMonkey Blog, Mozilla (2025)
- De-Panther/unity-webxr-export — GitHub repository
- Needle Engine Documentation — Needle (2026)
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.