const shapeSize = Math.hypot(innerWidth, innerHeight) / Math.hypot(1920, 1080) * 50;
const colors = ["#FFC30F", "#581845", "#900C3F", "#C70039", "#FF5733", "#1AC7C4"];
createCanvas(windowWidth, windowHeight, WEBGL);
sh = createShader(vert,frag);
sh.setUniform("u_resolution", [width*pixelDensity(),height*pixelDensity()]);
sh.setUniform("u_tex", this._renderer);
for(var j = 0; j < 60; j++){
const s = random(shapeSize/3, shapeSize);
const x = random(width - s * 2) + s - width/2;
const y = random(height - s * 2) + s - height/2;
for(var i = 0; i < resolution; i++){
x: sin(i / resolution * TWO_PI) * s + x,
y: cos(i / resolution * TWO_PI) * s + y,
particles.push(new Particle(x, y, coords1));
sh.setUniform("u_time", millis());
rect(-width/2, -height/2, width, height);
const numParticles = particles.length;
for(var i = 0; i < numParticles; i++){
const particle1 = particles[i];
for(var j = 0; j < numParticles; j++){
const particle2 = particles[j];
var distX = (particle2.pos.x - particle1.pos.x);
var distY = (particle2.pos.y - particle1.pos.y);
var d = distX*distX+distY*distY;
if(d < config.maxDist*config.maxDist){
const intersect = clipper2lib.Clipper.intersect(particle1.shape, particle2.shape, clipper2lib.FillRule.NonZero, -1);
if(intersect.length > 0){
for(var k = 0; k < intersect.length; k++){
var p = getCentroid(intersect[k]);
for(var l = 0; l < intersect[k].length; l++){
x: intersect[k]._path[l*2+0],
y: intersect[k]._path[l*2+1],
const particle3 = new Particle(p.x, p.y, coords);
const difference1 = clipper2lib.Clipper.difference(particle1.shape, particle3.shape, clipper2lib.FillRule.NonZero, -1);
const difference2 = clipper2lib.Clipper.difference(particle2.shape, particle3.shape, clipper2lib.FillRule.NonZero, -1);
particle1.updateShape(difference1);
particle2.updateShape(difference2);
var d = sqrt(accumX*accumX+accumY*accumY);
particle1.acc.x += accumX/d * 0.52;
particle1.acc.y += accumY/d * 0.52;
var xAccell = particle1.acc.x * (config.acceleration/2);
var yAccell = particle1.acc.y * (config.acceleration/2);
particle1.v.add(xAccell, yAccell);
particle1.v.limit(config.maxAcceleration);
rect(-width/2,-height/2,width,height);
constructor(x, y, coords) {
this.acc = createVector(0, 0);
this.v = createVector(random(-2, 2), random(-2, 2));
this.color = random(colors);
const shape = new clipper2lib.PathsD();
this.area = getArea(shape);
var p = getCentroid(shape[0]);
this.area = getArea(shape);
var tempX = this.pos.x + this.v.x;
var tempY = this.pos.y + this.v.y;
if(tempX < -width/2 || tempX > width/2)
if(tempY < -height/2 || tempY > height/2)
this.lastPos.x = this.pos.x;
this.lastPos.y = this.pos.y;
this.pos.x = this.pos.x + this.v.x;
this.pos.y = this.pos.y + this.v.y;
movePathsD(this.shape, this.pos.x - this.lastPos.x, this.pos.y - this.lastPos.y);
function getCentroid(p) {
const numPoints = p.length;
for (let i = 0; i < numPoints; i++) {
const j = (i + 1) % numPoints;
const xi = p._path[i * 2 + 0], yi = p._path[i * 2 + 1];
const xj = p._path[j * 2 + 0], yj = p._path[j * 2 + 1];
const a = xi * yj - xj * yi;
const numPoints = p[0].length;
for (let i = 0; i < numPoints; i++) {
const j = (i + 1) % numPoints;
const xi = p[0]._path[i * 2 + 0], yi = p[0]._path[i * 2 + 1];
const xj = p[0]._path[j * 2 + 0], yj = p[0]._path[j * 2 + 1];
const a = xi * yj - xj * yi;
function movePathsD(p, x, y){
for(var j = 0; j < p.length; j++){
for(var i = 0; i < p[j].length; i++){
for(var j = 0; j < p.length; j++){
for(var i = 0; i < p[j].length; i++){
vertices.push({x: p[j]._path[i*2+0], y: p[j]._path[i*2+1]});
const hobbyPoints = createHobbyBezier(vertices, { tension: 1, cyclic: true });
vertex(vertices[0].x, vertices[0].y);
hobbyPoints.forEach(({ startControl, endControl, point }) => {
bezierVertex(startControl.x, startControl.y, endControl.x, endControl.y, point.x, point.y);