How to Use Three.js Shading Language TSL for WebGPU

This article provides a practical guide on utilizing the Three.js Shading Language (TSL) to build node-based materials tailored for WebGPU. You will learn the core concepts of TSL, how it replaces traditional GLSL in the WebGPU workflow, and how to construct, compile, and apply custom node materials to your 3D scenes using the modern Three.js WebGPURenderer.

Understanding Three.js Shading Language (TSL)

With the advent of WebGPU, Three.js introduced a powerful node-based system called Three.js Shading Language (TSL). Unlike traditional WebGL workflows that require writing raw GLSL shader strings, TSL allows developers to build shaders imperatively using JavaScript or TypeScript.

TSL acts as an abstraction layer. It compiles your JavaScript node graphs into WGSL (WebGPU Shading Language) for WebGPU, or falls back to GLSL if the browser only supports WebGL2. This eliminates the need to write separate shader codes for different rendering backends.

Setting Up the WebGPURenderer

To use TSL, you must initialize your scene using the WebGPU-compatible renderer instead of the standard WebGL renderer.

import * as THREE from 'three';

// Initialize the WebGPU Renderer
const renderer = new THREE.WebGPURenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

Creating Your First Node Material

TSL shaders are built by assigning node expressions to specific properties of a node-compatible material, such as MeshBasicNodeMaterial or MeshStandardNodeMaterial. These materials expose properties like colorNode, positionNode, roughnessNode, and metalnessNode.

Here is how to import TSL functions and apply a simple solid color node:

import { color } from 'three/tsl';

// Create a Node Material
const material = new THREE.MeshBasicNodeMaterial();

// Define a static color node and assign it to the material
material.colorNode = color(0x00ffcc);

Building Dynamic Effects with TSL Functions

TSL shines when creating dynamic, mathematical shaders. Instead of writing raw equations, you use TSL helper functions to represent mathematical operations and retrieve mesh data.

Common TSL Inputs and Operations:

Example: Animated UV Pattern

Below is a complete example of creating a material with a dynamic, time-based color gradient using TSL:

import * as THREE from 'three';
import { uv, time, oscSine, vec3, vec2 } from 'three/tsl';

// Initialize a material that supports nodes
const material = new THREE.MeshBasicNodeMaterial();

// Create dynamic UV coordinates shifted by time
const animatedUV = uv().add(vec2(time, 0));

// Generate a shifting RGB color using the animated UV and a sine oscillator
const dynamicColor = vec3(
  animatedUV.x, 
  animatedUV.y, 
  oscSine(time.mul(2))
);

// Assign the resulting node to the color output of the material
material.colorNode = dynamicColor;

// Apply to a mesh
const geometry = new THREE.BoxGeometry(1, 1, 1);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

Manipulating Vertices via positionNode

TSL is not limited to fragment shaders (colors). You can also manipulate the vertices of your geometry directly in JavaScript using positionNode.

import { positionLocal, oscSine, time, vec3 } from 'three/tsl';

const material = new THREE.MeshStandardNodeMaterial({ color: 0xff0000 });

// Calculate a wave displacement offset
const wave = oscSine(time.mul(5)).mul(0.2);

// Apply the offset to the Y axis of the local vertex position
const displacedPosition = positionLocal.add(vec3(0, wave, 0));

// Assign to the position node to deform the mesh
material.positionNode = displacedPosition;

By leveraging TSL, you gain access to a unified, type-safe, and highly performant shader pipeline optimized specifically for modern graphics APIs like WebGPU.