fractal-nebula-in-motion-vth9/index.html

169 lines
No EOL
5.4 KiB
HTML

```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fractal Nebula</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #0a0a0a;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
font-family: Arial, sans-serif;
}
#canvas {
display: block;
}
#attribution {
position: absolute;
bottom: 20px;
color: #666;
font-size: 12px;
text-align: center;
width: 100%;
}
</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');
// Set canvas to full window size
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
// Parameters
const params = {
motion: 0.5,
density: 0.5,
complexity: 0.5,
connectedness: 0.5,
lifespan: 0.5,
pulse: { avg: 1.05, min: 1.0, max: 1.1 },
tone: { anger: 0.0, sadness: 0.0, curiosity: 0.1, dryness: 0.9, playfulness: 0.0, tension: 0.0 }
};
// Fractal system
class FractalBranch {
constructor(x, y, length, angle, depth, width) {
this.x = x;
this.y = y;
this.length = length;
this.angle = angle;
this.depth = depth;
this.width = width * 0.8;
this.color = this.getColor();
this.children = [];
this.pulse = params.pulse.avg;
this.maxDepth = Math.floor(params.complexity * 10);
}
getColor() {
const baseHue = 180 + Math.random() * 30;
const saturation = 50 + Math.random() * 30;
const lightness = 40 + Math.random() * 20;
return `hsl(${baseHue}, ${saturation}%, ${lightness}%)`;
}
grow() {
if (this.depth >= this.maxDepth || Math.random() > 0.6) return;
const newLength = this.length * (0.6 + Math.random() * 0.4);
const newWidth = this.width * (0.6 + Math.random() * 0.4);
const newAngle = this.angle + (Math.random() - 0.5) * Math.PI * 0.2;
const child = new FractalBranch(
this.x + Math.cos(this.angle) * this.length,
this.y + Math.sin(this.angle) * this.length,
newLength,
newAngle,
this.depth + 1,
newWidth
);
this.children.push(child);
child.grow();
}
draw(parentAlpha = 1) {
const pulseFactor = params.pulse.min + (params.pulse.max - params.pulse.min) * (0.5 + Math.sin(Date.now() * 0.005) * 0.5);
this.pulse = this.pulse * 0.9 + pulseFactor * 0.1;
const alpha = parentAlpha * (this.depth / this.maxDepth);
ctx.globalAlpha = alpha;
ctx.strokeStyle = this.color;
ctx.lineWidth = this.width * this.pulse;
ctx.lineCap = 'round';
ctx.beginPath();
ctx.moveTo(this.x, this.y);
ctx.lineTo(
this.x + Math.cos(this.angle) * this.length * this.pulse,
this.y + Math.sin(this.angle) * this.length * this.pulse
);
ctx.stroke();
for (const child of this.children) {
child.draw(alpha);
}
}
}
// Initialize system
let rootBranches = [];
function initSystem() {
rootBranches = [];
const maxBranches = 5 + Math.floor(params.density * 20);
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const maxLength = 50 + params.complexity * 200;
for (let i = 0; i < maxBranches; i++) {
const angle = Math.random() * Math.PI * 2;
const length = maxLength * (0.5 + Math.random() * 0.5);
const branch = new FractalBranch(centerX, centerY, length, angle, 0, 3);
branch.grow();
rootBranches.push(branch);
}
}
// Animation loop
function animate() {
// Fade previous frame
ctx.fillStyle = 'rgba(0, 0, 0, 0.05)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.globalAlpha = 1;
// Update and draw branches
for (const branch of rootBranches) {
branch.draw();
}
requestAnimationFrame(animate);
}
// Start animation
initSystem();
animate();
// Handle parameter changes (simulated)
window.addEventListener('click', () => {
initSystem();
});
</script>
</body>
</html>
```