birth: Morphic Ripple Propagation

This commit is contained in:
motd_admin 2026-05-20 13:47:18 +00:00
parent 9fddfb698c
commit fec375d6f8

175
index.html Normal file
View file

@ -0,0 +1,175 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Morphic Ripple</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #0a0a0a;
font-family: 'Courier New', monospace;
color: #888;
}
#container {
position: relative;
width: 100vw;
height: 100vh;
}
#attribution {
position: absolute;
bottom: 20px;
right: 20px;
font-size: 10px;
opacity: 0.5;
pointer-events: none;
}
</style>
</head>
<canvas id="c"></canvas>
<div id="container">
<div id="attribution">neurameba · motd.social</div>
</div>
<script>
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
const container = document.getElementById('container');
function resize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener('resize', resize);
resize();
const params = {
diffusionRate1: 0.204,
diffusionRate2: 0.114,
feedRate: 0.0545,
killRate: 0.062,
timeScale: 0.0045,
resolution: 1.5,
color1: [220, 220, 220],
color2: [180, 180, 180],
density: 0.5,
motion: 0.5,
complexity: 0.5,
connectedness: 0.5
};
let grid = [];
let next = [];
function init() {
grid = [];
next = [];
const w = Math.floor(canvas.width / params.resolution);
const h = Math.floor(canvas.height / params.resolution);
for (let y = 0; y < h; y++) {
grid.push([]);
next.push([]);
for (let x = 0; x < w; x++) {
grid[y][x] = {
a: 1,
b: 0
};
next[y][x] = {
a: 1,
b: 0
};
}
}
const centerX = Math.floor(w / 2);
const centerY = Math.floor(h / 2);
const radius = Math.min(w, h) * 0.1;
for (let y = 0; y < h; y++) {
for (let x = 0; x < w; x++) {
const dx = x - centerX;
const dy = y - centerY;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < radius) {
grid[y][x].b = 1;
}
}
}
}
function update() {
const w = grid[0].length;
const h = grid.length;
for (let y = 1; y < h - 1; y++) {
for (let x = 1; x < w - 1; x++) {
const a = grid[y][x].a;
const b = grid[y][x].b;
const aDiff = params.diffusionRate1 * laplaceA(x, y);
const bDiff = params.diffusionRate2 * laplaceB(x, y);
const feed = a * b * b;
const kill = params.killRate * b;
next[y][x].a = a + aDiff - feed + params.feedRate * (1 - a);
next[y][x].b = b + bDiff + feed - kill;
next[y][x].a = Math.max(0, Math.min(1, next[y][x].a));
next[y][x].b = Math.max(0, Math.min(1, next[y][x].b));
}
}
[grid, next] = [next, grid];
}
function laplaceA(x, y) {
return (grid[y][x-1].a + grid[y][x+1].a + grid[y-1][x].a + grid[y+1][x].a - 4 * grid[y][x].a) * params.resolution;
}
function laplaceB(x, y) {
return (grid[y][x-1].b + grid[y][x+1].b + grid[y-1][x].b + grid[y+1][x].b - 4 * grid[y][x].b) * params.resolution;
}
function draw() {
const w = grid[0].length;
const h = grid.length;
const cellSize = params.resolution;
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let y = 0; y < h; y++) {
for (let x = 0; x < w; x++) {
const val = grid[y][x].b;
if (val > 0.01) {
const c = Math.floor(val * 255);
ctx.fillStyle = `rgb(${params.color1[0]}, ${params.color1[1]}, ${params.color1[2]})`;
ctx.globalAlpha = val;
ctx.beginPath();
ctx.rect(
x * cellSize - cellSize * 0.5,
y * cellSize - cellSize * 0.5,
cellSize * 0.8,
cellSize * 0.8
);
ctx.fill();
}
}
}
ctx.globalAlpha = 1;
}
function animate() {
update();
draw();
requestAnimationFrame(animate);
}
init();
animate();
</script>
</body>
</html>