World/Brain: lit pathways + experience-scaled neurons
ci / gates (push) Has been cancelled
ci / rust (push) Has been cancelled
ci / sandbox-k8s (push) Has been cancelled
ci / frontend (push) Has been cancelled
ci / e2e (push) Has been cancelled

- brain edges now use per-vertex colors driven by endpoint activity, so a firing
  neuron lights its pathways (gradient from the brighter end) while dormant
  connections stay faint — the wiring visibly comes alive.
- neuron size now grows with cumulative activity (experience), so the agents that
  do the most work read as the largest, most-developed neurons.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
Omar Sobh
2026-06-23 23:36:51 -07:00
co-authored by Claude Opus 4.8
parent 9725d620cf
commit 05cf132957
+33 -3
View File
@@ -259,12 +259,14 @@ export function WorldCanvas({ roots, selectedId, onSelect, expanded, onToggleExp
const brainEdgeGeom = new THREE.BufferGeometry(); const brainEdgeGeom = new THREE.BufferGeometry();
const brainEdges = new THREE.LineSegments( const brainEdges = new THREE.LineSegments(
brainEdgeGeom, brainEdgeGeom,
new THREE.LineBasicMaterial({ color: 0x6fb6ff, transparent: true, opacity: 0.14, blending: THREE.AdditiveBlending, depthWrite: false }), new THREE.LineBasicMaterial({ vertexColors: true, transparent: true, opacity: 0.95, blending: THREE.AdditiveBlending, depthWrite: false }),
); );
brainGroup.add(brainEdges); brainGroup.add(brainEdges);
const brainPositions = new Map<string, THREE.Vector3>(); const brainPositions = new Map<string, THREE.Vector3>();
const neuronAct = new Map<string, number>(); const neuronAct = new Map<string, number>();
const neuronExp = new Map<string, number>(); // cumulative experience → neuron size
const brainAdj = new Map<string, string[]>(); const brainAdj = new Map<string, string[]>();
let brainEdgeIds: string[] = []; // flat [a0,b0,a1,b1,…] for per-edge lighting
let brainCount = -1; let brainCount = -1;
// particle system (sparks + trails): additive points, ring buffer, RGB→0 fade // particle system (sparks + trails): additive points, ring buffer, RGB→0 fade
@@ -376,6 +378,7 @@ export function WorldCanvas({ roots, selectedId, onSelect, expanded, onToggleExp
brainAdj.clear(); brainAdj.clear();
const pos = ids.map((id) => brainPositions.get(id)!); const pos = ids.map((id) => brainPositions.get(id)!);
const segs: number[] = []; const segs: number[] = [];
const pairs: string[] = [];
for (let i = 0; i < ids.length; i++) { for (let i = 0; i < ids.length; i++) {
const ds = ids.map((_, j) => ({ j, d: i === j ? Infinity : pos[i].distanceToSquared(pos[j]) })); const ds = ids.map((_, j) => ({ j, d: i === j ? Infinity : pos[i].distanceToSquared(pos[j]) }));
ds.sort((a, b) => a.d - b.d); ds.sort((a, b) => a.d - b.d);
@@ -384,11 +387,16 @@ export function WorldCanvas({ roots, selectedId, onSelect, expanded, onToggleExp
for (let n = 0; n < k; n++) { for (let n = 0; n < k; n++) {
const j = ds[n].j; const j = ds[n].j;
nbrs.push(ids[j]); nbrs.push(ids[j]);
if (i < j) segs.push(pos[i].x, pos[i].y, pos[i].z, pos[j].x, pos[j].y, pos[j].z); if (i < j) {
segs.push(pos[i].x, pos[i].y, pos[i].z, pos[j].x, pos[j].y, pos[j].z);
pairs.push(ids[i], ids[j]);
}
} }
brainAdj.set(ids[i], nbrs); brainAdj.set(ids[i], nbrs);
} }
brainEdgeIds = pairs;
brainEdgeGeom.setAttribute("position", new THREE.Float32BufferAttribute(segs, 3)); brainEdgeGeom.setAttribute("position", new THREE.Float32BufferAttribute(segs, 3));
brainEdgeGeom.setAttribute("color", new THREE.Float32BufferAttribute(new Float32Array(segs.length), 3));
brainEdgeGeom.attributes.position.needsUpdate = true; brainEdgeGeom.attributes.position.needsUpdate = true;
}; };
@@ -422,8 +430,11 @@ export function WorldCanvas({ roots, selectedId, onSelect, expanded, onToggleExp
if (Math.random() < dt * 0.5) act = Math.min(1, act + 0.08); // resting shimmer → the brain breathes if (Math.random() < dt * 0.5) act = Math.min(1, act + 0.08); // resting shimmer → the brain breathes
act = Math.max(ne.agent ? 0.06 : 0.025, act - dt * 0.22); act = Math.max(ne.agent ? 0.06 : 0.025, act - dt * 0.22);
neuronAct.set(ne.id, act); neuronAct.set(ne.id, act);
let exp = neuronExp.get(ne.id) ?? 0;
exp = Math.min(1, exp + act * dt * 0.15); // experience grows with activity
neuronExp.set(ne.id, exp);
g.position.copy(pos); g.position.copy(pos);
const base = ne.agent ? 16 : 8; const base = (ne.agent ? 16 : 8) + exp * (ne.agent ? 16 : 7);
const gs = base + act * (ne.agent ? 36 : 16); const gs = base + act * (ne.agent ? 36 : 16);
g.scale.set(gs, gs, 1); g.scale.set(gs, gs, 1);
const gm = g.material as THREE.SpriteMaterial; const gm = g.material as THREE.SpriteMaterial;
@@ -445,6 +456,25 @@ export function WorldCanvas({ roots, selectedId, onSelect, expanded, onToggleExp
} }
} }
} }
// light up pathways by endpoint activity (lit pathways stand out; gradient
// from each end so the brighter neuron dominates its edges)
const ecol = brainEdgeGeom.getAttribute("color") as THREE.BufferAttribute | undefined;
if (ecol) {
const arr = ecol.array as Float32Array;
for (let e = 0; e + 1 < brainEdgeIds.length; e += 2) {
const va = 0.05 + (neuronAct.get(brainEdgeIds[e]) ?? 0) * 0.95;
const vb = 0.05 + (neuronAct.get(brainEdgeIds[e + 1]) ?? 0) * 0.95;
const ia = e * 3;
const ib = (e + 1) * 3;
arr[ia] = 0.43 * va;
arr[ia + 1] = 0.71 * va;
arr[ia + 2] = va;
arr[ib] = 0.43 * vb;
arr[ib + 1] = 0.71 * vb;
arr[ib + 2] = vb;
}
ecol.needsUpdate = true;
}
for (const [id, g] of neuronGlows) { for (const [id, g] of neuronGlows) {
if (!seen.has(id)) { if (!seen.has(id)) {
brainGroup.remove(g); brainGroup.remove(g);