birth: Fractal Tendrils in Pulsing Stillness
This commit is contained in:
parent
cd81545635
commit
6c316ab240
1 changed files with 151 additions and 0 deletions
151
index.html
Normal file
151
index.html
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Organic Fractal Bloom</title>
|
||||
<style>
|
||||
body { margin: 0; overflow: hidden; background: #0a0a0a; }
|
||||
canvas { display: block; }
|
||||
#attribution { position: fixed; bottom: 10px; right: 10px; color: #555; 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();
|
||||
|
||||
// L-system parameters derived from the abstract specs
|
||||
const params = {
|
||||
axiom: 'X',
|
||||
rules: {
|
||||
'X': 'F-[[X]+X]+F[+FX]-X',
|
||||
'F': 'FF'
|
||||
},
|
||||
angle: 25,
|
||||
iterations: 4,
|
||||
stepLength: 20,
|
||||
stepReduction: 0.92,
|
||||
randomness: 0.2,
|
||||
colorBase: [220, 220, 220],
|
||||
saturation: 0.1,
|
||||
brightness: 0.5,
|
||||
pulsationSpeed: 0.5
|
||||
};
|
||||
|
||||
let angle = 0;
|
||||
let pulsation = 0.72;
|
||||
|
||||
function drawLSys() {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.save();
|
||||
|
||||
// Apply pulsation effect
|
||||
pissation = 0.72 + Math.sin(Date.now() * 0.001 * params.pulsationSpeed) * 0.5;
|
||||
ctx.translate(canvas.width / 2, canvas.height / 1.5);
|
||||
|
||||
// Draw recursive branches
|
||||
const turns = ['F', '+', '-', '[', ']'];
|
||||
let stack = [];
|
||||
let hueVariation = 0;
|
||||
let branchCount = 0;
|
||||
|
||||
function drawSegment(length, depth) {
|
||||
branchCount++;
|
||||
if (branchCount > 11) return; // Branch count constraint
|
||||
|
||||
// Color variation based on dryness
|
||||
const hue = (20 + hueVariation * 10) % 360;
|
||||
const saturation = params.saturation * 100;
|
||||
const brightness = params.brightness * (0.5 + pulsation * 0.5);
|
||||
ctx.strokeStyle = `hsl(${hue}, ${saturation}%, ${brightness}%)`;
|
||||
ctx.lineWidth = Math.max(0.5, length * 0.05 * (1 + pulsation * 0.5));
|
||||
|
||||
// Add some organic roughness
|
||||
ctx.lineCap = 'round';
|
||||
ctx.lineJoin = 'round';
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, 0);
|
||||
const x = length * Math.cos(angle);
|
||||
const y = length * Math.sin(angle);
|
||||
ctx.lineTo(x, y);
|
||||
ctx.stroke();
|
||||
|
||||
// Branch recursion
|
||||
if (depth > 0) {
|
||||
ctx.save();
|
||||
ctx.translate(x, y);
|
||||
angle += Math.random() * params.randomness - params.randomness * 0.5;
|
||||
drawSegment(length * params.stepReduction, depth - 1);
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
// Generate path with loops
|
||||
let currentString = params.axiom;
|
||||
for (let i = 0; i < params.iterations; i++) {
|
||||
let nextString = '';
|
||||
for (let c of currentString) {
|
||||
if (params.rules[c]) {
|
||||
nextString += params.rules[c];
|
||||
} else {
|
||||
nextString += c;
|
||||
}
|
||||
}
|
||||
currentString = nextString;
|
||||
}
|
||||
|
||||
// Draw the L-system with some randomness
|
||||
ctx.save();
|
||||
for (let i = 0; i < currentString.length; i++) {
|
||||
const c = currentString[i];
|
||||
if (c === 'F') {
|
||||
drawSegment(params.stepLength, 5);
|
||||
angle += Math.random() * params.randomness;
|
||||
} else if (c === '+') {
|
||||
angle += params.angle * (1 + Math.random() * params.randomness);
|
||||
} else if (c === '-') {
|
||||
angle -= params.angle * (1 + Math.random() * params.randomness);
|
||||
} else if (c === '[') {
|
||||
stack.push({x: ctx.lineTo(), y: ctx.lineTo(), angle: angle});
|
||||
} else if (c === ']') {
|
||||
const point = stack.pop();
|
||||
if (point) {
|
||||
ctx.lineTo(point.x, point.y);
|
||||
angle = point.angle;
|
||||
}
|
||||
} else if (c === 'X') {
|
||||
// Skip drawing X elements directly
|
||||
}
|
||||
|
||||
// Adjust color slightly for each segment
|
||||
hueVariation += 0.1;
|
||||
}
|
||||
ctx.restore();
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
function animate() {
|
||||
angle += 0.01;
|
||||
drawLSys();
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
animate();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
Loading…
Add table
Reference in a new issue