const dprCap = 2; let dpr = Math.min(window.devicePixelRatio || 1, dprCap);
function resize(){ dpr = Math.min(window.devicePixelRatio || 1, dprCap); const w = Math.max(1, Math.floor(window.innerWidth * dpr)); const h = Math.max(1, Math.floor(window.innerHeight * dpr)); if(canvas.width !== w || canvas.height !== h){ canvas.width = w; canvas.height = h; gl.viewport(0,0,w,h); } } window.addEventListener('resize', resize, { passive: true }); resize();
const vert = #version 300 es precision highp float; layout(location=0) in vec2 a_pos; out vec2 v_uv; void main(){ v_uv = a_pos*0.5 + 0.5; gl_Position = vec4(a_pos, 1.0); };
const frag = `#version 300 es precision highp float; out vec4 fragColor; in vec2 v_uv;
uniform vec2 u_res; uniform float u_time; uniform float u_dpr;
// Hash & noise helpers float hash11(float p){ vec3 p3 = fract(vec3(p) * 0.1031); p3 += dot(p3, p3.yzx + 33.33); return fract((p3.x + p3.y) * p3.z); } float hash21(vec2 p){ vec3 p3 = fract(vec3(p, p.x+p.y) * 0.1031); p3 += dot(p3, p3.yzx + 33.33); return fract((p3.x + p3.y) * p3.z); } vec2 hash22(vec2 p){ float n = sin(dot(p, vec2(41.2321, 289.133))) * 43758.5453; return fract(vec2(n, n*1.2154)); }
float noise(vec2 x){ vec2 p = floor(x); vec2 f = fract(x); f = ff(3.0-2.0*f); float a = hash21(p); float b = hash21(p + vec2(1.0, 0.0)); float c = hash21(p + vec2(0.0, 1.0)); float d = hash21(p + vec2(1.0, 1.0)); return mix(mix(a,b,f.x), mix(c,d,f.x), f.y); }
// Background "reality" texture: dark, cinematic, with grid and faint code haze vec3 realityBg(vec2 uv){ // Subtle parallax warp to feel like depth behind vec2 p = uv; float vign = smoothstep(1.1, 0.2, length(uv - 0.5) * 1.3); vec3 base = mix(vec3(0.01, 0.03, 0.05), vec3(0.0, 0.06, 0.03), uv.y); // Smart grid vec2 gUv = uv * vec2(1.0, 1.25); vec2 grid = abs(fract(gUv * vec2(32.0, 40.0)) - 0.5); float gridLine = smoothstep(0.49, 0.495, max(grid.x, grid.y)); vec3 gridCol = vec3(0.0, 0.2, 0.11) * gridLine * 0.08;
// Faint falling code haze
vec2 cell = floor(uv * vec2(80.0, 150.0));
float rnd = hash21(cell + floor(u_time*vec2(0.0, 4.0)));
float glow = smoothstep(0.98, 1.0, rnd) * 0.12;
float streak = smoothstep(0.0, 0.02, fract(uv.y*150.0 + rnd*10.0 + u_time*2.5) - 0.98);
vec3 haze = vec3(0.0, 0.9, 0.5) * (glow * streak);
// Diagonal bloom streaks
float diag = pow(max(0.0, 1.0 - abs(sin((uv.x - uv.y*1.2 + u_time*0.02)*20.0))), 6.0) * 0.05;
return base * vign + gridCol + haze + vec3(0.05, 0.12, 0.09) * diag;
// Faint falling code haze
vec2 cell = floor(uv * vec2(80.0, 150.0));
float rnd = hash21(cell + floor(u_time*vec2(0.0, 4.0)));
float glow = smoothstep(0.98, 1.0, rnd) * 0.12;
float streak = smoothstep(0.0, 0.02, fract(uv.y*150.0 + rnd*10.0 + u_time*2.5) - 0.98);
vec3 haze = vec3(0.0, 0.9, 0.5) * (glow * streak);
// Diagonal bloom streaks
float diag = pow(max(0.0, 1.0 - abs(sin((uv.x - uv.y*1.2 + u_time*0.02)*20.0))), 6.0) * 0.05;
return base * vign + gridCol + haze + vec3(0.05, 0.12, 0.09) * diag;
}
// Environment map for chrome reflections (fake neon tubes and dark room) vec3 envMap(vec3 r){ // r assumed normalized, use XY for 2D env projection vec2 e = r.xy; float ang = atan(e.y, e.x); float rad = length(e); vec3 env = vec3(0.01, 0.02, 0.03);
// Neon arc
float arc = smoothstep(0.25, 0.0, abs(rad - 0.35));
float arc2 = smoothstep(0.25, 0.0, abs(rad - 0.7));
env += vec3(0.0, 0.9, 0.5) * (pow(max(0.0, arc), 2.0) * 0.4 + pow(max(0.0, arc2), 2.0) * 0.3);
// Vertical tubes
float stripe = smoothstep(0.015, 0.0, abs(fract(e.x * 8.0 - u_time*0.05) - 0.5) - 0.45);
env += vec3(0.2, 1.0, 0.7) * stripe * 0.2;
// Horizontal dim bars
float hbar = smoothstep(0.02, 0.0, abs(fract(e.y * 6.0 + 0.25) - 0.5) - 0.47);
env += vec3(0.03, 0.12, 0.09) * hbar * 0.15;
return env;
// Neon arc
float arc = smoothstep(0.25, 0.0, abs(rad - 0.35));
float arc2 = smoothstep(0.25, 0.0, abs(rad - 0.7));
env += vec3(0.0, 0.9, 0.5) * (pow(max(0.0, arc), 2.0) * 0.4 + pow(max(0.0, arc2), 2.0) * 0.3);
// Vertical tubes
float stripe = smoothstep(0.015, 0.0, abs(fract(e.x * 8.0 - u_time*0.05) - 0.5) - 0.45);
env += vec3(0.2, 1.0, 0.7) * stripe * 0.2;
// Horizontal dim bars
float hbar = smoothstep(0.02, 0.0, abs(fract(e.y * 6.0 + 0.25) - 0.5) - 0.47);
env += vec3(0.03, 0.12, 0.09) * hbar * 0.15;
return env;
}
// Parameters controlling density float colCount(){ // Scale with resolution for consistent density float base = mix(10.0, 24.0, clamp(u_res.x / 1400.0, 0.0, 1.0)); return base; }
// Compute liquid heightfield at uv (0..1). Also returns optional local emissive intensity via out param. float liquidHeight(vec2 uv, out float emissive){ float N = colCount(); float idx = floor(uv.x * N); float h = 0.0; emissive = 0.0;
// Viscous base near bottom pooling
float poolZone = smoothstep(0.22, 0.0, uv.y); // stronger near bottom
h += 0.003 * pow(poolZone, 1.5);
// Neighbor columns sampling (metaball-like blending)
for(int k=-2;k<=2;k++){
float i = idx + float(k);
// wrap index to [0,N)
float iw = mod(i + N*10.0, N);
float phase = hash11(iw*13.37) * 6.28318;
float cx = (iw + 0.5)/N;
// Wobble/wave
float wob = sin((uv.y*18.0 - u_time*0.7) + iw*1.7) * 0.006;
cx += wob;
float speed = mix(0.08, 0.18, hash11(iw*3.17));
float life = fract(1.0 - u_time*speed + phase*0.1);
float headY = life * 1.25 + 0.05; // spawn above and drip down
// radial thickness around column center
float baseR = mix(0.007, 0.018, hash11(iw*0.73));
float dx = abs(uv.x - cx);
// lateral distortion from turbulence
dx -= (noise(vec2(uv.y*10.0 + iw*0.7, u_time*0.4)) - 0.5) * 0.01;
// Viscous core and shoulder
float core = smoothstep(baseR*1.2, 0.0, dx);
float shoulder = smoothstep(baseR*2.5, baseR*1.2, dx) * 0.5;
// Vertical extent: below head only (melting trail)
float trail = smoothstep(0.0, 0.18, headY - uv.y);
// Drip bulb near head
float headMask = exp(-pow((uv.y - headY) / (0.03 + baseR*1.2), 2.0));
float bulb = core * headMask * 0.9;
// Accumulate height contribution with syrup heaviness
float local = (core*0.9 + shoulder*0.6) * trail + bulb;
// Thicken near bottom due to pooling
local += core * smoothstep(0.08, 0.0, uv.y) * 0.4;
// Splash ripples near bottom
float rippleZone = smoothstep(0.22, 0.0, uv.y);
float r = distance(vec2(uv.x, uv.y*4.0), vec2(cx, 0.0));
float ripple = sin(r*60.0 - u_time*8.0 + phase) * exp(-r*6.5) * rippleZone * 0.006;
// Dripping globules separated from main stream (occasional)
float glob = 0.0;
if(headY < 1.1){
float dropPhase = fract(u_time*speed*0.5 + phase);
float gy = headY - dropPhase * 0.4;
float gmask = exp(-pow((uv.y - gy)/(0.02 + baseR), 2.0));
glob = smoothstep(baseR*1.5, 0.0, dx) * gmask * 0.7;
}
// Total local thickness
float th = local * 0.02 + glob * 0.03 + ripple;
h += th;
// Emissive glyph glow along column
// Discretize along y to cells and use random bits per cell
float cellH = mix(0.012, 0.018, hash11(iw*5.19));
float row = floor(uv.y / cellH);
float col = floor((uv.x - cx + 0.5/N) * N * 1.8 + 0.5);
float rnd = hash21(vec2(iw*37.1 + row, col*11.3));
float bit = step(0.73, rnd);
// Flicker
float flick = 0.7 + 0.3 * sin(u_time*20.0 + iw + row*0.7);
float glyph = bit * core * trail * flick;
// Glow falloff from center
float glowRad = smoothstep(baseR*2.2, 0.0, dx);
emissive += glyph * glowRad * 0.15;
}
// Clamp to avoid explosion
return clamp(h, 0.0, 0.25);
// Viscous base near bottom pooling
float poolZone = smoothstep(0.22, 0.0, uv.y); // stronger near bottom
h += 0.003 * pow(poolZone, 1.5);
// Neighbor columns sampling (metaball-like blending)
for(int k=-2;k<=2;k++){
float i = idx + float(k);
// wrap index to [0,N)
float iw = mod(i + N*10.0, N);
float phase = hash11(iw*13.37) * 6.28318;
float cx = (iw + 0.5)/N;
// Wobble/wave
float wob = sin((uv.y*18.0 - u_time*0.7) + iw*1.7) * 0.006;
cx += wob;
float speed = mix(0.08, 0.18, hash11(iw*3.17));
float life = fract(1.0 - u_time*speed + phase*0.1);
float headY = life * 1.25 + 0.05; // spawn above and drip down
// radial thickness around column center
float baseR = mix(0.007, 0.018, hash11(iw*0.73));
float dx = abs(uv.x - cx);
// lateral distortion from turbulence
dx -= (noise(vec2(uv.y*10.0 + iw*0.7, u_time*0.4)) - 0.5) * 0.01;
// Viscous core and shoulder
float core = smoothstep(baseR*1.2, 0.0, dx);
float shoulder = smoothstep(baseR*2.5, baseR*1.2, dx) * 0.5;
// Vertical extent: below head only (melting trail)
float trail = smoothstep(0.0, 0.18, headY - uv.y);
// Drip bulb near head
float headMask = exp(-pow((uv.y - headY) / (0.03 + baseR*1.2), 2.0));
float bulb = core * headMask * 0.9;
// Accumulate height contribution with syrup heaviness
float local = (core*0.9 + shoulder*0.6) * trail + bulb;
// Thicken near bottom due to pooling
local += core * smoothstep(0.08, 0.0, uv.y) * 0.4;
// Splash ripples near bottom
float rippleZone = smoothstep(0.22, 0.0, uv.y);
float r = distance(vec2(uv.x, uv.y*4.0), vec2(cx, 0.0));
float ripple = sin(r*60.0 - u_time*8.0 + phase) * exp(-r*6.5) * rippleZone * 0.006;
// Dripping globules separated from main stream (occasional)
float glob = 0.0;
if(headY < 1.1){
float dropPhase = fract(u_time*speed*0.5 + phase);
float gy = headY - dropPhase * 0.4;
float gmask = exp(-pow((uv.y - gy)/(0.02 + baseR), 2.0));
glob = smoothstep(baseR*1.5, 0.0, dx) * gmask * 0.7;
}
// Total local thickness
float th = local * 0.02 + glob * 0.03 + ripple;
h += th;
// Emissive glyph glow along column
// Discretize along y to cells and use random bits per cell
float cellH = mix(0.012, 0.018, hash11(iw*5.19));
float row = floor(uv.y / cellH);
float col = floor((uv.x - cx + 0.5/N) * N * 1.8 + 0.5);
float rnd = hash21(vec2(iw*37.1 + row, col*11.3));
float bit = step(0.73, rnd);
// Flicker
float flick = 0.7 + 0.3 * sin(u_time*20.0 + iw + row*0.7);
float glyph = bit * core * trail * flick;
// Glow falloff from center
float glowRad = smoothstep(baseR*2.2, 0.0, dx);
emissive += glyph * glowRad * 0.15;
}
// Clamp to avoid explosion
return clamp(h, 0.0, 0.25);
}
// Compute normals from heightfield vec3 heightNormal(vec2 uv){ float dummy; float e = 1.0 / max(u_res.x, u_res.y); float hC = liquidHeight(uv, dummy); float hX = liquidHeight(uv + vec2(e, 0.0), dummy) - hC; float hY = liquidHeight(uv + vec2(0.0, e), dummy) - hC; vec3 n = normalize(vec3(-hX600.0, -hY600.0, 1.0)); return n; }
void main(){ vec2 uv = v_uv; // Device-pixel aware AA epsilon float px = 1.0 / u_res.x;
// Liquid field
float emi;
float h = liquidHeight(uv, emi);
vec3 n = heightNormal(uv);
// Refraction of background by surface normals
vec2 refractOffset = n.xy * clamp(h*2.5, 0.0, 0.08);
vec3 bg0 = realityBg(clamp(uv + refractOffset, 0.0, 1.0));
vec3 bg1 = realityBg(clamp(uv - refractOffset*0.35, 0.0, 1.0));
vec3 bg = mix(bg0, bg1, 0.25);
// Chrome reflection
vec3 V = normalize(vec3(0.0, 0.0, 1.6));
vec3 N = n;
vec3 R = reflect(-V, N);
float ndv = max(dot(N, V), 0.0);
float fres = pow(1.0 - ndv, 5.0);
vec3 env = envMap(R);
// Specular glints
float gloss = 900.0;
float spec = pow(max(dot(normalize(vec3(0.2, 0.4, 1.0)), N), 0.0), gloss) * 1.4
+ pow(max(dot(normalize(vec3(-0.3, 0.1, 1.0)), N), 0.0), 600.0) * 0.9;
// Base chrome/mercury tone
vec3 chrome = env * (0.35 + 0.65 * fres) + vec3(0.9) * spec * 0.7;
// Glowing mercury-green emissive on streams
vec3 neon = vec3(0.25, 1.0, 0.65) * emi * (0.8 + 0.2 * sin(u_time*10.0));
// Cheap bloom for emissive
float bloom = emi;
for(int i=0;i<8;i++){
float a = float(i) * 6.28318 / 8.0;
vec2 o = vec2(cos(a), sin(a)) * (2.5 + float(i))*px*1.8;
float e2;
bloom += liquidHeight(uv + o, e2), bloom += e2;
}
bloom = bloom * 0.06;
vec3 col = bg;
// Composite: refracted background under liquid, then reflective chrome, then emissive with bloom
float cover = smoothstep(0.0005, 0.002, h); // presence mask
col = mix(col, col * 0.9 + chrome, cover); // put chrome on liquid
col += neon * 0.8 + vec3(0.15, 0.75, 0.5) * bloom;
// Ripples distort the background even where thin
col = mix(col, col*vec3(0.95,1.02,0.98), clamp(h*12.0, 0.0, 0.35));
// Vignette and grading
float vign = smoothstep(1.3, 0.35, length(uv - 0.5) * 1.2);
col *= vign;
// Grain
float grain = hash21(uv * u_res + u_time)*0.015;
col += grain;
// Tone map
col = col / (col + vec3(1.0));
fragColor = vec4(col, 1.0);
// Liquid field
float emi;
float h = liquidHeight(uv, emi);
vec3 n = heightNormal(uv);
// Refraction of background by surface normals
vec2 refractOffset = n.xy * clamp(h*2.5, 0.0, 0.08);
vec3 bg0 = realityBg(clamp(uv + refractOffset, 0.0, 1.0));
vec3 bg1 = realityBg(clamp(uv - refractOffset*0.35, 0.0, 1.0));
vec3 bg = mix(bg0, bg1, 0.25);
// Chrome reflection
vec3 V = normalize(vec3(0.0, 0.0, 1.6));
vec3 N = n;
vec3 R = reflect(-V, N);
float ndv = max(dot(N, V), 0.0);
float fres = pow(1.0 - ndv, 5.0);
vec3 env = envMap(R);
// Specular glints
float gloss = 900.0;
float spec = pow(max(dot(normalize(vec3(0.2, 0.4, 1.0)), N), 0.0), gloss) * 1.4
+ pow(max(dot(normalize(vec3(-0.3, 0.1, 1.0)), N), 0.0), 600.0) * 0.9;
// Base chrome/mercury tone
vec3 chrome = env * (0.35 + 0.65 * fres) + vec3(0.9) * spec * 0.7;
// Glowing mercury-green emissive on streams
vec3 neon = vec3(0.25, 1.0, 0.65) * emi * (0.8 + 0.2 * sin(u_time*10.0));
// Cheap bloom for emissive
float bloom = emi;
for(int i=0;i<8;i++){
float a = float(i) * 6.28318 / 8.0;
vec2 o = vec2(cos(a), sin(a)) * (2.5 + float(i))*px*1.8;
float e2;
bloom += liquidHeight(uv + o, e2), bloom += e2;
}
bloom = bloom * 0.06;
vec3 col = bg;
// Composite: refracted background under liquid, then reflective chrome, then emissive with bloom
float cover = smoothstep(0.0005, 0.002, h); // presence mask
col = mix(col, col * 0.9 + chrome, cover); // put chrome on liquid
col += neon * 0.8 + vec3(0.15, 0.75, 0.5) * bloom;
// Ripples distort the background even where thin
col = mix(col, col*vec3(0.95,1.02,0.98), clamp(h*12.0, 0.0, 0.35));
// Vignette and grading
float vign = smoothstep(1.3, 0.35, length(uv - 0.5) * 1.2);
col *= vign;
// Grain
float grain = hash21(uv * u_res + u_time)*0.015;
col += grain;
// Tone map
col = col / (col + vec3(1.0));
fragColor = vec4(col, 1.0);
}`;
function createProgram(gl, vs, fs){ function compile(type, src){ const s = gl.createShader(type); gl.shaderSource(s, src); gl.compileShader(s); if(!gl.getShaderParameter(s, gl.COMPILE_STATUS)){ const info = gl.getShaderInfoLog(s); gl.deleteShader(s); throw new Error(info); } return s; } const v = compile(gl.VERTEX_SHADER, vs); const f = compile(gl.FRAGMENT_SHADER, fs); const p = gl.createProgram(); gl.attachShader(p, v); gl.attachShader(p, f); gl.linkProgram(p); if(!gl.getProgramParameter(p, gl.LINK_STATUS)){ const info = gl.getProgramInfoLog(p); gl.deleteProgram(p); throw new Error(info); } gl.deleteShader(v); gl.deleteShader(f); return p; }
const program = createProgram(gl, vert, frag); const loc = { res: gl.getUniformLocation(program, 'u_res'), time: gl.getUniformLocation(program, 'u_time'), dpr: gl.getUniformLocation(program, 'u_dpr') };
// Fullscreen triangle const vao = gl.createVertexArray(); gl.bindVertexArray(vao); const vbo = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vbo); const tri = new Float32Array([ -1, -1, 3, -1, -1, 3 ]); gl.bufferData(gl.ARRAY_BUFFER, tri, gl.STATIC_DRAW); gl.enableVertexAttribArray(0); gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); gl.bindVertexArray(null);
gl.disable(gl.DEPTH_TEST); gl.disable(gl.BLEND);
let start = performance.now(); function frame(t){ const now = performance.now(); const time = (now - start) * 0.001;
resize();
gl.viewport(0,0,canvas.width, canvas.height);
gl.useProgram(program);
gl.bindVertexArray(vao);
gl.uniform2f(loc.res, canvas.width, canvas.height);
gl.uniform1f(loc.time, time);
gl.uniform1f(loc.dpr, dpr);
gl.drawArrays(gl.TRIANGLES, 0, 3);
gl.bindVertexArray(null);
requestAnimationFrame(frame);
resize();
gl.viewport(0,0,canvas.width, canvas.height);
gl.useProgram(program);
gl.bindVertexArray(vao);
gl.uniform2f(loc.res, canvas.width, canvas.height);
gl.uniform1f(loc.time, time);
gl.uniform1f(loc.dpr, dpr);
gl.drawArrays(gl.TRIANGLES, 0, 3);
gl.bindVertexArray(null);
requestAnimationFrame(frame);
} requestAnimationFrame(frame); })(); </script>
</body> </html>_