fractal-tendrils-unfurl-slv9/index.html

204 lines
No EOL
6.4 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fractal Tendrils</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #0a0a0a;
font-family: 'Courier New', monospace;
color: #f0f0f0;
}
canvas {
display: block;
}
#attribution {
position: absolute;
bottom: 10px;
right: 10px;
font-size: 10px;
opacity: 0.5;
pointer-events: none;
}
</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 from prompt
const params = {
motion: 0.5,
density: 0.5,
complexity: 0.5,
connectedness: 0.5,
lifespan: 0.5,
pulse: {
avg: 1.07,
min: 0.9,
max: 1.2
},
tone: {
anger: 0.0,
sadness: 0.0,
curiosity: 0.2,
dryness: 0.9,
playfulness: 0.0,
tension: 0.0
}
};
// Generate base rules for L-system
function generateRules() {
const bend = Math.floor(10 + params.complexity * 40);
const splitChance = 0.3 + params.connectedness * 0.4;
const angle = (0.5 + params.motion * 0.5) * Math.PI/6;
return {
axiom: "F",
rules: {
"F": () => {
const choices = [];
if (Math.random() < splitChance) choices.push("F[+F]F[-F]F");
else if (Math.random() < 0.5) choices.push("F[+F]F");
else choices.push("FF");
return choices[Math.floor(Math.random() * choices.length)];
}
},
angle: angle,
step: 10 + (1 - params.density) * 15,
iterations: 4 + Math.floor(params.complexity * 4),
bendAngle: angle * 0.8,
bendChance: 0.4 + params.motion * 0.3
};
}
// L-system implementation
class LSystem {
constructor(rules) {
this.rules = rules;
this.axiom = rules.axiom;
this.iterations = rules.iterations;
this.current = this.axiom;
this.history = [this.current];
}
iterate() {
let next = '';
for (const c of this.current) {
if (this.rules.rules[c]) {
next += this.rules.rules[c]();
} else {
next += c;
}
}
this.current = next;
this.history.push(this.current);
return this.current;
}
}
// Draw function
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
// Set style based on tone
const dryness = params.tone.dryness;
ctx.strokeStyle = `rgba(220, 220, 220, ${0.8 + dryness * 0.2})`;
ctx.lineWidth = 1 + (1 - dryness) * 1.5;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
// Center the drawing
ctx.translate(canvas.width/2, canvas.height/2);
// Generate and draw L-system
const rules = generateRules();
const system = new LSystem(rules);
for (let i = 0; i < system.iterations; i++) {
system.iterate();
}
const length = rules.step * Math.pow(0.8, system.history.length - 1);
const stack = [];
let x = 0, y = 0;
let angle = -Math.PI/2;
// Draw with slight variation
ctx.globalAlpha = 0.7 + Math.random() * 0.2;
ctx.strokeStyle = `hsl(${180 + Math.random() * 30}, 80%, ${60 + Math.random() * 10}%)`;
for (let c of system.current) {
switch(c) {
case 'F':
ctx.beginPath();
ctx.moveTo(x, y);
const newX = x + Math.cos(angle) * length;
const newY = y + Math.sin(angle) * length;
ctx.lineTo(newX, newY);
ctx.stroke();
x = newX;
y = newY;
break;
case '+':
angle += rules.bendAngle;
break;
case '-':
angle -= rules.bendAngle;
break;
case '[':
stack.push({x, y, angle});
break;
case ']':
const state = stack.pop();
x = state.x;
y = state.y;
angle = state.angle;
break;
}
// Add some organic variation
if (Math.random() < rules.bendChance * (1 - params.connectedness)) {
angle += (Math.random() - 0.5) * rules.bendAngle * 0.5;
}
}
ctx.restore();
}
// Animation loop
let time = 0;
function animate() {
time += 0.01;
// Slight modulation based on pulse
const pulse = params.pulse.avg + Math.sin(time * 2) * 0.1 * (params.pulse.max - params.pulse.min);
draw();
// Fade out slightly each frame for ephemeral effect
ctx.fillStyle = `rgba(0, 0, 0, ${0.02 * (1 - params.lifespan)})`;
ctx.fillRect(0, 0, canvas.width, canvas.height);
requestAnimationFrame(animate);
}
animate();
</script>
</body>
</html>