teal-sediments-of-inquiry-k6i4/index.html

156 lines
No EOL
4.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>Organic Memory</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #0a0a0a;
font-family: 'Courier New', monospace;
}
canvas {
display: block;
}
#attribution {
position: absolute;
bottom: 1em;
right: 1em;
color: #444;
font-size: 0.6em;
text-shadow: 0 0 5px rgba(0,0,0,0.7);
}
</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');
// Set canvas to full window size
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
// Parameters derived from abstract inputs
const params = {
motion: 0.5,
density: 0.5,
complexity: 0.5,
connectedness: 0.5,
lifespan: 0.5,
tone: {
curiosity: 0.7,
dryness: 0.8,
playfulness: 0.3
},
pulse: { avg: 1.07 }
};
// Color palette (teals + dryness)
const palette = {
bg: '#0a0a0a',
cell: `hsl(170, 30%, ${70 + Math.sin(Date.now() * 0.0005) * 15}%)`,
edge: `hsl(180, 20%, ${80 + Math.sin(Date.now() * 0.001) * 10}%)`,
trail: `hsla(170, 30%, 70%, 0.1)`
};
// Cellular automaton grid
const gridSize = 60;
const cellSize = Math.min(canvas.width, canvas.height) / gridSize;
const cols = Math.floor(canvas.width / cellSize);
const rows = Math.floor(canvas.height / cellSize);
let grid = Array(rows).fill().map(() =>
Array(cols).fill().map(() => Math.random() > 0.5 ? 1 : 0)
);
// Stabilize states over time
let stableCounter = 0;
const maxStableFrames = 200;
// Animation loop
function animate() {
// Clear with trail effect
ctx.fillStyle = palette.trail;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw grid
ctx.strokeStyle = palette.cell;
ctx.lineWidth = cellSize * 0.4;
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
const state = grid[y][x];
if (state) {
const px = x * cellSize + cellSize/2;
const py = y * cellSize + cellSize/2;
// Draw cell with subtle motion
ctx.beginPath();
ctx.arc(
px + Math.sin(y * 0.1 + Date.now() * 0.0005) * 2,
py + Math.cos(x * 0.1 + Date.now() * 0.0007) * 2,
cellSize * 0.4,
0,
Math.PI * 2
);
ctx.stroke();
}
}
}
// Update cellular automaton (Rule 30-like)
const newGrid = grid.map(arr => [...arr]);
for (let y = 1; y < rows - 1; y++) {
for (let x = 1; x < cols - 1; x++) {
const neighbors = (
grid[y-1][x-1] + grid[y-1][x] + grid[y-1][x+1] +
grid[y][x-1] + grid[y][x+1] +
grid[y+1][x-1] + grid[y+1][x] + grid[y+1][x+1]
);
// More aggressive state changes based on parameters
const changeProb = 0.3 + params.motion * 0.5;
if (Math.random() < changeProb) {
newGrid[y][x] = neighbors % 2;
}
}
}
grid = newGrid;
// Stabilization check
let isStable = grid.flat().every((val, i, arr) => {
if (i === 0) return true;
return val === arr[i-1];
});
if (isStable) {
stableCounter++;
} else {
stableCounter = 0;
}
// Rare reset to maintain life
if (stableCounter > maxStableFrames || Math.random() < 0.002) {
grid = Array(rows).fill().map(() =>
Array(cols).fill().map(() => Math.random() > 0.6 ? 1 : 0)
);
stableCounter = 0;
}
requestAnimationFrame(animate);
}
// Start animation
animate();
</script>
</body>
</html>