const c = document.createElement('canvas');
document.body.appendChild(c);
c.style.width = '100%'; c.style.height = '100%';
const ctx = c.getContext('2d');
const small = document.createElement('canvas');
const sctx = small.getContext('2d');
const SW = 96, SH = 60;
small.width = SW; small.height = SH;
const img = sctx.createImageData(SW, SH);
// 클래식 그래디언트(펄린) 노이즈: 격자점마다 랜덤 방향 벡터를 두고 내적을 보간
const GRID = 16;
const grads = [];
for (let i = 0; i < GRID * GRID; i++) { const a = Math.random() * Math.PI * 2; grads.push([Math.cos(a), Math.sin(a)]); }
function grad(ix, iy) { return grads[((iy % GRID + GRID) % GRID) * GRID + ((ix % GRID + GRID) % GRID)]; }
function fade(t) { return t * t * t * (t * (t * 6 - 15) + 10); }
function lerp(a, b, t) { return a + (b - a) * t; }
function perlin(x, y) {
const x0 = Math.floor(x), y0 = Math.floor(y), xf = x - x0, yf = y - y0;
const g00 = grad(x0, y0), g10 = grad(x0 + 1, y0), g01 = grad(x0, y0 + 1), g11 = grad(x0 + 1, y0 + 1);
const d00 = g00[0] * xf + g00[1] * yf, d10 = g10[0] * (xf - 1) + g10[1] * yf;
const d01 = g01[0] * xf + g01[1] * (yf - 1), d11 = g11[0] * (xf - 1) + g11[1] * (yf - 1);
const u = fade(xf), v = fade(yf);
return lerp(lerp(d00, d10, u), lerp(d01, d11, u), v);
}
const cs = getComputedStyle(document.documentElement);
const rgb = (cs.getPropertyValue('--accent').trim() || '#5b5bf7').match(/[0-9a-f]{2}/gi).map(x => parseInt(x, 16));
function render(t) {
for (let y = 0; y < SH; y++) for (let x = 0; x < SW; x++) {
const n = (perlin(x * 0.12 + t * 0.6, y * 0.12) + 1) / 2;
const i = (y * SW + x) * 4;
img.data[i] = rgb[0] * n; img.data[i + 1] = rgb[1] * n; img.data[i + 2] = rgb[2] * n; img.data[i + 3] = 255;
}
sctx.putImageData(img, 0, 0);
ctx.imageSmoothingEnabled = true;
ctx.drawImage(small, 0, 0, c.width, c.height);
}
function resize() {
const dpr = Math.min(devicePixelRatio || 1, 2);
c.width = innerWidth * dpr; c.height = innerHeight * dpr;
}
addEventListener('resize', resize);
resize();
render(0); // 첫 프레임부터 바로 보이게
let t = 0;
(function loop() { t += 0.02; render(t); requestAnimationFrame(loop); })();