fractured-light-in-motion-l12x/index.html

162 lines
No EOL
5.6 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Voronoi Dreams</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #0a0a0a;
font-family: 'Courier New', monospace;
}
canvas {
display: block;
}
#attribution {
position: absolute;
bottom: 10px;
right: 10px;
color: #444;
font-size: 10px;
mix-blend-mode: difference;
}
</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 based on input
const params = {
motion: 0.5,
density: 0.5,
complexity: 0.5,
connectedness: 0.5,
lifespan: 0.5,
pulse: { avg: 1.1, min: 1.0, max: 1.2 },
tone: { dryness: 0.9, curiosity: 0.1 }
};
// Voronoi setup
const cells = [];
const maxPoints = Math.floor(200 + 200 * params.density);
const maxNoiseScale = 5 * params.complexity;
const connectionProb = 0.2 * params.connectedness;
const pointSize = 2 + 3 * params.complexity;
const colorVariation = 30 * params.dryness;
// Generate random points
for (let i = 0; i < maxPoints; i++) {
cells.push({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
vx: (Math.random() - 0.5) * params.motion * 2,
vy: (Math.random() - 0.5) * params.motion * 2,
persistent: Math.random() < params.lifespan,
color: `hsl(200, ${params.tone.dryness * 30 + 20}%, ${params.tone.dryness * 50 + 30}%)`
});
}
// Generate connections
const connections = [];
for (let i = 0; i < cells.length; i++) {
for (let j = i + 1; j < cells.length; j++) {
if (Math.random() < connectionProb) {
connections.push({ a: i, b: j });
}
}
}
// Noise function for organic movement
function noise(x, y, scale) {
const X = Math.floor(x / scale) % 255;
const Y = Math.floor(y / scale) % 255;
return (Math.sin(X) * Math.cos(Y)) * 0.5 + 0.5;
}
function drawVoronoi() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Move points with noise-based variation
cells.forEach(cell => {
const scale = maxNoiseScale * (0.5 + 0.5 * Math.sin(Date.now() * 0.001));
const n = noise(cell.x * 0.01, cell.y * 0.01, scale);
cell.x += cell.vx + (n - 0.5) * 0.2;
cell.y += cell.vy + (n - 0.5) * 0.2;
// Boundary bounce
if (cell.x < 0 || cell.x > canvas.width) cell.vx *= -1;
if (cell.y < 0 || cell.y > canvas.height) cell.vy *= -1;
});
// Calculate voronoi diagram (simplified)
const voronoiCells = cells.map(cell => ({
point: cell,
neighbors: []
}));
// Find neighbors (simplified proximity check)
voronoiCells.forEach((cellA, i) => {
voronoiCells.forEach((cellB, j) => {
if (i !== j && Math.hypot(cellA.point.x - cellB.point.x, cellA.point.y - cellB.point.y) < 200) {
cellA.neighbors.push(j);
}
});
});
// Draw cells
voronoiCells.forEach(cell => {
ctx.beginPath();
ctx.arc(cell.point.x, cell.point.y, pointSize, 0, Math.PI * 2);
ctx.fillStyle = cell.point.color;
ctx.fill();
// Draw connections
ctx.strokeStyle = `rgba(255, 255, 255, ${params.tone.dryness * 0.3})`;
ctx.lineWidth = 1;
cell.neighbors.forEach(neighborIndex => {
const neighbor = voronoiCells[neighborIndex].point;
ctx.beginPath();
ctx.moveTo(cell.point.x, cell.point.y);
ctx.lineTo(neighbor.x, neighbor.y);
ctx.stroke();
});
});
// Draw pulses (expanding circles)
const pulseIntensity = params.pulse.avg + params.pulse.min * Math.sin(Date.now() * 0.002);
const pulsePoints = Math.floor(5 * pulseIntensity);
for (let i = 0; i < pulsePoints; i++) {
const x = Math.random() * canvas.width;
const y = Math.random() * canvas.height;
const radius = 50 + Math.random() * 50;
const opacity = 0.1 + 0.1 * Math.random();
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.strokeStyle = `hsla(200, 30%, 70%, ${opacity * params.tone.dryness})`;
ctx.lineWidth = 1;
ctx.stroke();
}
}
function animate() {
drawVoronoi();
requestAnimationFrame(animate);
}
animate();
</script>
</body>
</html>