Portal

html #animation#canvas#generative art
<div>
  <canvas id="canvas" width="64px" height="64px"></canvas>
  <canvas id="canvas2" width="64px" height="64px"></canvas>
</div>

<script>
  const clamp = function (min, n, max) {
    return Math.min(Math.max(n, min), max)
  }

  document.addEventListener(
    "DOMContentLoaded",
    function () {
      const canvas = document.getElementById("canvas")
      const canvas2 = document.getElementById("canvas2")
      const context = canvas.getContext("2d")
      const context2 = canvas2.getContext("2d")
      let time = 0

      context.scale(2, 2)
      context2.scale(2, 2)

      const color = function (x, y, r, g, b) {
        ;[context, context2].forEach((c) => {
          c.fillStyle = "rgb(" + r + ", " + g + ", " + b + ")"
          c.fillRect(x, y, 10, 10)
        })
      }

      const R = function (x, y, time) {
        return Math.floor(180 + 64 * Math.cos((x * x - y * y) / 300 + time))
      }

      const G = function (x, y, time) {
        return Math.floor(
          180 +
            64 *
              Math.sin(
                (x * x * Math.cos(time / 2) + y * y * Math.sin(time / 3)) / 300,
              ),
        )
      }

      const B = function (x, y, time) {
        return Math.floor(
          200 +
            64 *
              Math.sin(
                5 * Math.sin(time / 9) +
                  ((x - 100) * (x - 100) + (y - 100) * (y - 100)) / 1100,
              ),
        )
      }

      const startAnimation = function () {
        for (x = 0; x <= 32; x++) {
          for (y = 0; y <= 32; y++) {
            const r = clamp(80, R(x, y, time), 255)
            const g = clamp(80, G(x, y, time), 245)
            const b = clamp(80, B(x, y, time), 255)

            color(x, y, r, g, b)
          }
        }
        time = time + 0.02
        window.requestAnimationFrame(startAnimation)
      }
      startAnimation()
    },
    false,
  )
</script>

<style>
  canvas {
    width: 40px;
    height: 40px;
    border-radius: 8px;
  }

  #canvas2 {
    position: absolute;
    inset: 0;
    mix-blend-mode: multiply;
  }
</style>