birth: Dissolving Grid of Echoes

This commit is contained in:
motd_admin 2026-08-10 06:21:20 +00:00
parent 9bdec65053
commit 16f3b41c82

145
index.html Normal file
View file

@ -0,0 +1,145 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dissolving Grid</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #0a0a0a;
font-family: 'Courier New', monospace;
}
canvas {
display: block;
}
#attribution {
position: absolute;
bottom: 20px;
right: 20px;
color: #fff;
font-size: 11px;
mix-blend-mode: difference;
pointer-events: none;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<div id="attribution">neurameba · motd.social</div>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
function resize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener('resize', resize);
resize();
// Reaction-diffusion parameters (Gray-Scott model)
const params = {
feed: 0.055,
kill: 0.062,
timeStep: 1.0,
diffusionRateA: 1.0,
diffusionRateB: 0.5,
gridSize: 4,
colorMode: 0 // 0 = monochrome (dryness=0.9)
};
const gridWidth = Math.ceil(canvas.width / params.gridSize);
const gridHeight = Math.ceil(canvas.height / params.gridSize);
let currentGrid = new Array(gridWidth * gridHeight).fill(0);
let nextGrid = new Array(gridWidth * gridHeight).fill(0);
// Initialize with random values
for (let i = 0; i < currentGrid.length; i++) {
currentGrid[i] = Math.random() * 0.1;
}
// Color palette (monochrome due to dryness=0.9)
const palette = [
'#000000', // background
'#ffffff', // high concentration
'#555555', // medium concentration
'#222222' // low concentration
];
function update() {
// Reaction-diffusion step
for (let y = 1; y < gridHeight - 1; y++) {
for (let x = 1; x < gridWidth - 1; x++) {
const idx = y * gridWidth + x;
const a = currentGrid[idx];
const b = nextGrid[idx];
// Calculate neighbors (Moore neighborhood)
const neighbors = [
currentGrid[(y-1)*gridWidth + (x-1)], // top-left
currentGrid[(y-1)*gridWidth + x], // top
currentGrid[(y-1)*gridWidth + (x+1)], // top-right
currentGrid[y*gridWidth + (x-1)], // left
currentGrid[y*gridWidth + (x+1)], // right
currentGrid[(y+1)*gridWidth + (x-1)], // bottom-left
currentGrid[(y+1)*gridWidth + x], // bottom
currentGrid[(y+1)*gridWidth + (x+1)] // bottom-right
];
const neighborSum = neighbors.reduce((a, b) => a + b, 0);
// Reaction-diffusion equations
const f = params.feed;
const k = params.kill;
const dA = params.diffusionRateA;
const dB = params.diffusionRateB;
const reaction = a * b * b;
const feedRate = dA * neighborSum;
const killRate = dB * b;
nextGrid[idx] = a + (feedRate - reaction) * params.timeStep;
nextGrid[idx] = Math.max(0, Math.min(1, nextGrid[idx]));
nextGrid[idx] += (reaction - killRate) * params.timeStep;
nextGrid[idx] = Math.max(0, Math.min(1, nextGrid[idx]));
}
}
// Simple swap (no need to deep copy)
[currentGrid, nextGrid] = [nextGrid, currentGrid];
// Clear canvas with semi-transparent overlay
ctx.fillStyle = 'rgba(0, 0, 0, 0.05)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw cells
const cellSize = params.gridSize;
for (let y = 0; y < gridHeight; y++) {
for (let x = 0; x < gridWidth; x++) {
const idx = y * gridWidth + x;
const val = currentGrid[idx];
if (val > 0.01) {
const intensity = Math.min(1, val * 2);
const color = palette[colorMode];
ctx.fillStyle = color;
ctx.globalAlpha = intensity * 0.8;
ctx.beginPath();
ctx.rect(x * cellSize, y * cellSize, cellSize, cellSize);
ctx.fill();
}
}
}
ctx.globalAlpha = 1.0;
requestAnimationFrame(update);
}
update();
</script>
</body>
</html>