const minRadiusOfBall = 7;
const maxRadiusOfBall = 14;
p5.disableFriendlyErrors = true;
createCanvas(960, 540, WEBGL);
directionalLight(color("white"), 0, 0, -1);
directionalLight(color("gray"), 0, 1, -1);
worms.forEach((e) => e.moveHead());
worms.forEach((e) => e.attractNeighborParts());
balls.forEach((e) => e.wrap());
balls.forEach((e) => e.update());
worms.forEach((e) => e.show());
if (!mouseIsPressed) mouseHistory.length = 0;
for (let i = numOfWorms; i--; ) {
for (j = lengthOfWorm; j--; ) {
worms.push(new Worm(group));
function avoidOverlapping() {
const all = balls.concat(mouseHistory);
for (let i = all.length; i--; ) {
const delta = A.pos.copy().sub(B.pos);
const distance = sqrt(delta.x ** 2 + delta.y ** 2);
const sumRadius = A.radius + B.radius;
if (distance < sumRadius) {
const k = 1 - distance / sumRadius;
const force = delta.mult(k);
const v = createVector(mouseX - width / 2, mouseY - height / 2);
function drawMouseHistory() {
mouseHistory = mouseHistory.filter((m) => {
vertex(m.pos.x, m.pos.y);
constructor(arrayOfBalls) {
this.parts = arrayOfBalls;
this.color = color(random(256), random(256), random(256));
this.direction = random(2 * PI);
this.rotation = random(2) < 1 ? -0.01 : 0.01;
const head = this.parts[0];
this.direction += this.rotation;
const unitVector = createVector(1, 0).rotate(this.direction);
head.vel.add(unitVector);
for (let i = 1; i < this.parts.length; i++) {
const current = this.parts[i];
const prev = this.parts[i - 1];
const delta = current.pos.copy().sub(prev.pos);
const force = delta.normalize().mult(2);
for (const p of this.parts) {
this.pos = createVector(random(-1, 1), random(-1, 1));
this.vel = createVector(0, 0);
this.acc = createVector(0, 0);
this.radius = random(minRadiusOfBall, maxRadiusOfBall);
this.vel.add(this.acc.mult(0.5));
const r = this.radius + margin;
this.pos.x = constrain(this.pos.x, r - width / 2, width / 2 - r);
this.pos.y = constrain(this.pos.y, r - height / 2, height / 2 - r);