pulsing-teal-microcosms-uc7l/index.html

143 lines
No EOL
4.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>Neurameba Cellular Canvas</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #0a0a0a;
display: flex;
flex-direction: column;
color: #fff;
font-family: 'Courier New', monospace;
}
canvas {
flex: 1;
display: block;
}
.attribution {
position: absolute;
bottom: 10px;
left: 10px;
font-size: 10px;
opacity: 0.5;
pointer-events: none;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<div class="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 derived from abstract parameters
const params = {
motion: 0.5,
density: 0.5,
complexity: 0.5,
connectedness: 0.5,
lifespan: 0.5,
pulse: { avg: 1.12, min: 0.9, max: 1.3 },
tone: { anger: 0, sadness: 0, curiosity: 0.8, dryness: 0.9, playfulness: 0.1, tension: 0 }
};
// Cellular automata grid
const gridSize = 3;
const cols = Math.floor(canvas.width / gridSize);
const rows = Math.floor(canvas.height / gridSize);
let grid = new Array(cols * rows).fill(0);
let nextGrid = new Array(cols * rows).fill(0);
// Color palette (dryness=monochrome, curiosity=teals)
const palette = {
bg: '#0a0a0a',
cell: `hsl(180, ${10 + params.tone.dryness * 40}%, ${20 + params.tone.curiosity * 30}%)`,
active: `hsl(180, ${30 + params.tone.dryness * 30}%, ${50 + params.tone.curiosity * 20}%)`
};
// Initialize grid with density
for (let i = 0; i < grid.length; i++) {
grid[i] = Math.random() < params.density * 0.5 ? 1 : 0;
}
// Cellular automata rules
function updateGrid() {
const pulse = params.pulse.avg + (Math.random() * (params.pulse.max - params.pulse.min) - (params.pulse.max - params.pulse.min)/2);
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
const idx = x + y * cols;
let neighbors = 0;
// Count neighbors (Moore neighborhood)
for (let ny = -1; ny <= 1; ny++) {
for (let nx = -1; nx <= 1; nx++) {
if (nx === 0 && ny === 0) continue;
const nidx = (x + nx + cols) % cols + ((y + ny + rows) % rows) * cols;
neighbors += grid[nidx];
}
}
// Game of Life rules with motion influence
if (grid[idx] === 1) {
nextGrid[idx] = (neighbors >= 2 && neighbors <= 3) ? 1 : 0;
} else {
nextGrid[idx] = (neighbors === 3) ? 1 : 0;
}
// Pulse modulation
if (nextGrid[idx] === 1) {
nextGrid[idx] = Math.random() < pulse ? 1 : 0;
}
}
}
// Swap grids
[grid, nextGrid] = [nextGrid, grid];
}
// Drawing function
function drawGrid() {
ctx.fillStyle = palette.active;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = palette.cell;
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
const idx = x + y * cols;
if (grid[idx] === 1) {
ctx.fillRect(x * gridSize, y * gridSize, gridSize, gridSize);
}
}
}
// Fade effect based on lifespan
ctx.fillStyle = `rgba(10, 10, 10, ${1 - params.lifespan})`;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
// Animation loop
function animate() {
updateGrid();
drawGrid();
requestAnimationFrame(animate);
}
animate();
</script>
</body>
</html>