feat: new website

This commit is contained in:
Dan Finch 2026-05-13 02:03:11 +02:00
commit 63a654aab4
64 changed files with 2904 additions and 0 deletions

258
assets/background.js Normal file
View file

@ -0,0 +1,258 @@
// 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();
}