canvas.canvas
a.codepen-link(href='https://www.codepen.io/seanfree' target='_blank')
View Compiled
body
  background-color: black
.canvas
  position: absolute
  top: 0
  left: 0
  height: 100vh
  width: 100vw
  box-sizing: border-box
  filter: blur(18px) contrast(5)
.codepen-link
  position: absolute
  bottom: 30px
  right: 30px
  height: 40px
  width: 40px
  z-index: 10
  border-radius: 50%
  box-sizing: border-box
  background-image: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/544318/logo.jpg')
  background-position: center center
  background-size: cover
  opacity: 0.5
  transition: all 0.25s
  &:hover
    opacity: 0.8
    box-shadow: 0 0 6px #efefef
View Compiled
/* jshint esversion: 6 */
((window, document, lib, undefined) => {

  'use strict';

  this.requestAnimationFrame = (() => {
    return window.requestAnimationFrame ||
      window.webkitRequestAnimationFrame ||
      window.mozRequestAnimationFrame ||
      window.oRequestAnimationFrame ||
      window.msRequestAnimationFrame ||
      function(callback) {
        window.setTimeout(callback, 1000 / 60);
      };
  })();

  const RAND = Math.random,
    COS = Math.cos,
    SIN = Math.sin,
    PI = Math.PI,
    TAU = PI * 2;

  class Particle extends lib.v2 {
    constructor(x, y) {
      super(x, y);
      this.noiseVelocity = new lib.v2();
      this.baseVelocity = new lib.v2(1 - RAND() * 2, RAND() * 4)
    }
    update() {
      if (this.baseVelocity.y > -5) {
        this.baseVelocity.y -= 0.2;
      }
      this.add(this.baseVelocity).add(this.noiseVelocity);
    }
  }

  class App {
    constructor() {
      this.mouseOver = false;
      this.mouse = new lib.v2();
      this.base = new lib.v2();
      this.position = new lib.v2();
      this.canvas = document.querySelector('.canvas');
      this.ctx = this.canvas.getContext('2d');
      this.tick = 0;
      this.particles = [];
      let self = this;
      window.onresize = () => {
        self.resize();
      };
      window.onmousemove = (e) => {
        this.mouseHandler(e);
      };
      window.onmouseout = (e) => {
        this.mouseHandler(e);
      };
      window.onclick = (e) => {
        this.mouseHandler(e);
      };
      this.resize();
      this.loop();
    }

    resize() {
      this.width = this.canvas.width = window.innerWidth;
      this.height = this.canvas.height = window.innerHeight;
      this.base.x = this.width / 2;
      this.base.y = this.height - 100;
    }

    mouseHandler(e) {
      this.mouse.x = e.clientX;
      this.mouse.y = e.clientY;
      if (e.type === 'mousemove') {
        this.mouseOver = true;
      }
      if (e.type === 'click') {
        for (let i = 0; i < 20; i++) {
          let p = new Particle(e.clientX, e.clientY);
          p.baseVelocity = new lib.v2(6 - RAND() * 12, RAND() * 8)
          this.particles.push(p);
        }
      }
      if (e.type === 'mouseout') {
        this.mouseOver = false;
      }
    }

    render() {
      this.tick++;
      this.ctx.fillStyle = 'rgba(0,0,0,1)';
      this.ctx.fillRect(0, 0, this.width, this.height);
      this.particles.push(new Particle(this.position.x, this.position.y));
      this.ctx.save();
      for (let i = this.particles.length - 1; i > 0; i--) {
        let p = this.particles[i],
          opacity = (p.y / window.innerHeight + 0.1).toFixed(2),
          theta = ((lib.noise.simplex3(p.x * 0.002, p.y, this.tick) + 1) / 2) * PI,
          velocity = new lib.v2(COS(theta) * 20, SIN(theta) * -5);
        p.noiseVelocity.lerp(velocity);
        p.update();
        this.ctx.fillStyle = 'hsla(40,50%,50%,' + opacity + ')';
        this.ctx.fillRect(p.x - i / 4, p.y - 30, i / 2, 60);
        if (p.y < 0) {
          this.particles.splice(i, 1);
        }
      }
      this.ctx.restore();
      if (this.mouseOver) {
        this.position.lerp(this.mouse);
      } else {
        this.position.lerp(this.base);
      }
    }

    loop() {
      let self = this;
      self.render();
      window.requestAnimationFrame(self.loop.bind(self));
    }
  }

  this.onload = () => {
    let app = new App();
  };

})(this, document, {
  v2: Vector2,
  noise: noise
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdn.jsdelivr.net/gh/josephg/noisejs@master/perlin.js
  2. https://cdn.jsdelivr.net/gh/SeanFree/Vector2@master/Vector2.min.js