107 lines
No EOL
3.2 KiB
HTML
107 lines
No EOL
3.2 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>chaos bloom</title>
|
|
<style>
|
|
body, html {
|
|
margin: 0;
|
|
padding: 0;
|
|
overflow: hidden;
|
|
background: #0a0a0a;
|
|
height: 100%;
|
|
}
|
|
canvas {
|
|
display: block;
|
|
}
|
|
.attribution {
|
|
position: fixed;
|
|
bottom: 10px;
|
|
right: 10px;
|
|
color: #444;
|
|
font-family: monospace;
|
|
font-size: 10px;
|
|
pointer-events: none;
|
|
z-index: 100;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<canvas id="c"></canvas>
|
|
<div class="attribution">neurameba · motd.social</div>
|
|
<script>
|
|
const canvas = document.getElementById('c');
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
function resize() {
|
|
canvas.width = window.innerWidth;
|
|
canvas.height = window.innerHeight;
|
|
}
|
|
window.addEventListener('resize', resize);
|
|
resize();
|
|
|
|
// Strange attractor parameters
|
|
const points = [];
|
|
const maxPoints = 10000;
|
|
const a = 4.5 + 1.4 * (Math.random() * 2 - 1);
|
|
const b = 0.2 + 0.1 * (Math.random() * 2 - 1);
|
|
const c = 5.8 + 1.2 * (Math.random() * 2 - 1);
|
|
let x = 0.1, y = 0.1;
|
|
|
|
// Stroke style based on tone parameters
|
|
const dryness = 0.8;
|
|
const playfulness = 0.1;
|
|
const hue = 180 + (playfulness * 60);
|
|
const sat = 15 + (dryness * 85);
|
|
const light = 70 + (1 - dryness) * 30;
|
|
|
|
function update() {
|
|
// Motion influence
|
|
const motion = 0.5;
|
|
const steps = Math.floor(1 + motion * 20);
|
|
|
|
for (let i = 0; i < steps; i++) {
|
|
if (points.length < maxPoints) {
|
|
points.push({x: x * canvas.width/2 + canvas.width/2,
|
|
y: y * canvas.height/2 + canvas.height/2,
|
|
size: 1 + Math.random() * 1.5,
|
|
alpha: 0.7 + Math.random() * 0.3});
|
|
|
|
// Strange attractor equations
|
|
const xn = Math.sin(a * y) - Math.cos(b * x);
|
|
const yn = Math.sin(x) - Math.cos(c * y);
|
|
x = xn;
|
|
y = yn;
|
|
}
|
|
}
|
|
|
|
// Fading trail effect
|
|
ctx.fillStyle = `rgba(0, 0, 0, 0.05)`;
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
|
// Draw points
|
|
points.forEach((p, i) => {
|
|
const age = i / points.length;
|
|
const size = p.size * (1 - age * 0.7);
|
|
const alpha = p.alpha * (1 - age * 0.5);
|
|
ctx.fillStyle = `hsla(${hue}, ${sat}%, ${light}%, ${alpha})`;
|
|
ctx.beginPath();
|
|
ctx.arc(p.x, p.y, size, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
});
|
|
|
|
// Cull old points
|
|
while (points.length > maxPoints) {
|
|
points.shift();
|
|
}
|
|
}
|
|
|
|
function animate() {
|
|
update();
|
|
requestAnimationFrame(animate);
|
|
}
|
|
animate();
|
|
</script>
|
|
</body>
|
|
</html> |