JSFiddle - React, Tailwind, and code Playground

by Josh Pullen

HTML

<canvas id="canvas"></canvas>

CSS

#canvas {
  border: 1px solid black;
}

JavaScript

const canvas = document.querySelector("#canvas");
const ctx = canvas.getContext("2d");

canvas.width = 400;
canvas.height = 400;

let particles = [];
for (let i = 0; i < 10; i++) {
	particles.push({
  	x: Math.random() * 400,
    y: Math.random() * 400,
    xv: (Math.random() - 0.5) * 4,
    yv: (Math.random() - 0.5) * 4
  });
}

function tick() {
	requestAnimationFrame(tick);
  
  ctx.clearRect(0, 0, 400, 400);
	
  for (const particle of particles) {
  	const { x, y, xv, yv } = particle;
  	ctx.beginPath();
    ctx.arc(x, y, 4, 0, 2 * Math.PI);
    ctx.fill();
  }
  
  particles = particles.map(particle => {
  	const newParticle = { ...particle };
    newParticle.x += particle.xv;
    newParticle.y += particle.yv;
    
    const speed = Math.hypot(particle.xv, particle.yv);
    const angle = Math.atan2(particle.yv, particle.xv);
    pushParticle(newParticle, angle, -0.0001 * speed ** 3);
    
    pushParticle(
    	newParticle,
      Math.atan2(particle.y - 200, particle.x - 200) + Math.PI,
      100 / Math.hypot(particle.x - 200, particle.y - 200)
    );
    
    for (const p of particles) {
    	pushParticle(
      	newParticle,
        Math.atan2(particle.y - p.y, particle.x - p.x),
        -1 / (1.8 * particles.length) * Math.hypot(particle.x - p.x, particle.y - p.y)
      );
    }

    return newParticle;
  });
}

function pushParticle(particle, angle, a) {
	particle.xv += a * Math.cos(angle);
  particle.yv += a * Math.sin(angle);
}

tick();