flowing-tendrils-of-time-5p6s/index.html

215 lines
No EOL
8.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>Flow Field Dance</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #0a0a0a;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
font-family: 'Courier New', monospace;
}
canvas {
display: block;
}
#attribution {
position: absolute;
bottom: 20px;
color: #555;
font-size: 10px;
text-align: center;
width: 100%;
}
</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 from submission
const params = {
motion: 0.5,
density: 0.5,
complexity: 0.5,
connectedness: 0.5,
lifespan: 0.5,
pulse: { avg: 1.1, min: 0.9, max: 1.2 },
tone: { dryness: 0.8, playfulness: 0.1 }
};
// Create flow field
const cols = Math.floor(30 + params.density * 30);
const rows = Math.floor(20 + params.density * 20);
const field = [];
const fieldSize = Math.min(canvas.width, canvas.height) * 0.8;
// Initialize flow field with Perlin noise for organic flow
for (let y = 0; y < rows; y++) {
field[y] = [];
for (let x = 0; x < cols; x++) {
const angle = (Math.random() * Math.PI * 2) * params.complexity;
const magnitude = 0.5 + Math.random() * 0.5 * params.motion;
field[y][x] = {
vx: Math.cos(angle) * magnitude,
vy: Math.sin(angle) * magnitude
};
}
}
// Particle system
const particles = [];
const particleCount = Math.floor(100 + params.density * 300);
for (let i = 0; i < particleCount; i++) {
particles.push({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
vx: 0,
vy: 0,
size: 1 + Math.random() * 2 * (1 - params.dryness),
life: 0,
maxLife: 100 + Math.random() * 100 * params.lifespan,
hue: params.tone.dryness > 0.7 ? 0 : Math.random() * 360,
saturation: params.tone.playfulness > 0.5 ? 100 : (1 - params.tone.dryness) * 100,
alpha: params.tone.dryness > 0.7 ? 0.6 : 0.8
});
}
// Animation loop
let time = 0;
function animate() {
// Clear with semi-transparent background for trail effect
ctx.fillStyle = `rgba(10, 10, 10, ${0.1 + 0.1 * params.dryness})`;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Update flow field with time-based variation
time += 0.01 * params.motion;
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
const n = noise(x * 0.1, y * 0.1, time * 0.5) * 2 - 1;
const angle = (Math.PI * 2 + n * Math.PI) * params.complexity;
const magnitude = 0.3 + 0.7 * params.motion;
field[y][x].vx = Math.cos(angle) * magnitude;
field[y][x].vy = Math.sin(angle) * magnitude;
}
}
// Update particles
particles.forEach(p => {
// Get field value at particle position
const fieldX = Math.floor(p.x / (canvas.width / cols));
const fieldY = Math.floor(p.y / (canvas.height / rows));
const fx = field[fieldY][fieldX].vx;
const fy = field[fieldY][fieldX].vy;
// Update velocity with field influence
p.vx = p.vx * 0.95 + fx * 0.3 * params.motion;
p.vy = p.vy * 0.95 + fy * 0.3 * params.motion;
// Apply pulse
const pulse = params.pulse.avg + (params.pulse.max - params.pulse.min) *
(Math.sin(time * 2) * 0.5 + 0.5) * 0.1;
p.vx *= pulse;
p.vy *= pulse;
// Update position
p.x += p.vx;
p.y += p.vy;
// Wrap around edges
if (p.x < 0) p.x = canvas.width;
if (p.x > canvas.width) p.x = 0;
if (p.y < 0) p.y = canvas.height;
if (p.y > canvas.height) p.y = 0;
// Update life
p.life++;
if (p.life > p.maxLife) {
p.life = 0;
p.x = Math.random() * canvas.width;
p.y = Math.random() * canvas.height;
}
// Draw particle
ctx.beginPath();
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
ctx.fillStyle = `hsla(${p.hue}, ${p.saturation}%, ${50 + Math.sin(p.life * 0.1) * 20}%, ${p.alpha * (1 - p.life / p.maxLife)})`;
ctx.fill();
});
requestAnimationFrame(animate);
}
// Simple noise function
function noise(x, y, z) {
const X = Math.floor(x) & 255;
const Y = Math.floor(y) & 255;
const Z = Math.floor(z) & 255;
x -= Math.floor(x);
y -= Math.floor(y);
z -= Math.floor(z);
const u = noiseFade(x);
const v = noiseFade(y);
const w = noiseFade(z);
const A = p[X] + Y;
const AA = p[A] + Z;
const AB = p[A + 1] + Z;
const B = p[X + 1] + Y;
const BA = p[B] + Z;
const BB = p[B + 1] + Z;
return noiseLerp(w, noiseLerp(v, noiseLerp(u, grad(p[AA], x, y, z),
grad(p[BA], x - 1, y, z)),
noiseLerp(u, grad(p[AB], x, y - 1, z),
grad(p[BB], x - 1, y - 1, z))),
noiseLerp(v, noiseLerp(u, grad(p[AA + 1], x, y, z - 1),
grad(p[BA + 1], x - 1, y, z - 1)),
noiseLerp(u, grad(p[AB + 1], x, y - 1, z - 1),
grad(p[BB + 1], x - 1, y - 1, z - 1))));
}
function noiseLerp(t, a, b) { return a + t * (b - a); }
function noiseFade(t) { return t * t * t * (t * (t * 6 - 15) + 10); }
function grad(hash, x, y, z) {
const h = hash & 15;
const u = h < 8 ? x : y;
const v = h < 4 ? y : (h === 12 || h === 14 ? x : z);
return ((h & 1) === 0 ? u : -u) + ((h & 2) === 0 ? v : -v);
}
// Permutation table
const p = new Array(512);
const permutation = [151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,190,6,148,
247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,168,68,175,
74,165,71,134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,102,143,
54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186,3,
64,52,217,226,250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,223,183,170,
213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,172,9,129,22,39,253,19,98,108,110,79,113,224,232,178,185,
112,104,218,246,97,228,251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,14,239,107,49,192,214,31,
181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,
66,215,61,156,180];
for (let i = 0; i < 256; i++) {
p[i] = p[i + 256] = permutation[i];
}
animate();
</script>
</body>
</html>