← back to Model Arena

data/artifacts/097db196034e/grok.html

358 lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Agent Constellation</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
html,body{width:100%;height:100%;overflow:hidden;background:#03050a}
canvas{display:block;width:100%;height:100%;cursor:crosshair}
</style>
</head>
<body>
<canvas id="c"></canvas>
<script>
const canvas=document.getElementById('c');
const ctx=canvas.getContext('2d');
let W,H,dpr;

const NAMES=['Orion','Nova','Lyra','Vega','Pulsar','Quasar','Nebula','Axiom','Cipher','Helix','Prism','Synapse','Vector','Flux','Echo','Nexus','Atlas','Zephyr','Ion','Quark','Photon','Tensor','Lattice','Monad'];
const AGENT_TYPES=['Reasoner','Planner','Retriever','Critic','Coder','Memory','Router','Observer'];

function resize(){
  dpr=window.devicePixelRatio||1;
  W=window.innerWidth;H=window.innerHeight;
  canvas.width=W*dpr;canvas.height=H*dpr;
  ctx.setTransform(dpr,0,0,dpr,1,0);
}
resize();
window.addEventListener('resize',resize);

const rand=(a,b)=>a+Math.random()*(b-a);
const pick=a=>a[Math.floor(Math.random()*a.length)];
const dist=(a,b)=>Math.hypot(a.x-b.x,a.y-b.y);

const NODE_COUNT=18;
const nodes=[];
const edges=[];
const messages=[];
const stars=[];

for(let i=0;i<180;i++){
  stars.push({
    x:Math.random(),y:Math.random(),
    r:rand(0.3,1.4),a:rand(0.15,0.7),
    tw:rand(0.5,2.5),ph:Math.random()*Math.PI*2
  });
}

for(let i=0;i<NODE_COUNT;i++){
  const angle=Math.random()*Math.PI*2;
  const rad=rand(0.15,0.42)*Math.min(W,H);
  nodes.push({
    id:i,
    name:NAMES[i%NAMES.length],
    type:pick(AGENT_TYPES),
    x:W/2+Math.cos(angle)*rad+rand(-40,40),
    y:H/2+Math.sin(angle)*rad+rand(-40,40),
    vx:rand(-0.15,0.15),
    vy:rand(-0.15,0.15),
    r:rand(6,11),
    pulse:Math.random()*Math.PI*2,
    hue:rand(170,200),
    highlighted:false,
    neighbor:false
  });
}

const edgeSet=new Set();
function addEdge(a,b){
  if(a===b)return;
  const key=a<b?a+','+b:b+','+a;
  if(edgeSet.has(key))return;
  edgeSet.add(key);
  edges.push({a,b,phase:Math.random()*Math.PI*2,strength:rand(0.4,1)});
}
for(let i=0;i<NODE_COUNT;i++){
  const targets=1+Math.floor(Math.random()*3);
  for(let t=0;t<targets;t++){
    addEdge(i,Math.floor(Math.random()*NODE_COUNT));
  }
}
for(let i=0;i<NODE_COUNT;i++){
  let nearest=-1,nd=Infinity;
  for(let j=0;j<NODE_COUNT;j++){
    if(i===j)continue;
    const d=dist(nodes[i],nodes[j]);
    if(d<nd){nd=d;nearest=j;}
  }
  if(nearest>=0)addEdge(i,nearest);
}

let mouse={x:-9999,y:-9999};
let hoverNode=null;
canvas.addEventListener('mousemove',e=>{
  const rect=canvas.getBoundingClientRect();
  mouse.x=e.clientX-rect.left;
  mouse.y=e.clientY-rect.top;
});
canvas.addEventListener('mouseleave',()=>{mouse.x=-9999;mouse.y=-9999;});

function spawnMessage(){
  if(!edges.length)return;
  const e=pick(edges);
  const from=Math.random()<0.5?e.a:e.b;
  const to=from===e.a?e.b:e.a;
  messages.push({
    from,to,
    t:0,
    speed:rand(0.006,0.014),
    life:1,
    size:rand(2,3.5),
    trail:[]
  });
}

let msgTimer=0;
const IDEAL=140;
const REPULSE=8000;
const CENTER_PULL=0.0008;
const DAMP=0.92;
const DRIFT=0.02;

function simulate(dt){
  for(let i=0;i<NODE_COUNT;i++){
    const a=nodes[i];
    let fx=0,fy=0;
    for(let j=0;j<NODE_COUNT;j++){
      if(i===j)continue;
      const b=nodes[j];
      let dx=a.x-b.x,dy=a.y-b.y;
      let d=Math.hypot(dx,dy)||0.01;
      const rep=REPULSE/(d*d);
      fx+=(dx/d)*rep;
      fy+=(dy/d)*rep;
    }
    fx+=(W/2-a.x)*CENTER_PULL*60;
    fy+=(H/2-a.y)*CENTER_PULL*60;
    a.vx+=fx*dt+rand(-DRIFT,DRIFT);
    a.vy+=fy*dt+rand(-DRIFT,DRIFT);
  }
  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.hypot(dx,dy)||0.01;
    const force=(d-IDEAL)*0.0006*e.strength;
    const fx=(dx/d)*force*60;
    const fy=(dy/d)*force*60;
    a.vx+=fx;a.vy+=fy;
    b.vx-=fx;b.vy-=fy;
  }
  for(const n of nodes){
    n.vx*=DAMP;n.vy*=DAMP;
    const sp=Math.hypot(n.vx,n.vy);
    if(sp>2.5){n.vx=(n.vx/sp)*2.5;n.vy=(n.vy/sp)*2.5;}
    n.x+=n.vx;n.y+=n.vy;
    const m=40;
    if(n.x<m)n.vx+=0.05;if(n.x>W-m)n.vx-=0.05;
    if(n.y<m)n.vy+=0.05;if(n.y>H-m)n.vy-=0.05;
    n.pulse+=dt*2.2;
  }

  hoverNode=null;
  let best=18;
  for(const n of nodes){
    n.highlighted=false;
    n.neighbor=false;
    const d=Math.hypot(n.x-mouse.x,n.y-mouse.y);
    if(d<best){best=d;hoverNode=n;}
  }
  if(hoverNode){
    hoverNode.highlighted=true;
    for(const e of edges){
      if(e.a===hoverNode.id)nodes[e.b].neighbor=true;
      if(e.b===hoverNode.id)nodes[e.a].neighbor=true;
    }
  }

  msgTimer-=dt;
  if(msgTimer<=0){
    spawnMessage();
    if(Math.random()<0.4)spawnMessage();
    msgTimer=rand(0.25,0.9);
  }
  for(let i=messages.length-1;i>=0;i--){
    const m=messages[i];
    m.t+=m.speed*dt*60;
    const a=nodes[m.from],b=nodes[m.to];
    const px=a.x+(b.x-a.x)*Math.min(m.t,1);
    const py=a.y+(b.y-a.y)*Math.min(m.t,1);
    m.trail.push({x:px,y:py,a:1});
    if(m.trail.length>12)m.trail.shift();
    for(const t of m.trail)t.a*=0.88;
    if(m.t>=1){
      m.life-=dt*3;
      if(m.life<=0)messages.splice(i,1);
    }
  }
}

function drawGlow(x,y,r,color,alpha){
  const g=ctx.createRadialGradient(x,y,0,x,y,r);
  g.addColorStop(0,color.replace('ALP',String(alpha)));
  g.addColorStop(0.4,color.replace('ALP',String(alpha*0.35)));
  g.addColorStop(1,color.replace('ALP','0'));
  ctx.fillStyle=g;
  ctx.beginPath();
  ctx.arc(x,y,r,0,Math.PI*2);
  ctx.fill();
}

function render(time){
  ctx.fillStyle='#03050a';
  ctx.fillRect(0,0,W,H);

  for(const s of stars){
    const tw=0.5+0.5*Math.sin(time*0.001*s.tw+s.ph);
    ctx.beginPath();
    ctx.arc(s.x*W,s.y*H,s.r*tw,0,Math.PI*2);
    ctx.fillStyle=`rgba(180,220,255,${s.a*tw})`;
    ctx.fill();
  }

  const dimmed=!!hoverNode;

  for(const e of edges){
    const a=nodes[e.a],b=nodes[e.b];
    const isHot=hoverNode&&(e.a===hoverNode.id||e.b===hoverNode.id);
    const baseAlpha=dimmed?(isHot?0.55:0.06):0.18;
    const pulse=0.5+0.5*Math.sin(time*0.003+e.phase);
    const alpha=baseAlpha*(0.7+0.3*pulse)*e.strength;

    ctx.beginPath();
    ctx.moveTo(a.x,a.y);
    ctx.lineTo(b.x,b.y);
    ctx.strokeStyle=`rgba(0,220,255,${alpha})`;
    ctx.lineWidth=isHot?1.8:1;
    ctx.stroke();

    if(isHot||!dimmed){
      const mid=0.5+0.5*Math.sin(time*0.004+e.phase*2);
      const mx=a.x+(b.x-a.x)*mid;
      const my=a.y+(b.y-a.y)*mid;
      drawGlow(mx,my,isHot?14:8,'rgba(0,230,255,ALP)',isHot?0.25:0.08*pulse);
    }
  }

  for(const m of messages){
    const a=nodes[m.from],b=nodes[m.to];
    const t=Math.min(m.t,1);
    const ease=t*t*(3-2*t);
    const px=a.x+(b.x-a.x)*ease;
    const py=a.y+(b.y-a.y)*ease;
    const alpha=m.life*(dimmed?0.5:1);

    for(let i=0;i<m.trail.length;i++){
      const tr=m.trail[i];
      const ta=tr.a*alpha*0.6;
      ctx.beginPath();
      ctx.arc(tr.x,tr.y,m.size*(0.3+i/m.trail.length*0.7),0,Math.PI*2);
      ctx.fillStyle=`rgba(100,255,255,${ta})`;
      ctx.fill();
    }
    drawGlow(px,py,16,'rgba(0,255,255,ALP)',0.45*alpha);
    ctx.beginPath();
    ctx.arc(px,py,m.size,0,Math.PI*2);
    ctx.fillStyle=`rgba(200,255,255,${0.95*alpha})`;
    ctx.fill();
  }

  for(const n of nodes){
    const isH=n.highlighted;
    const isN=n.neighbor;
    const fade=dimmed&&!isH&&!isN?0.2:1;
    const breath=1+0.08*Math.sin(n.pulse);
    const radius=n.r*breath*(isH?1.35:isN?1.15:1);

    drawGlow(n.x,n.y,radius*5,'rgba(0,210,255,ALP)',(isH?0.35:isN?0.22:0.12)*fade);
    drawGlow(n.x,n.y,radius*2.2,'rgba(80,240,255,ALP)',(isH?0.55:0.3)*fade);

    ctx.beginPath();
    ctx.arc(n.x,n.y,radius,0,Math.PI*2);
    const grad=ctx.createRadialGradient(n.x-radius*0.3,n.y-radius*0.3,radius*0.1,n.x,n.y,radius);
    grad.addColorStop(0,`rgba(220,255,255,${0.95*fade})`);
    grad.addColorStop(0.5,`rgba(0,230,255,${0.85*fade})`);
    grad.addColorStop(1,`rgba(0,140,200,${0.7*fade})`);
    ctx.fillStyle=grad;
    ctx.fill();

    ctx.beginPath();
    ctx.arc(n.x,n.y,radius,0,Math.PI*2);
    ctx.strokeStyle=`rgba(150,255,255,${(isH?0.9:0.35)*fade})`;
    ctx.lineWidth=isH?2:1;
    ctx.stroke();

    if(isH||isN||!dimmed){
      const labelAlpha=(isH?0.95:isN?0.7:0.45)*fade;
      ctx.font=`${isH?11:9}px "Segoe UI",system-ui,sans-serif`;
      ctx.textAlign='center';
      ctx.fillStyle=`rgba(180,240,255,${labelAlpha})`;
      ctx.fillText(n.name,n.x,n.y+radius+14);
      if(isH){
        ctx.font='9px "Segoe UI",system-ui,sans-serif';
        ctx.fillStyle=`rgba(100,200,220,${labelAlpha*0.8})`;
        ctx.fillText(n.type,n.x,n.y+radius+26);
      }
    }
  }

  if(hoverNode){
    const n=hoverNode;
    const links=edges.filter(e=>e.a===n.id||e.b===n.id).length;
    ctx.font='10px "Segoe UI",system-ui,sans-serif';
    ctx.textAlign='left';
    const tip=`${n.name} · ${n.type} · ${links} links`;
    const tw=ctx.measureText(tip).width;
    const tx=Math.min(mouse.x+16,W-tw-20);
    const ty=Math.max(mouse.y-12,24);
    ctx.fillStyle='rgba(0,20,30,0.75)';
    ctx.strokeStyle='rgba(0,200,255,0.35)';
    ctx.lineWidth=1;
    roundRect(tx-8,ty-14,tw+16,22,4);
    ctx.fill();ctx.stroke();
    ctx.fillStyle='rgba(160,240,255,0.9)';
    ctx.fillText(tip,tx,ty);
  }

  ctx.font='11px "Segoe UI",system-ui,sans-serif';
  ctx.textAlign='left';
  ctx.fillStyle='rgba(0,180,220,0.35)';
  ctx.fillText('AI AGENT CONSTELLATION',16,28);
  ctx.font='9px "Segoe UI",system-ui,sans-serif';
  ctx.fillStyle='rgba(0,160,200,0.25)';
  ctx.fillText(`${NODE_COUNT} agents  ·  ${edges.length} channels  ·  ${messages.length} in flight`,16,44);
}

function roundRect(x,y,w,h,r){
  ctx.beginPath();
  ctx.moveTo(x+r,y);
  ctx.arcTo(x+w,y,x+w,y+h,r);
  ctx.arcTo(x+w,y+h,x,y+h,r);
  ctx.arcTo(x,y+h,x,y,r);
  ctx.arcTo(x,y,x+w,y,r);
  ctx.closePath();
}

let last=performance.now();
function frame(now){
  let dt=Math.min((now-last)/1000,0.05);
  last=now;
  simulate(dt);
  render(now);
  requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
</script>
</body>
</html>