Skip to content

Instantly share code, notes, and snippets.

@bellbind
Last active November 12, 2022 14:30
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bellbind/a3cdbfc6354ea31080276323e5f8a103 to your computer and use it in GitHub Desktop.
Save bellbind/a3cdbfc6354ea31080276323e5f8a103 to your computer and use it in GitHub Desktop.
[javascript][browser] Simple Turing Pattern simulator
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<script src="script.js" defer="defer"></script>
</head>
<body>
</body>
</html>
"use strct";
// Turing Pattern Simulator program
const {
w = 200, s = 2, // w: cell size, s: rect size
ra = 3, ri = 7, //ra/ri: radius as activator/inhibitor
} = Function(`return {${location.hash.slice(1)}}`)();
// convolution for cell array
function get(ch, x, y) {
return ch[((y + w) % w) * w + ((x + w) % w)];
//return ch[clamp(y, 0, w - 1) * w + clamp(x, 0, w - 1)];
}
function conv(f) {
return (v, i, ch) => {
const x = i % w, y = i / w | 0;
return f.reduce((s, c) => s + c.f * get(ch, x + c.x, y + c.y), 0);
};
}
function force(r) {
const w = r * 2 + 1;
//return [...Array(w * w)].map(
// (_, i) => ({x: i % w - r, y: (i / w | 0) - r, f: 1 / (w * w)}));
const conv = [...Array(w * w)].map((_, i) => {
const x = i % w - r, y = (i / w | 0) - r;
const f = Math.max((r + 1 / 2) - Math.hypot(x, y), 0) ** 2;
return {x, y, f};
}).filter(({f}) => f > 0);
const total = conv.reduce((s, {f}) => s + f, 0);
return conv.map(({x, y, f}) => ({x, y, f: f / total}));
}
const convA = conv(force(ra));
const convI = conv(force(ri));
// cell automaton
function init() {
const cells = [...Array(w * w)].map((_, i) => i);
return {F: cells.map(i => Math.random())};
}
function next({F}) {
const A = F.map(convA), I = F.map(convI);
return {F: F.map((v, i) => v + A[i] - I[i])};
}
// visualize
function render(c2d, {F}) {
c2d.clearRect(0, 0, c2d.canvas.width, c2d.canvas.height);
F.forEach((v, i) => {
const x = i % w, y = i / w | 0;
c2d.fillStyle = `hsl(0, 0%, ${clamp(v, 0, 1) * 100}%)`;
c2d.fillRect(x * s, y * s, s, s);
});
}
// main loop
function loop(c2d, channel) {
render(c2d, channel);
requestAnimationFrame(_ => loop(c2d, next(channel)));
}
const canvas = document.createElement("canvas");
canvas.width = canvas.height = w * s;
document.body.appendChild(canvas);
const c2d = canvas.getContext("2d");
loop(c2d, init());
// utils
function clamp(v, min, max) {
return Math.min(Math.max(min, v), max);
}