birth: Pulsing Molecular Thoughts
This commit is contained in:
parent
ce01326d6f
commit
2cfc8d77a4
1 changed files with 259 additions and 0 deletions
259
index.html
Normal file
259
index.html
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Neurameba Dynamic Network</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background: #0a0a0a;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
canvas {
|
||||
display: block;
|
||||
}
|
||||
#info {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
color: #666;
|
||||
font-size: 10px;
|
||||
pointer-events: none;
|
||||
}
|
||||
</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();
|
||||
|
||||
// Configuration from parameters
|
||||
const config = {
|
||||
motion: 0.504,
|
||||
density: 0.459,
|
||||
complexity: 0.470,
|
||||
connectedness: 0.493,
|
||||
lifespan: 0.414,
|
||||
survivingNodes: 68,
|
||||
branchCount: 52,
|
||||
loops: 172,
|
||||
maxDepth: 15,
|
||||
thicknessRatio: 1.25,
|
||||
fractalDim: 1.816,
|
||||
pulse: {
|
||||
avg: 0.54,
|
||||
min: 0.30,
|
||||
max: 1.45
|
||||
},
|
||||
tone: {
|
||||
anger: 0.00,
|
||||
sadness: 0.00,
|
||||
curiosity: 0.60,
|
||||
dryness: 0.90,
|
||||
playfulness: 0.10,
|
||||
tension: 0.00
|
||||
}
|
||||
};
|
||||
|
||||
// Calculate derived properties
|
||||
const nodeCount = Math.floor(config.survivingNodes * (1 + config.connectedness));
|
||||
const edgeCount = Math.floor(config.branchCount * (1 + config.connectedness));
|
||||
const lineThickness = 0.5 + config.thicknessRatio * 0.7;
|
||||
const pulseRange = config.pulse.max - config.pulse.min;
|
||||
const energy = config.pulse.avg * 100;
|
||||
|
||||
// Color palette based on tone
|
||||
const hue = 160 + config.tone.curiosity * 100; // Teals
|
||||
const saturation = config.tone.dryness > 0.5 ? 0.3 : 0.7;
|
||||
const brightness = 0.7 + config.tone.curiosity * 0.2;
|
||||
const bgColor = `hsl(200, 10%, 5%)`;
|
||||
const nodeColor = `hsl(${hue}, ${saturation * 100}%, ${(brightness - 0.2) * 100}%)`;
|
||||
const edgeColor = `hsl(${hue}, ${saturation * 80}%, ${brightness * 80}%)`;
|
||||
const pulseColor = `hsl(${hue}, 100%, 80%)`;
|
||||
|
||||
// Network graph setup
|
||||
class Node {
|
||||
constructor() {
|
||||
this.x = Math.random() * canvas.width;
|
||||
this.y = Math.random() * canvas.height;
|
||||
this.vx = (Math.random() - 0.5) * 2 * config.motion;
|
||||
this.vy = (Math.random() - 0.5) * 2 * config.motion;
|
||||
this.size = 2 + Math.random() * 3 * config.density;
|
||||
this.connections = [];
|
||||
this.life = 0;
|
||||
this.maxLife = 100 + Math.random() * 200 * config.lifespan;
|
||||
this.depth = Math.floor(Math.random() * config.maxDepth);
|
||||
this.pulsePhase = Math.random() * Math.PI * 2;
|
||||
}
|
||||
|
||||
update() {
|
||||
this.x += this.vx;
|
||||
this.y += this.vy;
|
||||
|
||||
// Boundary check
|
||||
if (this.x < 0 || this.x > canvas.width) this.vx *= -1;
|
||||
if (this.y < 0 || this.y > canvas.height) this.vy *= -1;
|
||||
|
||||
// Life cycle
|
||||
this.life++;
|
||||
if (this.life > this.maxLife) {
|
||||
this.life = 0;
|
||||
this.maxLife = 100 + Math.random() * 200 * config.lifespan;
|
||||
}
|
||||
|
||||
// Pulse effect
|
||||
this.pulsePhase += 0.01 * config.pulse.avg;
|
||||
}
|
||||
|
||||
connect(other) {
|
||||
this.connections.push(other);
|
||||
}
|
||||
|
||||
draw() {
|
||||
const pulse = (Math.sin(this.pulsePhase) * 0.5 + 0.5) * config.pulse.avg;
|
||||
const alpha = Math.min(1, this.life / this.maxLife);
|
||||
const size = this.size * (0.8 + pulse * 0.2);
|
||||
const glow = Math.min(1, this.life / (this.maxLife * 0.5)) * 5;
|
||||
|
||||
// Draw glow
|
||||
ctx.shadowColor = pulseColor;
|
||||
ctx.shadowBlur = glow;
|
||||
ctx.globalAlpha = alpha;
|
||||
ctx.fillStyle = nodeColor;
|
||||
ctx.beginPath();
|
||||
ctx.arc(this.x, this.y, size, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
ctx.shadowBlur = 0;
|
||||
ctx.globalAlpha = 1;
|
||||
}
|
||||
}
|
||||
|
||||
class Edge {
|
||||
constructor(a, b) {
|
||||
this.nodes = [a, b];
|
||||
this.age = 0;
|
||||
this.maxAge = 50 + Math.random() * 200 * config.lifespan;
|
||||
this.createPhase = Math.random() * Math.PI * 2;
|
||||
}
|
||||
|
||||
update() {
|
||||
this.age++;
|
||||
this.createPhase += 0.02;
|
||||
}
|
||||
|
||||
draw() {
|
||||
const a = this.nodes[0];
|
||||
const b = this.nodes[1];
|
||||
|
||||
// Color based on age and pulse
|
||||
const ageRatio = Math.min(1, this.age / this.maxAge);
|
||||
const pulseA = (Math.sin(a.pulsePhase) * 0.5 + 0.5) * config.pulse.avg;
|
||||
const pulseB = (Math.sin(b.pulsePhase) * 0.5 + 0.5) * config.pulse.avg;
|
||||
const alpha = 0.3 + ageRatio * 0.7;
|
||||
const width = lineThickness * (0.5 + ageRatio + (pulseA + pulseB) * 0.2);
|
||||
|
||||
// Draw edge with thickness variation
|
||||
ctx.globalAlpha = alpha;
|
||||
ctx.strokeStyle = edgeColor;
|
||||
ctx.lineWidth = width;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(a.x, a.y);
|
||||
ctx.lineTo(b.x, b.y);
|
||||
ctx.stroke();
|
||||
ctx.globalAlpha = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Create network
|
||||
const nodes = [];
|
||||
const edges = [];
|
||||
|
||||
// Create nodes
|
||||
for (let i = 0; i < nodeCount; i++) {
|
||||
nodes.push(new Node());
|
||||
}
|
||||
|
||||
// Create edges based on connectedness
|
||||
const desiredEdges = edgeCount;
|
||||
const possibleEdges = nodeCount * (nodeCount - 1) / 2;
|
||||
|
||||
// Create initial connections
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
for (let j = i + 1; j < nodes.length; j++) {
|
||||
if (Math.random() < config.connectedness * 0.1) {
|
||||
nodes[i].connect(nodes[j]);
|
||||
nodes[j].connect(nodes[i]);
|
||||
edges.push(new Edge(nodes[i], nodes[j]));
|
||||
if (edges.length >= desiredEdges) break;
|
||||
}
|
||||
}
|
||||
if (edges.length >= desiredEdges) break;
|
||||
}
|
||||
|
||||
// Add extra connections for loops
|
||||
for (let i = 0; i < config.loops * 0.5; i++) {
|
||||
if (edges.length < desiredEdges * 2) {
|
||||
const a = nodes[Math.floor(Math.random() * nodes.length)];
|
||||
const b = a.connections[Math.floor(Math.random() * a.connections.length)];
|
||||
if (b) {
|
||||
a.connect(b);
|
||||
b.connect(a);
|
||||
edges.push(new Edge(a, b));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Animation
|
||||
function animate() {
|
||||
// Clear with slight fade
|
||||
ctx.fillStyle = `rgba(10, 10, 10, ${0.05 + config.tone.dryness * 0.1})`;
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Update and draw nodes
|
||||
nodes.forEach(node => {
|
||||
node.update();
|
||||
node.draw();
|
||||
});
|
||||
|
||||
// Update and draw edges
|
||||
edges.forEach(edge => {
|
||||
edge.update();
|
||||
edge.draw();
|
||||
});
|
||||
|
||||
// Occasionally add new connections
|
||||
if (Math.random() < 0.02 * config.connectedness) {
|
||||
const a = nodes[Math.floor(Math.random() * nodes.length)];
|
||||
const b = nodes[Math.floor(Math.random() * nodes.length)];
|
||||
if (a !== b && !a.connections.includes(b)) {
|
||||
a.connect(b);
|
||||
b.connect(a);
|
||||
edges.push(new Edge(a, b));
|
||||
// Remove old edges if we're over capacity
|
||||
while (edges.length > desiredEdges * 1.5) {
|
||||
edges.shift();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
animate();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Reference in a new issue