Skip to main content
skill Claude Code CX Codex Cursor GitHub Copilot

threejs-materials · cloudai-x — Claude Code skill: materials and setup

Helps choose and configure Three.js materials for meshes, lines, and points. Use it when a scene needs PBR, lighting, transparency, or controlled surface behavior.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add CloudAI-X/threejs-skills --skill threejs-materials -a claude-code
Open on GitHub

What threejs-materials does

The skill covers Three.js materials for several rendering needs: MeshBasicMaterial without lighting, Lambert and Phong for diffuse and specular response, Standard and Physical for PBR, and Toon or Matcap for stylized surfaces. It also covers point and line materials, plus ShaderMaterial and RawShaderMaterial for custom GLSL.

The examples show color, normal, roughness, metalness, ambient-occlusion, light, and environment maps, along with transparency, blending, stencil, and surface-side settings. WebGPU node materials and TSL equivalents are included. The examples target three.js r186: the main section uses WebGLRenderer, while the WebGPU and TSL section uses WebGPURenderer. That renderer split is worth checking before applying the guidance to a WebGPU project.

What it helps with

  • choose a material for a lit or unlit surface
  • configure PBR with roughness and metalness
  • connect color, normal, and environment maps
  • fix aoMap sampling across UV channels
  • adapt material code for WebGPU and TSL

Example prompts for the agent

  • Choose a Three.js material for a metal surface and show the `MeshStandardMaterial` settings.
  • Check why `aoMap` renders incorrectly and explain the `uv1` and `texture.channel` setup.
  • Compare `MeshBasicMaterial`, `MeshPhongMaterial`, and `MeshPhysicalMaterial` for this scene.

How to install threejs-materials

1Skills CLI (Vercel)

npx skills add CloudAI-X/threejs-skills --skill threejs-materials -a claude-code

2Manually: put the files in place

~/.claude/skills/threejs-materials/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/threejs-materials/

From the same set10

The whole set →
threejs-animation CloudAI-X Skill for keyframe, skeletal and morph-target animation in Three.js. Play glTF animations, control bones, blend and crossfade clips on WebGL and WebGPU. Claude Code CX Codex Cursor +1 20,100installs threejs-fundamentals CloudAI-X Sets up Three.js r186 scenes, cameras, renderers, and animation loops for WebGL or WebGPU with TSL. Claude Code CX Codex Cursor +1 17,100installs threejs-shaders CloudAI-X Guides the agent through writing GLSL shaders, custom materials, and TSL nodes for Three.js r186 across WebGL and WebGPU. Claude Code CX Codex Cursor +1 15,400installs threejs-geometry CloudAI-X Teaches your coding agent how to build standard geometries, generate 3D text, and construct custom vertex buffers in three.js. Claude Code CX Codex Cursor +1 14,400installs threejs-interaction CloudAI-X Teaches your agent how to set up raycasting, camera controls, and object dragging in three.js for WebGL and WebGPU. Claude Code CX Codex Cursor +1 14,100installs threejs-postprocessing CloudAI-X A skill that adds post-processing pass chains to Three.js: bloom, depth of field, ambient occlusion, and custom screen shaders. Use it when you need visual effects, color grading, or blur on WebGL and WebGPU. Claude Code CX Codex Cursor +1 13,500installs threejs-lighting CloudAI-X Three.js r186 lighting skill for WebGL and WebGPU. It guides physical light units, cascaded shadow maps, and RectAreaLight setup. Claude Code CX Codex Cursor +1 13,100installs threejs-loaders CloudAI-X Teaches your AI agent to load glTF models, textures, and HDR maps in three.js r186. Useful when wiring loading progress, color spaces, or WebGPU environments. Claude Code CX Codex Cursor +1 12,700installs

Other versions of threejs-materials

Similar

venice-audio-music nexu-io Queues and retrieves music generation jobs through Venice.ai endpoints. Built for short jingles, background loops, and prototype scoring. Claude Code CX Codex Cursor +1 2,700installs game-development sickn33 Guides game architecture decisions, platform choices, and core loop structure. Directs the assistant to specialized sub-skills for web, mobile, PC, or VR. Claude Code CX Codex Cursor +1 3,800installs orbit-card heygen-com Adds a 10-second 1920×1080 HyperFrames motion block where a single feature card orbits a dot sphere using exported Blender camera paths. Claude Code CX Codex Cursor +1 9,800installs sdf earthtojake This skill authors and validates SDFormat files for models and worlds, then helps prepare them for simulator handoff. Claude Code CX Codex Cursor +1 10,500installs urdf earthtojake This skill helps create, validate, and debug URDF robot descriptions. Use it when working with links, joints, geometry, meshes, or inertial data. Claude Code CX Codex Cursor +1 10,400installs hatch-pet official openai Creates and packages animated pets and spritesheets for Codex. Use it to turn a character concept, reference image, or brand cues into a pet. Claude Code CX Codex Cursor +1 3,300installs gcode earthtojake Slices STL, 3MF, and OBJ models into printer-ready G-code through OrcaSlicer. Use it when you need a print file without connecting to the printer. Claude Code CX Codex Cursor +1 9,500installs dxf earthtojake Generates and validates 2D DXF drawings from Python build123d scripts: outlines, gaskets, panels, flat patterns for cutting. Claude Code CX Codex Cursor +1 9,100installs

Questions about threejs-materials

How do I install threejs-materials in Claude Code?

Run npx skills add CloudAI-X/threejs-skills --skill threejs-materials -a claude-code, run npx skills add CloudAI-X/threejs-skills --skill threejs-materials -a claude-code again, then check ~/.claude/skills/threejs-materials/SKILL.md.

What does threejs-materials do?

The skill helps select and configure Three.js materials, from Basic, Lambert, and Phong to Standard, Physical, Toon, point, and line materials. It also covers maps, lighting, environment response, and WebGPU options using TSL.

Which Three.js version do the examples target?

The examples target three.js r186. The main section uses WebGLRenderer; the WebGPU and TSL section uses WebGPURenderer.

Source text

SKILL.md · MIT
---
name: threejs-materials
description: "Choosing and configuring three.js materials - unlit, diffuse, specular, PBR, toon, point and line materials - shared properties like blending and stencil, environment response, and the WebGPU node-material and TSL equivalents. Use when styling meshes or tuning material behavior."
---

# Three.js Materials

Targets three.js r186. Examples use WebGLRenderer; the WebGPU and TSL section uses WebGPURenderer.

## Quick Start

```javascript
import * as THREE from "three";

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
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.set(0, 0, 3);

// PBR material - the default choice for lit, realistic surfaces
const material = new THREE.MeshStandardMaterial({
  color: 0x00aaff,
  roughness: 0.35,
  metalness: 0.1,
});
const mesh = new THREE.Mesh(new THREE.SphereGeometry(1, 48, 32), material);
scene.add(mesh);
scene.add(new THREE.HemisphereLight(0xffffff, 0x444444, 1)); // lit materials render black without lights
const light = new THREE.DirectionalLight(0xffffff, 2);
light.position.set(3, 3, 3);
scene.add(light);

function animate() {
  mesh.rotation.y += 0.01;
  renderer.render(scene, camera);
}

renderer.setAnimationLoop(animate);
```

## Material Types Overview

| Material | Use case | Lighting |
| -------------------- | ------------------------------------- | ------------------ |
| MeshBasicMaterial | Unlit color, textures, wireframes | None |
| MeshLambertMaterial | Matte diffuse surfaces | Diffuse |
| MeshPhongMaterial | Shiny plastic, cheap specular | Diffuse + specular |
| MeshStandardMaterial | PBR metal/rough workflow | PBR |
| MeshPhysicalMaterial | PBR plus clearcoat, transmission, sheen, iridescence | PBR |
| MeshToonMaterial | Cel shading | Toon steps |
| MeshMatcapMaterial | Lit look baked into a matcap texture, no lights needed | Baked |
| ShadowMaterial | Transparent surface that shows only the shadows it receives | Shadows only |
| PointsMaterial | Point clouds | None |
| LineBasicMaterial / LineDashedMaterial | Lines | None |
| ShaderMaterial / RawShaderMaterial | Custom GLSL, WebGLRenderer only | Custom |

Color data textures (map, emissiveMap, specularMap, sheenColorMap, specularColorMap, LDR matcap, a canvas or video used as color) need `texture.colorSpace = THREE.SRGBColorSpace`, or colors render washed out. Scalar maps (normal, bump, displacement, roughness, metalness, ao, alpha, toon gradientMap) keep the default. HDR loaders set their own color space.

## MeshBasicMaterial

No lighting calculations. Fast, always visible. Supports `envMap` with `reflectivity` (0 to 1) and `combine` (`THREE.MultiplyOperation`, `THREE.MixOperation`, `THREE.AddOperation`); `specularMap` (sRGB) masks the env-map contribution.

```javascript
const map = await new THREE.TextureLoader().loadAsync("color.jpg");
map.colorSpace = THREE.SRGBColorSpace;

const material = new THREE.MeshBasicMaterial({
  color: 0xffffff, // multiplied with the map
  map,
  transparent: true,
  opacity: 0.5,
  side: THREE.DoubleSide, // FrontSide (default), BackSide, DoubleSide
  fog: true, // affected by scene fog
});
```

## MeshLambertMaterial and MeshPhongMaterial

Lambert is diffuse-only. Phong adds a specular highlight; it is the cheap alternative when Standard is too heavy.

```javascript
const lambert = new THREE.MeshLambertMaterial({
  color: 0x00ff00,
  emissive: 0x111111, // self-illumination color
  emissiveIntensity: 1,
});

const phong = new THREE.MeshPhongMaterial({
  color: 0x0000ff,
  specular: 0x111111, // highlight color
  shininess: 30, // higher = sharper highlight
  flatShading: false, // flat vs smooth normals
});
```

Both also support `map`, `emissiveMap`, `specularMap` (sRGB; masks the env-map reflection, and the highlight on Phong), `normalMap`, `bumpMap`, `displacementMap`, `aoMap`, `envMap`, `envMapIntensity` and `envMapRotation`.

## MeshStandardMaterial (PBR)

Physically-based rendering. Recommended for realistic results.

```javascript
const map = await new THREE.TextureLoader().loadAsync("color.jpg");
map.colorSpace = THREE.SRGBColorSpace;
const ormMap = await new THREE.TextureLoader().loadAsync("orm.jpg"); // glTF ORM: ao red, roughness green, metalness blue; scalar: keep default

const material = new THREE.MeshStandardMaterial({
  color: 0xffffff, // multiplied with the map
  roughness: 1.0, // 0 = mirror, 1 = fully diffuse; multiplied by roughnessMap (green)
  metalness: 1.0, // 0 = dielectric, 1 = metal; multiplied by metalnessMap (blue), so keep 1 with a map
  map,
  roughnessMap: ormMap,
  metalnessMap: ormMap,
  normalMap: null, // surface detail; scale with normalScale (a Vector2)
  aoMap: ormMap, // ambient occlusion (red); intensity with aoMapIntensity
  displacementMap: null, // vertex displacement; scale with displacementScale, offset with displacementBias
  emissive: 0x000000,
  emissiveIntensity: 1,
  emissiveMap: null, // sRGB
  envMap: null, // or rely on scene.environment (see below)
  fog: true,
});
```

`lightMap` (with `lightMapIntensity`) bakes indirect light the same way aoMap bakes occlusion; an HDR or EXR lightMap is `LinearSRGBColorSpace`, which the loaders set.

### UV channels: aoMap and lightMap read the channel you pick

Every UV-mapped texture samples the attribute selected by `texture.channel`: 0 reads `uv` (the default), 1 reads `uv1`, 2 reads `uv2`, 3 reads `uv3`. aoMap and lightMap need no second UV set - they sample `uv` unless you change `channel`. GLTFLoader loads `TEXCOORD_1` as `uv1` and sets `channel` itself.

```javascript
const aoMap = await new THREE.TextureLoader().loadAsync("ao.jpg"); // scalar: keep default
const material = new THREE.MeshStandardMaterial({ aoMap }); // samples uv: no extra attribute needed

// Alternative for a separate unwrap (a baked AO or light map): copy your baker's u,v pairs into uv1
const bakedUv = new THREE.Float32BufferAttribute(mesh.geometry.attributes.position.count * 2, 2); // zero-filled until you copy them in
mesh.geometry.setAttribute("uv1", bakedUv); mesh.material = material;
aoMap.channel = 1; // this material now samples uv1 instead of uv
```

## MeshPhysicalMaterial (Advanced PBR)

Extends MeshStandardMaterial with layer and volume features. Texture variants exist for most of them: `clearcoatMap`, `clearcoatRoughnessMap`, `clearcoatNormalMap`, `transmissionMap`, `thicknessMap`, `iridescenceMap`, `iridescenceThicknessMap`, `sheenColorMap`, `sheenRoughnessMap`, `anisotropyMap`, `specularIntensityMap`, `specularColorMap`.

```javascript
const material = new THREE.MeshPhysicalMaterial({
  // Clearcoat (car paint, lacquer)
  clearcoat: 1.0, // 0 = none, 1 = full coat
  clearcoatRoughness: 0.1,
  // Transmission (glass, water) - refracts the opaque objects and scene.background behind it
  transmission: 1.0, // 0 = opaque, 1 = fully transmissive
  thickness: 0.5, // volume thickness for refraction
  attenuationDistance: 1, // world units; Infinity (the default) = no absorption
  attenuationColor: new THREE.Color(0xffaa66), // white light turns this color after attenuationDistance
  ior: 1.5, // index of refraction, 1 to 2.333
  // Sheen (fabric, velvet)
  sheen: 1.0,
  sheenColor: new THREE.Color(0xffffff),
  sheenRoughness: 0.5,
  // Iridescence (soap bubbles, oil slicks)
  iridescence: 1.0,
  iridescenceIOR: 1.3,
  iridescenceThicknessRange: [100, 400], // nanometers
  // Anisotropy (brushed metal); direction with anisotropyRotation
  anisotropy: 1.0,
  // Dispersion (prismatic glass); retroreflectivity (road signs, safety gear)
  dispersion: 0.0,
  retroreflectivity: 0.0,
  // Specular overrides: specularIntensity, specularColor (plus their maps)
});
```

Glass - transmission with a smooth surface and something behind it to refract:

```javascript
const glass = new THREE.MeshPhysicalMaterial({
  color: 0xffffff,
  metalness: 0,
  roughness: 0,
  transmission: 1,
  thickness: 0.5,
  ior: 1.5,
});
// transmission refracts the opaque objects and background behind it; scene.environment adds its reflections
```

## MeshToonMaterial

Cel shading. The gradient map is non-color data - keep its default color space.

```javascript
const steps = new Uint8Array([120, 200]); // two shading steps
const gradientMap = new THREE.DataTexture(steps, 2, 1, THREE.RedFormat);
gradientMap.minFilter = THREE.NearestFilter; // hard steps; LinearFilter blends them
gradientMap.magFilter = THREE.NearestFilter;
gradientMap.needsUpdate = true;

const material = new THREE.MeshToonMaterial({ color: 0x00ff00, gradientMap });
```

## MeshNormalMaterial and MeshDepthMaterial

```javascript
// Normals encoded as RGB - debugging and flat illustrative looks
const normals = new THREE.MeshNormalMaterial({ flatShading: false, wireframe: false });

// Depth as grayscale (BasicDepthPacking, the default); RGBADepthPacking packs it for reading back in a shader
const depth = new THREE.MeshDepthMaterial();
```

## PointsMaterial

For point clouds. `size` is in pixels; with `sizeAttenuation` (the default, perspective cameras only) it is multiplied by half the canvas height and divided by the point's view-space depth.

```javascript
const geometry = new THREE.BufferGeometry();
geometry.setAttribute("position", new THREE.Float32BufferAttribute([0, 0, 0, 1, 0.5, 0, 0.5, 1, 0], 3));

const material = new THREE.PointsMaterial({
  color: 0xffffff,
  size: 0.1,
  sizeAttenuation: true, // scale with distance
  alphaTest: 0.5, // with a map or alphaMap: discard fragments below this alpha
});

const points = new THREE.Points(geometry, material);
scene.add(points);
```

## LineBasicMaterial and LineDashedMaterial

WebGL and WebGPU always draw line primitives 1 px wide; `linewidth`, `linecap` and `linejoin` only affect the SVGRenderer. For thick lines under WebGLRenderer use `Line2` with `LineGeometry` and `LineMaterial` from `three/addons/lines/` (`linewidth` in pixels); LineMaterial is a ShaderMaterial, so WebGPURenderer needs `Line2` from `three/addons/lines/webgpu/Line2.js`, which uses `Line2NodeMaterial`.

```javascript
const geometry = new THREE.BufferGeometry();
geometry.setAttribute("position", new THREE.Float32BufferAttribute([-1, 0, 0, 1, 0, 0], 3));

const solid = new THREE.LineBasicMaterial({ color: 0xffffff }); // always 1 px

const dashed = new THREE.LineDashedMaterial({ color: 0xffffff, dashSize: 0.5, gapSize: 0.25 });
const line = new THREE.Line(geometry, dashed);
line.computeLineDistances(); // required, or the line draws solid
scene.add(line);
```

## ShaderMaterial (WebGLRenderer only)

Custom GLSL with three.js uniforms injected for you. It always compiles as GLSL ES 3.00: `texture()`, `textureLod()` and `dFdx` work, and `texture2D` / `gl_FragColor` remain as aliases - leave `glslVersion` unset. ShaderMaterial, RawShaderMaterial and `onBeforeCompile` have no WebGPURenderer path; there, use node materials and TSL.

```javascript
const timer = new THREE.Timer();
timer.connect(document); // pause when the tab is hidden

const material = new THREE.ShaderMaterial({
  uniforms: { time: { value: 0 } },
  vertexShader: `
    varying vec2 vUv;
    uniform float time;
    void main() {
      vUv = uv;
      vec3 pos = position;
      pos.z += sin(pos.x * 10.0 + time) * 0.1;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
    }
  `,
  fragmentShader: `
    varying vec2 vUv;
    uniform float time;
    void main() {
      vec3 col = 0.5 + 0.5 * cos(time + vUv.xyx * 3.0);
      gl_FragColor = vec4(col, 1.0);
    }
  `,
});

function animate() {
  timer.update(); // first thing every frame, no argument
  material.uniforms.time.value = timer.getElapsed();
  renderer.render(scene, camera);
}
```

Declared for you in the shader: `modelMatrix`, `modelViewMatrix`, `projectionMatrix`, `viewMatrix`, `normalMatrix`, `cameraPosition`, and the attributes `position`, `normal`, `uv` (plus `instanceMatrix` on an InstancedMesh, which you apply yourself: `projectionMatrix * modelViewMatrix * instanceMatrix * vec4(pos, 1.0)`, and `color` with vertexColors). The extra uv sets and `tangent` are not declared - add `in vec2 uv1;` yourself.

## RawShaderMaterial (WebGLRenderer only)

No declarations are prepended (only `#version` from glslVersion, the `SHADER_TYPE` and `SHADER_NAME` defines, and your `defines`), but the renderer still sets `projectionMatrix`, `modelViewMatrix`, `viewMatrix`, `normalMatrix`, `modelMatrix` and `cameraPosition` - declare them in the shader and never pass them in `uniforms`: a same-named entry is overwritten every draw (`modelMatrix`, `modelViewMatrix`, `normalMatrix`) or replaces the camera's value (`projectionMatrix`, `viewMatrix`, `cameraPosition`).

```javascript
const material = new THREE.RawShaderMaterial({
  glslVersion: THREE.GLSL3, // without it the shader is GLSL ES 1.00, which has no texture()
  vertexShader: `
    precision highp float;
    in vec3 position;
    uniform mat4 projectionMatrix;
    uniform mat4 modelViewMatrix;
    void main() {
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
  fragmentShader: `
    precision highp float;
    out highp vec4 fragColor; // your own output variable
    void main() {
      fragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }
  `,
});
```

## Common Properties

```javascript
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });

material.visible = true;
material.transparent = false; // true for opacity below 1; toggling it after the first render needs needsUpdate
material.opacity = 1.0;
material.alphaTest = 0; // discard fragments with alpha below the value
material.side = THREE.FrontSide; // FrontSide, BackSide, DoubleSide
material.depthTest = true;
material.depthWrite = true;
material.blending = THREE.NormalBlending; // or AdditiveBlending, CustomBlending
material.polygonOffset = false; // with the two values below, fixes z-fighting
material.polygonOffsetFactor = 0;
material.polygonOffsetUnits = 0;
material.toneMapped = true; // false keeps exact colors (UI overlays)
material.dithering = false; // true reduces banding in gradients
material.vertexColors = false;
```

```javascript
// Multiply and subtractive blending require premultiplied alpha (both renderers)
const material = new THREE.MeshBasicMaterial({ color: 0x8844ff });
material.blending = THREE.SubtractiveBlending; // same for THREE.MultiplyBlending
material.premultipliedAlpha = true; // without it the renderer logs an error and sets no blend function

// Stencil is off by default in both renderers: create the renderer with { stencil: true }
const maskMaterial = new THREE.MeshBasicMaterial({ colorWrite: false, depthWrite: false });
maskMaterial.stencilWrite = true; // master switch for the stencil test and write
maskMaterial.stencilRef = 1;
maskMaterial.stencilZPass = THREE.ReplaceStencilOp; // writes 1 where the mask draws; also stencilFail, stencilZFail
const contentMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000, stencilWrite: true, stencilRef: 1 });
contentMaterial.stencilFunc = THREE.EqualStencilFunc; // draws only where the mask wrote 1
contentMaterial.stencilFuncMask = 0xff; // compare mask; stencilWriteMask masks the write (both default 0xff)
// draw the mask first: give its mesh a lower renderOrder than the content mesh
```

## Multiple Materials per Mesh

```javascript
// One material per geometry group; BoxGeometry has six, in the order +x, -x, +y, -y, +z, -z
const geometry = new THREE.BoxGeometry(1, 1, 1);
const colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff];
const mesh = new THREE.Mesh(geometry, colors.map((color) => new THREE.MeshBasicMaterial({ color })));

// Custom groups: start, count, material index. Indexed geometry counts indices (BoxGeometry has 36),
// non-indexed geometry counts vertices; ranges outside every group are not drawn
geometry.clearGroups();
geometry.addGroup(0, 18, 0);
geometry.addGroup(18, 18, 1);
```

## Environment Maps

An equirectangular HDR file lights and reflects every PBR material in the scene. Both renderers prefilter it themselves - no PMREM step needed.

```javascript
import { HDRLoader } from "three/addons/loaders/HDRLoader.js";

const env = await new HDRLoader().loadAsync("environment.hdr");
env.mapping = THREE.EquirectangularReflectionMapping; // without it a background draws as a flat quad
scene.environment = env; // lights and reflects PBR materials
scene.background = env;
scene.environmentIntensity = 0.8; // scene-wide dimmer; scene.environmentRotation tilts it
```

```javascript
const cubeEnv = new THREE.CubeTextureLoader().load([
  "px.jpg", "nx.jpg", "py.jpg", "ny.jpg", "pz.jpg", "nz.jpg",
]);

const material = new THREE.MeshStandardMaterial({ envMap: cubeEnv });
material.envMapIntensity = 1.5; // scales only this material's own envMap
material.envMapRotation = new THREE.Euler(0, Math.PI / 4, 0);
```

`scene.environment` lights MeshStandardMaterial and MeshPhysicalMaterial on both renderers. With WebGLRenderer it also lights MeshLambertMaterial and MeshPhongMaterial; WebGPURenderer does not.

## Runtime Changes, Cloning, Disposal

```javascript
mesh.material.color.set(0xff0000); // uniform values: no recompile needed
mesh.material.roughness = 0.8;

// material.needsUpdate = true after changing a setting compiled into the shader:
// transparent, side, blending, flatShading, vertexColors, dithering, premultipliedAlpha, alphaHash,
// adding or removing a map slot, or shader source (alphaTest and envMap recompile by themselves)

const clone = mesh.material.clone(); // copies values; map slots share their textures, ShaderMaterial uniforms are cloned
```

Dispose a material nothing else uses: `material.dispose()` releases its GPU programs, which are deleted once no other material uses them; textures need their own `texture.dispose()`.

Performance, in order of impact:

1. Share one material instance across meshes - the renderer re-uploads material uniforms whenever consecutive draws switch material (identical settings already share one compiled program).
2. Prefer opaque rendering: transparent materials are sorted back to front by object and drawn after the opaque ones, still writing depth unless `depthWrite: false`, so stacked layers overdraw and intersecting ones misorder; for cutouts, alphaTest keeps the material opaque, so it draws in the opaque pass, correctly in any order.
3. Pick the cheapest material that looks right: Basic, then Lambert, Phong, Standard, Physical.

## WebGPU and TSL

MeshStandardMaterial and the other built-in classes run under WebGPURenderer unchanged - the renderer converts them to node materials automatically; ShaderMaterial, RawShaderMaterial, MeshDepthMaterial and MeshDistanceMaterial have no node equivalent and log `Material "..." is not compatible`. For custom shading use the node material classes from `three/webgpu` with TSL nodes from `three/tsl`; they target WebGPURenderer, and WebGLRenderer draws them only through the `WebGLNodesHandler` addon (`three/addons/tsl/WebGLNodesHandler.js`, passed to `renderer.setNodesHandler()`), a migration aid without VSM shadows, MRT, transmission or the WebGPU post-processing stack.

```javascript
import * as THREE from "three/webgpu";

const renderer = new THREE.WebGPURenderer({ antialias: true });
await renderer.init(); // before any other use of the renderer
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

const scene = new THREE.Scene();
scene.background = new THREE.Color(0x111111); // the WebGPU canvas is transparent by default
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(0, 0, 3);

function animate() {
  renderer.render(scene, camera);
}

renderer.setAnimationLoop(animate);
```

```javascript
import * as THREE from "three/webgpu";
import { texture, mix, color, float, time, positionLocal, normalLocal, sin } from "three/tsl";

// Unlit: the node equivalent of a color ShaderMaterial
const unlit = new THREE.MeshBasicNodeMaterial();
unlit.colorNode = mix(color(0x0066ff), color(0xff0066), sin(time.mul(0.5)).mul(0.5).add(0.5));

// Lit: node slots replace the fixed properties; needs lights or scene.environment
const map = await new THREE.TextureLoader().loadAsync("color.jpg");
map.colorSpace = THREE.SRGBColorSpace;
const lit = new THREE.MeshStandardNodeMaterial();
lit.colorNode = texture(map);
lit.roughnessNode = float(0.2);

// Vertex displacement on positionLocal, which already carries skinning, morphs, instancing
const shell = new THREE.MeshStandardNodeMaterial();
shell.positionNode = positionLocal.add(normalLocal.mul(sin(positionLocal.y.mul(6))).mul(0.05));
```

```javascript
import * as THREE from "three/webgpu";
import { instancedBufferAttribute } from "three/tsl";

// The WebGPU backend draws Points at 1 px whatever the size; draw larger points as quads
const positions = new THREE.InstancedBufferAttribute(new Float32Array(1500), 3); // fill with x, y, z per point
const points = new THREE.Sprite(
  new THREE.PointsNodeMaterial({ positionNode: instancedBufferAttribute(positions), size: 4, sizeAttenuation: false }) // 4 px squares
);
points.count = 500; points.frustumCulled = false; // one quad per point; culling would test only a 0.7-unit sphere at the sprite origin
scene.add(points);
```

## Common Mistakes

- `geometry.setAttribute("uv2", geometry.attributes.uv)` → `const material = new THREE.MeshStandardMaterial({ aoMap });`: aoMap samples the attribute picked by `aoMap.channel`, 0 (`uv`) by default, so it needs no extra attribute and uv2 is read only with `aoMap.channel = 2`; only a separate unwrap goes in `uv1` with `aoMap.channel = 1`.
- `new RGBELoader()` → `new HDRLoader()`: HDRLoader was renamed from RGBELoader in r180; RGBELoader is deprecated and still loads, with a warning - import HDRLoader from "three/addons/loaders/HDRLoader.js".
- `"three/examples/jsm/loaders/HDRLoader.js"` → `"three/addons/loaders/HDRLoader.js"`: the browser import maps map `three/addons/` to examples/jsm/ and define no `three/examples/` key, so the examples path fails there.
- `material.blending = THREE.MultiplyBlending;` → `material.blending = THREE.MultiplyBlending; material.premultipliedAlpha = true;`: multiply and subtractive blending require premultiplied alpha, or the renderer logs an error and sets no blend function.
- `material.stencilMask = 0xff;` → `material.stencilFuncMask = 0xff; material.stencilWriteMask = 0xff;`: `stencilMask` does not exist; assigning it just creates an unused field.
- `new THREE.WebGLRenderer({ antialias: true })` → `new THREE.WebGLRenderer({ antialias: true, stencil: true })`: the stencil buffer is off by default, so material stencil settings are silently ignored without it.
- `material.envMapIntensity = 0.5;` → `scene.environmentIntensity = 0.5;`: to dim scene.environment use the scene property - envMapIntensity attenuates only the material's own envMap.
- `material.uniforms.time.value = clock.getElapsedTime();` → `material.uniforms.time.value = timer.getElapsed();`: Clock is deprecated in r183 - create a THREE.Timer, call timer.connect(document) once, and timer.update() (no argument) first in the loop.
- `new THREE.ShaderMaterial({ glslVersion: THREE.GLSL3, fragmentShader })` → `new THREE.ShaderMaterial({ fragmentShader })`: ShaderMaterial already compiles as GLSL ES 3.00, so `texture()` works with glslVersion unset, while GLSL3 removes the `gl_FragColor` alias and a shader that writes it fails to compile.
- `uniforms: { modelViewMatrix: { value: new THREE.Matrix4() } }` → `uniform mat4 modelViewMatrix;`: declare the built-in matrices in a RawShaderMaterial's shader source - a same-named uniforms entry is overwritten every draw (modelMatrix, modelViewMatrix, normalMatrix) or replaces the camera's value (projectionMatrix, viewMatrix, cameraPosition).
- `new THREE.LineBasicMaterial({ linewidth: 5 })` → `new Line2(new LineGeometry().setPositions([-1, 0, 0, 1, 0, 0]), new LineMaterial({ linewidth: 5 }))`: WebGLRenderer always draws 1 px line primitives; Line2 needs a LineGeometry, not a BufferGeometry, and all three come from three/addons/lines/ (under WebGPURenderer, Line2 comes from three/addons/lines/webgpu/Line2.js with Line2NodeMaterial).
- `new THREE.ShaderMaterial({ vertexShader, fragmentShader })` → `new THREE.MeshBasicNodeMaterial()`: under WebGPURenderer use node materials and TSL - ShaderMaterial and RawShaderMaterial have no node equivalent and log "is not compatible".
- `import { texture } from "three/nodes";` → `import { texture } from "three/tsl";`: `three/nodes` does not exist - node materials come from `three/webgpu`, TSL functions from `three/tsl`.
- `import * as THREE from "three"; const m = new THREE.MeshStandardNodeMaterial();` → `import * as THREE from "three/webgpu"; const m = new THREE.MeshStandardNodeMaterial();`: node materials are exported only by three/webgpu and target WebGPURenderer.
- `new THREE.PointsMaterial({ size: 5 })` → `new THREE.Sprite(new THREE.PointsNodeMaterial({ positionNode: instancedBufferAttribute(positions) }))`: the WebGPU backend draws Points at 1 px whatever the size - set sprite.count to the number of points.
- `material.positionNode = positionGeometry.add(offset);` → `material.positionNode = positionLocal.add(offset);`: positionLocal carries skinning, morph targets, batching and instancing; positionGeometry is the raw rest-pose attribute.
- `map.encoding = THREE.sRGBEncoding;` → `map.colorSpace = THREE.SRGBColorSpace;`: `encoding` and `sRGBEncoding` no longer exist, so the old line does nothing and the color map renders washed out.
- `const m = new THREE.MeshStandardNodeMaterial(); m.colorNode = color(0xff0000);` → `const m = new THREE.MeshBasicNodeMaterial(); m.colorNode = color(0xff0000);`: MeshStandardNodeMaterial is lit and renders black without lights or an environment, so an unlit effect uses MeshBasicNodeMaterial.
- `new THREE.ShaderMaterial({ extensions: { derivatives: true }, fragmentShader })` → `new THREE.ShaderMaterial({ fragmentShader })`: the WebGL1 extension flags do nothing, because GLSL ES 3.00 has `dFdx`, `gl_FragDepth` and `textureLod` built in.

## See Also

- `threejs-textures` - loading, color spaces, filters and channels in depth
- `threejs-shaders` - GLSL authoring, shader chunks and debugging under WebGLRenderer
- `threejs-lighting` - lights, shadows and how they interact with materials

© CloudAI-X · MIT · CloudAI-X/threejs-skills

See also

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor