121 lines
No EOL
3.9 KiB
HTML
121 lines
No EOL
3.9 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Diffusive Pulse</title>
|
|
<style>
|
|
body { margin: 0; overflow: hidden; background: #0a0a0a; }
|
|
canvas { display: block; }
|
|
#info { position: absolute; bottom: 10px; right: 10px; color: #fff; font-family: monospace; font-size: 10px; }
|
|
</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();
|
|
|
|
const params = {
|
|
motion: 0.5,
|
|
density: 0.5,
|
|
complexity: 0.5,
|
|
connectedness: 0.5,
|
|
lifespan: 0.5,
|
|
pulse: { avg: 0.91, min: 0.3, max: 1.4 },
|
|
tone: { dryness: 0.9, curiosity: 0.1 }
|
|
};
|
|
|
|
const gridSize = 60;
|
|
const cols = Math.floor(canvas.width / gridSize) + 2;
|
|
const rows = Math.floor(canvas.height / gridSize) + 2;
|
|
const cells = new Array(cols * rows).fill(0);
|
|
|
|
// Initialize with sparse random cells
|
|
for (let i = 0; i < cells.length * 0.1; i++) {
|
|
const idx = Math.floor(Math.random() * cells.length);
|
|
cells[idx] = Math.random() > 0.5 ? 1 : 0.5;
|
|
}
|
|
|
|
const kernel = () => [
|
|
[0.05, 0.2, 0.05],
|
|
[0.2, -1, 0.2],
|
|
[0.05, 0.2, 0.05]
|
|
];
|
|
|
|
function updateCells() {
|
|
const newCells = new Array(cols * rows).fill(0);
|
|
const k = kernel();
|
|
|
|
for (let y = 0; y < rows; y++) {
|
|
for (let x = 0; x < cols; x++) {
|
|
let sum = 0;
|
|
for (let ky = -1; ky <= 1; ky++) {
|
|
for (let kx = -1; kx <= 1; kx++) {
|
|
const nx = (x + kx + cols) % cols;
|
|
const ny = (y + ky + rows) % rows;
|
|
const idx = ny * cols + nx;
|
|
const weight = k[ky + 1][kx + 1];
|
|
sum += cells[idx] * weight;
|
|
}
|
|
}
|
|
|
|
const idx = y * cols + x;
|
|
newCells[idx] = Math.min(Math.max(sum, 0), 1);
|
|
}
|
|
}
|
|
|
|
// Apply pulse variation
|
|
const pulseFactor = params.pulse.avg * (1 + 0.2 * Math.sin(performance.now() * 0.0005));
|
|
for (let i = 0; i < newCells.length; i++) {
|
|
newCells[i] *= pulseFactor;
|
|
}
|
|
|
|
cells.splice(0, cells.length, ...newCells);
|
|
}
|
|
|
|
function draw() {
|
|
ctx.fillStyle = '#0a0a0a';
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
|
ctx.strokeStyle = `rgba(255, 255, 255, 0.3)`;
|
|
ctx.lineWidth = 1;
|
|
|
|
for (let y = 0; y < rows; y++) {
|
|
for (let x = 0; x < cols; x++) {
|
|
const idx = y * cols + x;
|
|
const val = cells[idx];
|
|
|
|
if (val > 0.01) {
|
|
const alpha = val * 0.7;
|
|
const size = gridSize * 0.8;
|
|
const px = x * gridSize + gridSize/2;
|
|
const py = y * gridSize + gridSize/2;
|
|
|
|
ctx.fillStyle = `rgba(255, 255, 255, ${alpha})`;
|
|
ctx.beginPath();
|
|
ctx.arc(px, py, size * val, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function animate() {
|
|
updateCells();
|
|
draw();
|
|
requestAnimationFrame(animate);
|
|
}
|
|
|
|
animate();
|
|
</script>
|
|
</body>
|
|
</html> |