body
background: #222
canvas
position: absolute
top: 50%
left: 50%
transform: translate3d(-50%, -50%, 0)
box-shadow: 0 0 24px 8px rgba(0, 0, 0, 0.2)
const L = 600;
const N = 50;
const COLOR = [180, 50, 120];
const SPEED = 1 / 8;
const R_MAX = L * 3;
const PI2 = Math.PI * 2;
const canvas = document.createElement('canvas');
canvas.height = canvas.width = L;
canvas.style.height = canvas.style.width = `${L / 2}px`;
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
ctx.lineWidth = 2;
ctx.strokeStyle = `rgb(${COLOR})`;
ctx.globalCompositeOperation = 'lighter';
// Cache the x and y values for fast lookup.
const xBuffer = new ArrayBuffer(N * Uint16Array.BYTES_PER_ELEMENT);
const yBuffer = new ArrayBuffer(N * Uint16Array.BYTES_PER_ELEMENT);
const x = new Int16Array(xBuffer);
const y = new Int16Array(yBuffer);
for (let n = 0; n < N; n++) {
const angle = n * PI2 / N;
x[n] = (1.5 + Math.cos(angle)) * L / 3;
y[n] = (1.5 + Math.sin(angle)) * L / 3;
}
(function draw (t) {
ctx.clearRect(0, 0, L, L);
for (let n = 0; n < N; n++) {
const R = (t * SPEED + n / N * R_MAX) % R_MAX;
ctx.beginPath();
ctx.fillStyle = `rgba(${COLOR}, ${R_MAX / N / R})`;
ctx.arc(x[n], y[n], R, 0, PI2);
ctx.stroke();
ctx.fill();
}
requestAnimationFrame(draw);
})(0);
Also see: Tab Triggers