
Threejs Impl Webgpu
- 19 installs
- 11 repo stars
- Updated July 8, 2026
- openaec-foundation/three.js-claude-skill-package
Helps with ai & agent building tasks.
About
threejs-impl-webgpu is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- threejs-impl-webgpu
- AI & Agent Building
- AI-coding skill
Threejs Impl Webgpu by the numbers
- 19 all-time installs (skills.sh)
- +2 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #10,587 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/openaec-foundation/three.js-claude-skill-package --skill threejs-impl-webgpuAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 19 |
|---|---|
| repo stars | ★ 11 |
| Last updated | July 8, 2026 |
| Repository | openaec-foundation/three.js-claude-skill-package ↗ |
What it does
Helps with ai & agent building tasks.
Files
threejs-impl-webgpu
EXPERIMENTAL — The WebGPU renderer and TSL are under active development.
API surfaces may change between Three.js releases. Pin your Three.js version.
Quick Reference
WebGPURenderer Setup
import * as THREE from 'three/webgpu';
const renderer = new THREE.WebGPURenderer({ antialias: true });
await renderer.init(); // MUST await before first render
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setAnimationLoop(animate);
document.body.appendChild(renderer.domElement);ALWAYS call await renderer.init() before the first renderer.render() call. ALWAYS use renderer.setAnimationLoop(callback) instead of requestAnimationFrame. NEVER call renderer.render() before init() resolves — this throws a runtime error.
Browser Support
| Browser | Version | Status |
|---|---|---|
| Chrome | 113+ | Full support |
| Edge | 113+ | Full support |
| Safari | 18+ | Supported |
| Firefox | Experimental | Behind flag (dom.webgpu.enabled) |
Feature Detection and Fallback
import { WebGPU } from 'three/webgpu';
if (WebGPU.isAvailable()) {
const renderer = new THREE.WebGPURenderer({ antialias: true });
await renderer.init();
} else {
const renderer = new THREE.WebGLRenderer({ antialias: true });
}ALWAYS check WebGPU.isAvailable() before creating a WebGPURenderer. ALWAYS provide a WebGLRenderer fallback for unsupported browsers.
Critical Warnings
NEVER write raw GLSL or WGSL strings — ALWAYS use TSL functions. WebGPU compiles TSL to WGSL automatically. GLSL is NOT supported by the WebGPU backend.
NEVER use ShaderMaterial with WebGPURenderer — use NodeMaterial with TSL instead.
NEVER use requestAnimationFrame with WebGPURenderer — ALWAYS use renderer.setAnimationLoop().
NEVER use EffectComposer (three/examples) with WebGPURenderer — use the PostProcessing class with TSL nodes instead.
---
Node Materials
Every classic Three.js material has a node-based equivalent. Classic materials auto-convert when used with WebGPURenderer, but node materials provide full TSL customization.
Material Mapping
| Classic Material | Node Material |
|---|---|
MeshBasicMaterial | MeshBasicNodeMaterial |
MeshStandardMaterial | MeshStandardNodeMaterial |
MeshPhysicalMaterial | MeshPhysicalNodeMaterial |
MeshPhongMaterial | MeshPhongNodeMaterial |
MeshLambertMaterial | MeshLambertNodeMaterial |
LineBasicMaterial | LineBasicNodeMaterial |
LineDashedMaterial | LineDashedNodeMaterial |
PointsMaterial | PointsNodeMaterial |
SpriteMaterial | SpriteNodeMaterial |
ALWAYS import node materials from 'three/webgpu', not from 'three'.
NodeMaterial Input Properties
All properties accept TSL node values. All are optional and override defaults.
Common inputs (all node materials):
| Property | TSL Type | Purpose |
|---|---|---|
.colorNode | vec4 | Base color |
.opacityNode | float | Opacity |
.normalNode | vec3 | Normal map replacement |
.emissiveNode | color | Emissive output |
.positionNode | vec3 | Vertex displacement |
.fragmentNode | vec4 | Full fragment shader replacement |
.vertexNode | vec4 | Full vertex shader replacement |
.outputNode | vec4 | Final output override |
.aoNode | float | Ambient occlusion |
.alphaTestNode | float | Alpha test threshold |
.depthNode | float | Custom depth |
.castShadowNode | vec4 | Shadow casting override |
Standard/Physical inputs:
| Property | TSL Type | Purpose |
|---|---|---|
.metalnessNode | float | Metalness |
.roughnessNode | float | Roughness |
.envNode | color | Environment map |
.lightsNode | — | Lighting model override |
Physical-only inputs: .clearcoatNode, .clearcoatRoughnessNode, .clearcoatNormalNode, .sheenNode, .iridescenceNode, .iridescenceIORNode, .iridescenceThicknessNode, .specularIntensityNode, .specularColorNode, .iorNode, .transmissionNode, .thicknessNode, .attenuationDistanceNode, .attenuationColorNode, .dispersionNode, .anisotropyNode.
---
TSL (Three Shading Language)
TSL is a JavaScript-based node graph system that compiles to GLSL (WebGL2) and WGSL (WebGPU). It replaces raw shader strings entirely.
Type System
| Category | Functions |
|---|---|
| Scalars | float(), int(), uint(), bool() |
| Vectors | vec2(), vec3(), vec4(), ivec2(), ivec3(), ivec4(), uvec2(), uvec3(), uvec4() |
| Matrices | mat2(), mat3(), mat4() |
| Color | color() |
| Conversion | .toFloat(), .toVec3(), .toColor() |
Variables and Uniforms
| Function | Purpose |
|---|---|
uniform(value) | GPU-side dynamic value |
toVar(node) | Reusable shader variable |
toConst(node) | Inline constant |
varying(node) | Vertex-to-fragment interpolation |
vertexStage(node) | Force computation in vertex shader |
attribute(name, type) | Access buffer attributes |
Uniforms support callbacks: .onRenderUpdate(fn), .onFrameUpdate(fn), .onObjectUpdate(fn).
Operators
All operators are chainable on TSL nodes:
- Arithmetic:
.add(),.sub(),.mul(),.div(),.mod() - Comparison:
.equal(),.notEqual(),.lessThan(),.greaterThan(),.lessThanEqual(),.greaterThanEqual() - Logical:
.and(),.or(),.not(),.xor() - Assignment:
.assign(),.addAssign(),.subAssign(),.mulAssign(),.divAssign() - Bitwise:
.bitAnd(),.bitOr(),.bitXor(),.shiftLeft(),.shiftRight()
Geometry Nodes
| Category | Nodes |
|---|---|
| Position | positionGeometry, positionLocal, positionWorld, positionView, positionWorldDirection, positionViewDirection |
| Normal | normalGeometry, normalLocal, normalView, normalWorld |
| Tangent | tangentGeometry, tangentLocal, tangentView, tangentWorld |
| UV | uv(index) |
| Screen | screenUV, screenCoordinate, screenSize |
| Viewport | viewportUV, viewportCoordinate, viewportSize |
Camera and Model Nodes
- Camera:
cameraNear,cameraFar,cameraPosition,cameraProjectionMatrix,cameraViewMatrix,cameraWorldMatrix,cameraNormalMatrix - Model:
modelViewMatrix,modelNormalMatrix,modelWorldMatrix,modelPosition,modelScale,modelDirection
Animation Nodes
| Node | Purpose |
|---|---|
time | Elapsed seconds since start |
deltaTime | Frame delta in seconds |
oscSine(timer) | Sine oscillator (0-1) |
oscSquare(timer) | Square wave oscillator |
oscTriangle(timer) | Triangle wave oscillator |
oscSawtooth(timer) | Sawtooth oscillator |
Texture Operations
| Function | Purpose |
|---|---|
texture(tex, uv, level) | Sample with interpolation |
textureLoad(tex, uv, level) | Sample without interpolation |
textureStore(tex, uv, value) | Write to storage texture |
textureSize(tex, level) | Get texture dimensions |
cubeTexture(tex, uvw, level) | Sample cube map |
triplanarTexture(texX, texY, texZ, scale, position, normal) | Triplanar mapping |
Control Flow
ALWAYS use capital If — lowercase if is JavaScript, not TSL.
If(condition, () => {
// true branch
}).ElseIf(otherCondition, () => {
// else-if branch
}).Else(() => {
// false branch
});select(condition, trueVal, falseVal)— ternary operatorLoop(count, ({ i }) => { })— GPU loop, supportsBreak(),Continue()Switch(value).Case(val, fn).Default(fn)— no fallthroughDiscard()— discard fragmentReturn()— early return
Function Definition
const myFn = Fn(([param1, param2]) => {
return param1.add(param2);
});
// Call: myFn(nodeA, nodeB)---
Compute Shaders
WebGPU enables general-purpose GPU compute via TSL. Compute shaders are NOT available in WebGL fallback mode.
Setup
import { compute, storage } from 'three/webgpu';
const computeNode = compute(shaderFn, count, workgroupSize);
await renderer.computeAsync(computeNode);ALWAYS use await renderer.computeAsync() — compute dispatch is asynchronous.
Storage and Atomics
- Storage:
storage(attribute, type, count),storageTexture(texture) - Atomics:
atomicAdd(),atomicSub(),atomicMax(),atomicMin(),atomicAnd(),atomicOr(),atomicXor(),atomicStore(),atomicLoad() - Barriers:
workgroupBarrier(),storageBarrier(),textureBarrier() - Built-in IDs:
workgroupId,localId,globalId,numWorkgroups,subgroupSize
---
WebGPU Post-Processing
NEVER use the WebGL EffectComposer with WebGPURenderer. Use the PostProcessing class instead.
import { PostProcessing } from 'three/webgpu';
import { bloom, renderOutput } from 'three/tsl';
const postProcessing = new PostProcessing(renderer);
const scenePass = renderOutput(scene, camera);
postProcessing.outputNode = bloom(scenePass);Available Post-Processing Nodes
bloom(), dof(), fxaa(), smaa(), gaussianBlur(), ssr(), ssgi(), ao(), chromaticAberration(), film(), dotScreen(), sobel(), afterImage(), anamorphic(), denoise(), lut3D(), motionBlur(), outline(), rgbShift(), transition(), traa(), renderOutput()
Color Operations
luminance(), saturation(), vibrance(), hue(), posterize(), grayscale(), sepia()
Blend Modes
blendBurn(), blendDodge(), blendOverlay(), blendScreen(), blendColor()
---
WebGL to WebGPU Migration
| Step | Action |
|---|---|
| 1 | Replace import * as THREE from 'three' with import * as THREE from 'three/webgpu' |
| 2 | Replace new WebGLRenderer() with new WebGPURenderer() and add await renderer.init() |
| 3 | Replace classic materials with NodeMaterial equivalents (or keep classic — they auto-convert) |
| 4 | Replace EffectComposer with PostProcessing class and TSL post-processing nodes |
| 5 | Replace raw GLSL ShaderMaterial with TSL-based NodeMaterial |
| 6 | Replace requestAnimationFrame with renderer.setAnimationLoop() |
ALWAYS make the entry point async when using WebGPURenderer — renderer.init() returns a Promise.
---
Reference Links
- references/methods.md — API signatures for WebGPURenderer, NodeMaterial, TSL, compute
- references/examples.md — Working code examples for WebGPU scenes
- references/anti-patterns.md — Common mistakes and how to avoid them
Official Sources
- https://threejs.org/docs/
- https://github.com/mrdoob/three.js/wiki/Three.js-Shading-Language
- https://threejs.org/examples/?q=webgpu
Anti-Patterns (Three.js WebGPU)
1. Rendering Before Async Init
// WRONG: render() called before init() resolves — throws runtime error
const renderer = new THREE.WebGPURenderer({ antialias: true });
renderer.render(scene, camera); // ERROR: renderer not initialized
// CORRECT: ALWAYS await init() before any render call
const renderer = new THREE.WebGPURenderer({ antialias: true });
await renderer.init();
renderer.render(scene, camera);WHY: WebGPURenderer requires asynchronous GPU adapter and device initialization. Calling render() before init() completes causes a runtime error because the GPU device handle does not exist yet.
---
2. No Browser Support Check
// WRONG: Assumes WebGPU is available everywhere
const renderer = new THREE.WebGPURenderer({ antialias: true });
await renderer.init();
// CORRECT: ALWAYS check availability and provide fallback
import { WebGPU } from 'three/webgpu';
let renderer;
if (WebGPU.isAvailable()) {
renderer = new THREE.WebGPURenderer({ antialias: true });
await renderer.init();
} else {
renderer = new THREE.WebGLRenderer({ antialias: true });
}WHY: WebGPU is NOT available in Firefox (without a flag), older browsers, or many mobile browsers. Without a fallback, the application crashes for a large portion of users.
---
3. Writing Raw GLSL with WebGPU
// WRONG: GLSL strings are NOT supported by WebGPU backend
const material = new THREE.ShaderMaterial({
vertexShader: `void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`,
fragmentShader: `void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }`,
});
// CORRECT: Use TSL with NodeMaterial
import { MeshBasicNodeMaterial } from 'three/webgpu';
import { vec4, float } from 'three/tsl';
const material = new MeshBasicNodeMaterial();
material.colorNode = vec4(float(1.0), float(0.0), float(0.0), float(1.0));WHY: WebGPU uses WGSL, not GLSL. Three.js compiles TSL to WGSL automatically. ShaderMaterial with GLSL strings only works with WebGLRenderer. ALWAYS use TSL for cross-backend compatibility.
---
4. Using requestAnimationFrame
// WRONG: requestAnimationFrame does not integrate with WebGPU frame timing
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
// CORRECT: ALWAYS use setAnimationLoop for WebGPU
renderer.setAnimationLoop((time) => {
renderer.render(scene, camera);
});WHY: renderer.setAnimationLoop() handles WebGPU frame scheduling, XR session loops, and renderer lifecycle correctly. Using requestAnimationFrame bypasses these mechanisms and can cause timing issues or missed frames with WebGPU.
---
5. Using EffectComposer with WebGPU
// WRONG: EffectComposer is WebGL-only
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';
const composer = new EffectComposer(renderer); // FAILS with WebGPURenderer
// CORRECT: Use PostProcessing class with TSL nodes
import { PostProcessing } from 'three/webgpu';
import { pass, bloom, renderOutput } from 'three/tsl';
const postProcessing = new PostProcessing(renderer);
const scenePass = pass(scene, camera);
postProcessing.outputNode = renderOutput(bloom(scenePass));WHY: The EffectComposer and its passes use WebGL-specific render targets and GLSL shaders. They are fundamentally incompatible with WebGPURenderer. The PostProcessing class uses TSL nodes that compile to WGSL.
---
6. Importing from Wrong Module Path
// WRONG: Standard three import does not include WebGPU classes
import * as THREE from 'three';
const renderer = new THREE.WebGPURenderer(); // undefined
// CORRECT: ALWAYS import from 'three/webgpu' for WebGPU classes
import * as THREE from 'three/webgpu';
const renderer = new THREE.WebGPURenderer({ antialias: true });WHY: WebGPURenderer, node materials, and WebGPU utilities are only exported from 'three/webgpu'. The standard 'three' entry point does not include them.
---
7. Using Lowercase if in TSL
// WRONG: JavaScript if does not generate shader conditionals
if (someNode.greaterThan(float(0.5))) {
material.colorNode = color(0xff0000);
}
// CORRECT: ALWAYS use TSL's capital If for shader-level conditionals
import { If, float, color } from 'three/tsl';
If(someNode.greaterThan(float(0.5)), () => {
material.colorNode = color(0xff0000);
}).Else(() => {
material.colorNode = color(0x0000ff);
});WHY: JavaScript if evaluates at material creation time, not per-fragment. TSL If() generates actual GPU conditional instructions that execute per-fragment on the GPU. Using JavaScript if produces a static material instead of a dynamic one.
---
8. Synchronous Compute Dispatch
// WRONG: computeAsync returns a Promise — ignoring it causes race conditions
renderer.computeAsync(computeNode); // fire-and-forget
renderer.render(scene, camera); // may render before compute finishes
// CORRECT: ALWAYS await compute before rendering dependent results
await renderer.computeAsync(computeNode);
renderer.render(scene, camera);WHY: GPU compute operations are asynchronous. Rendering before the compute pass finishes can display stale or incomplete data. ALWAYS await computeAsync() when the render depends on compute results.
---
9. Forgetting to Make Entry Point Async
// WRONG: Cannot use await at top level in non-module scripts
const renderer = new THREE.WebGPURenderer();
renderer.init(); // Promise ignored, init never completes
// CORRECT: Use async entry point
async function init() {
const renderer = new THREE.WebGPURenderer({ antialias: true });
await renderer.init();
// ... rest of setup
}
init();WHY: WebGPURenderer.init() is a Promise. Without await, the renderer is used before GPU initialization completes. ALWAYS wrap WebGPU setup in an async function or use top-level await in ES modules.
---
10. Mixing Classic and Node Material APIs Incorrectly
// WRONG: Setting classic property after assigning node — node takes precedence
const material = new MeshStandardNodeMaterial();
material.colorNode = color(0xff0000);
material.color.set(0x00ff00); // has NO effect — colorNode overrides
// CORRECT: Use EITHER classic props OR node props, not both
// Option A: Classic props only (auto-converts for WebGPU)
const material = new MeshStandardNodeMaterial({ color: 0x00ff00 });
// Option B: Node props only (full TSL control)
const material = new MeshStandardNodeMaterial();
material.colorNode = color(0x00ff00);WHY: When a node property (e.g., colorNode) is set, it completely overrides the corresponding classic property (e.g., color). Setting both creates confusion and the classic value is silently ignored. Choose one approach and use it consistently.
Working Code Examples (Three.js WebGPU)
Example 1: Minimal WebGPU Scene with Fallback
import * as THREE from 'three/webgpu';
import { WebGPU } from 'three/webgpu';
async function init() {
let renderer;
if (WebGPU.isAvailable()) {
renderer = new THREE.WebGPURenderer({ antialias: true });
await renderer.init();
} else {
renderer = new THREE.WebGLRenderer({ antialias: true });
}
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75, window.innerWidth / window.innerHeight, 0.1, 1000
);
camera.position.z = 5;
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshStandardNodeMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(5, 5, 5);
scene.add(light);
renderer.setAnimationLoop((time) => {
cube.rotation.x = time * 0.001;
cube.rotation.y = time * 0.0015;
renderer.render(scene, camera);
});
}
init();---
Example 2: TSL Custom Material with Animated Color
import * as THREE from 'three/webgpu';
import { color, oscSine, time, mix, vec4 } from 'three/tsl';
async function init() {
const renderer = new THREE.WebGPURenderer({ antialias: true });
await renderer.init();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.z = 3;
// Create node material with animated color
const material = new THREE.MeshStandardNodeMaterial();
const colorA = color(0xff0000); // red
const colorB = color(0x0000ff); // blue
const t = oscSine(time); // oscillate 0-1 over time
material.colorNode = vec4(mix(colorA, colorB, t), 1.0);
const sphere = new THREE.Mesh(new THREE.SphereGeometry(1, 32, 32), material);
scene.add(sphere);
const light = new THREE.DirectionalLight(0xffffff, 2);
light.position.set(3, 3, 3);
scene.add(light);
scene.add(new THREE.AmbientLight(0x404040));
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
}
init();---
Example 3: Vertex Displacement with TSL
import * as THREE from 'three/webgpu';
import { positionLocal, normalLocal, sin, time, float, vec3 } from 'three/tsl';
async function init() {
const renderer = new THREE.WebGPURenderer({ antialias: true });
await renderer.init();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.z = 4;
const material = new THREE.MeshStandardNodeMaterial({ color: 0x44aaff });
// Displace vertices along their normals using a sine wave
const displacement = sin(
positionLocal.y.mul(float(4.0)).add(time.mul(float(2.0)))
).mul(float(0.3));
material.positionNode = positionLocal.add(normalLocal.mul(displacement));
const geometry = new THREE.SphereGeometry(1, 64, 64);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
const light = new THREE.DirectionalLight(0xffffff, 2);
light.position.set(5, 5, 5);
scene.add(light);
scene.add(new THREE.AmbientLight(0x222222));
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
}
init();---
Example 4: Compute Shader — Particle Position Update
import * as THREE from 'three/webgpu';
import {
compute, storage, float, vec3, Fn,
globalId, sin, cos, time
} from 'three/tsl';
async function init() {
const renderer = new THREE.WebGPURenderer({ antialias: true });
await renderer.init();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 50;
const particleCount = 10000;
// Create storage buffer for positions
const positionAttribute = new THREE.StorageBufferAttribute(
new Float32Array(particleCount * 3), 3
);
// Initialize positions
for (let i = 0; i < particleCount; i++) {
positionAttribute.setXYZ(i,
(Math.random() - 0.5) * 40,
(Math.random() - 0.5) * 40,
(Math.random() - 0.5) * 40
);
}
// Compute shader: update positions with circular motion
const positionStorage = storage(positionAttribute, 'vec3', particleCount);
const computeFn = Fn(() => {
const idx = globalId.x;
const pos = positionStorage.element(idx);
const angle = time.add(float(idx).mul(float(0.01)));
pos.x.assign(sin(angle).mul(float(20.0)));
pos.z.assign(cos(angle).mul(float(20.0)));
});
const computeNode = compute(computeFn, particleCount);
// Create points geometry
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', positionAttribute);
const material = new THREE.PointsNodeMaterial({
size: 0.2,
sizeAttenuation: true,
color: 0x00ffaa,
});
const points = new THREE.Points(geometry, material);
scene.add(points);
renderer.setAnimationLoop(async () => {
await renderer.computeAsync(computeNode);
renderer.render(scene, camera);
});
}
init();---
Example 5: WebGPU Post-Processing with Bloom
import * as THREE from 'three/webgpu';
import { PostProcessing } from 'three/webgpu';
import { pass, bloom, renderOutput } from 'three/tsl';
async function init() {
const renderer = new THREE.WebGPURenderer({ antialias: true });
await renderer.init();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.toneMapping = THREE.ACESFilmicToneMapping;
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.z = 5;
// Emissive sphere for bloom effect
const material = new THREE.MeshStandardNodeMaterial({
color: 0x000000,
emissive: 0xff6600,
emissiveIntensity: 2,
});
const sphere = new THREE.Mesh(new THREE.SphereGeometry(1, 32, 32), material);
scene.add(sphere);
scene.add(new THREE.AmbientLight(0x111111));
// Set up post-processing pipeline
const postProcessing = new PostProcessing(renderer);
const scenePass = pass(scene, camera);
const bloomPass = bloom(scenePass, {
strength: 1.5,
radius: 0.4,
threshold: 0.6,
});
postProcessing.outputNode = renderOutput(bloomPass);
renderer.setAnimationLoop((time) => {
sphere.rotation.y = time * 0.001;
postProcessing.render();
});
}
init();API Signatures Reference (Three.js WebGPU)
WebGPURenderer
import * as THREE from 'three/webgpu';
const renderer = new THREE.WebGPURenderer({
canvas: HTMLCanvasElement, // optional, target canvas
antialias: boolean, // default: false
alpha: boolean, // default: false, transparent background
depth: boolean, // default: true
stencil: boolean, // default: false
powerPreference: string, // 'high-performance' | 'low-power'
forceWebGL: boolean, // default: false, force WebGL2 backend
});Instance Methods
// Initialization — MUST await before rendering
await renderer.init(): Promise<void>
// Rendering
renderer.render(scene: Scene, camera: Camera): void
renderer.setAnimationLoop(callback: (time: DOMHighResTimeStamp) => void): void
// Sizing
renderer.setSize(width: number, height: number, updateStyle?: boolean): void
renderer.setPixelRatio(ratio: number): void
renderer.getSize(target: Vector2): Vector2
// Compute (WebGPU only)
await renderer.computeAsync(computeNode: ComputeNode): Promise<void>
// Disposal
renderer.dispose(): voidInstance Properties
renderer.domElement: HTMLCanvasElement // the canvas element
renderer.info: Object // render statistics
renderer.toneMapping: ToneMapping // default: NoToneMapping
renderer.toneMappingExposure: number // default: 1
renderer.outputColorSpace: string // default: SRGBColorSpace
renderer.shadowMap.enabled: boolean // default: false
renderer.shadowMap.type: ShadowMapType // default: PCFShadowMap---
WebGPU Feature Detection
import { WebGPU } from 'three/webgpu';
WebGPU.isAvailable(): boolean
// Returns true if the browser supports WebGPU.
// Returns false if WebGPU is unavailable (use WebGLRenderer fallback).---
NodeMaterial Base Class
All node materials extend NodeMaterial. These properties are available on every node material type.
// Common input nodes (all optional, accept TSL nodes)
material.colorNode: Node | null // vec4 — base color
material.opacityNode: Node | null // float — opacity
material.normalNode: Node | null // vec3 — normal override
material.emissiveNode: Node | null // color — emissive
material.positionNode: Node | null // vec3 — vertex displacement
material.fragmentNode: Node | null // vec4 — full fragment replacement
material.vertexNode: Node | null // vec4 — full vertex replacement
material.outputNode: Node | null // vec4 — final output override
material.aoNode: Node | null // float — ambient occlusion
material.alphaTestNode: Node | null // float — alpha test threshold
material.depthNode: Node | null // float — custom depth
material.castShadowNode: Node | null // vec4 — shadow casting---
MeshStandardNodeMaterial
import { MeshStandardNodeMaterial } from 'three/webgpu';
const material = new MeshStandardNodeMaterial({
color: 0xffffff, // base color (classic prop, still works)
metalness: 0.0, // classic prop
roughness: 1.0, // classic prop
});
// Node inputs (override classic props)
material.metalnessNode: Node | null // float
material.roughnessNode: Node | null // float
material.envNode: Node | null // color — environment map
material.lightsNode: Node | null // lighting model override---
MeshPhysicalNodeMaterial
Extends MeshStandardNodeMaterial with additional physical inputs:
import { MeshPhysicalNodeMaterial } from 'three/webgpu';
material.clearcoatNode: Node | null
material.clearcoatRoughnessNode: Node | null
material.clearcoatNormalNode: Node | null
material.sheenNode: Node | null
material.iridescenceNode: Node | null
material.iridescenceIORNode: Node | null
material.iridescenceThicknessNode: Node | null
material.specularIntensityNode: Node | null
material.specularColorNode: Node | null
material.iorNode: Node | null
material.transmissionNode: Node | null
material.thicknessNode: Node | null
material.attenuationDistanceNode: Node | null
material.attenuationColorNode: Node | null
material.dispersionNode: Node | null
material.anisotropyNode: Node | null---
TSL Core Functions
Type Constructors
import {
float, int, uint, bool,
vec2, vec3, vec4,
ivec2, ivec3, ivec4,
uvec2, uvec3, uvec4,
mat2, mat3, mat4,
color
} from 'three/tsl';
float(1.0): FloatNode
vec3(1.0, 0.0, 0.0): Vec3Node
color(0xff0000): ColorNodeVariable Management
import { uniform, toVar, toConst, varying, vertexStage, attribute } from 'three/tsl';
uniform(initialValue): UniformNode
// .onRenderUpdate((frame) => newValue)
// .onFrameUpdate((frame) => newValue)
// .onObjectUpdate((frame) => newValue)
toVar(node): VarNode // reusable shader variable
toConst(node): ConstNode // inline constant
varying(node): VaryingNode // vertex-to-fragment interpolation
vertexStage(node): Node // force vertex shader execution
attribute(name, type): AttributeNode // buffer attribute accessMath Library
import {
abs, acos, asin, atan, ceil, clamp, cos, cross, degrees,
distance, dot, exp, floor, fract, inverseSqrt, length, log,
max, min, mix, normalize, pow, radians, reflect, refract,
round, saturate, sign, sin, smoothstep, sqrt, step, tan, trunc,
faceforward, dFdx, dFdy, fwidth,
negate, oneMinus, reciprocal, cbrt, pow2, pow3, pow4
} from 'three/tsl';
// Constants
import { EPSILON, INFINITY, PI, TWO_PI, HALF_PI } from 'three/tsl';Geometry Nodes
import {
positionGeometry, positionLocal, positionWorld, positionView,
positionWorldDirection, positionViewDirection,
normalGeometry, normalLocal, normalView, normalWorld,
tangentGeometry, tangentLocal, tangentView, tangentWorld,
bitangentGeometry, bitangentLocal, bitangentView, bitangentWorld,
uv,
screenUV, screenCoordinate, screenSize,
viewportUV, viewportCoordinate, viewportSize
} from 'three/tsl';
uv(0): UVNode // UV channel 0 (default)
uv(1): UVNode // UV channel 1Camera Nodes
import {
cameraNear, cameraFar, cameraPosition,
cameraProjectionMatrix, cameraProjectionMatrixInverse,
cameraViewMatrix, cameraWorldMatrix, cameraNormalMatrix
} from 'three/tsl';Model Nodes
import {
modelViewMatrix, modelNormalMatrix, modelWorldMatrix,
modelPosition, modelScale, modelDirection,
modelViewPosition, modelWorldMatrixInverse
} from 'three/tsl';Texture Functions
import { texture, textureLoad, textureStore, textureSize, cubeTexture, triplanarTexture, textureBicubic } from 'three/tsl';
texture(tex, uv?, level?): TextureNode
textureLoad(tex, uv, level?): TextureNode
textureStore(tex, uv, value): StorageTextureNode
textureSize(tex, level?): Vec2Node
cubeTexture(tex, uvw?, level?): CubeTextureNode
triplanarTexture(texX, texY, texZ, scale?, position?, normal?): Node
textureBicubic(textureNode, strength?): NodeAnimation Nodes
import { time, deltaTime, oscSine, oscSquare, oscTriangle, oscSawtooth } from 'three/tsl';
time: FloatNode // elapsed seconds
deltaTime: FloatNode // frame delta seconds
oscSine(timer?): FloatNode // sine oscillation 0-1
oscSquare(timer?): FloatNode // square wave 0-1
oscTriangle(timer?): FloatNode // triangle wave 0-1
oscSawtooth(timer?): FloatNode // sawtooth wave 0-1Randomization
import { hash, range } from 'three/tsl';
hash(seed): FloatNode // deterministic 0-1 hash
range(min, max): FloatNode // attribute-based random rangeControl Flow
import { If, select, Loop, Break, Continue, Discard, Return, Switch, Fn } from 'three/tsl';
If(condition, thenFn).ElseIf(condition, thenFn).Else(elseFn): void
select(condition, trueVal, falseVal): Node
Loop(count, ({ i }) => { }): void
Switch(value).Case(val, fn).Default(fn): void
Break(): void
Continue(): void
Discard(): void
Return(): void
const myFn = Fn(([param1, param2]) => {
return param1.add(param2);
});Color Operations
import { luminance, saturation, vibrance, hue, posterize, grayscale, sepia } from 'three/tsl';Blend Modes
import { blendBurn, blendDodge, blendOverlay, blendScreen, blendColor } from 'three/tsl';---
Compute Shader API
import { compute, storage, storageTexture } from 'three/tsl';
compute(shaderFn: Fn, count: number, workgroupSize?: number[]): ComputeNode
storage(attribute, type, count): StorageBufferNode
storageTexture(texture): StorageTextureNode
// Dispatch
await renderer.computeAsync(computeNode): Promise<void>Atomic Operations
import {
atomicAdd, atomicSub, atomicMax, atomicMin,
atomicAnd, atomicOr, atomicXor,
atomicStore, atomicLoad
} from 'three/tsl';Barriers
import { workgroupBarrier, storageBarrier, textureBarrier, barrier } from 'three/tsl';Built-in IDs
import { workgroupId, localId, globalId, numWorkgroups, subgroupSize } from 'three/tsl';---
PostProcessing Class
import { PostProcessing } from 'three/webgpu';
import { bloom, fxaa, renderOutput } from 'three/tsl';
const postProcessing = new PostProcessing(renderer: WebGPURenderer);
postProcessing.outputNode = Node; // assign TSL post-processing chain
// Available post-processing nodes:
import {
bloom, dof, fxaa, smaa, gaussianBlur, ssr, ssgi, ao,
chromaticAberration, film, dotScreen, sobel, afterImage,
anamorphic, denoise, lut3D, motionBlur, outline, rgbShift,
transition, traa, renderOutput
} from 'three/tsl';