<h1 class="title">Particle<br />Cloud</h1>
<div class="particles-container"></div>
body {
  background-color: #333;
}

.particles-container {
  position: relative;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  
  .particle {
    background-color: #44ff33;
    border-radius: 3px;
    box-shadow: 0 0 8px rgba(0, 0, 0, 0.3);
    position: absolute;
    left: 0;
    top: 0;
    width: 15px;
    height: 15px;
    outline: 1px solid transparent; // Prevent transform aliasing
  }
}

.title {
  color: #555;
  font-weight: normal;
  font-size: 25vmin;
  font-family: sans-serif;
  line-height: 1em;
  position: absolute;
  text-align: center;
  width: 100%;
  text-shadow: 0 0 20px rgba(0, 10, 0, 0.4);
}
View Compiled
var particles = initParticles(200);

var throttledParticlePush = _.throttle(function (x, y) {
  var particleX,
      particleY,
      force,
      maxForce = 200,
      distance,
      direction,
      maxDistance = 300;
  
  for (var i = 0; i < particles.length; i++) {
    particleX = particles[i]._gsTransform.x;
    particleY = particles[i]._gsTransform.y;
    distance = getDistance(x, y, particleX, particleY);
    
    if (distance < maxDistance) {
      force = (1 - (distance / maxDistance)) * maxForce;
      direction = getDirection(x, y, particleX, particleY);
      
      TweenMax.to(particles[i], 1, {
        x: "+=" + (force * -direction.x),
        y: "+=" + (force * -direction.y)
      });
    }
  }
}, 100);

document.addEventListener("mousemove", function (event) {
  throttledParticlePush(event.pageX, event.pageY);
});

function initParticles (totalParticles) {
  var container = document.querySelector('.particles-container'),
      particles = [],
      particle;
  
  for (var i = 0; i < totalParticles; i++) {
    particle = document.createElement('div');
    particle.className = 'particle';
    container.appendChild(particle);
    particles.push(particle);
    spawnParticle(particle);
  }
  
  return particles;
}

function spawnParticle (particle) {
  var initialRotation = random(0, 360),
      initialScale = random(0.9, 1.5),
      initialX = random(0, document.documentElement.clientWidth),
      initialY = random(0, document.documentElement.clientHeight),
      initialOpacity = random(0.7, 0.9),
      xSpeed = random(-10, 10),
      ySpeed = random(-10, 10),
      rotation = random(-360, 360),
      scale = random(-0.25, 0.5),
      life = random(10, 15),
      delay = random(0, 12),
      fadeTime = 2,
      pulseTime = 1.5;
  
  // Set the initial state of the particle
  TweenMax.set(particle, {
    opacity: 0,
    x: initialX,
    y: initialY,
    rotation: initialRotation,
    scale: 0
  });
  
  // Fade in animation
  TweenMax.to(particle, fadeTime, {
    opacity: initialOpacity,
    scale: initialScale,
    delay: delay,
    ease: Elastic.easeOut
  });
  
  // Movement animation
  TweenMax.to(particle, life, {
    x: initialX + xSpeed * life,
    y: initialY + ySpeed * life,
    rotation: initialRotation + rotation,
    delay: delay,
    ease: Linear.easeNone
  });
  
  // Pulsing animation
  TweenMax.to(particle, pulseTime, {
    backgroundColor: '#227722',
    yoyo: true,
    repeat: -1,
    delay: delay + fadeTime,
    ease: Power2.easeInOut
  });
  
  // Fade out animation
  TweenMax.to(particle, fadeTime, {
    opacity: 0,
    scale: 0,
    delay: life,
    ease: Power2.easeIn,
    onComplete: spawnParticle,
    onCompleteParams: [particle]
  });
}

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

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

function getDirection (x1, y1, x2, y2) {
  var x = x1 - x2,
      y = y1 - y2,
      magnitude = getDistance(x1, y1, x2, y2);
  
  return {
    x: x / magnitude,
    y: y / magnitude
  };
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. //cdnjs.cloudflare.com/ajax/libs/gsap/1.18.2/TweenMax.min.js
  2. //cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js