Skip to content

Custom Debug Views

Custom views register on the native root export. Pass the same view list into createDebugRenderPlan() or, with the optional R3F adapter, into DebugViews.

For simple shader debugging, add a DebugView object with a TSL node:

import { float, vec4 } from "three/tsl"
const constantView = { label: "Constant", node: float(0.5), mode: "depth" as const, scale: 1 }
const colorView = { label: "Custom Color", node: vec4(1, 0, 0, 1), mode: "passthrough" as const }

When React may recreate the node instance between renders, prefer createCustomDebugView() with a stable id.

import { float, vec4 } from "three/tsl"
import {
createCustomDebugView,
DEFAULT_DEBUG_VIEWS,
createDebugRenderPlan,
createDebugPipelineRuntime,
createDebugViewUniforms,
resolveDebugViewLayout,
} from "threejs-debug-view"
const fresnelView = createCustomDebugView({
id: "shader:fresnel",
label: "Fresnel",
node: vec4(float(1), float(0), float(0), float(1)),
})
const views = [...DEFAULT_DEBUG_VIEWS, fresnelView]
const plan = createDebugRenderPlan(views, views.length - 1, resolveDebugViewLayout("single"))
const runtime = createDebugPipelineRuntime(
scene,
camera,
plan,
resolveDebugViewLayout("single"),
renderer,
createDebugViewUniforms(),
)

See Native Runtime for the full frame loop.

import { float, vec4 } from "three/tsl"
import {
createCustomDebugView,
DEFAULT_DEBUG_VIEWS,
} from "threejs-debug-view"
import { DebugViews } from "threejs-debug-view/r3f"
const fresnelView = createCustomDebugView({
id: "shader:fresnel",
label: "Fresnel",
node: vec4(float(1), float(0), float(0), float(1)),
})
<DebugViews views={[...DEFAULT_DEBUG_VIEWS, fresnelView]} />

The viewport render graph uses the stable id in pass keys. The compose runtime also includes custom node identity in its pipeline key, so replacing a custom node instance with the same label/source still rebuilds the pipeline instead of rendering stale shader code.