Waltz of Circles, Canvas
Thousands of circles whose positions, radii and colors are computed by trigonometric functions, frame by frame, and drawn one by one with the Canvas renderer. A reimplementation of Ricky Reusser's Instanced WebGL Circles. Compare with the WebGL and GLSL versions.
js
const count = Inputs.range([1000, 10000], {label: "Circles", step: 1000, value: 2500});js
const width = 640;
const height = 640;
const theta = Array.from({length: count}, (_, i) => (i / count) * Math.PI * 2);
const renderer = cm.canvas({width, height});
const root = cm.flow().renderer(renderer);
let time = 0;
requestAnimationFrame(function frame() {
time += 0.02;
const dist = (d) => 0.6 + 0.2 * Math.cos(d * 6.0 + Math.cos(d * 8.0 + time));
root.append(cm.clear, {fill: "black"});
root
.data(theta)
.append(cm.circle, {
x: (d) => Math.cos(d) * dist(d),
y: (d) => Math.sin(d) * dist(d),
r: (d) => 0.2 + 0.12 * Math.cos(d * 9.0 - time * 2.0),
stroke: (d) => {
const th = 8.0 * d + time * 2.0;
const r = 0.6 + 0.4 * Math.cos(th);
const g = 0.6 + 0.4 * Math.cos(th - Math.PI / 3);
const b = 0.6 + 0.4 * Math.cos(th - (Math.PI * 2.0) / 3.0);
return `rgb(${Math.round(r * 255)}, ${Math.round(g * 255)}, ${Math.round(b * 255)})`;
},
fill: "transparent", // A canvas circle without a fill is filled black.
strokeOpacity: (0.15 * 2000) / count,
})
.transform(cm.mapPosition, {padding: 100})
.transform(cm.mapAttrs, {r: {range: [24, 96]}});
root.render();
requestAnimationFrame(frame);
});
document.body.append(renderer.node());