JavaScript 3D library providing WebGL and WebGPU renderers for creating interactive 3D graphics in web browsers
—
Three Shading Language (TSL) is a node-based shading system that provides a JavaScript-friendly syntax for creating custom materials and effects. TSL enables developers to write shaders using familiar JavaScript constructs while generating optimized GPU code for both WebGL and WebGPU renderers.
Import: TSL functionality is available via the dedicated TSL build:
import {
color, vec3, vec4, float, int, bool,
uniform, attribute, varying, texture,
add, mul, sin, cos, normalize, dot,
mix, smoothstep, clamp, pow
} from 'three/tsl';Fundamental building blocks for creating shader node graphs with type-safe operations.
/**
* Base class for all TSL nodes
*/
abstract class Node {
/** Node type identifier */
nodeType: string;
/** Node value type (float, vec3, etc.) */
type: string | null;
/** Unique identifier for this node */
uuid: string;
/**
* Get node hash for caching
* @returns Hash string
*/
getHash(): string;
/**
* Update node before rendering
* @param frame - Current render frame
*/
update(frame: NodeFrame): void;
/**
* Build node for specific renderer
* @param builder - Node builder context
* @returns Generated code string
*/
build(builder: NodeBuilder): string;
/**
* Analyze node dependencies
* @param builder - Node builder context
*/
analyze(builder: NodeBuilder): void;
/**
* Generate code for this node
* @param builder - Node builder context
* @returns Generated shader code
*/
generate(builder: NodeBuilder): string;
}
/**
* Create uniform input node
* @param value - Uniform value (number, Vector3, Color, etc.)
* @returns Uniform node
*/
declare function uniform(value: any): UniformNode;
/**
* Create vertex attribute input node
* @param name - Attribute name in geometry
* @param type - Attribute data type
* @returns Attribute node
*/
declare function attribute(name: string, type?: string): AttributeNode;
/**
* Create varying node for vertex-fragment communication
* @param node - Node to pass from vertex to fragment
* @param name - Optional varying name
* @returns Varying node
*/
declare function varying(node: Node, name?: string): VaryingNode;Type-safe vector and scalar operations with automatic type inference and conversion.
/**
* Create float/scalar node
* @param value - Numeric value or node
* @returns Float node
*/
declare function float(value: NodeRepresentation): FloatNode;
/**
* Create integer node
* @param value - Integer value or node
* @returns Integer node
*/
declare function int(value: NodeRepresentation): IntNode;
/**
* Create boolean node
* @param value - Boolean value or node
* @returns Boolean node
*/
declare function bool(value: NodeRepresentation): BoolNode;
/**
* Create 2D vector node
* @param x - X component (number/node) or Vector2
* @param y - Y component (number/node, optional if x is Vector2)
* @returns Vec2 node
*/
declare function vec2(x?: NodeRepresentation, y?: NodeRepresentation): Vec2Node;
/**
* Create 3D vector node
* @param x - X component (number/node) or Vector3
* @param y - Y component (number/node, optional if x is Vector3)
* @param z - Z component (number/node, optional if x is Vector3)
* @returns Vec3 node
*/
declare function vec3(x?: NodeRepresentation, y?: NodeRepresentation, z?: NodeRepresentation): Vec3Node;
/**
* Create 4D vector node
* @param x - X component (number/node) or Vector4
* @param y - Y component (number/node, optional if x is Vector4)
* @param z - Z component (number/node, optional if x is Vector4)
* @param w - W component (number/node, optional if x is Vector4)
* @returns Vec4 node
*/
declare function vec4(
x?: NodeRepresentation,
y?: NodeRepresentation,
z?: NodeRepresentation,
w?: NodeRepresentation
): Vec4Node;
/**
* Create color node
* @param color - Color value (Color, hex number, or RGB components)
* @param g - Green component (if color is red component)
* @param b - Blue component (if color is red component)
* @returns Color node
*/
declare function color(color: NodeRepresentation, g?: NodeRepresentation, b?: NodeRepresentation): ColorNode;
/**
* Create matrix nodes
*/
declare function mat3(...values: NodeRepresentation[]): Mat3Node;
declare function mat4(...values: NodeRepresentation[]): Mat4Node;Usage Example:
import { vec3, color, float, uniform } from 'three/tsl';
// Create vector nodes
const position = vec3(0, 1, 0);
const direction = vec3(1, 0, 0);
const scale = float(2.0);
// Create uniform inputs
const time = uniform(0);
const baseColor = uniform(color(0xff0000));
const intensity = uniform(1.0);
// Combine nodes with operations
const animatedPosition = position.add(direction.mul(time));
const scaledColor = baseColor.mul(intensity);Comprehensive mathematical functions and operators for shader computations.
/**
* Arithmetic operations
*/
declare function add(a: NodeRepresentation, b: NodeRepresentation): Node;
declare function sub(a: NodeRepresentation, b: NodeRepresentation): Node;
declare function mul(a: NodeRepresentation, b: NodeRepresentation): Node;
declare function div(a: NodeRepresentation, b: NodeRepresentation): Node;
declare function mod(a: NodeRepresentation, b: NodeRepresentation): Node;
declare function pow(a: NodeRepresentation, b: NodeRepresentation): Node;
/**
* Trigonometric functions
*/
declare function sin(node: NodeRepresentation): Node;
declare function cos(node: NodeRepresentation): Node;
declare function tan(node: NodeRepresentation): Node;
declare function asin(node: NodeRepresentation): Node;
declare function acos(node: NodeRepresentation): Node;
declare function atan(node: NodeRepresentation): Node;
declare function atan2(y: NodeRepresentation, x: NodeRepresentation): Node;
/**
* Exponential and logarithmic functions
*/
declare function exp(node: NodeRepresentation): Node;
declare function exp2(node: NodeRepresentation): Node;
declare function log(node: NodeRepresentation): Node;
declare function log2(node: NodeRepresentation): Node;
declare function sqrt(node: NodeRepresentation): Node;
declare function inversesqrt(node: NodeRepresentation): Node;
/**
* Common mathematical functions
*/
declare function abs(node: NodeRepresentation): Node;
declare function sign(node: NodeRepresentation): Node;
declare function floor(node: NodeRepresentation): Node;
declare function ceil(node: NodeRepresentation): Node;
declare function fract(node: NodeRepresentation): Node;
declare function min(a: NodeRepresentation, b: NodeRepresentation): Node;
declare function max(a: NodeRepresentation, b: NodeRepresentation): Node;
declare function clamp(value: NodeRepresentation, min: NodeRepresentation, max: NodeRepresentation): Node;
declare function mix(a: NodeRepresentation, b: NodeRepresentation, t: NodeRepresentation): Node;
declare function step(edge: NodeRepresentation, value: NodeRepresentation): Node;
declare function smoothstep(edge0: NodeRepresentation, edge1: NodeRepresentation, value: NodeRepresentation): Node;
/**
* Vector operations
*/
declare function length(node: NodeRepresentation): Node;
declare function distance(a: NodeRepresentation, b: NodeRepresentation): Node;
declare function dot(a: NodeRepresentation, b: NodeRepresentation): Node;
declare function cross(a: NodeRepresentation, b: NodeRepresentation): Node;
declare function normalize(node: NodeRepresentation): Node;
declare function reflect(incident: NodeRepresentation, normal: NodeRepresentation): Node;
declare function refract(incident: NodeRepresentation, normal: NodeRepresentation, eta: NodeRepresentation): Node;
/**
* Matrix operations
*/
declare function matrixMultiply(a: NodeRepresentation, b: NodeRepresentation): Node;
declare function transpose(matrix: NodeRepresentation): Node;
declare function determinant(matrix: NodeRepresentation): Node;
declare function inverse(matrix: NodeRepresentation): Node;Usage Example:
import { vec3, float, sin, cos, normalize, dot, mix, smoothstep, time } from 'three/tsl';
// Create animated wave effect
const position = attribute('position');
const waveFreq = float(2.0);
const waveAmp = float(0.5);
const wave = sin(position.y.mul(waveFreq).add(time)).mul(waveAmp);
const animatedPosition = position.add(vec3(wave, 0, 0));
// Create lighting calculation
const normal = attribute('normal');
const lightDir = normalize(vec3(1, 1, 1));
const lightIntensity = max(dot(normal, lightDir), 0);
// Create color mixing
const baseColor = color(0x3366ff);
const highlightColor = color(0xffffff);
const finalColor = mix(baseColor, highlightColor, lightIntensity);Comprehensive texture sampling and manipulation functions for material creation.
/**
* Create texture sampling node
* @param texture - Texture uniform or object
* @param uv - UV coordinates (vec2 node)
* @param bias - Mipmap bias (optional)
* @returns Texture sample node
*/
declare function texture(texture: NodeRepresentation, uv?: NodeRepresentation, bias?: NodeRepresentation): TextureNode;
/**
* Create cube texture sampling node
* @param texture - Cube texture uniform
* @param direction - Sample direction (vec3 node)
* @param bias - Mipmap bias (optional)
* @returns Cube texture sample node
*/
declare function cubeTexture(texture: NodeRepresentation, direction: NodeRepresentation, bias?: NodeRepresentation): CubeTextureNode;
/**
* Texture derivative functions
*/
declare function textureGrad(
texture: NodeRepresentation,
uv: NodeRepresentation,
dPdx: NodeRepresentation,
dPdy: NodeRepresentation
): Node;
declare function textureLod(texture: NodeRepresentation, uv: NodeRepresentation, lod: NodeRepresentation): Node;
/**
* Derivative functions for texture sampling
*/
declare function dFdx(node: NodeRepresentation): Node;
declare function dFdy(node: NodeRepresentation): Node;
declare function fwidth(node: NodeRepresentation): Node;
/**
* Texture information functions
*/
declare function textureSize(texture: NodeRepresentation, lod?: NodeRepresentation): Node;
declare function textureQueryLod(texture: NodeRepresentation, uv: NodeRepresentation): Node;Usage Example:
import { texture, vec2, mul, add, sin, cos, time } from 'three/tsl';
// Basic texture sampling
const diffuseMap = uniform(myTexture);
const uv = attribute('uv');
const diffuseColor = texture(diffuseMap, uv);
// Animated texture coordinates
const scrollSpeed = float(0.1);
const animatedUV = uv.add(vec2(time.mul(scrollSpeed), 0));
const scrollingTexture = texture(diffuseMap, animatedUV);
// Distorted texture sampling
const distortion = sin(uv.y.mul(10).add(time)).mul(0.02);
const distortedUV = uv.add(vec2(distortion, 0));
const distortedTexture = texture(diffuseMap, distortedUV);
// Multi-texture blending
const normalMap = uniform(myNormalTexture);
const roughnessMap = uniform(myRoughnessTexture);
const albedo = texture(diffuseMap, uv);
const normal = texture(normalMap, uv);
const roughness = texture(roughnessMap, uv).r; // Red channel onlyConditional operations and control flow constructs for complex shader logic.
/**
* Conditional node for branching logic
* @param condition - Boolean condition node
* @param ifTrue - Node to use when condition is true
* @param ifFalse - Node to use when condition is false
* @returns Conditional result node
*/
declare function cond(condition: NodeRepresentation, ifTrue: NodeRepresentation, ifFalse: NodeRepresentation): Node;
/**
* Logical operations
*/
declare function and(a: NodeRepresentation, b: NodeRepresentation): Node;
declare function or(a: NodeRepresentation, b: NodeRepresentation): Node;
declare function not(node: NodeRepresentation): Node;
declare function xor(a: NodeRepresentation, b: NodeRepresentation): Node;
/**
* Comparison operations
*/
declare function equal(a: NodeRepresentation, b: NodeRepresentation): Node;
declare function notEqual(a: NodeRepresentation, b: NodeRepresentation): Node;
declare function lessThan(a: NodeRepresentation, b: NodeRepresentation): Node;
declare function lessThanEqual(a: NodeRepresentation, b: NodeRepresentation): Node;
declare function greaterThan(a: NodeRepresentation, b: NodeRepresentation): Node;
declare function greaterThanEqual(a: NodeRepresentation, b: NodeRepresentation): Node;
/**
* Select operation (GPU-friendly conditional)
* @param condition - Boolean condition
* @param a - Value when condition is true
* @param b - Value when condition is false
* @returns Selected value
*/
declare function select(condition: NodeRepresentation, a: NodeRepresentation, b: NodeRepresentation): Node;Usage Example:
import { cond, greaterThan, lessThan, mix, float, color } from 'three/tsl';
// Conditional color based on height
const position = attribute('position');
const heightThreshold = float(2.0);
const lowColor = color(0x00ff00); // Green
const highColor = color(0xff0000); // Red
const finalColor = cond(
greaterThan(position.y, heightThreshold),
highColor,
lowColor
);
// Multi-level conditional
const midThreshold = float(1.0);
const midColor = color(0xffff00); // Yellow
const triColor = cond(
greaterThan(position.y, heightThreshold),
highColor,
cond(
greaterThan(position.y, midThreshold),
midColor,
lowColor
)
);Access to standard shader variables and mathematical constants.
/**
* Vertex shader built-ins
*/
declare const positionLocal: AttributeNode; // Local position
declare const positionWorld: Node; // World position
declare const positionView: Node; // View space position
declare const positionClip: Node; // Clip space position
declare const normalLocal: AttributeNode; // Local normal
declare const normalWorld: Node; // World normal
declare const normalView: Node; // View space normal
declare const uv: AttributeNode; // UV coordinates
declare const color: AttributeNode; // Vertex colors
/**
* Fragment shader built-ins
*/
declare const fragCoord: Node; // Fragment coordinates
declare const frontFacing: Node; // Whether fragment is front-facing
declare const pointCoord: Node; // Point sprite coordinates
/**
* Camera and view uniforms
*/
declare const cameraPosition: UniformNode; // World camera position
declare const cameraViewMatrix: UniformNode; // View matrix
declare const cameraProjectionMatrix: UniformNode; // Projection matrix
declare const cameraNear: UniformNode; // Near clipping plane
declare const cameraFar: UniformNode; // Far clipping plane
/**
* Object transform uniforms
*/
declare const modelMatrix: UniformNode; // Model matrix
declare const modelViewMatrix: UniformNode; // Model-view matrix
declare const normalMatrix: UniformNode; // Normal transformation matrix
/**
* Time and animation
*/
declare const time: UniformNode; // Global time in seconds
declare const deltaTime: UniformNode; // Frame delta time
/**
* Mathematical constants
*/
declare const PI: FloatNode; // π (3.14159...)
declare const PI2: FloatNode; // 2π
declare const PI_HALF: FloatNode; // π/2
declare const RECIPROCAL_PI: FloatNode; // 1/π
declare const RECIPROCAL_PI2: FloatNode; // 1/(2π)
declare const EPSILON: FloatNode; // Small epsilon value
declare const E: FloatNode; // Euler's number (2.718...)System for creating reusable custom node functions and effects.
/**
* Create custom function node
* @param inputs - Input parameter definitions
* @param returns - Return type
* @param body - Function body as TSL nodes
* @returns Custom function node
*/
declare function tslFn(inputs: { [key: string]: string }, returns: string, body: (inputs: any) => Node): TslFunction;
/**
* Create inline WGSL/GLSL code node
* @param code - Raw shader code string
* @param inputs - Input nodes array
* @returns Code node
*/
declare function wgsl(code: string, ...inputs: Node[]): CodeNode;
declare function glsl(code: string, ...inputs: Node[]): CodeNode;
/**
* Custom node class for complex operations
*/
abstract class TempNode extends Node {
/**
* Generate code for this custom node
* @param builder - Node builder context
* @param output - Output variable name
* @returns Generated shader code
*/
abstract generate(builder: NodeBuilder, output?: string): string;
}Usage Example:
import { tslFn, vec3, float, sin, cos, mul, add } from 'three/tsl';
// Create custom wave function
const wave = tslFn({
position: 'vec3',
time: 'float',
frequency: 'float',
amplitude: 'float'
}, 'vec3', ({ position, time, frequency, amplitude }) => {
const wave = sin(position.y.mul(frequency).add(time)).mul(amplitude);
return position.add(vec3(wave, 0, 0));
});
// Use custom function
const animatedPosition = wave({
position: attribute('position'),
time: time,
frequency: float(2.0),
amplitude: float(0.5)
});
// Create custom material effect
const customEffect = tslFn({
uv: 'vec2',
time: 'float'
}, 'vec3', ({ uv, time }) => {
const r = sin(uv.x.mul(10).add(time)).mul(0.5).add(0.5);
const g = cos(uv.y.mul(10).add(time)).mul(0.5).add(0.5);
const b = sin(uv.x.add(uv.y).mul(5).add(time)).mul(0.5).add(0.5);
return vec3(r, g, b);
});Integration with Three.js material system for creating custom shaders with TSL.
/**
* Create node material using TSL nodes
*/
class NodeMaterial extends ShaderMaterial {
/**
* Create node-based material
* @param parameters - Material parameters with TSL nodes
*/
constructor(parameters?: NodeMaterialParameters);
/** Whether this is a node material */
isNodeMaterial: true;
/** Vertex position output node */
positionNode: Node | null;
/** Fragment color output node */
colorNode: Node | null;
/** Normal output node */
normalNode: Node | null;
/** Opacity output node */
opacityNode: Node | null;
/** Emissive color node */
emissiveNode: Node | null;
/** Metalness node */
metalnessNode: Node | null;
/** Roughness node */
roughnessNode: Node | null;
/** Custom vertex shader nodes */
vertexNode: Node | null;
/** Custom fragment shader nodes */
fragmentNode: Node | null;
}
interface NodeMaterialParameters extends MaterialParameters {
colorNode?: Node;
opacityNode?: Node;
normalNode?: Node;
emissiveNode?: Node;
metalnessNode?: Node;
roughnessNode?: Node;
positionNode?: Node;
vertexNode?: Node;
fragmentNode?: Node;
}// TSL type system
type NodeRepresentation = Node | number | boolean | string | Vector2 | Vector3 | Vector4 | Color | Matrix3 | Matrix4;
// Node builder context
interface NodeBuilder {
material: Material;
renderer: Renderer;
object: Object3D;
camera: Camera;
scene: Scene;
getUniformFromNode(node: Node): string;
getVaryingFromNode(node: Node): string;
getAttributeFromNode(node: Node): string;
getCode(): string;
addFlowCode(code: string): void;
}
// Node frame context
interface NodeFrame {
time: number;
deltaTime: number;
frameId: number;
renderId: number;
updateBeforeRender(renderer: Renderer, scene: Scene, camera: Camera, geometry: BufferGeometry, material: Material, object: Object3D): void;
}
// Core node types
interface FloatNode extends Node { type: 'float'; }
interface IntNode extends Node { type: 'int'; }
interface BoolNode extends Node { type: 'bool'; }
interface Vec2Node extends Node { type: 'vec2'; }
interface Vec3Node extends Node { type: 'vec3'; }
interface Vec4Node extends Node { type: 'vec4'; }
interface ColorNode extends Node { type: 'vec3'; }
interface Mat3Node extends Node { type: 'mat3'; }
interface Mat4Node extends Node { type: 'mat4'; }
interface TextureNode extends Node { type: 'vec4'; }
interface UniformNode extends Node { }
interface AttributeNode extends Node { }
interface VaryingNode extends Node { }
interface CodeNode extends Node { }Complete Custom Material with TSL:
import * as THREE from 'three';
import {
NodeMaterial,
vec3, vec4, float, color,
texture, mix, sin, cos, normalize, dot,
time, uv, normalWorld, cameraPosition,
uniform, mul, add, pow, clamp
} from 'three/tsl';
// Create custom animated material
class AnimatedCrystalMaterial extends NodeMaterial {
constructor() {
super();
// Material properties
this.baseColor = uniform(color(0x3366ff));
this.emissionColor = uniform(color(0x66ccff));
this.roughness = uniform(0.1);
this.metalness = uniform(0.9);
// Animation parameters
this.animSpeed = uniform(1.0);
this.waveFreq = uniform(2.0);
this.waveAmp = uniform(0.1);
this.setupNodes();
}
setupNodes() {
// Animated vertex displacement
const position = positionLocal;
const displacement = sin(position.y.mul(this.waveFreq).add(time.mul(this.animSpeed)))
.mul(this.waveAmp);
this.positionNode = position.add(normalLocal.mul(displacement));
// Animated colors based on viewing angle
const viewDirection = normalize(cameraPosition.sub(positionWorld));
const fresnel = dot(normalWorld, viewDirection);
const fresnelPow = pow(clamp(fresnel, 0, 1), 2);
// Animated emission
const timeOscillation = sin(time.mul(2)).mul(0.3).add(0.7);
const animatedEmission = this.emissionColor.mul(timeOscillation).mul(fresnelPow);
// Final material properties
this.colorNode = this.baseColor;
this.emissiveNode = animatedEmission;
this.roughnessNode = this.roughness;
this.metalnessNode = this.metalness;
// Custom fragment effects
const sparkle = sin(uv.x.mul(50).add(time)).mul(sin(uv.y.mul(50).add(time)));
const sparkleIntensity = clamp(sparkle.mul(10), 0, 1);
this.colorNode = mix(
this.baseColor,
color(1, 1, 1),
sparkleIntensity.mul(0.3)
);
}
// Update animation parameters
updateAnimation(deltaTime) {
// Animation updates can be done here
// Properties are automatically updated via uniforms
}
}
// Usage in scene
const geometry = new THREE.IcosahedronGeometry(1, 2);
const material = new AnimatedCrystalMaterial();
const crystal = new THREE.Mesh(geometry, material);
scene.add(crystal);
// Animation loop
const clock = new THREE.Clock();
function animate() {
const deltaTime = clock.getDelta();
material.updateAnimation(deltaTime);
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();Install with Tessl CLI
npx tessl i tessl/npm-three