"> "> ">
<!-- tsParticles library -->
    <script src="<https://cdn.jsdelivr.net/npm/tsparticles@2.12.0/tsparticles.bundle.min.js>"></script>

    <!-- Simplex Noise library -->
    <script src="<https://cdn.jsdelivr.net/npm/simplex-noise@2.4.0/simplex-noise.min.js>"></script>
let noiseGenerator = new SimplexNoise(); 
let noiseZ;
let size;
let columns;
let rows;
let w;
let h;
let field;

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

function initField() {
  field = new Array(columns);
  for (let x = 0; x < columns; x++) {
    field[x] = new Array(rows);
    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 = noiseGenerator.noise3D(x / 50, y / 50, noiseZ) * Math.PI * 2;
      let length = noiseGenerator.noise3D(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;
  columns = Math.floor(w / size) + 1;
  rows = Math.floor(h / size) + 1;
  initField();
}

function drawField(ctx) {
  ctx.clearRect(0, 0, w, h); 
  for (let x = 0; x < columns; x++) {
    for (let y = 0; y < rows; y++) {
      let angle = field[x][y][0];
      let length = field[x][y][1];
      ctx.save();
      ctx.translate(x * size, y * size);
      ctx.rotate(angle);
      ctx.strokeStyle = "white";
      ctx.beginPath();
      ctx.moveTo(0, 0);
      ctx.lineTo(0, size * length);
      ctx.stroke();
      ctx.restore();
    }
  }
}

tsParticles
  .load("tsparticles", {
    background: {
      color: {
        value: "#000"
      }
    },
    fpsLimit: 120,
    particles: {
      number: {
        value: 200,
        density: {
          enable: true,
          value_area: 800
        }
      },
      color: {
        value: ["#5bc0eb", "#fde74c", "#9bc53d", "#e55934", "#fa7921"]
      },
      shape: {
        type: "square",
        stroke: {
          width: 0,
          color: "#000000"
        }
      },
      opacity: {
        value: 1
      },
      size: {
        value: 5
      },
      line_linked: {
        enable: false,
        distance: 150,
        color: "#ffffff",
        opacity: 0.4,
        width: 1
      },
      move: {
        enable: true,
        speed: 1,
        direction: "none",
        random: false,
        straight: false,
        outMode: "out",
        bounce: false,
        warp: true,
        noise: {
          enable: true,
          delay: {
            value: 0
          }
        },
        trail: {
          enable: false,
          color: {
            value: "#000"
          },
          length: 30
        }
      }
    },
    interactivity: {
      detect_on: "canvas",
      events: {
        resize: true
      }
    },
    detectRetina: true,
    pauseOnBlur: true
  })
  .then((container) => {
    container.setNoise({
      init: function () {
        setup(container);
      },
      update: function () {
        calculateField();
        noiseZ += 0.004;
        drawField(container.canvas.element.getContext("2d"));
      },
      generate: function (p) {
        const pos = p.getPosition();

        const px = Math.min(Math.max(Math.floor(pos.x / size), 0), columns - 1);
        const py = Math.min(Math.max(Math.floor(pos.y / size), 0), rows - 1);

        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();
  });