// p5 Hero — Instance Mode

const hero = function(p) {
  let balls = [];
  const colors = [
    [200, 30, 70], [50, 100, 220], [250, 180, 30],
    [30, 180, 120], [160, 60, 210], [240, 90, 40]
  ];

  p.setup = function() {
    let canvas = p.createCanvas(p.windowWidth, 400);
    canvas.position(0, 0);
    for (let i = 0; i < 35; i++) {
      balls.push({
        x: p.random(p.width), y: p.random(p.height),
        homeX: 0, homeY: 0,
        size: p.random(20, 56),
        speed: p.random(0.2, 0.8),
        col: colors[i % colors.length]
      });
      balls[i].homeX = balls[i].x;
      balls[i].homeY = balls[i].y;
    }
  };

  p.draw = function() {
    p.background(30, 30, 60);
    p.noStroke();

    for (let b of balls) {
      b.homeY += b.speed;
      if (b.homeY > p.height + 40) {
        b.homeY = -40;
        b.homeX = p.random(p.width);
        b.x = b.homeX;
      }

      let d = p.dist(p.mouseX, p.mouseY, b.x, b.y);
      if (d < 120) {
        let angle = p.atan2(b.y - p.mouseY, b.x - p.mouseX);
        let push = p.map(d, 0, 120, 3, 0);
        b.x += p.cos(angle) * push;
        b.y += p.sin(angle) * push;
      }
      b.x = p.lerp(b.x, b.homeX, 0.03);
      b.y = p.lerp(b.y, b.homeY, 0.03);

      p.fill(b.col[0], b.col[1], b.col[2]);
      p.circle(b.x, b.y, b.size);
      p.fill(b.col[0]*0.6, b.col[1]*0.6, b.col[2]*0.6);
      let s = b.size / 44;
      p.circle(b.x - 4*s, b.y - 5*s, 6*s);
      p.circle(b.x + 4*s, b.y - 5*s, 6*s);
      p.circle(b.x, b.y + 3*s, 6*s);
    }
  };

  p.windowResized = function() {
    p.resizeCanvas(p.windowWidth, 400);
  };
};

new p5(hero, document.getElementById('hero-sketch'));

// HTML

<div class="hero" id="hero-sketch">
  <div class="hero-overlay">
    <h1>...</h1>
  </div>
</div>

// CSS

.hero { position: relative; overflow: hidden; }
.hero canvas { position: absolute; top: 0; left: 0; }
.hero-overlay { position: relative; z-index: 1; }
p5.js Hero