birth: Flowing Memory in Static Fields
This commit is contained in:
parent
323e0b0982
commit
bfb32b963e
1 changed files with 120 additions and 0 deletions
120
index.html
Normal file
120
index.html
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Neurameba Flow Field</title>
|
||||||
|
<style>
|
||||||
|
body { margin: 0; overflow: hidden; background: #0a0a0a; }
|
||||||
|
canvas { display: block; }
|
||||||
|
#attribution { position: fixed; bottom: 10px; right: 10px; color: #444; font-family: monospace; font-size: 10px; }
|
||||||
|
</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 resizeCanvas() {
|
||||||
|
canvas.width = window.innerWidth;
|
||||||
|
canvas.height = window.innerHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('resize', resizeCanvas);
|
||||||
|
resizeCanvas();
|
||||||
|
|
||||||
|
// Flow field parameters
|
||||||
|
const motion = 0.5;
|
||||||
|
const density = 0.5;
|
||||||
|
const complexity = 0.5;
|
||||||
|
const connectedness = 0.5;
|
||||||
|
const lifespan = 0.5;
|
||||||
|
|
||||||
|
// Field dimensions and resolution
|
||||||
|
const gridSize = Math.min(20, 15 + Math.floor(complexity * 15));
|
||||||
|
const cellSize = Math.max(20, 50 - Math.floor(complexity * 30));
|
||||||
|
const fieldWidth = Math.floor(canvas.width / cellSize);
|
||||||
|
const fieldHeight = Math.floor(canvas.height / cellSize);
|
||||||
|
|
||||||
|
// Field vectors
|
||||||
|
const flowField = Array(fieldHeight).fill().map(() =>
|
||||||
|
Array(fieldWidth).fill().map(() => ({
|
||||||
|
x: Math.sin(Date.now() * 0.0005) * motion * 2,
|
||||||
|
y: Math.cos(Date.now() * 0.0003) * motion * 1.5
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Particle system
|
||||||
|
const particleCount = Math.floor(100 + density * 200);
|
||||||
|
const particles = Array(particleCount).fill().map(() => ({
|
||||||
|
x: Math.random() * canvas.width,
|
||||||
|
y: Math.random() * canvas.height,
|
||||||
|
size: 1 + Math.random() * 3 * density,
|
||||||
|
speed: 0.5 + Math.random() * 2 * motion,
|
||||||
|
hue: 180 + Math.random() * 30,
|
||||||
|
opacity: 0.5 + Math.random() * 0.5,
|
||||||
|
lifetime: lifespan * 1000 + Math.random() * 1000,
|
||||||
|
trail: []
|
||||||
|
}));
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
// Update and draw particles
|
||||||
|
particles.forEach(p => {
|
||||||
|
// Age particle
|
||||||
|
p.lifetime -= 16;
|
||||||
|
|
||||||
|
if (p.lifetime > 0) {
|
||||||
|
// Find closest field cell
|
||||||
|
const cellX = Math.floor(p.x / cellSize);
|
||||||
|
const cellY = Math.floor(p.y / cellSize);
|
||||||
|
const vx = flowField[Math.min(cellY, fieldHeight-1)][Math.min(cellX, fieldWidth-1)].x;
|
||||||
|
const vy = flowField[Math.min(cellY, fieldHeight-1)][Math.min(cellX, fieldWidth-1)].y;
|
||||||
|
|
||||||
|
// Move particle
|
||||||
|
p.x += vx * p.speed;
|
||||||
|
p.y += vy * p.speed;
|
||||||
|
|
||||||
|
// Wrap around edges
|
||||||
|
if (p.x < 0) p.x = canvas.width;
|
||||||
|
if (p.x > canvas.width) p.x = 0;
|
||||||
|
if (p.y < 0) p.y = canvas.height;
|
||||||
|
if (p.y > canvas.height) p.y = 0;
|
||||||
|
|
||||||
|
// Add to trail
|
||||||
|
p.trail.push({x: p.x, y: p.y});
|
||||||
|
if (p.trail.length > 20) p.trail.shift();
|
||||||
|
|
||||||
|
// Draw trail
|
||||||
|
ctx.strokeStyle = `hsla(${p.hue}, 70%, 60%, ${p.opacity * (p.lifetime/1000)})`;
|
||||||
|
ctx.lineWidth = p.size * 0.5;
|
||||||
|
ctx.beginPath();
|
||||||
|
p.trail.forEach((point, i) => {
|
||||||
|
if (i === 0) ctx.moveTo(point.x, point.y);
|
||||||
|
else ctx.lineTo(point.x, point.y);
|
||||||
|
});
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Occasionally reset dead particles
|
||||||
|
if (Math.random() < 0.02) {
|
||||||
|
particles.find(p => p.lifetime <= 0).lifetime = 2000;
|
||||||
|
particles.find(p => p.lifetime <= 0).x = Math.random() * canvas.width;
|
||||||
|
particles.find(p => p.lifetime <= 0).y = Math.random() * canvas.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function animate() {
|
||||||
|
draw();
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
}
|
||||||
|
|
||||||
|
animate();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
Add table
Reference in a new issue