← back to Model Arena
data/artifacts/097db196034e/claude-code.html
456 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Agent Constellation</title>
<style>
html, body {
margin: 0; padding: 0; height: 100%; overflow: hidden;
background: #03060f;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
#space { display: block; width: 100vw; height: 100vh; cursor: crosshair; }
.overlay {
position: fixed; top: 20px; left: 24px; z-index: 10;
color: #7fe9ff; pointer-events: none; user-select: none;
text-shadow: 0 0 12px rgba(60,200,255,0.5);
}
.overlay h1 {
margin: 0; font-size: 15px; font-weight: 600; letter-spacing: 3px;
text-transform: uppercase;
}
.overlay p {
margin: 6px 0 0; font-size: 11px; letter-spacing: 1px;
color: #4a7f96; opacity: 0.85;
}
#tooltip {
position: fixed; z-index: 20; pointer-events: none;
padding: 8px 12px; border-radius: 8px;
background: rgba(6, 18, 32, 0.85);
border: 1px solid rgba(80, 210, 255, 0.4);
box-shadow: 0 0 24px rgba(40,180,255,0.25);
color: #dff6ff; font-size: 12px; letter-spacing: 0.5px;
transform: translate(-50%, -140%); opacity: 0; transition: opacity 0.15s;
white-space: nowrap;
}
#tooltip .t-name { font-weight: 600; color: #8ff0ff; }
#tooltip .t-meta { font-size: 10px; color: #6fb8d0; margin-top: 2px; }
.legend {
position: fixed; bottom: 18px; left: 24px; z-index: 10;
color: #3f6b7e; font-size: 10px; letter-spacing: 1px; pointer-events: none;
}
</style>
</head>
<body>
<div class="overlay">
<h1>Agent Constellation</h1>
<p>force-directed mesh · live message passing</p>
</div>
<div id="tooltip"></div>
<div class="legend">hover a node to trace its neighbors</div>
<canvas id="space"></canvas>
<script>
(function () {
"use strict";
const canvas = document.getElementById("space");
const ctx = canvas.getContext("2d", { alpha: false });
const tooltip = document.getElementById("tooltip");
let W = 0, H = 0, DPR = Math.min(window.devicePixelRatio || 1, 2);
function resize() {
W = window.innerWidth;
H = window.innerHeight;
canvas.width = W * DPR;
canvas.height = H * DPR;
canvas.style.width = W + "px";
canvas.style.height = H + "px";
ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
}
window.addEventListener("resize", resize);
resize();
// ---- Agent taxonomy for flavor --------------------------------------
const ROLES = [
"Orchestrator", "Planner", "Researcher", "Coder", "Reviewer",
"Router", "Memory", "Retriever", "Critic", "Executor",
"Synthesizer", "Validator", "Scout", "Analyst", "Guardian",
"Compiler", "Indexer", "Broker", "Sentinel", "Weaver",
"Cartographer", "Herald", "Archivist", "Forecaster", "Navigator",
"Oracle", "Sculptor", "Prospector", "Curator", "Beacon"
];
// ---- Node / Edge model ----------------------------------------------
const NODE_COUNT = Math.round(Math.min(48, Math.max(26, (W * H) / 26000)));
const nodes = [];
const edges = [];
const adjacency = new Map();
function rand(a, b) { return a + Math.random() * (b - a); }
for (let i = 0; i < NODE_COUNT; i++) {
nodes.push({
id: i,
x: rand(W * 0.15, W * 0.85),
y: rand(H * 0.15, H * 0.85),
vx: 0, vy: 0,
r: rand(3.2, 6.5),
role: ROLES[i % ROLES.length],
hue: rand(-14, 26), // slight hue drift around cyan
pulse: Math.random() * Math.PI * 2,
pulseSpeed: rand(0.6, 1.4),
degree: 0
});
adjacency.set(i, new Set());
}
function edgeKey(a, b) { return a < b ? a + "_" + b : b + "_" + a; }
const edgeSet = new Set();
function addEdge(a, b) {
if (a === b) return;
const k = edgeKey(a, b);
if (edgeSet.has(k)) return;
edgeSet.add(k);
edges.push({ a, b, base: 0.16, glow: 0 });
adjacency.get(a).add(b);
adjacency.get(b).add(a);
nodes[a].degree++;
nodes[b].degree++;
}
// Connect each node to nearest neighbors -> organic mesh
for (let i = 0; i < nodes.length; i++) {
const dists = [];
for (let j = 0; j < nodes.length; j++) {
if (i === j) continue;
const dx = nodes[i].x - nodes[j].x;
const dy = nodes[i].y - nodes[j].y;
dists.push({ j, d: dx * dx + dy * dy });
}
dists.sort((p, q) => p.d - q.d);
const links = 2 + (Math.random() < 0.5 ? 1 : 0);
for (let k = 0; k < links; k++) addEdge(i, dists[k].j);
}
// A few long-range hub links for constellation feel
for (let k = 0; k < Math.round(NODE_COUNT * 0.15); k++) {
addEdge((Math.random() * NODE_COUNT) | 0, (Math.random() * NODE_COUNT) | 0);
}
const edgeIndex = new Map();
edges.forEach((e, idx) => {
edgeIndex.set(edgeKey(e.a, e.b), idx);
});
// ---- Messages traveling along edges ---------------------------------
const messages = [];
function spawnMessage() {
if (!edges.length) return;
const e = edges[(Math.random() * edges.length) | 0];
const forward = Math.random() < 0.5;
messages.push({
edge: e,
from: forward ? e.a : e.b,
to: forward ? e.b : e.a,
t: 0,
speed: rand(0.008, 0.02),
hue: rand(170, 195)
});
}
let spawnTimer = 0;
// ---- Interaction ----------------------------------------------------
let mouse = { x: -1e4, y: -1e4, active: false };
let hoverNode = null;
canvas.addEventListener("mousemove", (ev) => {
mouse.x = ev.clientX; mouse.y = ev.clientY; mouse.active = true;
});
canvas.addEventListener("mouseleave", () => {
mouse.active = false; mouse.x = -1e4; mouse.y = -1e4;
});
canvas.addEventListener("click", () => {
// clicking emits a burst of messages from the hovered node
if (hoverNode) {
adjacency.get(hoverNode.id).forEach((nb) => {
const e = edges[edgeIndex.get(edgeKey(hoverNode.id, nb))];
if (e) {
messages.push({
edge: e, from: hoverNode.id, to: nb, t: 0,
speed: rand(0.014, 0.024), hue: rand(175, 195)
});
}
});
}
});
// ---- Physics (light force-directed relaxation) ----------------------
function step(dt) {
const centerX = W / 2, centerY = H / 2;
const REPULSE = 5200;
const SPRING = 0.0016;
const REST = Math.min(W, H) * 0.16;
const DAMP = 0.86;
// repulsion
for (let i = 0; i < nodes.length; i++) {
const a = nodes[i];
for (let j = i + 1; j < nodes.length; j++) {
const b = nodes[j];
let dx = a.x - b.x, dy = a.y - b.y;
let d2 = dx * dx + dy * dy;
if (d2 < 1) d2 = 1;
if (d2 > 90000) continue; // ignore far pairs
const f = REPULSE / d2;
const d = Math.sqrt(d2);
const fx = (dx / d) * f, fy = (dy / d) * f;
a.vx += fx; a.vy += fy;
b.vx -= fx; b.vy -= fy;
}
}
// springs
for (const e of edges) {
const a = nodes[e.a], b = nodes[e.b];
let dx = b.x - a.x, dy = b.y - a.y;
let d = Math.sqrt(dx * dx + dy * dy) || 1;
const f = (d - REST) * SPRING;
const fx = (dx / d) * f, fy = (dy / d) * f;
a.vx += fx; a.vy += fy;
b.vx -= fx; b.vy -= fy;
}
// gentle gravity to center + drift + integrate
const time = performance.now() * 0.0002;
for (let i = 0; i < nodes.length; i++) {
const n = nodes[i];
n.vx += (centerX - n.x) * 0.00035;
n.vy += (centerY - n.y) * 0.00035;
// organic drift so nothing is ever fully static
n.vx += Math.cos(time + i * 1.7) * 0.010;
n.vy += Math.sin(time * 1.3 + i * 2.1) * 0.010;
// hover repel for a lively feel
if (mouse.active) {
const dx = n.x - mouse.x, dy = n.y - mouse.y;
const d2 = dx * dx + dy * dy;
if (d2 < 12000 && d2 > 1) {
const f = 900 / d2;
const d = Math.sqrt(d2);
n.vx += (dx / d) * f;
n.vy += (dy / d) * f;
}
}
n.vx *= DAMP; n.vy *= DAMP;
const vmax = 3;
n.vx = Math.max(-vmax, Math.min(vmax, n.vx));
n.vy = Math.max(-vmax, Math.min(vmax, n.vy));
n.x += n.vx; n.y += n.vy;
const m = 40;
if (n.x < m) { n.x = m; n.vx *= -0.5; }
if (n.x > W - m) { n.x = W - m; n.vx *= -0.5; }
if (n.y < m) { n.y = m; n.vy *= -0.5; }
if (n.y > H - m) { n.y = H - m; n.vy *= -0.5; }
n.pulse += 0.03 * n.pulseSpeed;
}
// find hovered node
hoverNode = null;
if (mouse.active) {
let best = 26 * 26;
for (const n of nodes) {
const dx = n.x - mouse.x, dy = n.y - mouse.y;
const d2 = dx * dx + dy * dy;
if (d2 < best) { best = d2; hoverNode = n; }
}
}
}
// ---- Rendering ------------------------------------------------------
const stars = [];
for (let i = 0; i < 140; i++) {
stars.push({ x: Math.random(), y: Math.random(), r: Math.random() * 1.2 + 0.2, tw: Math.random() * Math.PI * 2 });
}
function isNeighbor(id) {
return hoverNode && (id === hoverNode.id || adjacency.get(hoverNode.id).has(id));
}
function edgeIsHot(e) {
return hoverNode && (e.a === hoverNode.id || e.b === hoverNode.id);
}
function draw() {
// deep-space background with subtle vignette
ctx.fillStyle = "#03060f";
ctx.fillRect(0, 0, W, H);
const g = ctx.createRadialGradient(W / 2, H / 2, 0, W / 2, H / 2, Math.max(W, H) * 0.75);
g.addColorStop(0, "rgba(10,30,55,0.55)");
g.addColorStop(1, "rgba(2,4,10,0)");
ctx.fillStyle = g;
ctx.fillRect(0, 0, W, H);
// stars
const t = performance.now() * 0.001;
for (const s of stars) {
const a = 0.25 + Math.abs(Math.sin(t + s.tw)) * 0.5;
ctx.globalAlpha = a;
ctx.fillStyle = "#bfe9ff";
ctx.fillRect(s.x * W, s.y * H, s.r, s.r);
}
ctx.globalAlpha = 1;
// edges
ctx.lineCap = "round";
for (const e of edges) {
const a = nodes[e.a], b = nodes[e.b];
const hot = edgeIsHot(e);
const dim = hoverNode && !hot;
const pulse = e.glow;
let alpha = e.base + pulse * 0.6;
if (hot) alpha = 0.55 + pulse * 0.5;
if (dim) alpha *= 0.18;
ctx.strokeStyle = "rgba(70," + (180 + pulse * 60 | 0) + ",255," + alpha.toFixed(3) + ")";
ctx.lineWidth = hot ? 1.6 : 0.7 + pulse * 1.4;
if (hot || pulse > 0.05) {
ctx.shadowColor = "rgba(60,200,255,0.7)";
ctx.shadowBlur = hot ? 10 : 6 * pulse;
} else {
ctx.shadowBlur = 0;
}
ctx.beginPath();
ctx.moveTo(a.x, a.y);
ctx.lineTo(b.x, b.y);
ctx.stroke();
e.glow *= 0.94; // decay
}
ctx.shadowBlur = 0;
// messages
for (let i = messages.length - 1; i >= 0; i--) {
const m = messages[i];
m.t += m.speed;
const from = nodes[m.from], to = nodes[m.to];
if (m.t >= 1) {
// pulse the receiving node & light the edge
to.pulse = 0;
m.edge.glow = 1;
messages.splice(i, 1);
continue;
}
// light edge as it passes
m.edge.glow = Math.max(m.edge.glow, 0.85);
const x = from.x + (to.x - from.x) * m.t;
const y = from.y + (to.y - from.y) * m.t;
// trailing comet
const tx = from.x + (to.x - from.x) * Math.max(0, m.t - 0.08);
const ty = from.y + (to.y - from.y) * Math.max(0, m.t - 0.08);
const grad = ctx.createLinearGradient(tx, ty, x, y);
grad.addColorStop(0, "rgba(120,240,255,0)");
grad.addColorStop(1, "rgba(180,250,255,0.9)");
ctx.strokeStyle = grad;
ctx.lineWidth = 2.2;
ctx.beginPath();
ctx.moveTo(tx, ty);
ctx.lineTo(x, y);
ctx.stroke();
ctx.shadowColor = "rgba(120,240,255,0.9)";
ctx.shadowBlur = 12;
ctx.fillStyle = "rgba(220,252,255,0.95)";
ctx.beginPath();
ctx.arc(x, y, 2.4, 0, Math.PI * 2);
ctx.fill();
ctx.shadowBlur = 0;
}
// nodes
for (const n of nodes) {
const neighbor = isNeighbor(n.id);
const dim = hoverNode && !neighbor;
const isHover = hoverNode && n.id === hoverNode.id;
const pulseAmt = Math.max(0, 1 - n.pulse * 0.9); // flash after receiving
const breath = 0.5 + Math.sin(n.pulse) * 0.5;
const rr = n.r + (isHover ? 3 : 0) + pulseAmt * 3;
const baseA = dim ? 0.25 : 1;
// outer glow
const glowR = rr * (isHover ? 6 : 4.2);
const gg = ctx.createRadialGradient(n.x, n.y, 0, n.x, n.y, glowR);
const gAlpha = (isHover ? 0.55 : 0.28 + breath * 0.12 + pulseAmt * 0.4) * baseA;
gg.addColorStop(0, "rgba(90,220,255," + gAlpha.toFixed(3) + ")");
gg.addColorStop(1, "rgba(90,220,255,0)");
ctx.fillStyle = gg;
ctx.beginPath();
ctx.arc(n.x, n.y, glowR, 0, Math.PI * 2);
ctx.fill();
// core
ctx.globalAlpha = baseA;
ctx.shadowColor = "rgba(80,220,255,0.9)";
ctx.shadowBlur = isHover ? 18 : 8;
ctx.fillStyle = isHover ? "#eafcff" : "#a6ecff";
ctx.beginPath();
ctx.arc(n.x, n.y, rr, 0, Math.PI * 2);
ctx.fill();
// bright center dot
ctx.shadowBlur = 0;
ctx.fillStyle = "#ffffff";
ctx.beginPath();
ctx.arc(n.x, n.y, Math.max(1, rr * 0.35), 0, Math.PI * 2);
ctx.fill();
ctx.globalAlpha = 1;
}
ctx.shadowBlur = 0;
// tooltip
if (hoverNode) {
const deg = hoverNode.degree;
tooltip.style.opacity = "1";
tooltip.style.left = hoverNode.x + "px";
tooltip.style.top = hoverNode.y + "px";
tooltip.innerHTML =
'<div class="t-name">' + hoverNode.role + " · A" +
String(hoverNode.id).padStart(2, "0") + "</div>" +
'<div class="t-meta">' + deg + " link" + (deg === 1 ? "" : "s") +
" · active</div>";
canvas.style.cursor = "pointer";
} else {
tooltip.style.opacity = "0";
canvas.style.cursor = "crosshair";
}
}
// ---- Main loop (rAF, ~60fps) ---------------------------------------
let last = performance.now();
function loop(now) {
const dt = Math.min(50, now - last);
last = now;
spawnTimer += dt;
const interval = 140; // ms between message spawns
while (spawnTimer > interval) {
spawnTimer -= interval;
if (messages.length < 60) spawnMessage();
}
step(dt);
draw();
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
// seed a few messages at start
for (let i = 0; i < 6; i++) spawnMessage();
})();
</script>
</body>
</html>