mutracer README

The project's README from github.com/pissang/mutracer. As one markdown file: README.md.

MuTracer

npm version MIT license

MuTracer is a physically based path tracer for the web. It renders on WebGPU and falls back to WebGL 2, with OpenPBR materials, TinyBVH acceleration and denoising built in.

Try the viewer. MuTracer is a 0.x library; APIs may change between minor releases.

Rendered live in the browser. Click an image to open the scene in the viewer.

2025 Yamaha MT-09 SP V4
Yamaha MT-09 · metal and coat · VTX, CC-BY-NC-SA 4.0
Super Human
Super Human · subsurface skin · Davyd Vidiger, CC-BY 4.0
Gelatinous Cube
Gelatinous Cube · transmission · glenatron, CC-BY-NC 4.0
Glass dragon
Dragon · glass · Delatronic, CC-BY 3.0
Blender Cover Remix
Blender Cover Remix · characters · bandinopla, CC-BY 4.0
OpenPBR material spheres
Material spheres · OpenPBR layers · procedural, CC0

Performance

LEGO Technic Bugatti Chiron rendered in MuTracer

The LEGO Technic Bugatti Chiron (VTX, CC-BY-NC-SA 4.0) is a heavy scene that path traces interactively in the viewer:

Bugatti Chiron
Rendered triangles 4.6 M (2.5 M unique, 341 geometries in 560 meshes)
Materials 35
Download 12.8 MB .glb
BVH build, 1 thread 1.7 s (built-in builder) / 1.0 s (TinyBVH WebAssembly)
BVH build, 4 workers (default) 0.50 s (built-in builder) / 0.28 s (TinyBVH WebAssembly)
1920 × 1080, 64 spp 12.1 s path tracing, 12.4 s with OIDN denoising

Measured on an Apple M4 Max: rendering in Chrome with WebGPU at the viewer's default settings (6 bounces), BVH builds on CPU threads excluding worker startup.

Install

npm install @meimu/mutracer

Install three to load glTF with GLTFLoader + THREESync or to use the Three.js helpers. The quick start below also uses gl-matrix for matrix math.

The package is ESM with TypeScript declarations (lib/main.js). A UMD build with a MuTracer global is at dist/mutracer.umd.js; deploy its dist/assets/ alongside it. See loading and deployment.

Quick start

With Three.js

Build or load a Three.js scene as usual and let THREESync keep MuTracer in sync with it. RenderLoop renders progressively and calls the sync each frame.

import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { Engine, RenderLoop, THREESync } from '@meimu/mutracer';

const canvas = document.querySelector('canvas')!;

const scene = new THREE.Scene();
scene.add((await new GLTFLoader().loadAsync('model.glb')).scene);

const camera = new THREE.PerspectiveCamera(40, canvas.width / canvas.height, .1, 100);
camera.position.set(0, 2, 6);
const controls = new OrbitControls(camera, canvas);

const engine = new Engine({ canvas });
const sync = new THREESync(engine, { scene, camera, controls });
const loop = new RenderLoop(engine, { sync });
loop.start();

Custom Three.js materials and lights can use convertMaterial / convertLight in the same sync options; see Three.js integration.

With MuTracer scene classes

Describe meshes, materials, lights and the camera directly with MuTracer.

import { mat4 } from 'gl-matrix';
import {
  Camera, EnvironmentLight, Mesh, OpenPBRMaterial, RectLight, Engine, RenderLoop,
  Texture, createPlaneGeometry, createSphereGeometry, type Scene
} from '@meimu/mutracer';

const canvas = document.querySelector('canvas')!;

const red = new OpenPBRMaterial({ baseColor: [0.8, 0.1, 0.1], specularRoughness: 0.3 });

const ball = new Mesh(createSphereGeometry(64, 32), red);
mat4.fromTranslation(ball.transform, [0, 1, 0]);

const floor = new Mesh(createPlaneGeometry(), new OpenPBRMaterial());
mat4.fromScaling(floor.transform, [5, 1, 5]);

// Rect lights emit along their local -Z axis.
const light = new RectLight({ size: [2, 2], color: [1, 1, 1], intensity: 5 });
mat4.fromTranslation(light.transform, [0, 4, 0]);
mat4.rotateX(light.transform, light.transform, -Math.PI / 2);

// A constant grey sky as a 1x1 float environment map.
const sky = new Texture({ data: new Float32Array([0.3, 0.3, 0.3, 1]), width: 1, height: 1 });

const scene: Scene = {
  meshes: [ball, floor],
  lights: [light, new EnvironmentLight({ map: sky })]
};

const camera = new Camera({ projection: 'perspective', fov: 40, near: .1, far: 100 });
camera.position = [0, 2, 6];
camera.lookAt([0, 1, 0]);

const engine = new Engine({ canvas });
engine.on('error', error => console.error(error));
engine.scene = scene;
engine.camera = camera;
const loop = new RenderLoop(engine);
loop.start();

For many copies of one geometry, use InstancedMesh with shared materials and a Float32 matrix array. THREESync converts Three.js instancing into the same batches automatically. See the instancing example.

Rendering options

Enable denoising on an individual result:

loop.final.setDenoiser({ type: 'jnlm' }); // required type; call again to change it
loop.final.setDenoiser({ type: 'oidn', model: 'standard' }); // WebGPU; custom weightsUrl also supported
// Third-party WebGPU filters: { type: 'custom', denoiser: yourCallerOwnedDenoiser }.

Borrow WebGPU output with render.getOutputTexture('hdr') or render.getOutputTexture('display'), or add a display overlay through engine.display.addPostProcess(pass). Passes also run during PNG export; see the overlay example and texture lifetime.

Batch quality or display edits use Object.assign(loop.final, settings) or Object.assign(engine.display, settings), typed with RenderSettings or DisplaySettings; see the setting defaults.

RenderLoop provides preview and final results. For custom frame control, use Engine / Render / Display directly with a fallback, or a fixed-size invisible render for export. See independent render results.

Documentation

Guide Contents
Getting started Scene model, render loop, lights, export
Scene updates Editing a rendering scene, animation, update costs
API reference Engine, Display and Render options, methods and scene types
Materials OpenPBR inputs, textures, alpha, bevel, displacement
Three.js THREESync, supported materials and lights, userData keys
Denoising JNLM and OIDN denoisers, OIDN weights hosting, denoising while converging
Sampling Patterns, dimensions and progressive budgets
BVH BVH builders, TinyBVH layouts, worker preparation
Performance Tuning checklist
llms.txt Compact usage guide for coding agents

The examples cover a procedural scene, a Three.js/glTF scene and an OpenPBR gallery with denoising and PNG export. Run them with:

npm install
npm run dev

Browser support

The first frame compiles large shaders, which can take several seconds on some platforms; refine() reports the progress through its return status.

Contributing

Pull requests are not accepted at the moment; please open an issue instead. See CONTRIBUTING.md for development setup, required checks and GPU validation.

License

MuTracer is released under the MIT License. It includes third-party code and data under MIT, Apache-2.0 and CC0 licenses; see THIRD_PARTY_NOTICES.md.

For host WebGPU integration, supply your own GPUDevice.

Render on an OffscreenCanvas in a Worker with the worker integration guide.

mutracer (pissang/mutracer)

pbr
Searching Database…

What is this?MuTracer is a physically based path tracer for the web: it renders scenes with WebGPU (falling back to WebGL 2) using OpenPBR materials, TinyBVH acceleration and built-in denoising.
Is this a standalone renderer or a library I embed?It is a JavaScript/TypeScript library you install and embed in a web page, shipped as an ESM build with TypeScript declarations plus a UMD bundle, rather than a standalone app.
How do I add it to a web project?Build or load a Three.js scene as usual and let THREESync keep MuTracer synced to it while RenderLoop renders progressively each frame, or describe meshes, materials, lights and the camera directly with MuTracer's own scene classes.
Do I need Three.js to use it?No - MuTracer has its own scene model - but Three.js (for GLTFLoader + THREESync) and gl-matrix (for matrix math) are needed if you want to load glTF scenes or reuse Three.js materials and lights.
Is the API stable yet?No, it is still pre-1.0, so APIs can change between minor releases.
Does it require WebGPU, or can it run without it?WebGPU is recommended and required for features like OIDN denoising and HDR output, but it automatically falls back to WebGL 2 (which needs the EXT_color_buffer_float extension) on browsers without WebGPU support.
What is OpenPBR and why does it matter here?OpenPBR 1.1 is the physically based surface model MuTracer implements, covering F82 metals, anisotropic GGX specular and coat, thin-film interference, dispersive transmission with absorption, and subsurface scattering - giving accurate materials out of the box.
Can it handle heavy, high-poly scenes?Yes - a two-level BVH built with binned SAH in parallel Workers (a TinyBVH port or its WebAssembly builder) lets it path trace interactively; a 4.6M-triangle Bugatti Chiron model renders at 1920x1080, 64spp in about 12 seconds on an Apple M4 Max.
Is denoising included?Yes, it offers albedo- and normal-guided denoising using Intel Open Image Denoise on WebGPU, or a joint non-local means filter that works on both WebGPU and WebGL 2.
Can I export the final render?Yes, converge() produces a deterministic, variance-adaptive final frame with tone mapping (AgX, ACES, Cineon, Reinhard, linear), color grading, bloom, depth of field and transparent backgrounds, and these effects also apply during PNG export.
Creator
pissang
Size
35.8 MB
Stars
144
Age
18d
Commits · 90 Days