birth: Pulsing Voronoi Organism

This commit is contained in:
motd_admin 2026-05-24 09:47:21 +00:00
parent 9499543b21
commit 7bad73bc1f

247
index.html Normal file
View file

@ -0,0 +1,247 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Organic Voronoi Network</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background: #0a0a0a;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
font-family: 'Courier New', monospace;
}
canvas {
display: block;
}
#info {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
color: rgba(255, 255, 255, 0.3);
font-size: 12px;
text-align: center;
}
</style>
</head>
<body>
<canvas id="art"></canvas>
<div id="info">neurameba · motd.social</div>
<script>
const canvas = document.getElementById('art');
const ctx = canvas.getContext('2d');
// Set canvas to full window size
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// Parameters derived from the organism
const params = {
motion: 0.536,
density: 0.531,
complexity: 0.542,
connectedness: 0.459,
lifespan: 0.449,
pulse: { avg: 0.64, min: 0.3, max: 2.0 },
tone: { anger: 0.0, sadness: 0.0, curiosity: 0.6, dryness: 0.9, playfulness: 0.1, tension: 0.0 }
};
// Voronoi system configuration
const system = {
points: [],
colors: [],
maxPoints: Math.floor(62 * (params.density * 0.5 + 0.5)),
connections: [],
connectionStrength: 0.459 * 0.4 + 0.3,
growthRate: params.motion * 0.1 + 0.05,
complexity: params.complexity,
lifespanFactor: 1 - params.lifespan * 0.3,
pulse: { current: 1, target: 1, speed: 0.005 }
};
// Initialize points with some random positions
function initPoints() {
system.points = [];
system.colors = [];
system.connections = [];
const gridSize = 20 + 40 * params.complexity;
const cols = Math.floor(canvas.width / gridSize) + 1;
const rows = Math.floor(canvas.height / gridSize) + 1;
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
// Add some randomness to initial positions
const px = x * gridSize + (Math.random() * gridSize * 0.4 - gridSize * 0.2);
const py = y * gridSize + (Math.random() * gridSize * 0.4 - gridSize * 0.2);
system.points.push({
x: px,
y: py,
vx: 0,
vy: 0,
age: 0,
maxAge: 1000 + Math.random() * 1000 * params.lifespan,
color: getVoronoiColor(x, y, cols, rows),
size: 1.5 + Math.random() * 1.5 * params.density,
connections: []
});
}
}
// Create some initial connections
createConnections();
}
function getVoronoiColor(x, y, cols, rows) {
// Monochrome with slight variation based on position
const hue = 200 + (x / cols) * 30 + (y / rows) * 20;
const saturation = 20 + params.tone.dryness * 40;
const lightness = 60 + Math.sin(x * 0.1 + y * 0.1) * 10;
return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
}
function createConnections() {
// Find neighbors within a certain distance
system.points.forEach(point => {
point.connections = [];
});
for (let i = 0; i < system.points.length; i++) {
const p1 = system.points[i];
for (let j = i + 1; j < system.points.length; j++) {
const p2 = system.points[j];
const dx = p2.x - p1.x;
const dy = p2.y - p1.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 150 + 200 * params.connectedness) {
const connection = {
a: i,
b: j,
distance: distance,
strength: 0.3 + 0.7 * params.connectedness,
age: 0
};
p1.connections.push(connection);
p2.connections.push(connection);
system.connections.push(connection);
}
}
}
}
function updateSystem() {
// Update pulse
system.pulse.current += (system.pulse.target - system.pulse.current) * system.pulse.speed;
system.pulse.target = params.pulse.avg + (Math.random() * params.pulse.max - params.pulse.min) * 0.5;
// Update points
system.points.forEach(point => {
// Slow movement based on motion parameter
point.vx += (Math.random() - 0.5) * params.motion * 0.05;
point.vy += (Math.random() - 0.5) * params.motion * 0.05;
// Apply some damping
point.vx *= 0.92;
point.vy *= 0.92;
point.x += point.vx;
point.y += point.vy;
// Keep points within bounds
point.x = Math.max(0, Math.min(canvas.width, point.x));
point.y = Math.max(0, Math.min(canvas.height, point.y));
// Age the point
point.age += 1 * system.lifespanFactor;
point.size = 1.5 + (1 - point.age / point.maxAge) * 1.5;
// Make some points disappear and reappear
if (point.age > point.maxAge || Math.random() < 0.001 * (1 - params.lifespan)) {
point.age = 0;
point.x = Math.random() * canvas.width;
point.y = Math.random() * canvas.height;
point.maxAge = 1000 + Math.random() * 1000 * params.lifespan;
// Occasionally change color
if (Math.random() < 0.1) {
point.color = getVoronoiColor(
Math.floor(point.x / 30),
Math.floor(point.y / 30),
Math.floor(canvas.width / 30),
Math.floor(canvas.height / 30)
);
}
}
});
}
function drawSystem() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Set composite operation for glow effect
ctx.globalCompositeOperation = 'screen';
// Draw connections first
system.connections.forEach(conn => {
const p1 = system.points[conn.a];
const p2 = system.points[conn.b];
if (!p1 || !p2) return;
ctx.strokeStyle = p1.color;
ctx.lineWidth = (1 - conn.distance / 150) * 2 * params.connectedness * system.pulse.current;
ctx.beginPath();
ctx.moveTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.stroke();
});
// Then draw points
system.points.forEach(point => {
ctx.globalAlpha = 0.8 + Math.sin(Date.now() * 0.001) * 0.2;
ctx.fillStyle = point.color;
ctx.beginPath();
ctx.arc(point.x, point.y, point.size, 0, Math.PI * 2);
ctx.fill();
// Draw point outline
ctx.strokeStyle = 'rgba(255,255,255,0.2)';
ctx.lineWidth = 0.5;
ctx.stroke();
});
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = 'source-over';
}
function animate() {
updateSystem();
drawSystem();
requestAnimationFrame(animate);
}
// Handle window resize
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
initPoints();
});
// Initialize and start animation
initPoints();
animate();
</script>
</body>
</html>