struct Uniforms { resolution: vec2, time: f32, scene: f32, mouse: vec2, date: vec4, sampleRate: vec4, stemMultipliers: array, 8>, }; @group(0) @binding(0) var u: Uniforms; struct VsOut { @builtin(position) pos: vec4, @location(0) uv: vec2, }; @vertex fn vs(@builtin(vertex_index) i: u32) -> VsOut { let p = array, 3>( vec2(-1.0, -3.0), vec2(-1.0, 1.0), vec2(3.0, 1.0), ); let clip = p[i]; var out: VsOut; out.pos = vec4(clip, 0.0, 1.0); out.uv = clip; return out; } @fragment fn fs(in: VsOut) -> @location(0) vec4 { let res = u.resolution; let fragCoord = (in.uv * vec2(0.5) + vec2(0.5)) * res; var uv = (fragCoord - 0.5 * res) / res.y; let slowTime = u.time * 0.2; let bgGrey = vec3(0.11, 0.11, 0.12); let cardGrey = vec3(0.30, 0.32, 0.36); let coreWhite = vec3(0.90, 0.95, 0.90); // ---------- palette keyed by scene ---------- let scene0 = vec3(0.20, 0.75, 0.20); // HERO – emerald let scene1 = vec3(0.85, 0.55, 0.20); // ABOUT – amber let scene2 = vec3(0.25, 0.60, 0.95); // PROJECTS – cobalt let scene3 = vec3(0.90, 0.35, 0.55); // CONTACT – rose let sFloor = i32(clamp(floor(u.scene), 0.0, 3.0)); let sCeil = i32(clamp(ceil(u.scene), 0.0, 3.0)); var c0 = scene0; c0 = select(c0, scene1, sFloor == 1); c0 = select(c0, scene2, sFloor == 2); c0 = select(c0, scene3, sFloor == 3); var c1 = scene0; c1 = select(c1, scene1, sCeil == 1); c1 = select(c1, scene2, sCeil == 2); c1 = select(c1, scene3, sCeil == 3); let tBlend = u.scene - floor(u.scene); let sceneAccent = mix(c0, c1, tBlend * tBlend * (3.0 - 2.0 * tBlend)); // ---------- mouse-reactive distortion ---------- let mousePos = (u.mouse - 0.5 * res) / res.y; let distMouse = length(uv - mousePos); let influence = exp(-distMouse * 7.0); uv += (mousePos - uv) * influence * 0.12; let angle = atan2(uv.y, uv.x); let radius = length(uv); let baseSpiral = angle + (radius * 8.0) - slowTime; let ripple = sin(radius * 15.0 - (u.time * 0.5)) * 0.08; let distortedSpiral = baseSpiral + ripple; let armCount = 2.0; let rChannel = smoothstep(0.05, 0.55, abs(sin((distortedSpiral + 0.04) * armCount))); let gChannel = smoothstep(0.05, 0.55, abs(sin(distortedSpiral * armCount))); let bChannel = smoothstep(0.05, 0.55, abs(sin((distortedSpiral - 0.04) * armCount))); let patternChannels = vec3(rChannel, gChannel, bChannel); let coreNeon = vec3(0.02 / (patternChannels + 0.025)); let spiralColor = mix(cardGrey, sceneAccent, patternChannels.g); let primarySpiral = coreNeon * spiralColor; let ringWave = sin(radius * 12.0 - (u.time * 1.5)); let sharpRing = smoothstep(0.85, 0.95, ringWave); let ringMask = sharpRing * smoothstep(0.7, 0.1, radius); let secondaryRings = cardGrey * ringMask * 0.4; let vignette = smoothstep(0.95, 0.4, radius); var finalColor = bgGrey + secondaryRings; finalColor = mix(finalColor, primarySpiral, 0.85); let centerLight = 0.015 / (radius + 0.02); finalColor += centerLight * coreWhite; // soft cursor glow finalColor += exp(-distMouse * 4.0) * 0.03 * sceneAccent; // ---------- dim when not on the home page (u.date.x controls it) ---------- let dim = u.date.x; finalColor = mix(finalColor, vec3(0.0), dim); return vec4(finalColor * vignette, 1.0); }