/* ============================================================ CSL Landing — Interactive shader "wallpaper" for the hero. 5 WebGL fragment shaders that react to mouse position + clicks. Brand-tinted (periwinkle / ink). Unique top-level names so it never collides with the other Babel scripts' global lexicals. ============================================================ */ const { useRef: useShaderRef, useState: useShaderState, useEffect: useShaderEffect } = React; const SHADER_NAMES = ["Aurora"]; /* Single Aurora shader — flowing domain-warped periwinkle haze that glows toward the cursor, with a click ripple. */ const SHADER_BODY = `void main(){ vec2 uv=gl_FragCoord.xy/u_res; vec2 asp=vec2(u_res.x/u_res.y,1.0); float t=u_time*0.06; vec2 q=uv*3.0; q += 1.1*vec2(fbm(q + t + u_mouse*2.2), fbm(q*1.1 - t + 4.0)); float n=fbm(q + u_mouse*1.4); float d=distance(uv*asp, u_mouse*asp); vec3 col=mix(vec3(0.96,0.97,1.0), PERI, smoothstep(0.15,0.95,n)); col=mix(col, PERI_L, smoothstep(0.55,0.0,d)*0.45); col += PERI*ripple(uv)*0.35; gl_FragColor=vec4(col,1.0); }`; const SHADER_PRELUDE = `precision highp float; uniform vec2 u_res; uniform float u_time; uniform vec2 u_mouse; uniform vec3 u_click; const vec3 PERI = vec3(0.384,0.435,1.0); const vec3 PERI_L = vec3(0.671,0.694,1.0); const vec3 PERI_D = vec3(0.184,0.204,0.62); const vec3 INK = vec3(0.039,0.039,0.071); float hash21(vec2 p){ p=fract(p*vec2(123.34,456.21)); p+=dot(p,p+45.32); return fract(p.x*p.y); } float vnoise(vec2 p){ vec2 i=floor(p),f=fract(p); vec2 u=f*f*(3.0-2.0*f); float a=hash21(i),b=hash21(i+vec2(1.0,0.0)),c=hash21(i+vec2(0.0,1.0)),d=hash21(i+vec2(1.0,1.0)); return mix(mix(a,b,u.x),mix(c,d,u.x),u.y); } float fbm(vec2 p){ float v=0.0,a=0.5; for(int i=0;i<5;i++){ v+=a*vnoise(p); p*=2.02; a*=0.5; } return v; } float ripple(vec2 uv){ float ca=u_click.z; if(ca>3.0) return 0.0; float cd=distance(uv,u_click.xy); return sin(cd*38.0 - ca*9.0)*exp(-ca*2.4)*smoothstep(0.45,0.0,cd); } `; const SHADER_VERT = `attribute vec2 a_pos; void main(){ gl_Position=vec4(a_pos,0.0,1.0); }`; function cslCompile(gl, type, src) { const sh = gl.createShader(type); gl.shaderSource(sh, src); gl.compileShader(sh); if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) { console.warn("Shader compile:", gl.getShaderInfoLog(sh)); gl.deleteShader(sh); return null; } return sh; } function cslBuildProgram(gl, frag) { const vs = cslCompile(gl, gl.VERTEX_SHADER, SHADER_VERT); const fs = cslCompile(gl, gl.FRAGMENT_SHADER, SHADER_PRELUDE + frag); if (!vs || !fs) return null; const p = gl.createProgram(); gl.attachShader(p, vs); gl.attachShader(p, fs); gl.linkProgram(p); gl.deleteShader(vs); gl.deleteShader(fs); if (!gl.getProgramParameter(p, gl.LINK_STATUS)) { console.warn("Link:", gl.getProgramInfoLog(p)); return null; } return p; } function HeroShader() { const wrapRef = useShaderRef(null); const canvasRef = useShaderRef(null); const glRef = useShaderRef(null); const progRef = useShaderRef(null); const uRef = useShaderRef({}); const mouseRef = useShaderRef({ x: 0.5, y: 0.62, tx: 0.5, ty: 0.62 }); const clickRef = useShaderRef({ x: 0.5, y: 0.5, t: -1e9 }); const startRef = useShaderRef(0); /* gl setup, program build + render loop (runs once) */ useShaderEffect(() => { const canvas = canvasRef.current; const gl = canvas.getContext("webgl", { antialias: true, premultipliedAlpha: false }) || canvas.getContext("experimental-webgl"); if (!gl) { wrapRef.current && wrapRef.current.classList.add("shader--nogl"); return; } glRef.current = gl; startRef.current = performance.now(); const prog = cslBuildProgram(gl, SHADER_BODY); if (prog) { progRef.current = prog; uRef.current = { res: gl.getUniformLocation(prog, "u_res"), time: gl.getUniformLocation(prog, "u_time"), mouse: gl.getUniformLocation(prog, "u_mouse"), click: gl.getUniformLocation(prog, "u_click"), }; } const buf = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buf); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW); const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches; const tScale = reduce ? 0.18 : 1.0; const resize = () => { const r = canvas.getBoundingClientRect(); const dpr = Math.min(window.devicePixelRatio || 1, 2); const w = Math.max(1, Math.round(r.width * dpr)); const h = Math.max(1, Math.round(r.height * dpr)); if (canvas.width !== w || canvas.height !== h) { canvas.width = w; canvas.height = h; } gl.viewport(0, 0, canvas.width, canvas.height); }; const ro = new ResizeObserver(resize); ro.observe(canvas); resize(); const host = canvas.closest(".hero") || canvas.parentElement; const norm = (e) => { const r = canvas.getBoundingClientRect(); return { x: (e.clientX - r.left) / r.width, y: 1 - (e.clientY - r.top) / r.height }; }; const onMove = (e) => { const l = norm(e); const m = mouseRef.current; m.tx = l.x; m.ty = l.y; }; const onDown = (e) => { if (e.target.closest && e.target.closest(".hero__hud")) return; const l = norm(e); clickRef.current = { x: l.x, y: l.y, t: performance.now() }; }; host.addEventListener("pointermove", onMove, { passive: true }); host.addEventListener("pointerdown", onDown, { passive: true }); let raf; const draw = () => { raf = requestAnimationFrame(draw); const prog = progRef.current; if (!prog) return; const m = mouseRef.current; m.x += (m.tx - m.x) * 0.12; m.y += (m.ty - m.y) * 0.12; gl.useProgram(prog); const loc = gl.getAttribLocation(prog, "a_pos"); gl.bindBuffer(gl.ARRAY_BUFFER, buf); gl.enableVertexAttribArray(loc); gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0); const u = uRef.current; gl.uniform2f(u.res, canvas.width, canvas.height); gl.uniform1f(u.time, ((performance.now() - startRef.current) / 1000) * tScale); gl.uniform2f(u.mouse, m.x, m.y); gl.uniform3f(u.click, clickRef.current.x, clickRef.current.y, (performance.now() - clickRef.current.t) / 1000); gl.drawArrays(gl.TRIANGLES, 0, 3); }; draw(); return () => { cancelAnimationFrame(raf); ro.disconnect(); host.removeEventListener("pointermove", onMove); host.removeEventListener("pointerdown", onDown); }; }, []); return ( ); } window.HeroShader = HeroShader;