--- name: webgl-frame-profiling description: Finding what actually costs a frame — draw calls, overdraw, shader cost — using Chrome DevTools and Spector.js. when_to_use: You are the performance engineer on a three.js/WebGL team and a scene is dropping frames. tags: [threejs, profiling] --- # Find the frame's real cost before changing the scene A dropped frame has a small number of possible causes and they are distinguishable in minutes. Guessing usually leads to optimising geometry when the cost was overdraw, or the reverse. ## The order 1. **CPU or GPU?** Chrome DevTools Performance panel: record 5 seconds of the stall. Long scripting bars mean the CPU is the problem (scene-graph traversal, matrix updates, garbage). A near-idle main thread with dropped frames means the GPU is. 2. **How many draw calls?** `renderer.info.render.calls`. Anything in the thousands is the answer on its own — instance, merge, or batch by material. `renderer.info` is free and should be on screen during development. 3. **Overdraw?** Spector.js captures a frame and lists every GL call in order. Transparent objects drawn back-to-front over the whole viewport are the usual culprit; the same scene with `transparent: false` running fast confirms it in one test. 4. **Shader cost?** Only now. Halve the canvas resolution: if the frame time halves, you are fragment-bound and the shader (or overdraw) is the cost. If it does not move, you are not. ## The resolution test is the cheapest diagnostic `renderer.setPixelRatio(1)` versus `2` changes fragment work 4× and geometry work not at all. That one toggle separates vertex/CPU cost from fragment cost faster than any profiler, and it needs no tooling. ## What Spector.js is for It is a *capture*, not a sampler: one frame, every call, with state at each step. Use it to answer "what is this frame actually doing" — unexpected state changes, redundant binds, a texture uploaded per frame — not to measure time. For time, use the DevTools timeline. ## Measure the steady state The first seconds include shader compilation, texture upload and JIT warmup. three.js compiles a material's program on first use, so a stutter the first time an object becomes visible is a compile, not a leak. Pre-warm with `renderer.compile(scene, camera)` and profile after. Record the device and the pixel ratio with any number you report. A frame time without a resolution is not a measurement.