drifting-strange-attractor-.../index.html

148 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>Drifting Strange Attractor</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #0a0a0a;
font-family: 'Courier New', monospace;
color: #aaa;
}
canvas {
display: block;
}
#info {
position: fixed;
bottom: 20px;
left: 20px;
font-size: 10px;
opacity: 0.5;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<div id="info">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();
// Strange attractor parameters based on input
const params = {
a: 0.524 * 2.5 - 1.0,
b: 0.454 * 2.5,
c: 0.520 * 3.0 + 1.0,
d: 0.488 * 2.5 - 1.0,
motion: 0.58,
density: 0.454,
trailLength: 25 + Math.floor(0.454 * 30),
maxDepth: 15,
pulse: 0.58
};
// Color palette based on tone
const colors = {
curiosity: '#40e0d0', // teal
dryness: '#e0e0e0', // monochrome
playfulness: '#ff8888' // slightly vibrant
};
// Point system
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
this.history = [];
this.maxLength = params.trailLength;
this.pulse = params.pulse * (0.7 + Math.random() * 0.6);
this.depth = 0;
}
update(ax, ay, bx, by, cx, cy, dx, dy) {
const xn = Math.sin(ax * this.y) - Math.cos(bx * this.x);
const yn = Math.sin(cy * this.x) - Math.cos(dx * this.y);
this.x += xn * 0.01 * this.pulse;
this.y += yn * 0.01 * this.pulse;
// Add to history
this.history.push({x: this.x, y: this.y});
if (this.history.length > this.maxLength) {
this.history.shift();
}
this.depth = Math.min(this.depth + 0.01, params.maxDepth);
}
draw() {
ctx.beginPath();
// Draw trail
for (let i = 0; i < this.history.length; i++) {
const p = this.history[i];
const alpha = i / this.history.length;
const size = 1 + (this.maxLength - i) / this.maxLength * 0.8;
ctx.globalAlpha = alpha * 0.7;
ctx.fillStyle = colors.dryness;
ctx.beginPath();
ctx.arc(p.x * canvas.width, p.y * canvas.height, size, 0, Math.PI * 2);
ctx.fill();
}
ctx.globalAlpha = 1.0;
}
}
// Create points based on density
const points = [];
const count = 50 + Math.floor(params.density * 150);
for (let i = 0; i < count; i++) {
points.push(new Point(
Math.random(),
Math.random()
));
}
// Animation loop
let frame = 0;
function animate() {
ctx.fillStyle = 'rgba(0, 0, 0, 0.05)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Update and draw points
for (const p of points) {
p.update(
params.a + Math.sin(frame * 0.01) * 0.1,
params.b,
params.c,
params.d + Math.cos(frame * 0.02) * 0.05,
params.a,
params.b * 0.5,
params.c,
params.d * 1.2
);
p.draw();
}
frame++;
requestAnimationFrame(animate);
}
animate();
</script>
</body>
</html>