Description
Calling .toInspector() on the result of Fn() can silently fail to register an InspectorNode in three@0.185.1. The same expression registers successfully when wrapped with .toVar() or .debug() before .toInspector().
This was noticed when a node was missing from the Inspector Viewer. The registration failure has been independently reproduced at the node-builder setup stage without a browser or GPU. The reproduction below deliberately uses Fn(() => uv().x) to avoid dependencies on a larger scene, custom materials, or shader math.
Source analysis
In the installed version, makeValue() returns a VarNode with intent === true.
inspector() attaches an InspectorNode through node.before(...).
Node.build() processes the attached _beforeNodes.
VarNode.build() directly returns this.node.build(...) when the node is an unassigned intent variable, bypassing super.build(...) and the attached before-nodes.
This appears to explain why .toVar('value') works (an explicit non-intent VarNode) and .debug() also works (a DebugNode wrapper). The issue seems to concern preserving before-node behavior when forwarding an intent variable to its underlying node.
Reproduction steps
- In a scratch directory, install
three@0.185.1 (npm install three@0.185.1).
- Save the following code as
repro.mjs.
- Run
node repro.mjs.
- Compare the number of registered
InspectorNode instances. The Fn case returns 0, while the direct UV, explicit variable, and debug-wrapper cases return 1.
Code
import * as THREE from 'three/webgpu';
import { Fn, uv } from 'three/tsl';
const makeValue = () => Fn(() => uv().x)();
const cases = {
uv: () => uv().toInspector('uv'),
Fn: () => makeValue().toInspector('Fn'),
'Fn + toVar': () => makeValue().toVar('value').toInspector('Fn + toVar'),
'Fn + debug': () => makeValue().debug().toInspector('Fn + debug'),
};
for (const [name, createNode] of Object.entries(cases)) {
const geometry = new THREE.PlaneGeometry();
const material = new THREE.MeshBasicNodeMaterial();
const mesh = new THREE.Mesh(geometry, material);
// Only node setup is tested. This stub does not initialize a GPU or render.
const renderer = {
backend: { isWebGPUBackend: true },
inspector: new THREE.InspectorBase(),
};
const builder = new THREE.NodeBuilder(mesh, renderer);
builder.setBuildStage('setup');
builder.setShaderStage('fragment');
createNode().build(builder);
const count = [...builder.nodes].filter(node => node.isInspectorNode).length;
console.log(`${name}: ${count}`);
geometry.dispose();
material.dispose();
}
Actual output:
uv: 1
Fn: 0
Fn + toVar: 1
Fn + debug: 1
Expected: each case registers one InspectorNode. Inspecting a valid TSL expression should not require an extra variable declaration or debug wrapper.
Workaround
material.colorNode = makeValue().toVar('value').toInspector('Value');
Live example
No hosted browser example is provided. The self-contained Node.js reproduction above isolates the missing registration without needing a DOM, WebGPU device, or Inspector UI. The UI symptom has not been separately automated or captured in this report.
Screenshots
No response
Version
three@0.185.1 (REVISION 185). Verified using Node.js v24.19.0 on Windows. Not runtime-tested against the latest dev branch.
Device
Desktop
Browser
No response
OS
Windows
Description
Calling
.toInspector()on the result ofFn()can silently fail to register anInspectorNodein three@0.185.1. The same expression registers successfully when wrapped with.toVar()or.debug()before.toInspector().This was noticed when a node was missing from the Inspector Viewer. The registration failure has been independently reproduced at the node-builder setup stage without a browser or GPU. The reproduction below deliberately uses
Fn(() => uv().x)to avoid dependencies on a larger scene, custom materials, or shader math.Source analysis
In the installed version,
makeValue()returns aVarNodewithintent === true.inspector()attaches an InspectorNode throughnode.before(...).Node.build()processes the attached_beforeNodes.VarNode.build()directly returnsthis.node.build(...)when the node is an unassigned intent variable, bypassingsuper.build(...)and the attached before-nodes.This appears to explain why
.toVar('value')works (an explicit non-intent VarNode) and.debug()also works (a DebugNode wrapper). The issue seems to concern preserving before-node behavior when forwarding an intent variable to its underlying node.Reproduction steps
three@0.185.1(npm install three@0.185.1).repro.mjs.node repro.mjs.InspectorNodeinstances. TheFncase returns 0, while the direct UV, explicit variable, and debug-wrapper cases return 1.Code
Actual output:
Expected: each case registers one InspectorNode. Inspecting a valid TSL expression should not require an extra variable declaration or debug wrapper.
Workaround
Live example
No hosted browser example is provided. The self-contained Node.js reproduction above isolates the missing registration without needing a DOM, WebGPU device, or Inspector UI. The UI symptom has not been separately automated or captured in this report.
Screenshots
No response
Version
three@0.185.1 (REVISION 185). Verified using Node.js v24.19.0 on Windows. Not runtime-tested against the latest dev branch.
Device
Desktop
Browser
No response
OS
Windows