164 lines
No EOL
5.7 KiB
HTML
164 lines
No EOL
5.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>Recursive Convergence</title>
|
|
<style>
|
|
body { margin: 0; overflow: hidden; background: #0a0a0a; }
|
|
canvas { display: block; }
|
|
#info { position: absolute; bottom: 10px; right: 10px; color: #555; font-family: monospace; font-size: 10px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<canvas id="c"></canvas>
|
|
<div id="info">neurameba · motd.social</div>
|
|
|
|
<script>
|
|
const canvas = document.getElementById('c');
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
function resize() {
|
|
canvas.width = window.innerWidth;
|
|
canvas.height = window.innerHeight;
|
|
}
|
|
window.addEventListener('resize', resize);
|
|
resize();
|
|
|
|
// Fractal configuration derived from parameters
|
|
const config = {
|
|
motion: 0.486,
|
|
density: 0.512,
|
|
complexity: 0.435,
|
|
connectedness: 0.528,
|
|
lifespan: 0.437,
|
|
loops: 270,
|
|
fractalDepth: 18 * config.complexity,
|
|
thickness: 1.25 * config.connectedness,
|
|
energy: 296.6 * (1 - config.lifespan),
|
|
pulseRate: 1 / (0.54 * 1000),
|
|
minSize: 0.5,
|
|
maxSize: 1.75,
|
|
hue: 180,
|
|
saturation: 80 * (1 - config.dryness),
|
|
brightness: 60 + config.energy * 0.1,
|
|
nodes: 55,
|
|
branches: 47,
|
|
branchRatio: 0.7
|
|
};
|
|
|
|
// Fractal nodes with recursive growth
|
|
let nodes = [];
|
|
let branches = [];
|
|
let frames = 0;
|
|
|
|
class FractalNode {
|
|
constructor(x, y, size, depth, parent) {
|
|
this.x = x;
|
|
this.y = y;
|
|
this.size = size;
|
|
this.depth = depth;
|
|
this.parent = parent;
|
|
this.children = [];
|
|
this.life = 0;
|
|
this.energy = config.energy * (config.lifespan + Math.random() * (1 - config.lifespan));
|
|
this.hue = config.hue + Math.sin(depth * 0.1) * 30;
|
|
this.pulse = 1 + Math.sin(frames * config.pulseRate) * config.motion * 0.7;
|
|
this.alpha = 0.3 + this.energy / 400;
|
|
}
|
|
|
|
grow() {
|
|
if (this.depth >= config.fractalDepth) return;
|
|
|
|
const numChildren = Math.max(1, Math.floor(config.branches * config.branchRatio * config.density));
|
|
for (let i = 0; i < numChildren; i++) {
|
|
const angle = Math.PI * 2 * i / numChildren;
|
|
const distance = this.size * (0.7 + Math.sin(this.depth * 0.3) * 0.1);
|
|
const childSize = this.size * config.complexity * 0.5;
|
|
|
|
if (childSize < config.minSize) break;
|
|
|
|
const child = new FractalNode(
|
|
this.x + Math.cos(angle) * distance,
|
|
this.y + Math.sin(angle) * distance,
|
|
childSize,
|
|
this.depth + 1,
|
|
this
|
|
);
|
|
this.children.push(child);
|
|
branches.push(child);
|
|
}
|
|
}
|
|
|
|
update() {
|
|
this.life++;
|
|
this.size *= 1 + config.motion * 0.002;
|
|
this.alpha = this.alpha * 0.97 + 0.3 * (this.energy / 400);
|
|
this.pulse = 1 + Math.sin(frames * config.pulseRate + this.x * 0.01) * config.motion * 0.5;
|
|
|
|
this.children.forEach(child => child.update());
|
|
}
|
|
|
|
draw() {
|
|
const pulseSize = this.size * this.pulse;
|
|
const s = Math.max(0.5, pulseSize * config.thickness);
|
|
const a = Math.min(1, this.alpha);
|
|
|
|
// Draw branch to parent
|
|
if (this.parent) {
|
|
ctx.strokeStyle = `hsla(${this.hue}, ${config.saturation}%, ${config.brightness}%, ${a * 0.7})`;
|
|
ctx.lineWidth = s * config.thickness;
|
|
ctx.beginPath();
|
|
ctx.moveTo(this.x, this.y);
|
|
ctx.lineTo(this.parent.x, this.parent.y);
|
|
ctx.stroke();
|
|
}
|
|
|
|
// Draw node
|
|
ctx.fillStyle = `hsla(${this.hue}, ${config.saturation}%, ${config.brightness}%, ${a})`;
|
|
ctx.beginPath();
|
|
ctx.arc(this.x, this.y, s, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
|
|
this.children.forEach(child => child.draw());
|
|
}
|
|
}
|
|
|
|
function init() {
|
|
// Create initial nodes in a fractal pattern
|
|
const centerX = canvas.width / 2;
|
|
const centerY = canvas.height / 2;
|
|
const initialSize = 100 * config.density;
|
|
|
|
for (let i = 0; i < config.nodes; i++) {
|
|
const angle = Math.PI * 2 * i / config.nodes;
|
|
const distance = 30 + Math.random() * 50;
|
|
nodes.push(new FractalNode(
|
|
centerX + Math.cos(angle) * distance,
|
|
centerY + Math.sin(angle) * distance,
|
|
initialSize * (0.7 + Math.random() * 0.3),
|
|
0
|
|
));
|
|
branches.push(nodes[i]);
|
|
}
|
|
|
|
// Grow fractal branches
|
|
nodes.forEach(node => node.grow());
|
|
}
|
|
|
|
function animate() {
|
|
ctx.fillStyle = 'rgba(0, 0, 0, 0.05)';
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
|
branches.forEach(branch => branch.update());
|
|
branches.forEach(branch => branch.draw());
|
|
|
|
frames++;
|
|
requestAnimationFrame(animate);
|
|
}
|
|
|
|
init();
|
|
animate();
|
|
</script>
|
|
</body>
|
|
</html> |