<canvas id="canvas"></canvas>
body, html {
  margin: 0;
  overflow: hidden;
}

canvas {
  display: block;
  cursor: pointer;
}
/*
  Fredrika ❤
  Johan Karlsson, 2020
  https://twitter.com/DonKarlssonSan
  MIT License, see Details View
*/

class Particle {
  constructor(x, y) {
    this.pos = new Vector(x, y);
    let noise = simplex.noise2D(x / settings.zoom, y / settings.zoom);
    let angle = noise * Math.PI * 2;
    let vx = Math.cos(angle);
    let vy = Math.sin(angle);
    this.vel = new Vector(vx, vy);
    let hue = Math.random()*50 - 20;
    this.color = `hsla(${hue}, 100%, 50%, ${settings.alpha})`;
  }
  
  move(acc, gravity) {
    this.vel.addTo(acc);
    this.vel.addTo(gravity);
    this.pos.addTo(this.vel);
    if(this.vel.getLength() > 2) {
      this.vel.setLength(2);
    }
  }
  
  draw() {
    ctx.fillStyle = this.color;
    ctx.fillRect(this.pos.x, this.pos.y, settings.size, settings.size);
  }
}

let canvas;
let ctx;
let w, h;
let particles;
let textParticles;
let settings;
let simplex;

function setup() {
  canvas = document.querySelector("#canvas");
  ctx = canvas.getContext("2d");
  reset();
  window.addEventListener("resize", reset);
  canvas.addEventListener("click", draw);
  settings = {
    x: 0,
    y: -0.05,
    iterations: 50,
    zoom: 70,
    strength: 400,
    alpha: 0.008,
    numberOfParticles: w * h / 30,
    size: 1,
  };
}

function setupParticles() {
  particles = [];
  let r = Math.min(w, h) * 0.024;
  let deltaAngle = Math.PI * 2 / settings.numberOfParticles;
  for(let i = 0; i < settings.numberOfParticles; i += 1) {
    let angle = i * deltaAngle;
    let x = r * 16 * Math.pow(Math.sin(angle), 3);
    let y = -r * (13 * Math.cos(angle) - 5 * Math.cos(2 * angle) - 2 * Math.cos(3 * angle) - Math.cos(4 * angle));
    let particle = new Particle(w/2 + x, h * 0.47 + y);
    particles.push(particle);
  }
}

function reset() {
  w = canvas.width = window.innerWidth;
  h = canvas.height = window.innerHeight;
}

function draw() {
  simplex = new SimplexNoise();
  setupParticles();
  ctx.globalCompositeOperation = "source-over";
  ctx.fillStyle = "black";
  ctx.fillRect(0, 0, w, h);
  ctx.globalCompositeOperation = "lighter";
  drawStuff();
}

function drawStuff() {
  let gravity = new Vector(settings.x, settings.y);
  let zoom = settings.zoom + 2;
  let offset = 8000;
  let noise = new Vector(0, 0);
  for(let i = 0; i < settings.iterations; i++) {
    particles.forEach(p => {
      p.draw();
      let strength = simplex.noise2D(p.pos.x / zoom + offset, p.pos.y / zoom + offset) * settings.strength / 1000 + 0.01;
      let angle = simplex.noise2D(p.pos.x / zoom, p.pos.y / zoom) * Math.PI * 2;
      noise.x = Math.cos(angle) * strength;
      noise.y = Math.sin(angle) * strength;
      p.move(noise, gravity);
    });
  }
}

setup();
draw();

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://codepen.io/DonKarlssonSan/pen/bLGjLm.js
  2. https://codepen.io/DonKarlssonSan/pen/JreEJO.js