Shader Complexity Heatmap
The shaderCost view renders the scene with temporary bucketed override materials, then colorizes the result as a heatmap.
Use it to find programs/materials that are likely more complex than their neighbors before you reach for a dedicated GPU profiler.
Native
Section titled “Native”import { DEFAULT_DEBUG_VIEWS, createDebugRenderPlan, createDebugPipelineRuntime, createDebugViewUniforms, resolveDebugViewLayout, updateDebugViewUniforms,} from "threejs-debug-view"
const shaderCostIndex = DEFAULT_DEBUG_VIEWS.findIndex((view) => view.source === "shaderCost")const layout = resolveDebugViewLayout("single")const plan = createDebugRenderPlan(DEFAULT_DEBUG_VIEWS, shaderCostIndex, layout)const uniforms = createDebugViewUniforms()const runtime = createDebugPipelineRuntime(scene, camera, plan, layout, renderer, uniforms)
// Each frame:updateDebugViewUniforms(uniforms, plan.activePipelineView, layout, plan.pipelineViews.length, 1)runtime.pipeline.render()Full walkthrough: Native Runtime. Sample pixels with readHeatmapCostFromCanvas() from the root export.
import { DEFAULT_DEBUG_VIEWS } from "threejs-debug-view"import { DebugViews } from "threejs-debug-view/r3f"
const shaderCostIndex = DEFAULT_DEBUG_VIEWS.findIndex((view) => view.source === "shaderCost")
<DebugViews views={DEFAULT_DEBUG_VIEWS} activeView={shaderCostIndex} layout="single"/>Reading the colors
Section titled “Reading the colors”The heatmap is normalized from 0 to 1:
- Black: no cost signal.
- Green: cheaper materials.
- Yellow: moderate cost.
- Red: high cost.
- White: extreme end of the configured scale.
Click-to-sample inspector
Section titled “Click-to-sample inspector”When the active layout includes shaderCost, the R3F DebugViewLayer shows a legend ramp and a prompt to click the viewport.
- Click a pixel to place a centered crosshair on that screen point.
- The legend marker moves to the decoded heatmap cost for that pixel.
- Measured Overlap and Light Overlap keep their own legend ramps and do not share the shader-cost sampling UI.
This is a viewport inspector aid, not a GPU timestamp query or pass timer.
What increases complexity
Section titled “What increases complexity”The scorer infers active program/material signals into shader-unit buckets that commonly produce heavier shader paths:
- ALU proxy work from material/program profile
- texture samples and dependent texture risk
- texture resolution and bandwidth pressure
- transparency and alpha testing
- physical features such as transmission, clearcoat, sheen, and iridescence
- custom
ShaderMaterialsignals based on uniform count
The raw signal count is normalized against a stable global signal scale and mapped into a bounded bucket material set. That keeps the override pass predictable instead of creating one material per mesh.
R3F explicit panes
Section titled “R3F explicit panes”shaderCost can also be assigned to a viewport pane:
<DebugViews views={DEFAULT_DEBUG_VIEWS} viewportViews={[ { view: "beauty", label: "Beauty" }, { view: "shaderCost", label: "Shader Complexity", resolutionScale: 0.5 }, ]} layout="split-h" paneCount={2} showLabels/>Use a lower resolutionScale when the heatmap is a diagnostic companion rather than the primary view.
Limits
Section titled “Limits”The view answers “which visible programs/materials look more complex?” It does not answer “how many milliseconds did this draw cost?” It also does not parse GLSL/WGSL/TSL source in this version. For timing, validate with browser GPU tooling, renderer stats, or a frame profiler.