canvas
View Compiled
html, body, canvas
	width: 100%
	height: 100%
	overflow: hidden
	content-zooming: none
	user-select: none

body
	cursor: pointer
View Compiled
let af;
let t = 0;
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const avgNeighbors = (map, ro, co) => {
	let sum = 0;
	let count = 0;
	for (let r = ro - 1; r <= ro + 1; ++r) {
		if (r < 0 || r >= map.length) continue;
		for (let c = co - 1; c <= co + 1; ++c) {
			if (c < 0 || c >= map[0].length) continue;
			sum += map[r][c];
			++count;
		}
	}
	return sum / count;
};
const smoothMap = (oldMap) => {
	const newMap = oldMap.slice(0);
	for (let r = 0; r < oldMap.length; ++r) {
		for (let c = 0; c < oldMap[0].length; ++c) {
			const a = avgNeighbors(oldMap, r, c);
			newMap[r][c] = a;
		}
	}
	return newMap;
};
const createMap = (w, h) => {
	let map = new Array(h);
	for (let r = 0; r < h; ++r) {
		map[r] = new Array(w);
		for (let c = 0; c < w; ++c) {
			map[r][c] = Math.random();
		}
	}
	for (let i = 0; i < 5; ++i) map = smoothMap(map);
	let max = map[0][0];
	let min = map[0][0];
	for (let r = 0; r < h; ++r) {
		for (let c = 0; c < w; ++c) {
			max = Math.max(max, map[r][c]);
			min = Math.min(min, map[r][c]);
		}
	}
	return { map, max, min };
};
const project = (x, y, z) => {
	return {
		px: ((x * (z + 3)) / canvas.width) * 50 + canvas.width / 2,
		py: ((y * (z + 3)) / canvas.height) * 50 + canvas.height / 2
	};
};
const render = (mapValues) => {
	const { map, max, min } = mapValues;
	ctx.clearRect(0, 0, canvas.width, canvas.height);
	const w = map[0].length;
	const h = map.length;
	const points = new Array(h);
	for (let r = 0; r < h; ++r) {
		points[r] = new Array(w);
	}
	for (let r = 0; r < h; ++r) {
		for (let c = 0; c < w; ++c) {
			const x = ((c - w / 2) / w) * 2 * canvas.width;
			const y = ((r - h / 2) / h) * 2 * canvas.height;
			const v = (map[r][c] - min) / (max - min);
			const rad = (Math.sin(v * Math.PI * 2 + t) + 1) / 2;
			const { px, py } = project(x, y, rad);
			points[r][c] = { x: px, y: py, v: rad };
		}
	}
	for (let r = 0; r < h; ++r) {
		for (let c = 0; c < w; ++c) {
			const pa = points[r][c];
			if (r > 0 && c > 0) {
				const pb = points[r - 1][c];
				const pc = points[r][c - 1];
				const v = pa.v + pb.v + pc.v / 3;
				ctx.beginPath();
				ctx.moveTo(pa.x, pa.y);
				ctx.lineTo(pb.x, pb.y);
				ctx.lineTo(pc.x, pc.y);
				ctx.fillStyle = `hsl(${v * 30 + 200},100%,${51 + v * 15}%)`;
				ctx.fill();
			}
			if (r < h - 1 && c < w - 1) {
				const pb = points[r + 1][c];
				const pc = points[r][c + 1];
				const v = pa.v + pb.v + pc.v / 3;
				ctx.beginPath();
				ctx.moveTo(pa.x, pa.y);
				ctx.lineTo(pb.x, pb.y);
				ctx.lineTo(pc.x, pc.y);
				ctx.fillStyle = `hsl(${v * 30 + 200},100%,${50 + v * 15}%)`;
				ctx.fill();
			}
		}
	}
	t += 0.05;
	af = requestAnimationFrame(() => render(mapValues));
};
const init = () => {
	cancelAnimationFrame(af);
	canvas.width = window.innerWidth;
	canvas.height = window.innerHeight;
	render(createMap(16, 16));
};
window.onclick = init;
window.onresize = init;
init();

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.