Shader Lab
02.The three layers

The three layers

Arc 1 · Mental Model · Lesson 2 of 35
coordinate signal color
3.00
0.08
0.50

Purpose

Every shader does three things in order — transform the coordinate, compute a signal from it, map the signal to color — and every effect you'll ever direct lives in one or more of those layers.

Key insight

Think of the input coordinate (usually a pixel's 2D position from (0,0) to (1,1)) as the raw address. The coordinate layer warps or zooms that address. The signal layer computes a number from the address — a distance, a wave, a noise value. The color layer turns that number into a hue.

Effects stack by modifying any of these three stages. "Zoom the pattern" is coordinate work. "Sharpen the edges" is signal work. "Shift the palette warmer" is color work.

Each slider above controls exactly one layer. Move them one at a time to see what each layer does.

Break it

1. Crank zoom to max (10). The pattern becomes so tight it reads as grey noise — the rings are fine but the screen can't resolve them. Teaches: coordinate scale directly controls the spatial frequency you perceive.

2. Pull softness to minimum. Hard posterized bands appear. Teaches: smooth transitions aren't a color choice — they're a signal choice. Step vs. smoothstep is where softness lives.

3. Pull warmth to either extreme. The same shapes remain — only the palette shifts. Teaches: upstream signal is unchanged by color choices; they're separable.

Direct Claude

"tighter pattern" zoom up "looser / wider pattern" zoom down "sharper edges" softness down "dreamier / foggier" softness up "warmer look" warmth right "cooler look" warmth left
Meta-phrase you gain here: "work in the coordinate layer to..." / "in the signal layer to..." / "in the color layer to..." This framing alone prevents 90% of miscommunication with a shader developer.
Combines with: every subsequent lesson — each is an instance of modifying one or more of these layers.
the fragment shader running above
uniform float u_zoom;      // coordinate layer
uniform float u_softness;  // signal layer
uniform float u_warmth;    // color layer

void main() {
  // Center uv at (0,0), aspect-corrected
  vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution) / min(u_resolution.x, u_resolution.y);

  // COORDINATE LAYER — scale the address before anything else
  vec2 p = uv * u_zoom;

  // SIGNAL LAYER — compute a number from the transformed coordinate
  float d = length(p);
  float rings = fract(d * 1.5 - u_time * 0.25);
  float signal = smoothstep(0.5 - u_softness, 0.5 + u_softness, rings);

  // COLOR LAYER — map the signal to a hue, tinted by warmth
  vec3 cool_dark  = vec3(0.12, 0.16, 0.26);
  vec3 cool_light = vec3(0.45, 0.65, 0.90);
  vec3 warm_dark  = vec3(0.22, 0.10, 0.12);
  vec3 warm_light = vec3(0.96, 0.56, 0.34);
  vec3 dark  = mix(cool_dark,  warm_dark,  u_warmth);
  vec3 light = mix(cool_light, warm_light, u_warmth);
  vec3 col = mix(dark, light, signal);

  gl_FragColor = vec4(col, 1.0);
}