Three.js Materials: Which One Should You Use?

For most realistic objects, use MeshStandardMaterial: physically-based, responds correctly to lights, and the sensible default. Reach for MeshPhysicalMaterial when you need clearcoat, transmission (glass) or iridescence; MeshBasicMaterial for unlit flat-color or UI elements; and a custom ShaderMaterial or TSL node material when no built-in material can produce the look you need.

Last updated . Verified against three.js r181.

Read the FAQJump to code

The built-in materials, ranked by how "real" they look

  • MeshBasicMaterial: no lighting response at all, flat color or texture. Fast, used for UI overlays, wireframes, unlit sprites, or stylized flat-shaded looks.
  • MeshLambertMaterial: cheap diffuse-only lighting (no specular highlights). Rarely the right choice today; MeshStandardMaterial isn't meaningfully more expensive on modern GPUs and looks far better.
  • MeshPhongMaterial: adds a specular highlight, but not physically based; the highlight doesn't scale correctly with roughness or light intensity. Mostly legacy at this point.
  • MeshStandardMaterial: physically-based rendering (PBR): roughness, metalness, correct light response. The default choice for anything meant to look real.
  • MeshPhysicalMaterial: extends Standard with clearcoat (car paint, varnish), transmission (glass, water; refracts what's behind it), iridescence, sheen. More expensive; use it only for the specific objects that need these effects.

When a built-in material isn't enough

Built-in materials cover realistic PBR surfaces. You need a custom shader when the look isn't physically-based at all: toon/cel shading, dissolve effects, holograms, procedural patterns, vertex displacement (grass, water, cloth), or anything driven by a uniform that changes every frame in a way no built-in property exposes.

Two ways to write one:

  • GLSL ShaderMaterial: full control, write vertexShader/fragmentShader strings directly. Works on WebGLRenderer.
  • TSL node material: build the same logic as a JavaScript node graph (material.colorNode, .positionNode, etc.) that compiles to WGSL for WebGPURenderer and GLSL for WebGLRenderer from one source. The direction the ecosystem is moving; see the WebGPU vs WebGL guide.

The texture maps that make materials read as real

A flat color rarely looks convincing. The maps that carry the most visual weight, roughly in order of impact:

  1. map: the base color/albedo texture.
  2. normalMap: fakes surface detail (bumps, grooves) without extra geometry. Usually the single biggest visual upgrade for the least cost.
  3. roughnessMap: varies roughness across the surface (a scratched vs polished metal, for example).
  4. aoMap (ambient occlusion): darkens crevices, adds a sense of contact and depth.
  5. metalnessMap: marks which parts of a surface are metal vs dielectric, for mixed materials (e.g. a coin with a painted design).

Most of these come bundled as a texture set from a PBR material library. See the tools directory for texture sources.

Tools and libraries

Code

MeshStandardMaterial + maps (r181)
1import * as THREE from 'three'
2
3const loader = new THREE.TextureLoader()
4const material = new THREE.MeshStandardMaterial({
5 map: loader.load('/textures/albedo.jpg'),
6 normalMap: loader.load('/textures/normal.jpg'),
7 roughnessMap: loader.load('/textures/roughness.jpg'),
8 roughness: 1,
9 metalness: 0,
10})
11
12const mesh = new THREE.Mesh(new THREE.SphereGeometry(1, 64, 64), material)
13scene.add(mesh)
Custom TSL material (r181)
1import { texture, normalMap, mix, color, uv, timerLocal } from 'three/tsl'
2
3const material = new THREE.MeshStandardNodeMaterial()
4material.colorNode = mix(color(0x111827), color(0x6366f1), uv().y)
5material.normalNode = normalMap(texture(normalTexture))
6// A uniform-driven pulse, impossible to express as a static material property:
7material.emissiveNode = color(0x6366f1).mul(timerLocal().sin().add(1).mul(0.5))

Learn this properly

Learn Practical TSL

Your First Node Material

The starting point for writing custom TSL materials rather than configuring a built-in one.

Start the lesson (6 minutes)

Frequently asked questions

What material should I use by default in Three.js?

MeshStandardMaterial. It's physically-based, responds correctly to scene lighting, and is the right default for realistic surfaces. Reach for MeshPhysicalMaterial only when you specifically need clearcoat, transmission (glass) or similar advanced effects.

What's the difference between MeshStandardMaterial and MeshPhysicalMaterial?

MeshPhysicalMaterial extends MeshStandardMaterial with additional physically-based properties: clearcoat (car paint), transmission (glass/refraction), sheen and iridescence. It's more expensive to render, so use it only on the objects that need those specific effects.

When should I write a custom shader instead of using a built-in material?

When the effect isn't physically-based lighting at all: toon shading, dissolve, procedural patterns, vertex displacement like grass or water, or anything driven by a per-frame uniform no built-in property exposes.

Keep reading