// 100% VIBES const canvas = document.getElementById('background'); const gl = canvas.getContext('webgl'); if (gl) { function resize() { canvas.width = canvas.clientWidth * devicePixelRatio; canvas.height = canvas.clientHeight * devicePixelRatio; gl.viewport(0, 0, canvas.width, canvas.height); } window.addEventListener('resize', resize); resize(); gl.getExtension('OES_standard_derivatives'); const vsrc = ` attribute vec2 a_pos; void main() { gl_Position = vec4(a_pos, 0.0, 1.0); } `; const fsrc = ` #extension GL_OES_standard_derivatives : enable precision highp float; uniform float u_time; uniform float u_glitch; uniform vec2 u_res; #define COL_SKY_TOP vec3(0.04, 0.0, 0.12) #define COL_SKY_MID vec3(0.18, 0.02, 0.28) #define COL_SKY_LOW vec3(0.85, 0.25, 0.45) #define COL_HORIZON vec3(1.0, 0.55, 0.2) #define COL_GRID vec3(0.0, 0.95, 1.0) #define COL_GRID2 vec3(0.72, 0.4, 1.0) #define COL_MOUNT vec3(0.06, 0.0, 0.14) float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); } float noise(vec2 p) { vec2 i = floor(p); vec2 f = fract(p); f = f * f * (3.0 - 2.0 * f); float a = hash(i); float b = hash(i + vec2(1.0, 0.0)); float c = hash(i + vec2(0.0, 1.0)); float d = hash(i + vec2(1.0, 1.0)); return mix(mix(a, b, f.x), mix(c, d, f.x), f.y); } float mountain(float x, float scale, float height, float offset) { float m = 0.0; m += abs(fract(x * 0.4 + offset * 0.1) - 0.5) * 2.0 * 0.5; m += abs(fract(x * 0.85 + offset * 0.17) - 0.5) * 2.0 * 0.3; m += abs(fract(x * 1.9 + offset * 0.06) - 0.5) * 2.0 * 0.15; float cx = abs(x) / (scale * 0.1 + 0.5); float sideMask = smoothstep(0.0, 0.5, cx); return m * height * sideMask; } float glitch(float y, float t, float g) { if (g < 0.01) return 0.0; float band = step(0.7, hash(vec2(floor(y * 30.0), floor(t * 20.0)))); float off = (hash(vec2(floor(y * 30.0), floor(t * 30.0))) - 0.5) * 0.06; return off * band * g; } float glitchColor(float y, float t, float g) { float band = step(0.6, hash(vec2(floor(y * 25.0), floor(t * 15.0)))); return band * g; } void main() { vec2 uv = gl_FragCoord.xy / u_res; float aspect = u_res.x / u_res.y; float t = u_time; float gOff = glitch(uv.y, t, u_glitch); uv.x += gOff; vec2 cuv = (uv - 0.5) * vec2(aspect, 1.0); float horizon = 0.42; vec3 col = vec3(0.0); if (uv.y > horizon) { float skyT = (uv.y - horizon) / (1.0 - horizon); vec3 sky = vec3(0.0); if (skyT < 0.15) { sky = mix(COL_HORIZON, COL_SKY_LOW, skyT / 0.15); } else if (skyT < 0.5) { sky = mix(COL_SKY_LOW, COL_SKY_MID, (skyT - 0.15) / 0.35); } else { sky = mix(COL_SKY_MID, COL_SKY_TOP, (skyT - 0.5) / 0.5); } vec2 sunPos = vec2(-0.3, -0.02); float sunDist = length(cuv - sunPos); float sunR = 0.35; if (sunDist < sunR) { float sunT = (cuv.y - sunPos.y + sunR) / (2.0 * sunR); vec3 sunCol = mix(vec3(0.8, 0.05, 0.1), vec3(1.0, 0.85, 0.3), sunT); float sunEdge = smoothstep(sunR, sunR - 0.006, sunDist); sky = mix(sky, sunCol, sunEdge); } float glow = exp(-sunDist * 2.5) * 0.7; sky += vec3(1.0, 0.25, 0.15) * glow; float mx = cuv.x; float m1 = mountain(mx * 2.5, 8.0, 1.0, 0.0) * 0.45; float mH1 = (uv.y - horizon) - m1 * (1.0 - horizon); float fw1 = fwidth(mH1); float mMask1 = smoothstep(-fw1, fw1, mH1); sky = mix(sky, COL_MOUNT, 1.0 - mMask1); float m2 = mountain(mx * 3.5, 12.0, 1.0, 5.0) * 0.35; float mH2 = (uv.y - horizon) - m2 * (1.0 - horizon); float fw2 = fwidth(mH2); float mMask2 = smoothstep(-fw2, fw2, mH2); sky = mix(sky, COL_MOUNT * 0.7, 1.0 - mMask2); col = sky; } if (uv.y <= horizon) { float floorT = (horizon - uv.y) / horizon; float z = 0.3 / (floorT + 0.01); float x = cuv.x * z * 2.5; float scrollSpeed = 0.2; z += t * scrollSpeed; float gx = x * 1.8; float gz = z * 0.6; float lineX = abs(fract(gx) - 0.5); float lineZ = abs(fract(gz) - 0.5); float fwX = fwidth(gx) * 1.0; float fwZ = fwidth(gz) * 1.0; float gridX = smoothstep(fwX, 0.0, lineX) * 0.7; float gridZ = smoothstep(fwZ, 0.0, lineZ) * 0.7; float grid = max(gridX, gridZ); float fadeDist = smoothstep(0.85, 0.0, floorT); float fadeNear = smoothstep(0.0, 0.05, floorT); grid *= fadeDist * fadeNear; vec3 gridCol = mix(COL_GRID, COL_GRID2, floorT); vec3 ground = vec3(0.02, 0.0, 0.05); float hGlow = exp(-floorT * 6.0) * 0.3; ground += COL_HORIZON * hGlow; col = ground + gridCol * grid; } float hLine = exp(-abs(uv.y - horizon) * 120.0) * 0.5; col += vec3(1.0, 0.5, 0.3) * hLine; float scanline = sin(gl_FragCoord.y * 1.5) * 0.04 + 0.96; col *= scanline; vec2 vc = uv * 2.0 - 1.0; float vignette = 1.0 - dot(vc * 0.5, vc * 0.5); col *= smoothstep(0.0, 0.7, vignette); float gc = glitchColor(uv.y, t, u_glitch); col.r += gc * 0.15; col.b -= gc * 0.1; col = pow(col, vec3(0.95)); gl_FragColor = vec4(col, 1.0); } `; function compile(type, src) { const s = gl.createShader(type); gl.shaderSource(s, src); gl.compileShader(s); if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) { console.error(gl.getShaderInfoLog(s)); return null; } return s; } const vs = compile(gl.VERTEX_SHADER, vsrc); const fs = compile(gl.FRAGMENT_SHADER, fsrc); const prog = gl.createProgram(); gl.attachShader(prog, vs); gl.attachShader(prog, fs); gl.linkProgram(prog); if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) { console.error(gl.getProgramInfoLog(prog)); } gl.useProgram(prog); const buf = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buf); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1, 1,-1, -1,1, 1,1]), gl.STATIC_DRAW); const aPos = gl.getAttribLocation(prog, 'a_pos'); gl.enableVertexAttribArray(aPos); gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0); const uTime = gl.getUniformLocation(prog, 'u_time'); const uGlitch = gl.getUniformLocation(prog, 'u_glitch'); const uRes = gl.getUniformLocation(prog, 'u_res'); const glitchDuration = 300; let glitchStart = -glitchDuration; let elapsed = 0; let lastFrame = 0; let raf = null; document.addEventListener('click', function() { glitchStart = performance.now(); }, true); function glitchAmount(now) { const age = now - glitchStart; if (age < 0 || age > glitchDuration) return 0; return Math.sin((age / glitchDuration) * Math.PI); } function frame(now) { if (document.hidden) { raf = null; lastFrame = 0; return; } if (lastFrame) { elapsed += Math.min((now - lastFrame) / 1000, 0.1); } lastFrame = now; gl.uniform1f(uTime, elapsed); gl.uniform1f(uGlitch, glitchAmount(now)); gl.uniform2f(uRes, canvas.width, canvas.height); gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); raf = requestAnimationFrame(frame); } function startAnimation() { if (!raf && !document.hidden) { lastFrame = 0; raf = requestAnimationFrame(frame); } } document.addEventListener('visibilitychange', startAnimation); startAnimation(); }