
Whelp. I’m not a fan of that, but it was interesting enough. I got to brush off my tabbed view code. And do some weird things with CSS. Whee?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Genuary 26.28</title>
<style>
* {
box-sizing: border-box;
}
body {
margin: 0;
background: #0b0b0b;
overflow: hidden;
font-family: system-ui, sans-serif;
}
.controls {
position: fixed;
top: 1rem;
left: 1rem;
padding: 0.75rem 1rem;
background: rgba(0, 0, 0, 0.6);
border-radius: 0.75rem;
color: white;
font-size: 0.85rem;
backdrop-filter: blur(6px);
}
.controls label {
display: block;
margin-top: 0.5rem;
}
.controls input {
width: 160px;
}
.node {
position: absolute;
border-radius: 50%;
animation:
pulse 4s ease-in-out infinite,
drift 20s linear infinite;
transform-origin: center;
}
.node::after {
content: "";
position: absolute;
inset: 20%;
border-radius: 50%;
background: rgba(255,255,255,0.15);
filter: blur(6px);
}
@keyframes pulse {
0%, 100% { transform: scale(0.9); }
50% { transform: scale(1.15); }
}
@keyframes drift {
from { rotate: 0deg; }
to { rotate: 360deg; }
}
</style>
</head>
<body>
<div class="controls">
<label>
Max Depth
<input id="depth" type="range" min="1" max="8" step="1" value="6">
</label>
<label>
Split Chance
<input id="split" type="range" min="0" max="1" step="0.01" value="0.6">
</label>
</div>
<script>
let MAX_DEPTH = 6;
let SPLIT_CHANCE = 0.6;
const depthSlider = document.getElementById("depth");
const splitSlider = document.getElementById("split");
depthSlider.addEventListener("input", reset);
splitSlider.addEventListener("input", reset);
function reset() {
MAX_DEPTH = +depthSlider.value;
SPLIT_CHANCE = +splitSlider.value;
document.querySelectorAll(".node").forEach(n => n.remove());
createNode(
300,
300,
120,
0
);
}
function createNode(x, y, size, depth) {
const node = document.createElement("div");
node.className = "node";
node.style.width = size + "px";
node.style.height = size + "px";
node.style.left = x - size / 2 + "px";
node.style.top = y - size / 2 + "px";
const hue = (depth * 60 + Math.random() * 40) % 360;
node.style.background = `hsla(${hue}, 80%, 60%, 0.7)`;
node.style.animationDuration =
`${6 + Math.random() * 10}s, ${15 + Math.random() * 20}s`;
document.body.appendChild(node);
if (depth >= MAX_DEPTH || size < 20) return;
setTimeout(() => {
if (Math.random() < SPLIT_CHANCE) {
splitNode(x, y, size, depth);
} else {
budNode(x, y, size, depth);
}
}, 300 + Math.random() * 1200);
}
function splitNode(x, y, size, depth) {
const count = 2 + Math.floor(Math.random() * 3);
const radius = size * 0.9;
for (let i = 0; i < count; i++) {
const angle = (i / count) * Math.PI * 2;
createNode(
x + Math.cos(angle) * radius,
y + Math.sin(angle) * radius,
size * 0.6,
depth + 1
);
}
}
function budNode(x, y, size, depth) {
const angle = Math.random() * Math.PI * 2;
const distance = size * (0.6 + Math.random());
createNode(
x + Math.cos(angle) * distance,
y + Math.sin(angle) * distance,
size * 0.75,
depth + 1
);
}
reset();
</script>
</body>
</html>