<!-- tsParticles container -->
<div id="tsparticles"></div>
<!-- https://github.com/matteobruni/tsparticles -->
/* ---- reset ---- */
body {
  margin: 0;
  font: normal 75% Arial, Helvetica, sans-serif;
}
canvas {
  display: block;
  vertical-align: bottom;
}
/* ---- tsparticles container ---- */
#tsparticles {
  position: absolute;
  width: 100%;
  height: 100%;
  background-color: #0d47a1;
  background-image: url("");
  background-repeat: no-repeat;
  background-size: cover;
  background-position: 50% 50%;
}
let noiseZ;
let size;
let columns;
let rows;
let w;
let h;
let field;

function setup(container) {
  size = 10;
  noiseZ = 0;
  reset(container);
  window.addEventListener("resize", reset);
}

function initField() {
  field = new Array(columns);
  for (let x = 0; x < columns; x++) {
    field[x] = new Array(columns);
    for (let y = 0; y < rows; y++) {
      field[x][y] = [0, 0];
    }
  }
}

function calculateField() {
  for (let x = 0; x < columns; x++) {
    for (let y = 0; y < rows; y++) {
      let angle = noise.simplex3(x / 50, y / 50, noiseZ) * Math.PI * 2;
      let length = noise.simplex3(x / 100 + 40000, y / 100 + 40000, noiseZ);
      field[x][y][0] = angle;
      field[x][y][1] = length;
    }
  }
}

function reset(container) {
  w = container.canvas.size.width;
  h = container.canvas.size.height;
  noise.seed(Math.random());
  columns = Math.floor(w / size) + 1;
  rows = Math.floor(h / size) + 1;
  initField();
}

tsParticles
  .load("tsparticles", {
    fpsLimit: 60,
    emitters: {
      position: {
        x: 50,
        y: 50
      },
      size: {
        width: 100,
        height: 100,
        mode: "percent"
      },
      rate: {
        delay: 0.1,
        quantity: 20
      }
    },
    particles: {
      number: {
        value: 0,
        density: {
          enable: false,
          value_area: 800
        }
      },
      color: {
        value: ["#5bc0eb", "#fde74c", "#9bc53d", "#e55934", "#fa7921"]
      },
      shape: {
        type: "circle",
        stroke: {
          width: 0,
          color: "#000000"
        },
        polygon: {
          nb_sides: 5
        },
        image: {
          src: "https://cdn.matteobruni.it/images/particles/github.svg",
          width: 100,
          height: 100
        }
      },
      opacity: {
        value: 0.5,
        random: false,
        anim: {
          enable: false,
          speed: 1,
          opacity_min: 0.1,
          sync: false
        }
      },
      size: {
        value: 7,
        random: {
          enable: true,
          minimumValue: 4
        },
        anim: {
          enable: false,
          speed: 40,
          size_min: 0.1,
          sync: false
        }
      },
      line_linked: {
        enable: false,
        distance: 150,
        color: "#ffffff",
        opacity: 0.4,
        width: 1
      },
      move: {
        enable: true,
        speed: 4,
        direction: "none",
        random: false,
        straight: false,
        out_mode: "destroy",
        bounce: false,
        noise: {
          enable: true,
          delay: {
            value: 0
          }
        },
        trail: {
          enable: true,
          fillColor: "#000000",
          length: 20
        },
        attract: {
          enable: false,
          rotateX: 600,
          rotateY: 1200
        }
      }
    },
    interactivity: {
      detect_on: "canvas",
      events: {
        onhover: {
          enable: false,
          mode: "grab"
        },
        onclick: {
          enable: false,
          mode: "repulse"
        },
        resize: true
      },
      modes: {
        grab: {
          distance: 200,
          line_linked: {
            opacity: 1
          }
        },
        bubble: {
          distance: 400,
          size: 40,
          duration: 2,
          opacity: 8,
          speed: 3
        },
        repulse: {
          distance: 200
        },
        push: {
          particles_nb: 4
        },
        remove: {
          particles_nb: 2
        }
      }
    },
    retina_detect: true,
    pauseOnBlur: false
  })
  .then((container) => {
    container.setNoise({
      init: function () {
        setup(container);
      },
      update: function () {
        calculateField();
        noiseZ += 0.004;
      },
      generate: function (p) {
        const pos = p.getPosition();

        const px = Math.max(Math.floor(pos.x / size), 0);
        const py = Math.max(Math.floor(pos.y / size), 0);

        if (!field || !field[px] || !field[px][py]) {
          return { angle: 0, length: 0 };
        }

        return {
          angle: field[px][py][0],
          length: field[px][py][1]
        };
      }
    });

    container.refresh();
  });

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdn.jsdelivr.net/npm/tsparticles@1.18.3/dist/tsparticles.min.js
  2. https://codepen.io/DonKarlssonSan/pen/jBWaad