# MuTracer > MuTracer is a progressive, physically based path tracer for the browser. npm package `@meimu/mutracer`. WebGPU with a WebGL 2 fallback, OpenPBR 1.1 materials, BVH acceleration (JavaScript or TinyBVH WebAssembly), and denoising with a built-in JNLM filter or Intel Open Image Denoise (OIDN). MIT licensed. ## Essentials - Interactive loop: `new RenderLoop(engine, {sync, preview?, final?}).start()`; manual `tick()` is an alternative. Preview events/counts are excluded; [loop policy](docs/progressive-rendering.md#loop-policy). - Custom integrations: extend Sync(engine), write inputs in update, report activity through active; [API](docs/api.md#renderloop-and-sync). - Install: `npm install @meimu/mutracer` (+ `three` for Three.js integration). ESM and TypeScript; [quick start](docs/getting-started.md). - First layer: `new Engine({canvas})`, set `engine.scene` / `engine.camera`, then create `new Render(engine, options?)`; [independent renders](docs/progressive-rendering.md). - Frame: update controls/source adapters, call `engine.update()` once, `current.refine()`, then `engine.display.present(current)`. Only update detects/commits external edits; rendering compares revisions. - Sample batching: `batchSamples` (default false) lets one refine submit several samples; with `final: {batchSamples: true}`, RenderLoop also refines between display frames. Custom loops use `render.refineUntil(deadline, {signal})` before `present()`. [Two levels](docs/progressive-rendering.md#sample-batching). - Caustics: `render.caustics = {lightTracing:true, photonMapping:true}`; experimental, off by default and WebGPU only. [Guide](docs/caustics.md), [options](docs/api.md#caustics). - Batch settings: `Object.assign(render, settings)` / `Object.assign(engine.display, settings)` with `RenderSettings` / `DisplaySettings`; constructor options configure Render. [Defaults](docs/api.md). - Ownership: Engine owns shared scene/device/BVH/atlas; Display owns tone mapping, bloom, HDR and channel; Render owns resolution, quality, samples, fallback and denoiser. `transmissiveOutputAlphaMin` is a Render option; [API](docs/api.md). - Initialization: optional `await engine.init()` gives backend, adapter and limits; `backend: 'auto'` defaults to WebGPU with WebGL fallback. `webgpu` requires WebGPU and `webgl` forces WebGL 2. - Texture bindings: `new Engine({textureBinding:'auto'})` tries experimental WebGPU bindless textures with atlas fallback; default `'atlas'`. [Requirements and budget](docs/api.md#texture-bindings). - Status: each render exposes `status`, `samples` and `converged`; statuses are preparing, compiling, converging, denoising, finished and error. The first layer leaves frame selection/UI status to the application. - Scene: plain `{meshes, lights, background?}`; meshes use world `transform`. `engine.update()` detects assignments, materials and lights; in-place array/pixel edits need `markUpdated()` or dedicated geometry/instance notifications; [updates](docs/updates.md). - Conversion hooks: `convertMaterial` / `convertLight` on `FromTHREESceneOptions` and `THREESyncOptions`; undefined uses defaults, null skips; sync caches by source identity/version. [Ownership and light revisions](docs/threejs.md#custom-conversion). - Three.js: use `new THREESync(engine, {scene?, camera?, controls?})` with RenderLoop; use GLTFLoader for glTF. [Three.js](docs/threejs.md). - Preview: explicit `new Render(engine, {resolutionScale:.5, adaptiveResolution:{targetFrameMs:20,minScale:.25},bounces:2,bevel:false,dispersionWavelengths:1,directLightCandidates:2})`. Give the final render `fallback: preview`; [fallback rules](docs/progressive-rendering.md#caveats). - Denoising: required `type`, frozen `render.denoiser` getter; `await render.setDenoiser({type:'jnlm'})` (both backends) or `{type:'oidn',model:'standard'}` (WebGPU, ignored with a warning on WebGL). Schedules: converged (default), progressive, every-sample; [denoising](docs/denoising.md). - Sampling: `render.samplingPattern` is sobol (default), zsobol or random; [sampling](docs/sampling.md). - GPU output/overlays: `render.getOutputTexture('hdr'|'display')` and `display.addPostProcess(pass)` are WebGPU-only; present/export run display passes, raw readbacks do not. [Formats and lifetime](docs/post-processing.md). - Export: `await final.converge({samples:64,sampleSequenceOffset:0,camera?})`, `await final.readPixels('hdr')`, `await engine.display.toBlob(final)`. Fixed width/height are constructor-only; Blob uses render dimensions and leaves canvas unchanged. - Concurrency: converge blocks refine only on that render, copies its camera override, and rejects when engine input revisions change. readPixels and present always select the requested result. - BVH: automatic CPU preparation; optional `await engine.prepare(nextScene,{onProgress?,signal?})` leaves current GPU scene unchanged. Engine BVH options rebuild at the next update; [BVH](docs/bvh.md). - Events: `engine.on('progress'|'error'|'devicelost',listener)` returns unsubscribe; per-render samples/status are polled. Async errors use error events; [API](docs/api.md). - AOVs: `Render.aovs` + `aovSamples` (16); independent surface outputs, integer IDs and per-render motion; [formats and costs](docs/api.md#surface-outputs). Custom filters request `aovs` separately. `Display.channel` visualizes explicitly enabled kinds with [display-only mappings](docs/api.md#aov-visualization). - Depth: `await engine.readDepthSamples({x,y,size})` uses the committed engine camera; [depth sampling](docs/api.md#depth-sampling). - Procedural sky: `new ProceduralSky({resolution:[2048,1024],time:15})` for `EnvironmentLight.map` or backgrounds; Three.js uses the sync's `environment` option. [Options and limits](docs/api.md#proceduralsky). - Materials, lights, geometry: OpenPBR constructor parameters, rigid analytic-light transforms and itemSize=1 raw materialIndex; [materials](docs/materials.md), [getting started](docs/getting-started.md). - Release resources with `render.dispose()` or `engine.dispose()` for everything; `sync.dispose()` releases adapter caches. ## Common pitfalls - Texture pixel and geometry array edits need a notification; material parameters do not. See [updates](./docs/updates.md#what-to-do-after-a-change). - Use `geometry.markVerticesUpdated()`, not `markUpdated()`, for vertex animation; neither recomputes normals. - `createPlaneGeometry()` is 2×2 in XZ; `createSphereGeometry()` and `createCubeGeometry()` span -1..1. Scale with `transform`. - Rect lights are one-sided; rotate them to face the scene. - Custom denoisers: `{type:'custom',denoiser}` on WebGPU; caller owns disposal; tightly packed native HDR RGBA32F input/output before display composition, linear albedo and 0–1 normal guides; [lifecycle and buffers](docs/denoising.md#custom-denoisers). - OIDN: packaged `model` or custom `weightsUrl` through `render.setDenoiser()`; `/oidn` only exports weight constants/URL helpers. OIDN and HDR output require WebGPU; [options](docs/denoising.md#custom-oidn-networks). - Call `engine.display.setSize` with drawing-buffer pixels ; camera aspect is automatic unless explicitly set. - Changing `bvhBuilder`, `bvhLayout` or `bvhSpatialSplits` automatically rebuilds existing static BVHs on the next engine.update(). - In Vite development, add `optimizeDeps: { exclude: ['@meimu/mutracer'] }` so bundled assets load. ## Host integration - Caller-owned GPUDevice: pass `device` to Engine; obtain a descriptor with `getRecommendedDeviceDescriptor(adapter)`. Ownership and limits: [API](docs/api.md#device-requirements). - Device loss: shared WebGPU devices recover lazily; caller-owned devices require `await engine.setDevice(replacement)` then update. Active convergence rejects. [API](docs/api.md#device-loss). - OffscreenCanvas/Worker: Engine defaults to OffscreenCanvas without document; textures accept ImageBitmap/OffscreenCanvas. Forward visibility and controls; [worker guide](docs/getting-started.md#running-in-a-worker). ## Docs - [Getting started](docs/getting-started.md): scene model, render loop, geometry, cameras, lights, export, bundlers - [Scene updates](docs/updates.md): what to call after each kind of edit, animation, update costs - [API reference](docs/api.md): Engine, Display, Render, RenderLoop and Sync options and defaults, methods, statuses, scene types, helpers - [Materials](docs/materials.md): OpenPBR inputs, textures, alpha, bevel, displacement - [InstancedMesh](docs/updates.md#instanced-meshes): shared geometry/materials, active count, local Float32 transforms/colors; markUpdated notifies both arrays, markTransformsUpdated/markColorsUpdated avoids unrelated work; then engine.update. - [Three.js](docs/threejs.md): `THREESync`, conversions, skinning, instancing, `userData` keys - [Denoising](docs/denoising.md): JNLM and OIDN, options, weight hosting, denoising while converging - [Sampling](docs/sampling.md): choosing a sampling pattern - [BVH](docs/bvh.md): automatic and up-front preparation, builders, layouts - [Performance](docs/performance.md): tuning checklist; resident target dimensions/bytes and complete-image presentation diagnostics - [Examples](https://github.com/pissang/mutracer/tree/main/examples): basic, Three.js, animation, advanced