Daily shaders
Every morning at eight, a bot on my phone asks how I feel. I answer in a word or a few lines, or not at all. As soon as I answer, or at noon if I don't, it looks up the day's weather in Saitama, hands both to Claude, and Claude writes a WebGL fragment shader: a small program that draws a moving picture on its own.
Before it goes up, the code is checked against a few rules (WebGL 1, bounded loops, nothing but time and the canvas size as input), and sent back to be fixed if it breaks one. What you see is what came out. My answer to the bot stays private; the titles only borrow its images.
This one didn't compile in your browser.
Sourcerefractive rain-on-glass droplets over drifting bokeh
precision mediump float;
uniform vec2 u_resolution;
uniform float u_time;
float h(vec2 p){ return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
float h1(float x){ return fract(sin(x * 91.345) * 47453.123); }
float n(vec2 p){
vec2 i = floor(p); vec2 f = fract(p);
f = f * f * (3.0 - 2.0 * f);
return mix(mix(h(i), h(i + vec2(1.0, 0.0)), f.x), mix(h(i + vec2(0.0, 1.0)), h(i + vec2(1.0, 1.0)), f.x), f.y);
}
float fbm(vec2 p){
float s = 0.0; float a = 0.5;
for (int i = 0; i < 4; i++) { s += a * n(p); p *= 2.03; a *= 0.5; }
return s;
}
vec3 drops(vec2 uv, float sc, float t){
vec2 p = uv * sc;
vec2 id = floor(p);
vec2 f = fract(p) - 0.5;
float r = h(id);
float slide = fract(t * 0.04 * (0.6 + r) + r * 5.0);
vec2 c = vec2((fract(r * 7.3) - 0.5) * 0.4, 0.3 - slide * 0.6);
vec2 q = (f - c) * vec2(1.0, 0.85);
float d = length(q);
float rad = 0.13 + 0.1 * fract(r * 13.1);
float life = sin(slide * 3.14159);
float m = smoothstep(rad, rad - 0.06, d) * life;
return vec3(q / rad * m, m);
}
vec3 scene(vec2 uv, float t){
float g = uv.y * 0.7 + 0.5;
vec3 col = mix(vec3(0.30, 0.27, 0.24), vec3(0.34, 0.42, 0.44), clamp(g, 0.0, 1.0));
col += 0.06 * vec3(0.9, 0.8, 0.6) * fbm(uv * 1.5 + vec2(t * 0.015, 0.0));
for (int i = 0; i < 22; i++) {
float fi = float(i) + 1.0;
float a = h1(fi); float b = h1(fi * 2.17); float c = h1(fi * 3.71);
float x = mod(a * 2.0 - t * 0.025 * (0.5 + b), 2.0) - 1.0;
float y = (b - 0.5) * 0.55 - 0.08 + 0.02 * sin(t * 0.2 + fi);
float r = 0.05 + 0.08 * c;
float d = length(uv - vec2(x, y));
float disk = smoothstep(r, r * 0.82, d);
float rim = smoothstep(r * 0.55, r * 0.95, d) * disk;
vec3 lc = mix(vec3(1.0, 0.68, 0.34), vec3(0.72, 0.82, 0.42), step(0.55, h1(fi * 5.3)));
lc = mix(lc, vec3(0.55, 0.78, 0.80), step(0.85, h1(fi * 7.9)));
float pulse = 0.85 + 0.15 * sin(t * 0.3 + fi * 1.7);
col += lc * (disk * 0.22 + rim * 0.12) * pulse * (0.6 + 0.4 * c);
}
return col;
}
void main(){
vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution) / u_resolution.y;
float t = u_time;
vec3 d1 = drops(uv, 5.0, t);
vec3 d2 = drops(uv + vec2(0.31, 0.17), 7.5, t * 1.2);
vec2 off = d1.xy * 0.09 + d2.xy * 0.06;
float m = max(d1.z, d2.z);
float fog = 0.32 + 0.18 * fbm(uv * 2.2 + vec2(t * 0.02, -t * 0.01));
vec3 mistC = vec3(0.60, 0.63, 0.62);
vec3 outside = mix(scene(uv, t), mistC, fog);
vec3 inside = mix(scene(uv * 0.92 - off * 1.8, t), mistC, fog * 0.25) * 1.05;
vec3 col = mix(outside, inside, m);
vec2 hl = d1.xy + d2.xy;
col += vec3(0.9, 0.92, 0.9) * smoothstep(0.2, 0.7, dot(hl, vec2(-0.5, 0.6))) * m * 0.18;
col -= vec3(0.05) * smoothstep(0.3, 0.8, dot(hl, vec2(0.4, -0.7))) * m;
float v = 1.0 - 0.35 * dot(uv * vec2(0.9, 1.2), uv * vec2(0.9, 1.2));
col *= v;
col = mix(col, vec3(dot(col, vec3(0.33))), 0.12);
gl_FragColor = vec4(clamp(col, 0.04, 0.92), 1.0);
}