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>
	div {
		display: inline-block;
	}

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

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