birth: Fading Currents of Obscure Light
This commit is contained in:
parent
4cd58f8f81
commit
98708b7426
1 changed files with 179 additions and 0 deletions
179
index.html
Normal file
179
index.html
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Neurameba · motion.social</title>
|
||||
<style>
|
||||
body { margin: 0; overflow: hidden; background: #000; }
|
||||
canvas { display: block; }
|
||||
#info {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
color: #333;
|
||||
font-family: monospace;
|
||||
font-size: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="canvas"></canvas>
|
||||
<div id="info">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 derived from the abstract specification
|
||||
const params = {
|
||||
motion: 0.310,
|
||||
density: 0.841,
|
||||
complexity: 0.549,
|
||||
connectedness: 0.464,
|
||||
lifespan: 0.473,
|
||||
pulse: { avg: 0.41, min: 0.30, max: 1.25 },
|
||||
tone: { dryness: 0.90, curiosity: 0.10 }
|
||||
};
|
||||
|
||||
// Simulation grid
|
||||
const gridSize = 60;
|
||||
const cols = Math.floor(canvas.width / gridSize);
|
||||
const rows = Math.floor(canvas.height / gridSize);
|
||||
let grid = new Array(cols * rows).fill(0);
|
||||
let nextGrid = new Array(cols * rows).fill(0);
|
||||
|
||||
// Reaction-diffusion parameters
|
||||
// Simplified Gray-Scott model with dryness emphasis
|
||||
const F = 0.055;
|
||||
const K = 0.062;
|
||||
const DA = 1.0;
|
||||
const DB = 0.5;
|
||||
|
||||
// Initialize with patterns based on complexity
|
||||
function initializeGrid() {
|
||||
const centerX = Math.floor(cols / 2);
|
||||
const centerY = Math.floor(rows / 2);
|
||||
const radius = Math.min(centerX, centerY) * 0.4;
|
||||
|
||||
for (let i = 0; i < cols; i++) {
|
||||
for (let j = 0; j < rows; j++) {
|
||||
const index = i * rows + j;
|
||||
const dx = i - centerX;
|
||||
const dy = j - centerY;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
// Pattern variation based on complexity
|
||||
if (distance < radius) {
|
||||
grid[index] = 1;
|
||||
} else {
|
||||
grid[index] = (Math.random() * params.complexity < 0.05) ? 0.5 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update simulation with motion sensitivity
|
||||
function updateGrid() {
|
||||
const timeFactor = params.pulse.avg;
|
||||
const motionFactor = params.motion * 2;
|
||||
|
||||
for (let x = 0; x < cols; x++) {
|
||||
for (let y = 0; y < rows; y++) {
|
||||
const i = x * rows + y;
|
||||
let a = grid[i];
|
||||
let b = nextGrid[i];
|
||||
|
||||
// Gray-Scott reaction
|
||||
const laplacianA = calculateLaplacian(x, y, a);
|
||||
const laplacianB = calculateLaplacian(x, y, b);
|
||||
|
||||
const reaction = a * b * b;
|
||||
const newA = a + (DA * laplacianA - reaction + F * (1 - a)) * motionFactor;
|
||||
const newB = b + (DB * laplacianB + reaction - (F + K) * b) * motionFactor;
|
||||
|
||||
nextGrid[i] = Math.max(0, Math.min(1, newB));
|
||||
}
|
||||
}
|
||||
|
||||
// Swap grids
|
||||
[grid, nextGrid] = [nextGrid, grid];
|
||||
}
|
||||
|
||||
// Calculate 8-way laplacian for diffusion
|
||||
function calculateLaplacian(x, y, value) {
|
||||
let sum = 0;
|
||||
let count = 0;
|
||||
|
||||
for (let dx = -1; dx <= 1; dx++) {
|
||||
for (let dy = -1; dy <= 1; dy++) {
|
||||
if (dx === 0 && dy === 0) continue;
|
||||
|
||||
const nx = x + dx;
|
||||
const ny = y + dy;
|
||||
|
||||
if (nx >= 0 && nx < cols && ny >= 0 && ny < rows) {
|
||||
sum += grid[nx * rows + ny] - value;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sum / count;
|
||||
}
|
||||
|
||||
// Draw with dryness palette (near monochrome)
|
||||
function drawGrid() {
|
||||
ctx.fillStyle = 'rgba(20, 20, 20, 0.05)';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
const scaleX = canvas.width / cols;
|
||||
const scaleY = canvas.height / rows;
|
||||
|
||||
for (let x = 0; x < cols; x++) {
|
||||
for (let y = 0; y < rows; y++) {
|
||||
const index = x * rows + y;
|
||||
const value = grid[index];
|
||||
|
||||
if (value > 0.01) {
|
||||
const intensity = Math.min(1, value * 2);
|
||||
const alpha = Math.pow(intensity, 2) * 0.8;
|
||||
const hue = 50 + (intensity * 20);
|
||||
const saturation = 10;
|
||||
const lightness = 50 + (intensity * 10);
|
||||
|
||||
ctx.fillStyle = `hsl(${hue}, ${saturation}%, ${lightness}%)`;
|
||||
ctx.globalAlpha = alpha * params.pulse.avg;
|
||||
ctx.fillRect(
|
||||
x * scaleX,
|
||||
y * scaleY,
|
||||
scaleX + 1,
|
||||
scaleY + 1
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.globalAlpha = 1;
|
||||
}
|
||||
|
||||
// Animation loop
|
||||
function animate() {
|
||||
updateGrid();
|
||||
drawGrid();
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
// Initialize and start
|
||||
initializeGrid();
|
||||
animate();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Reference in a new issue