SuperAPI AI Directory

threejs-performance

General & other

View repository

Updated 10-16 08:43

About this skill

Three.js performance optimization guide

Golden rules

Draw calls < 100 per frame. Triangle count matters less than draw call count; beyond 500 draw calls, even a powerful GPU struggles. Use renderer.info.render.calls monitoring (for the full monitoring code, see references/examples.md)。

Diagnostic decision tree

Symptom Check first Go deeper
Dropped frames, stuttering renderer.info.render.calls Is it >100 Instancing/batching (below), post-processing chain
Memory keeps growing renderer.info.memory Are the geometries/textures counts only increasing, never decreasing dispose iron rule (below),references/examples.md full cleanup code
Slow loading, VRAM blowing up Are models uncompressed references/assets.md Compression pipeline
Particle/physics bottleneck Are CPU particles >50,000 WebGPU compute shaders,references/webgpu.md
R3F project re-rendering inexplicably Is there setState inside useFrame R3F three rules (below)

Quick reference of rules by area

WebGPU: when to migrate

Draw-call-heavy dropped frames / need compute shaders for physics particles (CPU limit around 50,000, GPU can reach millions) / complex post-processing chain stuttering. WebGPURenderer must await renderer.init(). TSL compiles once to both WGSL/GLSL automatically. Browser support baseline (data at time of recording, check caniuse for current status): Chrome/Edge 113+, Firefox 141+, Safari 26+. Initialization fallback, full TSL guide, compute shader examples:references/webgpu.md。

Draw call optimization

  • Large numbers ofidenticalgeometries (trees, rocks) → InstancedMesh: 1,000 trees = 1 draw
  • Multipledifferentgeometries sharing a material → BatchedMesh
  • Static small objects → mergeGeometries merge
  • Materialsharing and reuse, never in a loop new Material

Asset compression (benefit numbers)

  • Draco geometry compression: 90-95% size reduction
  • KTX2 textures: GPU memory reduced by about 10x
  • One command:gltf-transform optimize model.glb out.glb --texture-compress ktx2 --compress draco
  • Decoder path configuration and Meshopt/Draco selection:references/assets.md

Memory iron rules

Three.js does not automatically reclaim GPU resources. Removing an object requires: geometry.dispose() + traversing all texture properties of the material and disposing each one + material.dispose(); GLTF's ImageBitmap also needs texture.source.data.close(). Use an object pool for frequently added/removed objects. Full code:references/examples.md。

Three shader rules

① Mobile precision mediump float(about 2x faster) ② Use mix/step instead of if branches (branches break GPU parallelism) ③ Pack data into vec4 to fetch 4 values per texture lookup.

Lighting and shadow budget

Active lights ≤3; PointLight shadows = 6 shadow map renders per light; texture size 512-1024 on mobile, 1024-2048 on desktop; static scenes shadowMap.autoUpdate = false trigger manually + bake lighting.

React Three Fiber Three Rules

① Animation goes through useFrame direct ref modification,never setState inside useFrame ② Static scenes use frameloop="demand" + invalidate() on-demand rendering ③ Toggle visibility via visible property, do not conditionally mount {show && <Model/>}(remounting rebuilds resources). Complete optimization template:references/examples.md。

Post-processing selection

WebGL → pmndrs/postprocessing(merge multiple effects with EffectPass); WebGPU → native TSL post-processing pipeline. Full setup for both:references/examples.md。

Acceptance criteria (self-check before completing an optimization task)

  • renderer.info.render.calls < 100 (exceeding requires giving a scene rationale)
  • renderer.info.memory count falls back after adding/removing objects, no monotonic growth
  • target device measured frame rate meets the target (test on mobile too, not just desktop)
  • published assets go through the Draco/KTX2 compression pipeline
  • every optimization suggestion corresponds to a measured symptom of the user's scene, not a checklist-style blanket application

What not to do

  • General performance issues at the React component layer (memo/useMemo/bundle) → vercel-react-best-practices
  • Three.js basics teaching, scene setup introduction
  • Model/texture art production itself (only handles loading and rendering performance)
  • Optimizing before measuring: when there is no renderer.info or frame rate data, install monitoring first before starting

Known pitfalls

Pitfall Specific manifestation Countermeasure
Incomplete dispose Only the geometry is disposed, all textures attached to materials leak Traverse material properties and dispose each one; GLTF ImageBitmap also needs close()
setState inside useFrame Triggers a React re-render every frame, frame rate plummets Modify refs directly; only update state in interaction events
Switching models via conditional mount {show && <Model/>} geometry and textures are rebuilt every time Toggle the visible property
Forgetting invalidate in demand mode The camera moves but the picture does not, mistaken for "frozen" Call invalidate() in the interaction callback
new Material inside a loop 1000 meshes with 1000 materials, batching completely fails Hoist the material outside the loop and share it
Casually adding shadows to PointLight One point light does 6 shadow renders, mobile gives up immediately Prefer SpotLight/DirectionalLight shadows, keep only one point light shadow

Debugging tools

stats-gl(FPS/CPU/GPU)· lil-gui(real-time parameter tuning) · Spector.js(WebGL frame capture) · three-mesh-bvh(80k+ faces @60fps raycasting) · r3f-perf(R3F monitoring)

Reference docs (load on demand)

File When to read
references/webgpu.md When migrating to WebGPU / writing TSL / compute shaders
references/assets.md When configuring the compression pipeline, LOD, progressive loading
references/examples.md When you need complete paste-ready code (monitoring/memory cleanup/object pooling/particles/R3F templates/post-processing/context loss recovery)