← 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>