Shaders for three.js (ShaderMaterial)
three.js renders with WebGL, so your effects are GLSL. A ShaderMaterial takes vertex and fragment shader source plus uniforms. The export gives you the complete material; paste it onto a plane or any mesh that has UVs.
Things to know about GLSL
- Uniforms such as
timeare values you update every frame:material.uniforms.time.value = clock.getElapsedTime(). - UV coordinates arrive from the vertex shader as a
varying vec2 vUv. - ShaderBattle's mesh lessons use three.js directly, with real vertex and fragment shaders on 3D shapes.
Cheat sheet
| Idea | Write |
|---|---|
| Vectors | vec2 vec3 vec4 |
| Declare a value | float d = length(p); |
| A function | float circle(vec2 p, float r) { ... } |
| Matrices | mat2 mat3, multiply with M * v (this site writes v * M for lesson rotations) |
| Blend | mix(a, b, t) |
| Fractional part | fract(x) |
| Clamp to 0..1 | clamp(x, 0.0, 1.0) |
| Remainder | mod(a, b) (always positive for positive b) |
| Choose | c ? a : b |
| Angle of a vector | atan(y, x) |
| Time in seconds | time |
| Pixel position | uv (0..1, bottom-left) |
| Width / height | aspect |
| Entry point | vec4 frag(vec2 uv) { return …; } |
Handy built-ins: sin cos abs sign floor ceil fract step smoothstep clamp min max pow exp sqrt length distance dot cross normalize reflect
Gotchas
- Every pixel runs on its own, with no memory of other pixels.
- Declare a function above the place you use it.
- Prefer 2.0 over 2 in maths. This site fixes most cases for you; real engines may not.
- Loops need a small fixed count: for (int i = 0; i < 5; i++).
Example: a glowing ring
The same shader as it is written for three.js:
vec4 frag(vec2 uv)
{
vec2 p = (uv - 0.5) * vec2(aspect, 1.0);
float d = length(p);
float m = 1.0 - smoothstep(0.0, 0.02, abs(d - 0.3));
vec3 bg = vec3(0.08, 0.08, 0.12);
vec3 fg = vec3(0.4, 0.9, 1.0);
return vec4(mix(bg, fg, m), 1.0);
} Exporting to three.js
Put the material on a PlaneGeometry (or any mesh with UVs) and update the time uniform every frame.
// ShaderBattle: Glowing ring
import * as THREE from 'three';
export const material = new THREE.ShaderMaterial({
uniforms: {
time: { value: 0 },
aspect: { value: 1 }, // width / height of your plane
},
vertexShader: /* glsl */ `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: /* glsl */ `
uniform float time;
uniform float aspect;
varying vec2 vUv;
vec4 frag(vec2 uv)
{
vec2 p = (uv - 0.5) * vec2(aspect, 1.0);
float d = length(p);
float m = 1.0 - smoothstep(0.0, 0.02, abs(d - 0.3));
vec3 bg = vec3(0.08, 0.08, 0.12);
vec3 fg = vec3(0.4, 0.9, 1.0);
return vec4(mix(bg, fg, m), 1.0);
}
void main() {
gl_FragColor = frag(vUv);
}
`,
});
// in your render loop: material.uniforms.time.value = clock.getElapsedTime(); Questions
Can I use this on a 3D model and not just a plane?
Yes. Any mesh with UV coordinates works. The mesh lessons teach vertex shaders as well.
Other languages
Unity (Built-in) · Unity (URP) · Unreal · Shadertoy / WebGL · OpenGL (LWJGL, raylib) · GameMaker · LÖVE (Lua) · Godot · Apple (iOS, macOS) · WebGPU / Bevy