135 lines
4.7 KiB
HTML
135 lines
4.7 KiB
HTML
|
|
<!DOCTYPE html>
|
||
|
|
<html lang="en">
|
||
|
|
<head>
|
||
|
|
<meta charset="UTF-8">
|
||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
|
|
<title>Diffusive Echoes</title>
|
||
|
|
<style>
|
||
|
|
body { margin: 0; overflow: hidden; background: #000; }
|
||
|
|
canvas { display: block; }
|
||
|
|
#attribution { position: fixed; bottom: 10px; right: 10px; color: #fff; font-family: monospace; font-size: 12px; opacity: 0.7; }
|
||
|
|
</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');
|
||
|
|
|
||
|
|
// Set canvas to full window size
|
||
|
|
function resizeCanvas() {
|
||
|
|
canvas.width = window.innerWidth;
|
||
|
|
canvas.height = window.innerHeight;
|
||
|
|
}
|
||
|
|
window.addEventListener('resize', resizeCanvas);
|
||
|
|
resizeCanvas();
|
||
|
|
|
||
|
|
// Parameters
|
||
|
|
const params = {
|
||
|
|
motion: 0.5,
|
||
|
|
density: 0.5,
|
||
|
|
complexity: 0.5,
|
||
|
|
connectedness: 0.5,
|
||
|
|
lifespan: 0.5,
|
||
|
|
pulse: 1.07,
|
||
|
|
tone: { dryness: 0.8 },
|
||
|
|
width: canvas.width,
|
||
|
|
height: canvas.height
|
||
|
|
};
|
||
|
|
|
||
|
|
// Reaction-diffusion simulation
|
||
|
|
const gridSize = 2;
|
||
|
|
const cols = Math.floor(canvas.width / gridSize);
|
||
|
|
const rows = Math.floor(canvas.height / gridSize);
|
||
|
|
const grid = new Array(cols * rows * 4).fill(0);
|
||
|
|
|
||
|
|
// Initialize with random values
|
||
|
|
for (let i = 0; i < grid.length; i += 4) {
|
||
|
|
const val = 0.5 + (Math.random() - 0.5) * 0.2;
|
||
|
|
grid[i] = grid[i + 1] = grid[i + 2] = val;
|
||
|
|
grid[i + 3] = 1; // Alpha
|
||
|
|
}
|
||
|
|
|
||
|
|
// Gray-Wilson reaction-diffusion parameters
|
||
|
|
const Da = 1.0;
|
||
|
|
const Db = 0.5;
|
||
|
|
const feed = 0.054;
|
||
|
|
const kill = 0.062;
|
||
|
|
const dt = 0.1;
|
||
|
|
const iterations = 2;
|
||
|
|
|
||
|
|
function updateGrid() {
|
||
|
|
const newGrid = new Array(grid.length).fill(0);
|
||
|
|
|
||
|
|
for (let i = 0; i < cols; i++) {
|
||
|
|
for (let j = 0; j < rows; j++) {
|
||
|
|
const idx = (i * rows + j) * 4;
|
||
|
|
const a = grid[idx];
|
||
|
|
const b = grid[idx + 1];
|
||
|
|
|
||
|
|
// Gray-Scott reaction
|
||
|
|
const reaction = a * b * b;
|
||
|
|
const newA = a + (Da * (grid[((i-1+cols)%cols)*rows*4 + j*4] +
|
||
|
|
grid[((i+1)%cols)*rows*4 + j*4] +
|
||
|
|
grid[i*rows*4 + ((j-1+rows)%rows)*4] +
|
||
|
|
grid[i*rows*4 + ((j+1)%rows)*4] -
|
||
|
|
4*a) * dt - reaction + feed * (1 - a) * dt);
|
||
|
|
|
||
|
|
const newB = b + (Db * (grid[((i-1+cols)%cols)*rows*4 + j*4 + 1] +
|
||
|
|
grid[((i+1)%cols)*rows*4 + j*4 + 1] +
|
||
|
|
grid[i*rows*4 + ((j-1+rows)%rows)*4 + 1] +
|
||
|
|
grid[i*rows*4 + ((j+1)%rows)*4 + 1] -
|
||
|
|
4*b) * dt + reaction - (kill + feed) * b * dt);
|
||
|
|
|
||
|
|
newGrid[idx] = newA;
|
||
|
|
newGrid[idx + 1] = newB;
|
||
|
|
|
||
|
|
// Set alpha based on concentration
|
||
|
|
const alpha = Math.min(1, Math.pow(newA + newB, 1.5) * 1.5);
|
||
|
|
newGrid[idx + 3] = alpha;
|
||
|
|
|
||
|
|
// Gray value based on concentration contrast
|
||
|
|
const gray = (newA * 0.3 + newB * 0.7) * 255;
|
||
|
|
newGrid[idx] = newGrid[idx + 1] = newGrid[idx + 2] = gray;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Apply new grid
|
||
|
|
for (let i = 0; i < grid.length; i++) {
|
||
|
|
grid[i] = newGrid[i];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function draw() {
|
||
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||
|
|
|
||
|
|
const imageData = ctx.createImageData(cols, rows);
|
||
|
|
const data = imageData.data;
|
||
|
|
|
||
|
|
// Copy from grid to imageData
|
||
|
|
for (let i = 0; i < grid.length; i += 4) {
|
||
|
|
data[i] = grid[i]; // R
|
||
|
|
data[i + 1] = grid[i + 1]; // G
|
||
|
|
data[i + 2] = grid[i + 2]; // B
|
||
|
|
data[i + 3] = grid[i + 3] * 255; // A
|
||
|
|
}
|
||
|
|
|
||
|
|
// Scale up to canvas size
|
||
|
|
ctx.imageSmoothingEnabled = false;
|
||
|
|
ctx.putImageData(imageData, 0, 0, 0, 0, cols, rows);
|
||
|
|
}
|
||
|
|
|
||
|
|
function animate() {
|
||
|
|
for (let i = 0; i < iterations; i++) {
|
||
|
|
updateGrid();
|
||
|
|
}
|
||
|
|
draw();
|
||
|
|
requestAnimationFrame(animate);
|
||
|
|
}
|
||
|
|
|
||
|
|
animate();
|
||
|
|
</script>
|
||
|
|
</body>
|
||
|
|
</html>
|