← back to Model Arena
data/artifacts/097db196034e/kimi.html
353 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>
body {
margin: 0;
overflow: hidden;
background: #000;
font-family: 'Courier New', monospace;
}
canvas {
display: block;
background: radial-gradient(ellipse at center, #0a0e27 0%, #000000 100%);
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let width, height;
let mouseX = 0, mouseY = 0;
let hoveredNode = null;
function resize() {
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
}
window.addEventListener('resize', resize);
resize();
// Starfield background
const stars = [];
for (let i = 0; i < 200; i++) {
stars.push({
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
size: Math.random() * 1.5,
blink: Math.random() * Math.PI * 2
});
}
class Node {
constructor(id, x, y) {
this.id = id;
this.x = x;
this.y = y;
this.vx = (Math.random() - 0.5) * 0.5;
this.vy = (Math.random() - 0.5) * 0.5;
this.radius = 5 + Math.random() * 3;
this.mass = this.radius;
this.pulse = Math.random() * Math.PI * 2;
}
update() {
this.x += this.vx;
this.y += this.vy;
// Gentle drift damping
this.vx *= 0.98;
this.vy *= 0.98;
// Random gentle drift
this.vx += (Math.random() - 0.5) * 0.02;
this.vy += (Math.random() - 0.5) * 0.02;
// Boundary containment
if (this.x < 50) this.vx += 0.05;
if (this.x > width - 50) this.vx -= 0.05;
if (this.y < 50) this.vy += 0.05;
if (this.y > height - 50) this.vy -= 0.05;
this.pulse += 0.05;
}
draw(ctx, isHovered, isNeighbor) {
const glow = isHovered ? 30 : (isNeighbor ? 20 : 10);
const alpha = isHovered ? 1 : (isNeighbor ? 0.8 : 0.6);
// Outer glow
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius * 2, 0, Math.PI * 2);
ctx.fillStyle = `rgba(0, 255, 255, ${alpha * 0.1})`;
ctx.fill();
// Core
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = isHovered ? '#fff' : '#00ffff';
ctx.shadowBlur = glow;
ctx.shadowColor = '#00ffff';
ctx.fill();
ctx.shadowBlur = 0;
// Label
if (isHovered || isNeighbor) {
ctx.fillStyle = '#00ffff';
ctx.font = '11px monospace';
ctx.fillText(`AGENT-${this.id}`, this.x + 12, this.y + 4);
}
}
}
class Edge {
constructor(a, b) {
this.a = a;
this.b = b;
this.restLength = 120 + Math.random() * 80;
this.messages = [];
this.lastMessageTime = Math.random() * 100;
}
update() {
// Spring force
const dx = this.b.x - this.a.x;
const dy = this.b.y - this.a.y;
const dist = Math.sqrt(dx * dx + dy * dy) || 1;
const force = (dist - this.restLength) * 0.0002;
const fx = (dx / dist) * force;
const fy = (dy / dist) * force;
this.a.vx += fx / this.a.mass;
this.a.vy += fy / this.a.mass;
this.b.vx -= fx / this.b.mass;
this.b.vy -= fy / this.b.mass;
// Message spawning
this.lastMessageTime++;
if (this.lastMessageTime > 50 + Math.random() * 100) {
this.messages.push({
progress: 0,
speed: 0.01 + Math.random() * 0.02,
intensity: 1
});
this.lastMessageTime = 0;
}
// Update messages
for (let i = this.messages.length - 1; i >= 0; i--) {
const msg = this.messages[i];
msg.progress += msg.speed;
if (msg.progress >= 1) {
this.messages.splice(i, 1);
}
}
}
draw(ctx, isHighlighted) {
const dx = this.b.x - this.a.x;
const dy = this.b.y - this.a.y;
// Base connection
ctx.beginPath();
ctx.moveTo(this.a.x, this.a.y);
ctx.lineTo(this.b.x, this.b.y);
ctx.strokeStyle = isHighlighted ? 'rgba(0, 255, 255, 0.3)' : 'rgba(0, 255, 255, 0.08)';
ctx.lineWidth = isHighlighted ? 2 : 1;
ctx.stroke();
// Traveling messages
for (const msg of this.messages) {
const x = this.a.x + dx * msg.progress;
const y = this.a.y + dy * msg.progress;
// Message glow
ctx.beginPath();
ctx.arc(x, y, 4, 0, Math.PI * 2);
ctx.fillStyle = '#00ffff';
ctx.shadowBlur = 15;
ctx.shadowColor = '#00ffff';
ctx.fill();
ctx.shadowBlur = 0;
// Trail
ctx.beginPath();
const tailX = this.a.x + dx * Math.max(0, msg.progress - 0.1);
const tailY = this.a.y + dy * Math.max(0, msg.progress - 0.1);
ctx.moveTo(tailX, tailY);
ctx.lineTo(x, y);
ctx.strokeStyle = 'rgba(0, 255, 255, 0.6)';
ctx.lineWidth = 2;
ctx.stroke();
}
}
}
// Initialize graph
const nodes = [];
const edges = [];
const nodeCount = 24;
// Create nodes in circle initially to ensure spread
for (let i = 0; i < nodeCount; i++) {
const angle = (i / nodeCount) * Math.PI * 2;
const r = Math.min(width, height) * 0.3;
const x = width / 2 + Math.cos(angle) * r;
const y = height / 2 + Math.sin(angle) * r;
nodes.push(new Node(i, x, y));
}
// Create edges - ensure connected graph with random extra connections
for (let i = 0; i < nodeCount; i++) {
// Connect to next node (ring)
edges.push(new Edge(nodes[i], nodes[(i + 1) % nodeCount]));
// Random additional connections
const extra = 1 + Math.floor(Math.random() * 2);
for (let j = 0; j < extra; j++) {
const target = Math.floor(Math.random() * nodeCount);
if (target !== i && target !== (i + 1) % nodeCount && target !== (i - 1 + nodeCount) % nodeCount) {
// Check duplicate
const exists = edges.some(e =>
(e.a === nodes[i] && e.b === nodes[target]) ||
(e.a === nodes[target] && e.b === nodes[i])
);
if (!exists) {
edges.push(new Edge(nodes[i], nodes[target]));
}
}
}
}
// Build adjacency for hover highlighting
const adjacency = new Map();
nodes.forEach(n => adjacency.set(n, new Set()));
edges.forEach(e => {
adjacency.get(e.a).add(e.b);
adjacency.get(e.b).add(e.a);
});
// Mouse tracking
canvas.addEventListener('mousemove', (e) => {
mouseX = e.clientX;
mouseY = e.clientY;
hoveredNode = null;
for (const node of nodes) {
const dx = mouseX - node.x;
const dy = mouseY - node.y;
if (dx * dx + dy * dy < 100) {
hoveredNode = node;
break;
}
}
});
function updatePhysics() {
// Repulsion between all nodes
for (let i = 0; i < nodes.length; i++) {
for (let j = i + 1; j < nodes.length; j++) {
const a = nodes[i];
const b = nodes[j];
const dx = b.x - a.x;
const dy = b.y - a.y;
const distSq = dx * dx + dy * dy;
if (distSq === 0) continue;
const dist = Math.sqrt(distSq);
const force = 3000 / (distSq + 100); // Softened repulsion
const fx = (dx / dist) * force;
const fy = (dy / dist) * force;
a.vx -= fx / a.mass;
a.vy -= fy / a.mass;
b.vx += fx / b.mass;
b.vy += fy / b.mass;
}
}
// Center attraction (keep in view)
for (const node of nodes) {
const dx = (width / 2) - node.x;
const dy = (height / 2) - node.y;
node.vx += dx * 0.0001;
node.vy += dy * 0.0001;
}
// Update edges
for (const edge of edges) {
edge.update();
}
// Update nodes
for (const node of nodes) {
node.update();
}
}
function draw() {
// Clear
ctx.fillStyle = '#000005';
ctx.fillRect(0, 0, width, height);
// Draw stars
ctx.fillStyle = '#fff';
for (const star of stars) {
star.blink += 0.02;
const alpha = 0.3 + Math.sin(star.blink) * 0.3;
ctx.globalAlpha = alpha;
ctx.fillRect(star.x, star.y, star.size, star.size);
}
ctx.globalAlpha = 1;
// Determine highlighted elements
const highlightedNodes = new Set();
const highlightedEdges = new Set();
if (hoveredNode) {
highlightedNodes.add(hoveredNode);
adjacency.get(hoveredNode).forEach(n => highlightedNodes.add(n));
edges.forEach(e => {
if (e.a === hoveredNode || e.b === hoveredNode) {
highlightedEdges.add(e);
}
});
}
// Draw edges
for (const edge of edges) {
edge.draw(ctx, highlightedEdges.has(edge));
}
// Draw nodes
for (const node of nodes) {
const isHovered = node === hoveredNode;
const isNeighbor = highlightedNodes.has(node) && !isHovered;
node.draw(ctx, isHovered, isNeighbor);
}
// Instructions
ctx.fillStyle = 'rgba(0, 255, 255, 0.5)';
ctx.font = '12px monospace';
ctx.fillText('HOVER TO SCAN NEIGHBORS', 20, height - 20);
}
function loop() {
updatePhysics();
draw();
requestAnimationFrame(loop);
}
loop();
</script>
</body>
</html>