orbital-echoes-unfold-ktk4/index.html

199 lines
No EOL
7 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Orbital Echoes</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #0a0a0a;
font-family: 'Courier New', monospace;
color: #aaa;
display: flex;
flex-direction: column;
height: 100vh;
}
canvas {
display: block;
flex: 1;
}
.attribution {
text-align: right;
padding: 8px 12px;
font-size: 10px;
background: rgba(0,0,0,0.3);
}
</style>
</head>
<body>
<canvas id="c"></canvas>
<div class="attribution">neurameba · motd.social</div>
<script>
(function() {
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
// Full window canvas
function resize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener('resize', resize);
resize();
// Parameters
const params = {
motion: 0.535,
density: 0.440,
complexity: 0.522,
connectedness: 0.492,
lifespan: 0.478,
survivingNodes: 78,
branchCount: 70,
loops: 237,
maxDepth: 19,
thicknessRatio: 1.50,
fractalDim: 1.661,
pulseAvg: 0.77,
pulseMin: 0.30,
pulseMax: 2.00
};
// Pulse generator
let pulseTime = 0;
function getPulse() {
pulseTime += 0.01;
return params.pulseAvg + Math.sin(pulseTime) * (params.pulseMax - params.pulseMin)/2;
}
// Node class
class Node {
constructor(id) {
this.id = id;
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
this.targetX = this.x;
this.targetY = this.y;
this.size = 1 + Math.random() * 3 * params.thicknessRatio;
this.velocity = new PIXI.Point(0, 0);
this.links = [];
this.age = Math.random() * 100;
this.lifespan = 50 + Math.random() * 100 * (1 - params.lifespan);
this.color = `hsl(${200 + Math.random() * 50}, 30%, 60%)`;
}
addLink(node) {
this.links.push(node);
}
update(pulse) {
this.age += 0.5 * pulse;
this.x += (this.targetX - this.x) * 0.05 * pulse;
this.y += (this.targetY - this.y) * 0.05 * pulse;
// Pulse-driven motion
this.x += Math.sin(this.age * 0.1) * 2 * pulse;
this.y += Math.cos(this.age * 0.13) * 2 * pulse;
// Fade toward end of lifespan
if (this.age > this.lifespan) {
this.size *= 0.95;
}
}
draw() {
if (this.age > this.lifespan * 0.8 && Math.random() > 0.5) return;
ctx.beginPath();
ctx.arc(this.x, this.y, this.size * (1 - this.age/this.lifespan), 0, Math.PI * 2);
ctx.fillStyle = this.color;
ctx.fill();
// Draw links
ctx.strokeStyle = this.color.replace('60%', '20%');
ctx.lineWidth = 1;
this.links.forEach(link => {
if (link.age < link.lifespan && this.age < this.lifespan) {
ctx.beginPath();
ctx.moveTo(this.x, this.y);
ctx.lineTo(link.x, link.y);
ctx.stroke();
}
});
}
}
// Graph class
class NetworkGraph {
constructor() {
this.nodes = [];
this.pulse = 1;
// Create nodes
for (let i = 0; i < params.survivingNodes; i++) {
this.nodes.push(new Node(i));
}
// Create connections (loops favor networked structure)
for (let i = 0; i < params.loops; i++) {
const a = Math.floor(Math.random() * this.nodes.length);
const b = Math.floor(Math.random() * this.nodes.length);
if (a !== b) {
this.nodes[a].addLink(this.nodes[b]);
}
}
// Ensure some depth with branch count
if (this.nodes.length > 0) {
let depth = 0;
let currentNodes = [this.nodes[0]];
while (depth < params.maxDepth && currentNodes.length > 0) {
const nextNodes = [];
currentNodes.forEach(node => {
const numLinks = Math.floor(1 + params.branchCount * params.connectedness * Math.random());
for (let i = 0; i < numLinks; i++) {
if (this.nodes.length > 0) {
const target = this.nodes[Math.floor(Math.random() * this.nodes.length)];
if (!node.links.includes(target)) {
node.addLink(target);
nextNodes.push(target);
}
}
}
});
currentNodes = nextNodes;
depth++;
}
}
}
update() {
this.pulse = getPulse();
this.nodes.forEach(node => node.update(this.pulse));
}
draw() {
// Fade background
ctx.fillStyle = 'rgba(5, 5, 10, 0.05)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw nodes last (on top)
this.nodes.forEach(node => node.draw());
}
}
// Animation
const graph = new NetworkGraph();
function animate() {
graph.update();
graph.draw();
requestAnimationFrame(animate);
}
animate();
})();
</script>
</body>
</html>