As multi-agent LLM workflows become increasingly popular, the way we interface with AI agents has remained stuck in 2D chat threads. Chat UI tabs struggle when you need to visualize multiple agents working concurrently, passing context back and forth, or running asynchronous background tasks.
Pixel Council flips this mental model on its head. It treats AI agents as spatial entities placed on an infinite 2D canvas, represented by custom-generated, animated pixel-art avatars. With custom Animated Spark Edges, agents pass data packets visually across connection lines, triggering downstream execution pipelines while maintaining a real-time Task & Flow Audit Log.
In this technical deep-dive, we'll examine how Pixel Council was built using React 18, @xyflow/react, PixiJS v8, DiceBear Pixel-Art, Zustand, and IndexedDB.
Architectural Overview
Pixel Council combines high-performance 2D WebGL canvas graphics with React's component state, custom SVG motion paths, and asynchronous streaming data flows.
+-----------------------------------+
| React Flow Canvas |
| (Spatial Node Placement & Edges) |
+-----------------+-----------------+
|
+-----------------------+-----------------------+
| | |
v v v
+-----------------------+ +-----------------------+ +-----------------------+
| Agent Node | | Animated Spark Edge | | Output Node |
| - PixiJS v8 Container | | - SVG animateMotion | | - React Markdown |
| - DiceBear Avatar | | - Spark Particle Loop | | - Real-time Streaming |
| - Ticker Animation | | - Manual/Auto Trigger | | - Code Highlight |
+-----------+-----------+ +-----------+-----------+ +-----------------------+
| |
+-----------+-----------+
|
v
+---------------------------+
| Sidebar & Audit Log |
| - Task & Data Flow Log |
| - Real-time Search/Filter |
| - Inspector & Duplication |
+-------------+-------------+
|
v
+---------------------------+
| Zustand + IndexedDB |
| - Persistent State Engine |
| - Agent Credentials |
| - Audit History Array |
+---------------------------+
Core Technical Implementation Highlights
1. Custom Animated Spark Edges & Inter-Agent Pipeline Signals
Agents on the canvas pass context and output data to downstream agents or output notes using custom React Flow edge components. SVG animateMotion renders glowing trail particles (#F4A261 and #D95D39) when a data packet is dispatched.
// Excerpt from src/components/canvas/edges/AnimatedSparkEdge.tsx
import { BaseEdge, EdgeLabelRenderer, EdgeProps, getSmoothStepPath } from '@xyflow/react';
import { Zap } from 'lucide-react';
import { useStore } from '../../../store/useStore';
export default function AnimatedSparkEdge({
id,
sourceX,
sourceY,
targetX,
targetY,
sourcePosition,
targetPosition,
style = {},
markerEnd,
label,
selected,
}: EdgeProps) {
const [edgePath, labelX, labelY] = getSmoothStepPath({
sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition,
});
const { activeSignals, triggerSignal } = useStore();
const isSignalActive = activeSignals ? activeSignals.some((s) => s.connectionId === id) : false;
return (
<>
<BaseEdge
id={id}
path={edgePath}
markerEnd={markerEnd}
style={{
...style,
stroke: isSignalActive ? '#F4A261' : selected ? '#D95D39' : '#A8C686',
strokeWidth: isSignalActive ? 4 : selected ? 3.5 : 2.5,
}}
/>
{/* SVG Motion Particles for Data Sparks */}
{isSignalActive && (
<g>
<circle r="7" fill="#F4A261" className="animate-pulse filter drop-shadow-[0_0_8px_#F4A261]">
<animateMotion path={edgePath} dur="1.2s" repeatCount="indefinite" />
</circle>
<circle r="4" fill="#D95D39" opacity="0.8">
<animateMotion path={edgePath} dur="1.2s" begin="0.3s" repeatCount="indefinite" />
</circle>
</g>
)}
<EdgeLabelRenderer>
<div
style={{ position: 'absolute', transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`, pointerEvents: 'all' }}
className="nodrag nopan flex items-center gap-1.5 bg-[#FFFDF8] border border-[#D8D2C8] px-2 py-1 rounded-lg text-[10px] font-mono"
>
<button
onClick={(e) => { e.stopPropagation(); triggerSignal(id); }}
className="p-1 bg-[#F4A261]/10 text-[#F4A261] hover:bg-[#F4A261] hover:text-white rounded flex items-center gap-1 font-bold"
>
<Zap size={11} /> Pass Data
</button>
</div>
</EdgeLabelRenderer>
</>
);
}
2. High-Performance Pixel Art Rendering with PixiJS & DiceBear
Each agent node hosts an isolated, high-performance PixiJS v8 canvas application. To keep rendering lightweight while maintaining crisp pixel art optics, we combine DiceBear's seed-based generator with WebGL textures and PixiJS ticker loops.
// Excerpt from src/components/canvas/nodes/PixiCharacter.tsx
import { useEffect, useRef } from 'react';
import * as PIXI from 'pixi.js';
import { createAvatar } from '@dicebear/core';
import * as pixelArt from '@dicebear/pixel-art';
export default function PixiCharacter({ status, seed = 'default', size = 64 }: PixiCharacterProps) {
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const initPixi = async () => {
const app = new PIXI.Application();
await app.init({ width: size, height: size, backgroundAlpha: 0 });
const avatar = createAvatar(pixelArt, { seed, size: 128 });
const dataUri = await avatar.toDataUri();
const texture = await PIXI.Assets.load(dataUri);
const sprite = new PIXI.Sprite(texture);
sprite.anchor.set(0.5);
sprite.x = app.screen.width / 2;
sprite.y = app.screen.height / 2;
app.stage.addChild(sprite);
// Smooth status micro-animation ticker loop
app.ticker.add((ticker) => {
const centerY = app.screen.height / 2;
if (status === 'busy') {
sprite.y = centerY + Math.sin(ticker.lastTime * 0.005) * 3;
sprite.rotation = Math.sin(ticker.lastTime * 0.003) * 0.03;
} else if (status === 'sleeping') {
sprite.y = centerY + 3;
sprite.alpha = 0.9;
} else {
sprite.y = centerY + Math.sin(ticker.lastTime * 0.002) * 1.5;
sprite.rotation = 0;
}
});
};
initPixi();
}, [seed, size, status]);
return <div ref={containerRef} style={{ width: size, height: size, imageRendering: 'pixelated' }} />;
}
3. Task & Data Flow Audit Log Engine
To make agent activities auditable, all task starts, completions, errors, and inter-agent data transfers are recorded into an append-only audit trail in Zustand and IndexedDB:
// Excerpt from store task logging in src/store/useStore.ts
addTaskLog: (log) => {
const newEntry: TaskLogEntry = {
id: `log-${Math.random().toString(36).substring(7)}`,
timestamp: Date.now(),
...log,
};
setStore((state) => ({
...state,
taskLogs: [newEntry, ...(state.taskLogs || [])].slice(0, 100),
}));
getStore().saveToStorage();
}
4. Agent Duplication Logic
Users can duplicate any configured agent instantly across the UI (node badges, inspector sidebar, or modal dialog):
// Excerpt from src/store/useStore.ts
duplicateAgent: (id) => {
const state = getStore();
const sourceAgent = state.agents[id];
if (!sourceAgent) return null;
const newId = Math.random().toString(36).substring(7);
const newAgent: Agent = {
...sourceAgent,
id: newId,
name: `${sourceAgent.name} (Copy)`,
status: 'idle',
memory: sourceAgent.memory ? [...sourceAgent.memory] : [],
position: {
x: sourceAgent.position.x + 40,
y: sourceAgent.position.y + 40,
},
};
setStore((s) => ({
...s,
agents: { ...s.agents, [newId]: newAgent },
selectedAgentId: newId,
}));
getStore().saveToStorage();
return newId;
}
Spatial agent workspaces bridge the gap between human intuition and autonomous AI execution. By pairing retro 8-bit visual aesthetics with animated spark connection pipelines, audit logs, and LLM streaming APIs, Pixel Council turns multi-agent orchestration into an intuitive delight.
Check out the project online or try building your own agent council today!
Top comments (0)