JSFiddle - React, Tailwind, and code Playground
HTML
<canvas id="c"></canvas>
CSS
* { margin: 0px; overflow: hidden; }
JavaScript
c = document.getElementById("c");
w = c.width = innerWidth;
h = c.height = innerHeight;
d = c.getContext("2d");
function renderBackground(d) {
d.fillStyle = "black";
d.fillRect(0,0,w,h);
d.fillStyle = "white";
d.fillRect(0,0,w/2,h/2);
d.beginPath();
d.moveTo(-w/2,h);
d.lineTo(0,3 * h/2);
d.lineTo(3 * w/2, 0);
d.lineTo(w, -h/2);
d.closePath();
d.fillStyle = "rgb(128,128,128)";
d.fill();
}
// w : pixels / screen
// phase : div
function pathSeg(d, y, pixPerSeg, pixPerDiv, divPerCycle, pixPerAmp, c0, c1, phase) {
const segPerDiv = Math.round(pixPerDiv / pixPerSeg);
const segPerScreen = Math.ceil(segPerDiv * w / pixPerDiv);
for (let i = 0; i <= segPerScreen; i++) { // i : segment
const x = i * pixPerDiv / segPerDiv; // x : pixels
const nextX = (i + 1) * pixPerDiv / segPerDiv;
const cyc = i / (segPerDiv * divPerCycle); // cyc : cycles
const nextCyc = (i + 1) / (segPerDiv * divPerCycle);
const div = Math.floor(i / segPerDiv + phase) // div : division;
d.beginPath();
d.moveTo(x, y - pixPerAmp * Math.sin(2 * Math.PI * cyc));
d.lineTo(nextX, y - pixPerAmp * Math.sin(2 * Math.PI * nextCyc));
d.lineWidth = 2;
d.strokeStyle = div % 2 ? c1 : c0;
d.stroke();
}
}
let gphase = 0;
function render(d) {
renderBackground(d);
const NUM_WAVES = 7;
const OFFSET = 8;
const pixPerAmp = 6;
const pixPerDiv = 30;
const pixPerSeg = 2;
const c0 = "rgb(64,64,64)";
const c1 = "rgb(192,192,192)"
for (let i = 0; i < NUM_WAVES; i++) {
const phase = gphase + (i % 2 ? 0.5 : 0);
pathSeg(d, OFFSET + (i + 0.5) * h / NUM_WAVES, pixPerSeg, pixPerDiv, 2, pixPerAmp, c0, c1, phase);
pathSeg(d, -OFFSET + (i + 0.5) * h / NUM_WAVES, pixPerSeg, pixPerDiv, 2, pixPerAmp, c0, c1, phase);
}
gphase -= 0.02;
requestAnimationFrame(() => render(d));
}
render(d);