>_htmlhub
library/particles & systems/particle-collision-reaction

Particle Collision

Live previewPerformance varies by device
particle-collision-reaction.html

No account needed to copy or download. Check dependencies and thelicense status before publishing.

<!DOCTYPE html>
<html>
<head>
<title>Particle Collision Reaction</title>
<style>
  body, html {
    margin: 0;
    padding: 0;
    width: 100%;
    height: 100%;
    overflow: hidden;
    background-color: #050505;
  }
  .collision-canvas {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    filter: brightness(1.2);
  }
</style>
</head>
<body>
<canvas class="collision-canvas"></canvas>
<script>
  const canvas = document.querySelector('.collision-canvas');
  const ctx = canvas.getContext('2d');
  let width, height;
  let particles = [];
  let sparks = [];
  function setSize() {
    width = canvas.width = window.innerWidth;
    height = canvas.height = window.innerHeight;
  }
  class Particle {
    constructor(type) {
      this.type = type; // 0 for left, 1 for right
      this.reset();
    }
    reset(){
      this.x = (this.type === 0) ? -5 : width + 5;
      this.y = Math.random() * height;
      this.vx = (this.type === 0) ? 2 + Math.random() * 2 : -2 - Math.random() * 2;
      this.vy = Math.random() - 0.5;
      this.hue = (this.type === 0) ? 180 : 330;
      this.life = 100 + Math.random() * 100;
    }
    update(){
        this.x += this.vx;
        this.y += this.vy;
        this.life--;
        // Check collision zone and life
        const distToCenter = Math.abs(this.x - width/2);
        if (this.life <= 0 || (distToCenter < 50 && Math.random() > 0.8)) {
            for(let i=0; i < 5; i++) {
                sparks.push(new Spark(this.x, this.y, this.hue));
            }
            this.reset();
        }
    }
    draw() {
      ctx.fillStyle = `hsl(${this.hue}, 100%, 70%)`;
      ctx.beginPath();
      ctx.arc(this.x, this.y, 1.5, 0, Math.PI * 2);
      ctx.fill();
    }
  }
  class Spark {
      constructor(x, y, hue){
          this.x = x;
          this.y = y;
          const angle = Math.random() * Math.PI * 2;
          const speed = Math.random() * 3 + 1;
          this.vx = Math.cos(angle) * speed;
          this.vy = Math.sin(angle) * speed;
          this.life = 50;
          this.hue = hue + Math.random()*40-20;
      }
      update(){
          this.x += this.vx;
          this.y += this.vy;
          this.life--;
      }
      draw(){
          const alpha = this.life / 50;
          ctx.fillStyle = `hsla(${this.hue}, 100%, 80%, ${alpha})`;
          ctx.beginPath();
          ctx.arc(this.x, this.y, 1, 0, Math.PI * 2);
          ctx.fill();
      }
  }
  function init() {
    setSize();
    particles = [];
    sparks = [];
    for(let i=0; i<150; i++){
        particles.push(new Particle(0));
        particles.push(new Particle(1));
    }
  }
  function animate(){
      ctx.globalCompositeOperation = 'source-over';
      ctx.fillStyle = 'rgba(0,0,0,0.1)';
      ctx.fillRect(0,0,width,height);
      ctx.globalCompositeOperation = 'lighter';
      particles.forEach(p => { p.update(); p.draw(); });
      for(let i=sparks.length-1; i>=0; i--){
          sparks[i].update();
          sparks[i].draw();
          if(sparks[i].life <= 0) sparks.splice(i,1);
      }
      requestAnimationFrame(animate);
  }
  window.addEventListener('resize', init);
  init();
  animate();
</script>
</body>
</html>

About this animation

Particles colliding and reacting.

Source Code
Copied to clipboard