<html>
    <head>

    </head>
    <body>
        <canvas id="canvas"></canvas>
    </body>
</html>
var canvas = document.getElementById('canvas');
var c = canvas.getContext('2d');

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;


var colors = [
    {r: 255, g: 71, b: 71},
    {r: 0, g: 206, b: 237},
    {r: 255, g: 255, b: 255}
  ];

function Particle(x, y, dx, dy, radius){
    this.x = x;
    this.y = y;
    this.radius = radius;
    this.randomColor =  Math.floor(Math.random() * colors.length);;
    this.mass = 1;
    this.opacity = 1;
    this.deleted = false;
    this.timeToLive = 8;
    this.velocity = {
        x:  dx || (Math.random() - 0.5) * 4,
        y:  dy || (Math.random() - 0.5) * 4
    };
}

Particle.prototype.update = function(particles) {

    this.draw();
    
    for(let i = 0; i < particles.length; i++){
      if(this === particles[i]) continue;
      if(distance(this.x, this.y, particles[i].x, particles[i].y) - this.radius * 2 <= 0){
        resolveCollision(this, particles[i]);
      }
    }

    if(this.x - this.radius <= 0 || this.x + this.radius >= window.innerWidth){
      this.velocity.x = -this.velocity.x;
    }

    if(this.y - this.radius <= 0 || this.y + this.radius >= window.innerHeight){
      this.velocity.y = -this.velocity.y;
    }

    this.x += this.velocity.x;
    this.y += this.velocity.y;
}

Particle.prototype.draw = function() {
    c.beginPath();
    c.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
    c.strokeStyle = 'rgba(' +colors[this.randomColor].r +',' +
                            colors[this.randomColor].g +',' +
                            colors[this.randomColor].b +',' +
                            this.opacity +')';
    c.stroke();
    c.closePath();
}

function randomIntFromRange(min, max){
   return Math.floor( Math.random() * (max - min + 1) + min );
}

function distance(x1, y1, x2, y2){
    return Math.sqrt( Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
}

function rotate(velocity, angle) {
    const rotatedVelocities = {
        x: velocity.x * Math.cos(angle) - velocity.y * Math.sin(angle),
        y: velocity.x * Math.sin(angle) + velocity.y * Math.cos(angle)
    };

    return rotatedVelocities;
}

function resolveCollision(particle, otherParticle) {
    const xVelocityDiff = particle.velocity.x - otherParticle.velocity.x;
    const yVelocityDiff = particle.velocity.y - otherParticle.velocity.y;

    const xDist = otherParticle.x - particle.x;
    const yDist = otherParticle.y - particle.y;

    if (xVelocityDiff * xDist + yVelocityDiff * yDist >= 0) {

        const angle = -Math.atan2(otherParticle.y - particle.y, otherParticle.x - particle.x);

        const m1 = particle.mass;
        const m2 = otherParticle.mass;

        const u1 = rotate(particle.velocity, angle);
        const u2 = rotate(otherParticle.velocity, angle);

        const v1 = { x: u1.x * (m1 - m2) / (m1 + m2) + u2.x * 2 * m2 / (m1 + m2), y: u1.y };
        const v2 = { x: u2.x * (m1 - m2) / (m1 + m2) + u1.x * 2 * m2 / (m1 + m2), y: u2.y };

        const vFinal1 = rotate(v1, -angle);
        const vFinal2 = rotate(v2, -angle);

        particle.velocity.x = vFinal1.x;
        particle.velocity.y = vFinal1.y;

        otherParticle.velocity.x = vFinal2.x;
        otherParticle.velocity.y = vFinal2.y;
    }
}

var particles;
function init(){
    particles = [];
    let radius = 20;

    for(let i = 0; i < 40; i++){
        let x = randomIntFromRange(radius, window.innerWidth - radius);
        let y = randomIntFromRange(radius, window.innerHeight - radius);
        particles.push(new Particle(x, y, null, null, radius));
    }
}

function animate(){

    if(particles.length == 0) return;

    requestAnimationFrame(animate);

    c.fillStyle = 'rgba(0, 0, 0, 0.05)';
    c.fillRect(0, 0, canvas.width, canvas.height);

    particles.forEach(p => {
        p.update(particles);
    });

    particles = particles.filter(a => !a.deleted)
    if(particles.length === 0){
        c.fillStyle = 'rgba(0, 0, 0)';
        c.fillRect(0, 0, canvas.width, canvas.height);
    }
}

init();
animate();

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.