fractured-attractor-dreamin.../index.html

146 lines
No EOL
4.3 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Strange Attractor</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #0a0a0a;
color: #aaa;
font-family: monospace;
font-size: 10px;
}
canvas {
display: block;
}
#attribution {
position: absolute;
bottom: 10px;
left: 10px;
font-family: monospace;
font-size: 10px;
color: #555;
text-shadow: 0 0 2px rgba(0,0,0,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, false);
resizeCanvas();
// Strange attractor parameters (Lorenz system variant)
const params = {
a: 10,
b: 28,
c: 8/3,
dt: 0.01,
scale: 5,
alpha: 0.5,
hue: 180,
maxPoints: 50000
};
// State variables
let points = [];
let time = 0;
let frameCount = 0;
function lorenz(x, y, z) {
const dx = params.a * (y - x);
const dy = x * (params.b - z) - y;
const dz = x * y - params.c * z;
return { x: x + params.dt * dx, y: y + params.dt * dy, z: z + params.dt * dz };
}
function updateAttractor() {
if (points.length > params.maxPoints) {
points.shift();
}
let x = Math.sin(time * 0.3) * 10;
let y = Math.cos(time * 0.2) * 10;
let z = time * 0.1;
for (let i = 0; i < 5; i++) {
const next = lorenz(x, y, z);
x = next.x;
y = next.y;
z = next.z;
// Add some controlled randomization based on motion parameter
x += (Math.random() * 2 - 1) * params.motion * 0.2;
y += (Math.random() * 2 - 1) * params.motion * 0.2;
z += (Math.random() * 2 - 1) * params.motion * 0.2;
const scale = params.scale * (0.8 + 0.4 * params.density);
const px = canvas.width/2 + x * scale;
const py = canvas.height/2 + y * scale;
const pz = z * 0.2;
points.push({
x: px,
y: py,
z: pz,
size: 1 + params.density * 0.5,
opacity: 0.1 + params.alpha * 0.9
});
}
time += params.dt;
}
function drawAttractor() {
ctx.fillStyle = 'rgba(0, 0, 0, 0.05)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
gradient.addColorStop(0, `hsla(${params.hue}, 50%, 30%, ${params.alpha})`);
gradient.addColorStop(1, `hsla(${params.hue + 20}, 50%, 80%, ${params.alpha * 0.7})`);
ctx.strokeStyle = gradient;
ctx.lineWidth = 0.5 + params.complexity * 0.5;
ctx.beginPath();
for (let i = 1; i < points.length; i++) {
const p1 = points[i-1];
const p2 = points[i];
const alpha = p1.opacity * Math.min(1, 1000 / points.length);
ctx.strokeStyle = `hsla(${params.hue}, 50%, ${60 + p1.z * 10}%, ${alpha})`;
ctx.moveTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
}
ctx.stroke();
}
function animate() {
updateAttractor();
drawAttractor();
frameCount++;
requestAnimationFrame(animate);
}
// Initialize with some randomness
params.motion = 0.5;
params.density = 0.5;
params.complexity = 0.5;
params.alpha = 0.6;
animate();
</script>
</body>
</html>