Skip to content
Starting the WAMR3D renderer…

Unlock your imagination without limits. Native WebAssembly 3D.

A Rust 3D engine in WebAssembly with the three.js API. Change the import, call init(), keep your code.

WebAssembly Accelerated Modular Rendering 3D

$ npm install wamr3d
  • MIT license
  • No runtime dependencies
  • 567 KiB Wasm
  • Checked against three.js

Migration path

Start from three.js.
Change the import.

Scene, meshes, lights, shadows, materials and math keep their three.js names, properties and chaining. A parity script runs the same code on both libraries and compares every number.

  1. 01

    Change the import

    Point three at wamr3d/compat and call init() once.

  2. 02

    Swap the renderer

    Use WebGPURenderer and await renderer.init(). It draws with WebGPU, or WebGL 2 where WebGPU is missing; WebGLRenderer works too.

  3. 03

    Keep your scene code

    scene.add, position.set, rotation.y +=, lookAt and raycasting work as written for supported classes.

app.js
- import * as THREE from 'three';+ import * as THREE from 'wamr3d/compat';+ await THREE.init();
  const scene = new THREE.Scene();
  const mesh = new THREE.Mesh( geometry, material );
  scene.add( mesh );

- const renderer = new THREE.WebGLRenderer( { antialias: true } );+ const renderer = new THREE.WebGPURenderer( { antialias: true } );+ await renderer.init();

What it is

A native core.
A familiar surface.

Scene graph, transforms, cameras, geometry processing, culling and draw ordering run inside a WebAssembly module. You drive it from JavaScript through three.js-style classes or batched calls.

Rust → WebAssembly

The heavy lifting runs native.

Set transforms as cheaply as in three.js: the object API batches every change into the Rust engine once per frame. When you need more, the low-level API gives you handles, explicit disposal and zero-copy typed-array views of geometry and world matrices.

  • scene graph
  • world matrices
  • frustum culling
  • draw ordering
  • raycasting
  • batched updates

WebGPU + WebGL 2

Its own renderers.

Not a wrapper around three.js. WebGPU where the browser has it, a native WebGL 2 renderer everywhere else, chosen automatically. Meshes, lines, points, sprites and instanced meshes; basic, Lambert and standard materials; colour textures, fog, up to 16 lights, PCF shadows, tone mapping, render targets and 4× MSAA.

Object API

Code that reads like three.js.

Scene, Group, Mesh, InstancedMesh, Points, Sprite, render targets, cameras, eight shapes, six materials, textures, five light types, shadows, tone mapping, Raycaster, Clock and the math types.

Batched API

One call, thousands of objects.

Handles and batched calls such as setRotations; read world matrices and geometry through typed-array views over engine memory, with no copy.

Parity

Checked against.

Over 7,600 reference cases generated from the three.js source are replayed against the Rust code. All 23 renderer cases are compared with stored three.js images.

Roadmap

Coming next.

These features are not supported by either the WebGPU or WebGL 2 renderer yet. Keep three.js for them for now:

  • Light, bump and displacement maps
  • Point-light shadows and area lights
  • Physical extensions and Shader materials
  • Post-processing and cube textures
  • Shadow modes other than PCF
  • Most loaders, animation, audio and controls
  • Lines and points wider than one pixel

CPU + GPU

Turn your browser into
a powerful 3D machine.

Browser 3D usually spends its frame in JavaScript before the GPU sees a single triangle. WAMR3D puts both processors to work: compiled Rust does the scene work on the CPU at native speed, and the GPU receives a frame that is ready to draw, through WebGPU or WebGL 2. No plug-in, no install, just a tab.

01 · CPU, in WebAssembly

Native-speed scene work.

Rust compiled to WebAssembly with SIMD runs the whole scene: no garbage collector pauses, no object churn, data laid out in flat typed memory.

  • Transforms and world matrices, only for what changed
  • Frustum culling and draw ordering
  • Lights, shadows and materials packed into one uniform buffer
  • Thousands of objects moved by one batched call

02 · The boundary

One crossing per frame.

The object API records your changes and sends them to the engine in a batch. Geometry and world matrices are typed-array views over engine memory: read without copying.

  • Zero-copy views of geometry and matrices
  • Vertex and index buffers uploaded only when they change
  • No per-property calls into WebAssembly

03 · GPU, WebGPU or WebGL 2

A frame that is ready to draw.

A thin host turns the prepared frame into GPU commands: pipelines and programs are cached, instanced meshes draw in one call, and the same frame runs on WebGPU or WebGL 2.

  • Cached pipelines, no per-frame rebuilds
  • Instancing, 4× MSAA, PCF shadows, physically based lighting
  • Automatic WebGL 2 fallback on machines without WebGPU

The result: the CPU time three.js spends updating and submitting a scene shrinks, and more of each frame is left to the GPU. How much depends on the scene; the benchmarks show measured medians, including where WAMR3D is slower.

Measured, not promised

Batch the work.
See the difference.

The same scene, 6 renderers, one engine at a time. Medians from a single run on one machine; higher is better in every figure here. Open the showcase to measure yours.

60fps

10,000 separate meshes at 1280×720

WAMR3D batched. three.js ran at 42.4 fps with WebGL and 11.6 fps with WebGPU.

60fps

50,000 instanced bodies at 1280×720

WAMR3D batched. three.js ran at 19.6 fps with WebGL and 20.1 fps with WebGPU; its scene update took 44 to 41 ms against 12.2 ms.

1.4× faster

Draw submission than three.js WebGL

10,000 separate meshes at 1280×720: 13.2 ms of CPU time per frame against 18.3 ms. Computed from the measured medians.

53fps

The same 10,000 meshes on WebGL 2

WAMR3D batched on its WebGL 2 renderer, for machines without WebGPU: 1.3× the frame rate of three.js WebGL (42.4 fps) on the same graphics API.

The showcase scene, four renderers

Sun, belts, clusters and bodies: a four-level scene graph where every rotation changes each frame. Longer bars are better in the two frame-rate charts; the CPU chart below them is marked separately.

Frames per secondfps · 1280×720 · the display caps it at 60Higher is better
WAMR3D batched
60.0
WAMR3D object API
54.7
WAMR3D batched WebGL
53.0
WAMR3D object API WebGL
45.9
three.js WebGPU
11.6
three.js WebGL
42.4
Frames per second at each resolutionfps · same scene, four canvas sizesHigher is better
1280×720
60.0
54.7
53.0
45.9
11.6
42.4
1920×1080
59.4
56.7
53.9
42.1
11.8
36.8
2560×1440
59.6
48.4
40.9
35.0
10.7
39.1
3840×2160
55.3
53.4
52.1
44.7
10.2
19.7
Where the CPU time goesms per frame · 1280×720 · shorter bars mean less workLower is better
WAMR3D batched
15.6
WAMR3D object API
18.0
WAMR3D batched WebGL
18.6
WAMR3D object API WebGL
21.5
three.js WebGPU
84.6
three.js WebGL
23.2

scene update: animation, transforms, world matricesdraw submission: the renderer’s CPU work

This is the cost behind the frame rate above: CPU time past the dashed line cannot fit in a 60 fps frame. GPU time is not measured.

  • WAMR3D · batched API
  • WAMR3D · object API
  • WAMR3D · batched API · WebGL 2
  • WAMR3D · object API · WebGL 2
  • three.js · WebGPU
  • three.js · WebGL
Show as table
Showcase measurements for the selected resolution, mode and body count
Enginefps ▲update ms ▼submit ms ▼draw calls
WAMR3D · batched API60.02.413.29846
WAMR3D · object API54.74.213.89859
WAMR3D · batched API · WebGL 253.02.116.59872
WAMR3D · object API · WebGL 245.94.317.29894
three.js · WebGPU11.65.778.99870
three.js · WebGL42.44.918.39789

Where it is slower: calls that do very little work, such as a single vector operation or one property at a time, cost more than plain JavaScript because crossing into WebAssembly has a fixed cost. Single math calls measured 1.5× to 4.1× slower than three.js. GPU time is not measured; it only shows in the frame rate.

Intel(R) Core(TM) i9-10900X CPU @ 3.70GHz, ANGLE (NVIDIA, NVIDIA GeForce RTX 2070 SUPER, Chrome/155.0.8059.80 headless, undefined.
Fresh 1280×720 measurements; the higher-resolution charts use the earlier same-day run. Raw logs: bench/results/2026-10-15-current.

Live demos

Running in your browser.
Right now.

Every demo below is drawn by WAMR3D, with WebGPU or WebGL 2 depending on your browser, except the three.js engines you choose to compare against.

The demos need a browser with WebGPU, such as current Chrome or Edge.

Get started

Install.
Import. Render.

The package ships the WebAssembly module, the object API, the low-level API and TypeScript typings.

$ npm install wamr3d
Rendering
A browser with WebGPU (current Chrome or Edge). Pages must be served over HTTP, not opened from file://.
Without a renderer
Node.js 18 or newer runs the math and the scene graph.
Resolving the package
Use a bundler or an import map for the wamr3d name.
TypeScript
Typings are included. Set "lib": [ "esnext", "dom" ].
main.js
import * as WAMR3D from 'wamr3d/compat';

await WAMR3D.init();

const scene = new WAMR3D.Scene();
const camera = new WAMR3D.PerspectiveCamera( 50, innerWidth / innerHeight, 0.1, 100 );
camera.position.set( 3, 2, 5 );
camera.lookAt( 0, 0, 0 );

const cube = new WAMR3D.Mesh( new WAMR3D.BoxGeometry(), new WAMR3D.MeshLambertMaterial( { color: 0x4f8cff } ) );
scene.add( cube, new WAMR3D.DirectionalLight( 0xffffff, 2 ), new WAMR3D.HemisphereLight( 0xbfd4ff, 0x30281e, 1 ) );

const renderer = new WAMR3D.WebGPURenderer( { antialias: true } );
await renderer.init();
renderer.setSize( innerWidth, innerHeight );
document.body.appendChild( renderer.domElement );

renderer.setAnimationLoop( ( time ) => {
  cube.rotation.y = time / 1000;
  renderer.render( scene, camera );
} );

Open source · MIT

Bring your scene.
Measure it yourself.

Run your three.js scene on WAMR3D's WebGPU renderer, compare the numbers, then batch the hot paths as your scene grows.