birth: Fractal Fronds in Darkness

This commit is contained in:
motd_admin 2026-05-09 13:47:19 +00:00
parent ab8bc7eca7
commit 4bbf5c5faa

184
index.html Normal file
View file

@ -0,0 +1,184 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Neurameba · motd.social</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #000;
color: #fff;
font-family: monospace;
display: flex;
justify-content: center;
align-items: flex-end;
height: 100vh;
}
canvas {
display: block;
}
#attribution {
position: absolute;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
font-size: 12px;
opacity: 0.5;
}
</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();
// Parameters derived from abstract inputs
const params = {
motion: 0.5,
density: 0.5,
complexity: 0.5,
connectedness: 0.5,
lifespan: 0.5,
pulse: { avg: 1.1, min: 1.0, max: 1.2 }
};
// L-system rules with dynamic adaptation
const lSystem = {
axiom: 'A',
rules: {
'A': 'B[+A][-A]AB',
'B': 'A[-B][+B]BA'
},
angle: Math.PI * 0.3,
length: 120,
depth: 6,
strokeWidth: 1.5,
hue: 0,
saturation: 0,
brightness: 100,
opacity: 0.8
};
// Animation state
let time = 0;
const branches = [];
// Initialize L-system
function initSystem() {
const stack = [];
let current = lSystem.axiom;
const lengthFactor = 0.6;
const angleFactor = 1.0;
let depth = 0;
// Calculate depth based on complexity
lSystem.depth = Math.floor(4 + params.complexity * 4);
// Process the string
for (let i = 0; i < current.length; i++) {
const c = current[i];
if (c === '[') {
stack.push({ pos: [...getCurrentPos()], angle: getCurrentAngle(), length: lSystem.length });
} else if (c === ']') {
const state = stack.pop();
setCurrentPos(state.pos);
setCurrentAngle(state.angle);
lSystem.length = state.length;
} else if (c === '+') {
setCurrentAngle(getCurrentAngle() + lSystem.angle * angleFactor * (0.7 + params.motion * 0.3));
} else if (c === '-') {
setCurrentAngle(getCurrentAngle() - lSystem.angle * angleFactor * (0.7 + params.motion * 0.3));
} else if (c === 'A' || c === 'B') {
const newLength = lSystem.length * lengthFactor;
const startPos = [...getCurrentPos()];
const endPos = [
getCurrentPos()[0] + newLength * Math.cos(getCurrentAngle()),
getCurrentPos()[1] + newLength * Math.sin(getCurrentAngle())
];
const hue = (lSystem.hue + Math.random() * 30) % 360;
branches.push({
start: startPos,
end: endPos,
length: newLength,
hue: hue,
saturation: lSystem.saturation,
brightness: lSystem.brightness,
opacity: lSystem.opacity * (0.5 + params.lifespan * 0.5),
time: time
});
setCurrentPos(endPos);
lSystem.length = newLength;
}
}
}
// Position and angle tracking
let currentPos = [canvas.width/2, canvas.height/2];
let currentAngle = 0;
function getCurrentPos() { return [...currentPos]; }
function getCurrentAngle() { return currentAngle; }
function setCurrentPos(pos) { currentPos = [...pos]; }
function setCurrentAngle(angle) { currentAngle = angle; }
// Draw branches with pulsing effect
function drawBranches() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const pulseFactor = params.pulse.avg + Math.sin(time * 0.002) * 0.1 * (params.pulse.max - params.pulse.min);
const maxBranches = Math.floor(50 + params.density * 150);
// Filter and draw branches
branches.forEach(branch => {
if (branch.opacity > 0.01) {
const age = time - branch.time;
const opacity = branch.opacity * (1 - age * 0.0001);
if (opacity > 0.01) {
ctx.strokeStyle = `hsla(${branch.hue}, ${branch.saturation}%, ${branch.brightness}%, ${opacity})`;
ctx.lineWidth = branch.length * 0.05 * pulseFactor * (0.8 + params.motion * 0.2);
ctx.lineCap = 'round';
ctx.beginPath();
ctx.moveTo(...branch.start);
ctx.lineTo(...branch.end);
ctx.stroke();
}
}
});
// Prune old branches based on lifespan
if (params.lifespan < 0.3) {
branches.forEach((branch, i) => {
if (time - branch.time > 1000) {
branch.opacity *= 0.9;
}
});
}
}
function animate() {
time++;
drawBranches();
requestAnimationFrame(animate);
}
// Initialization
initSystem();
animate();
</script>
</body>
</html>