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