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);
}