# Canvas: Circles & Optical Illusions

Ciprian on Monday, March 21, 2022 in Canvas

Here is another experiment in my Canvas series. This time, I have 2 sets of circles being generated, one set increasing in radius and the other one decreasing. I won’t go into math details or break down the code.

This article is part of the JavaScript Canvas series where I post experiments, JavaScript generative art, visualizations, Canvas animations, code snippets and how-to’s.

``````document.addEventListener('DOMContentLoaded', () => {
let last_t = Date.now(),
timer = 0,
PI_BY_180 = Math.PI / 180,
PI2 = 2 * Math.PI,
c = document.querySelector('#canvas'),
ctx = c.getContext('2d'),
items = [],
cx = 200,
cy = 200,
base_line_width = 1,
base_speed = 10,
variable_speed = 50,
interval = 0.3;

function Circle () {
this.enabled = 0;
this.line_width = base_line_width;
}

Circle.prototype.draw = function () {
ctx.strokeStyle = 'black';
ctx.lineWidth = this.line_width;
ctx.beginPath();
ctx.arc(cx, cy, this.radius, 0, PI2, false);
ctx.stroke();
ctx.closePath();

return this;
};

Circle.prototype.update = function (dt) {

if (this.dir === 1) {
angle = (distance / outer_radius_diff) * 180 * PI_BY_180;
sin = Math.sin(angle);
this.radius += (variable_speed * sin + base_speed) * dt;
this.line_width = clamp(base_line_width + sin * 9, base_line_width, 9);

this.enabled = 0;
return false;
}
} else {
angle = (distance / inner_radius_diff) * 90 * PI_BY_180;
sin = Math.sin(angle);
this.radius -= (variable_speed * sin + base_speed) * dt;
this.line_width = clamp(base_line_width + sin * 9, base_line_width, 9);

this.enabled = 0;
return false;
}
}

return this;
};

function animate() {
let now = Date.now(),
circle = null,
i,
dt = (now - last_t) / 1000;

timer += dt;
last_t = now;
ctx.clearRect (0, 0, 600, 600);

if (timer > interval) {
timer = 0;

// outer circle
for (i = 0; i < items.length; i++) {
if (!items[i].enabled) break;
}
circle = items[i];
circle.dir = 1;
circle.enabled = 1;

// inner circle
for (i = i + 1; i < items.length; i++) {
if (!items[i].enabled) break;
}

circle = items[i];
circle.dir = -1;
circle.enabled = 1;
}

for (i = 0; i < items.length; i++) {
circle = items[i];
circle.enabled && circle.update(dt) && circle.draw();
}
window.requestAnimationFrame ? requestAnimationFrame(animate) : mozRequestAnimationFrame(animate);
}

function clamp(val, clamp_val_lower, clamp_val_upper) {
if (val < clamp_val_lower) {
return clamp_val_lower;
} else if (val > clamp_val_upper) {
return clamp_val_upper;
}

return val;
}

function start() {
let i;
items = [];

ctx.clearRect (0, 0, 400, 400);

i = 0;
while (++i < 60) {
items.push(new Circle());
}

animate();
}

start();
});``````