{
ctx.globalCompositeOperation = "darken";
drawBg(redrawOpacity);
ctx.globalCompositeOperation = "source-over";
const minR = size / 6;
const maxR = size / 3;
const r = CSMath.lerp(minR, maxR, radiusRatio);
const theta = Math.PI * 2 * t;
const freq = CSMath.lerp(1 / 100, 1, freqRatio);
const maxPhaseShift = Math.PI / 8;
const initialShift = Math.PI / 2;
const drawDot = ({ noiseShift, phaseShift = 0, color }) => {
const rad = theta + phaseShift * maxPhaseShift - initialShift;
const u = Math.cos(rad);
const v = Math.sin(rad);
const dr = CSRandom.noise3D(u, v, noiseShift, freq);
const dR = (dr * (maxR - minR)) / 2;
const x = width / 2 + u * (r + dR);
const y = height / 2 + v * (r + dR);
ctx.beginPath();
ctx.fillStyle = palette.fg();
ctx.arc(x, y, 1 + 7 * dotRadiusRatio, 0, Math.PI * 2);
ctx.fill();
};
heads.forEach((dot) => drawDot(dot));
}