Shader playground

Four fragment shaders, written by hand, compiled live with WebGL2. Each gets the same uniforms — u_time, u_mouse (normalized), u_scroll — plus the site palette. Move the cursor over the canvas; scroll the page.

Domain-warped value noise — the calm, organic default.

Fragment source

// fbm flow — domain-warped value noise, the kind of thing that makes
// a hero feel alive without shouting. Drag the mouse to lean the field.
float hash(vec2 p) {
  p = fract(p * vec2(123.34, 456.21));
  p += dot(p, p + 45.32);
  return fract(p.x * p.y);
}
float noise(vec2 p) {
  vec2 i = floor(p), f = fract(p);
  vec2 u = f * f * (3.0 - 2.0 * f);
  float a = hash(i),               b = hash(i + vec2(1, 0));
  float c = hash(i + vec2(0, 1)),  d = hash(i + vec2(1, 1));
  return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
}
float fbm(vec2 p) {
  float s = 0.0, a = 0.5;
  for (int i = 0; i < 6; i++) { s += a * noise(p); p *= 2.02; a *= 0.5; }
  return s;
}
void main() {
  float aspect = u_resolution.x / max(u_resolution.y, 1.0);
  vec2 p = vec2((v_uv.x - 0.5) * aspect, v_uv.y - 0.5) * 2.5;
  p += (u_mouse - 0.5) * 0.6;
  float t = u_time * 0.08;
  vec2 q = vec2(fbm(p + vec2(0.0, t)), fbm(p + vec2(5.2, -t)));
  vec2 r = vec2(fbm(p + 3.0 * q + vec2(1.7, 9.2)),
                fbm(p + 3.0 * q + vec2(8.3, 2.8)));
  float f = fbm(p + 2.0 * r + t);
  vec3 col = mix(u_bg, u_accent, smoothstep(0.2, 0.9, f));
  col = mix(col, u_accent2, smoothstep(0.6, 1.0, r.x + r.y) * 0.6);
  fragColor = vec4(col, 1.0);
}