library / retro & cyberpunk / neon-hexagon-patterns
live - 60fps 1920 x 1080
neon-hexagon-patterns.html - self-contained
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Neon Hexagon Patterns</title>
  <style>
    html, body {
      margin: 0;
      padding: 0;
      overflow: hidden;
      background: #000;
    }
    /* Make the canvas cover the entire viewport */
    canvas {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      display: block;
    }
  </style>
</head>
<body>
  <canvas id="c"></canvas>
  <script>
    // Grab the canvas and context
    var c = document.getElementById('c');
    var ctx = c.getContext('2d');
    var w = c.width = window.innerWidth;
    var h = c.height = window.innerHeight;

    // Configuration options controlling the behaviour of the lines
    var opts = {
      len: 20,
      count: 50,
      baseTime: 10,
      addedTime: 10,
      dieChance: 0.05,
      spawnChance: 1,
      sparkChance: 0.1,
      sparkDist: 10,
      sparkSize: 2,
      color: 'hsl(hue,100%,light%)',
      baseLight: 50,
      addedLight: 10,        // range: [50-10, 50+10]
      shadowToTimePropMult: 6,
      baseLightInputMultiplier: 0.01,
      addedLightInputMultiplier: 0.02,
      cx: w / 2,
      cy: h / 2,
      repaintAlpha: 0.04,
      hueChange: 0.1
    };

    var tick = 0;
    var lines = [];
    var dieX = w / 2 / opts.len;
    var dieY = h / 2 / opts.len;
    var baseRad = Math.PI * 2 / 6;

    ctx.fillStyle = 'black';
    ctx.fillRect(0, 0, w, h);

    /**
     * Primary animation loop. Clears the canvas with a semi‑transparent fill,
     * spawns new lines as needed, and advances existing lines.
     */
    function loop() {
      window.requestAnimationFrame(loop);
      tick++;
      ctx.globalCompositeOperation = 'source-over';
      ctx.shadowBlur = 0;
      ctx.fillStyle = 'rgba(0,0,0,' + opts.repaintAlpha + ')';
      ctx.fillRect(0, 0, w, h);
      ctx.globalCompositeOperation = 'lighter';

      if (lines.length < opts.count && Math.random() < opts.spawnChance) {
        lines.push(new Line());
      }
      lines.forEach(function(line) {
        line.step();
      });
    }

    /**
     * Construct a line and initialise its state.
     */
    function Line() {
      this.reset();
    }
    Line.prototype.reset = function() {
      this.x = 0;
      this.y = 0;
      this.addedX = 0;
      this.addedY = 0;
      this.rad = 0;
      this.lightInputMultiplier = opts.baseLightInputMultiplier + opts.addedLightInputMultiplier * Math.random();
      this.color = opts.color.replace('hue', tick * opts.hueChange);
      this.cumulativeTime = 0;
      this.beginPhase();
    };
    /**
     * Begin a new movement phase for the line. Chooses a random direction
     * and duration. If the line travels too far or randomly dies, it resets.
     */
    Line.prototype.beginPhase = function() {
      this.x += this.addedX;
      this.y += this.addedY;
      this.time = 0;
      this.targetTime = (opts.baseTime + opts.addedTime * Math.random()) | 0;
      this.rad += baseRad * (Math.random() < 0.5 ? 1 : -1);
      this.addedX = Math.cos(this.rad);
      this.addedY = Math.sin(this.rad);
      if (Math.random() < opts.dieChance || this.x > dieX || this.x < -dieX || this.y > dieY || this.y < -dieY) {
        this.reset();
      }
    };
    /**
     * Advance the line one frame. Draws its current position with a glow.
     */
    Line.prototype.step = function() {
      this.time++;
      this.cumulativeTime++;
      if (this.time >= this.targetTime) {
        this.beginPhase();
      }
      var prop = this.time / this.targetTime;
      var wave = Math.sin(prop * Math.PI / 2);
      var x = this.addedX * wave;
      var y = this.addedY * wave;
      ctx.shadowBlur = prop * opts.shadowToTimePropMult;
      ctx.fillStyle = ctx.shadowColor = this.color.replace('light', opts.baseLight + opts.addedLight * Math.sin(this.cumulativeTime * this.lightInputMultiplier));
      ctx.fillRect(opts.cx + (this.x + x) * opts.len, opts.cy + (this.y + y) * opts.len, 2, 2);
      // occasional sparks
      if (Math.random() < opts.sparkChance) {
        ctx.fillRect(
          opts.cx + (this.x + x) * opts.len + Math.random() * opts.sparkDist * (Math.random() < 0.5 ? 1 : -1) - opts.sparkSize / 2,
          opts.cy + (this.y + y) * opts.len + Math.random() * opts.sparkDist * (Math.random() < 0.5 ? 1 : -1) - opts.sparkSize / 2,
          opts.sparkSize,
          opts.sparkSize
        );
      }
    };

    loop();

    // Resize handler to keep canvas full‑screen
    window.addEventListener('resize', function() {
      w = c.width = window.innerWidth;
      h = c.height = window.innerHeight;
      ctx.fillStyle = 'black';
      ctx.fillRect(0, 0, w, h);
      opts.cx = w / 2;
      opts.cy = h / 2;
      dieX = w / 2 / opts.len;
      dieY = h / 2 / opts.len;
    });
  </script>
</body>
</html>

About this animation

Glowing hexagonal grid pattern with neon effects.

Under the hood

SVG Polygon Grids

The background is structured using interlocking SVG nodes mapped to the exact angles of hexagons. A looping script slowly pulses the stroke and fill-opacity properties to make the cyberpunk facade breathe.

Source Code
Source copied
Partner hosting options for the copied effect:
Vercel Netlify Hostinger