live - 60fps 1920 x 1080
<!DOCTYPE html>
<html>
<head>
<title>Wave Interference</title>
<style>
:root { --bg-color: #050912; --wave-1: #0ea5e9; --wave-2: #f472b6; }
* { margin: 0; padding: 0; }
body { height: 100vh; background: var(--bg-color); overflow: hidden; }
canvas { display: block; width: 100vw; height: 100vh; }
</style>
</head>
<body>
<canvas id="c"></canvas>
<script>
const cv = document.getElementById('c'), ctx = cv.getContext('2d');
const css = getComputedStyle(document.documentElement);
function hex(h){h=(h||'').trim().replace('#','');if(h.length===3)h=h.split('').map(c=>c+c).join('');const n=parseInt(h,16);return[(n>>16)&255,(n>>8)&255,n&255];}
const c1 = hex(css.getPropertyValue('--wave-1') || '#0ea5e9');
const c2 = hex(css.getPropertyValue('--wave-2') || '#f472b6');
const SCALE = 5; let lw, lh, img, buf;
const src = [];
function build() {
cv.width = Math.ceil(innerWidth / SCALE); cv.height = Math.ceil(innerHeight / SCALE);
lw = cv.width; lh = cv.height; img = ctx.createImageData(lw, lh); buf = img.data;
ctx.imageSmoothingEnabled = true;
src.length = 0;
for (let i = 0; i < 5; i++) src.push({ x: Math.random() * lw, y: Math.random() * lh, ax: Math.random() * 6, ay: Math.random() * 6, sx: 0.15 + Math.random() * 0.2, sy: 0.15 + Math.random() * 0.2 });
}
addEventListener('resize', build); build();
function step(t) {
for (const s of src) { s.px = lw / 2 + Math.cos(s.ax + t * s.sx) * lw * 0.4; s.py = lh / 2 + Math.sin(s.ay + t * s.sy) * lh * 0.4; }
for (let y = 0; y < lh; y++) {
for (let x = 0; x < lw; x++) {
let v = 0;
for (const s of src) { const d = Math.hypot(x - s.px, y - s.py); v += Math.cos(d * 0.28 - t * 2.4); }
const n = 0.5 + v / src.length * 0.5; // 0..1
const i = (y * lw + x) * 4;
buf[i] = c1[0] + (c2[0] - c1[0]) * n; buf[i + 1] = c1[1] + (c2[1] - c1[1]) * n; buf[i + 2] = c1[2] + (c2[2] - c1[2]) * n; buf[i + 3] = 255;
}
}
ctx.putImageData(img, 0, 0);
}
if (matchMedia('(prefers-reduced-motion: reduce)').matches) step(1.0);
else { let t = 0; (function loop(){ t += 0.02; step(t); requestAnimationFrame(loop); })(); }
</script>
</body>
</html> About this animation
Concentric ripples radiate from wandering sources and overlap into shifting moire interference — like raindrops on a pond of light.
Related