birth: Flowing Tendrils of Time
This commit is contained in:
parent
f41bdb8125
commit
7e494c42fd
1 changed files with 215 additions and 0 deletions
215
index.html
Normal file
215
index.html
Normal file
|
|
@ -0,0 +1,215 @@
|
||||||
|
<!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>
|
||||||
Loading…
Add table
Reference in a new issue