mcpField
T
threejs-syntax-shaders

threejs-syntax-shaders

github.com/impertio-studioDevelopment23143 installsUpdated 30w ago
Repository

About threejs-syntax-shaders

threejs-syntax-shaders is a shader reference skill for Three.js developers. It covers material options, GLSL syntax, and common pitfalls when writing or debugging custom shaders.

Key features

  1. 01Compare ShaderMaterial and RawShaderMaterial
  2. 02Explain uniform types and value formats
  3. 03Cover ShaderChunk and onBeforeCompile
  4. 04Describe GLSL3 syntax and define updates
  5. 05Flag common shader compilation errors

Use cases

  1. 01Build custom Three.js material shaders
  2. 02Port external GLSL shaders to Three.js
  3. 03Debug uniform or GLSL compilation issues

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/impertio-studio/three.js-claude-skill-package --skill threejs-syntax-shaders

After installing, type/threejs-syntax-shadersin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

references/
anti-patterns.md
examples.md
methods.md

Details

From the project README

threejs-syntax-shaders

Quick Reference

ShaderMaterial vs RawShaderMaterial
AspectShaderMaterialRawShaderMaterial
Built-in uniformsAutomatically injectedNONE -- you MUST declare everything
Built-in attributesAutomatically declaredNONE -- you MUST declare everything
#include <chunk>SupportedNOT supported
Precision declarationAutomaticYou MUST add precision mediump float;
Use caseExtend Three.js renderingFull shader control, porting external shaders
PerformanceSlight overhead from unused built-insMinimal shader overhead
Uniform Type Map
GLSL TypeJavaScript Value
float{ value: 1.0 }
int{ value: 1 }
bool{ value: true }
vec2{ value: new THREE.Vector2() }
vec3{ value: new THREE.Vector3() } or { value: new THREE.Color() }
vec4{ value: new THREE.Vector4() }
mat3{ value: new THREE.Matrix3() }
mat4{ value: new THREE.Matrix4() }
sampler2D{ value: texture } (a THREE.Texture instance)
samplerCube{ value: cubeTexture }
float[]{ value: [1.0, 2.0, 3.0] }
vec3[]{ value: [new THREE.Vector3(), ...] }
Critical Warnings

NEVER pass a bare value as a uniform -- ALWAYS wrap it in { value: ... }. Writing uniforms: { uTime: 0.0 } silently fails; ALWAYS write uniforms: { uTime: { value: 0.0 } }.

NEVER declare built-in uniforms or attributes in a ShaderMaterial shader -- Three.js injects them automatically. Redeclaring causes a GLSL compilation error.

ALWAYS declare ALL uniforms, attributes, and precision in RawShaderMaterial shaders -- nothing is injected for you.

NEVER use gl_FragColor or texture2D() when glslVersion is THREE.GLSL3 -- use a declared out vec4 variable and texture() instead.

ALWAYS call material.needsUpdate = true after changing defines -- defines are compiled into the shader, so changes require recompilation.


ShaderMaterial

Constructor
import * as THREE from 'three';

const material = new THREE.ShaderMaterial({
  uniforms: {
    uTime: { value: 0.0 },
    uColor: { value: new THREE.Color(0x00ff00) },
    uTexture: { value: someTexture },
  },
  vertexShader: `
    varying vec2 vUv;
    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
  fragmentShader: `
    uniform float uTime;
    uniform vec3 uColor;
    varying vec2 vUv;
    void main() {
      gl_FragColor = vec4(uColor * vUv.x, 1.0);
    }
  `,
  transparent: false,
  wireframe: false,
  side: THREE.FrontSide,
});
Properties
PropertyTypeDefaultDescription
uniformsObject{}{ name: { value: ... } } format
uniformsGroupsArray[]Uniform buffer objects (UBO)
vertexShaderstring--GLSL vertex shader source
fragmentShaderstring--GLSL fragment shader source
definesObject{}Preprocessor #define directives
extensionsObject{}GLSL extensions to enable
wireframebooleanfalseWireframe rendering
lightsbooleanfalsePass light uniforms to shader
fogbooleanfalsePass fog uniforms to shader
clippingbooleanfalseEnable clipping planes
glslVersionstring | nullnullnull for GLSL1, THREE.GLSL3 for GLSL 3.0 ES
defaultAttributeValuesObject--Fallback values for missing attributes

Built-in Uniforms (ShaderMaterial Only)

These are injected automatically. NEVER declare them yourself.

// Transform matrices
uniform mat4 modelMatrix;           // Object -> World
uniform mat4 modelViewMatrix;       // Object -> Camera
uniform mat4 projectionMatrix;      // Camera -> Clip
uniform mat4 viewMatrix;            // World -> Camera
uniform mat3 normalMatrix;          // Transpose inverse of modelViewMatrix

// Camera
uniform vec3 cameraPosition;        // Camera world position

// When lights: true
uniform vec3 ambientLightColor;
// Plus structured arrays for directional, point, spot, hemisphere lights

Built-in Attributes (ShaderMaterial Only)

These are injected automatically. NEVER declare them yourself.

attribute vec3 position;    // Vertex position
attribute vec3 normal;      // Vertex normal
attribute vec2 uv;          // Primary UV coordinates
attribute vec2 uv2;         // Secondary UV (for aoMap, lightMap)
attribute vec4 tangent;     // Tangent vector (if computeTangents was called)
attribute vec3 color;       // Vertex color (if geometry has color attribute)

RawShaderMaterial

Use when you need full control over the shader source. NOTHING is injected.

const material = new THREE.RawShaderMaterial({
  uniforms: {
    uModelViewMatrix: { value: new THREE.Matrix4() },
    uProjectionMatrix: { value: new THREE.Matrix4() },
  },
  vertexShader: `
    precision highp float;
    attribute vec3 position;
    uniform mat4 uModelViewMatrix;
    uniform mat4 uProjectionMatrix;
    void main() {
      gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(position, 1.0);
    }
  `,
  fragmentShader: `
    precision highp float;
    void main() {
      gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }
  `,
});

ALWAYS add precision highp float; (or mediump) at the top of both shaders in RawShaderMaterial. Omitting precision causes a GLSL compilation error on mobile and some desktop drivers.


ShaderChunk -- Reusing Three.js Shader Code

THREE.ShaderChunk contains all internal shader fragments. Use #include <chunk_name> in ShaderMaterial (NOT RawShaderMaterial).

Common Chunks
ChunkPurpose
<common>Shared constants and functions (PI, saturate, etc.)
<fog_pars_vertex> / <fog_vertex>Fog support (vertex)
<fog_pars_fragment> / <fog_fragment>Fog support (fragment)
<shadowmap_pars_vertex> / <shadowmap_vertex>Shadow support (vertex)
<shadowmap_pars_fragment> / <shadowmap_fragment>Shadow support (fragment)
<lights_pars_begin>Light structure declarations
<begin_vertex>Initializes transformed variable from position
<project_vertex>Applies modelViewMatrix and projectionMatrix
<normal_fragment_begin>Normal mapping setup
<color_pars_vertex> / <color_vertex>Vertex color support
Accessing Chunks Programmatically
// Read the source of any chunk
console.log(THREE.ShaderChunk.common);
console.log(THREE.ShaderChunk.fog_pars_vertex);

onBeforeCompile -- Patching Built-in Materials

Modify an existing material's shader at compile time. This preserves PBR lighting, shadows, and all built-in features.

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

material.onBeforeCompile = (shader) => {
  shader.uniforms.uTime = { value: 0.0 };

  shader.vertexShader = shader.vertexShader.replace(
    '#include <begin_vertex>',
    `
    #include <begin_vertex>
    transformed.y += sin(transformed.x * 5.0 + uTime) * 0.5;
    `
  );

  // Store reference for uniform updates
  material.userData.shader = shader;
};

// ALWAYS override customProgramCacheKey when using onBeforeCompile
material.customProgramCacheKey = () => 'my-wavy-material';

// In animation loop
if (material.userData.shader) {
  material.userData.shader.uniforms.uTime.value = clock.getElapsedTime();
}

ALWAYS override customProgramCacheKey() when using onBeforeCompile -- without it, Three.js may reuse a cached unpatched shader program, causing your modifications to silently disappear.

ALWAYS check material.userData.shader exists before accessing uniforms -- the shader object is created lazily on first render and can be recreated when material.needsUpdate = true.


Defines -- Preprocessor Directives

const material = new THREE.ShaderMaterial({
  defines: {
    USE_FOG: '',           // #define USE_FOG
    MAX_LIGHTS: 4,         // #define MAX_LIGHTS 4
    EPSILON: '0.001',      // #define EPSILON 0.001
  },
  // ...shaders
});

Changing defines at runtime:

material.defines.MAX_LIGHTS = 8;
material.needsUpdate = true;  // REQUIRED -- triggers recompilation

GLSL3 Mode

const material = new THREE.ShaderMaterial({
  glslVersion: THREE.GLSL3,
  vertexShader: `
    in vec3 position;           // 'attribute' becomes 'in'
    uniform mat4 modelViewMatrix;
    uniform mat4 projectionMatrix;
    out vec3 vPosition;         // 'varying' becomes 'out'

    void main() {
      vPosition = position;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
  fragmentShader: `
    precision highp float;
    in vec3 vPosition;          // 'varying' becomes 'in'
    out vec4 fragColor;         // replaces gl_FragColor

    void main() {
      fragColor = vec4(vPosition * 0.5 + 0.5, 1.0);
    }
  `,
});
GLSL1 vs GLSL3 Syntax
GLSL1GLSL3Context
attributeinVertex shader inputs
varying (vertex)outVertex shader outputs
varying (fragment)inFragment shader inputs
gl_FragColorDeclared out vec4Fragment shader output
texture2D()texture()Texture sampling
textureCube()texture()Cube texture sampling

Uniform Update Pattern

// At creation
const material = new THREE.ShaderMaterial({
  uniforms: {
    uTime: { value: 0.0 },
    uResolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) },
    uMouse: { value: new THREE.Vector2() },
  },
  vertexShader: '...',
  fragmentShader: '...',
});

// In animation loop -- update the .value property directly
function animate() {
  material.uniforms.uTime.value = performance.now() / 1000;
  material.uniforms.uMouse.value.set(mouseX, mouseY);
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
}

NEVER replace the uniform object itself (e.g., material.uniforms.uTime = { value: 5 }). ALWAYS mutate the existing .value property. Replacing the object breaks the internal reference.


Reference Links

Official Sources

Comments

Markdown supported