Skip to content

Commit 2bbf7f7

Browse files
committed
feat: Implement Agent-Driven State for Neural Swarm (#8674)
1 parent 13f89b2 commit 2bbf7f7

1 file changed

Lines changed: 27 additions & 13 deletions

File tree

apps/portal/canvas/HomeCanvas.mjs

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const
55
SECONDARY = '#8BA6FF',
66
HIGHLIGHT = '#40C4FF',
77
NODE_COUNT = 150,
8-
NODE_STRIDE = 8, // x, y, vx, vy, radius, layer, parentId, phase
8+
NODE_STRIDE = 9, // x, y, vx, vy, radius, layer, parentId, phase, energy
99
AGENT_COUNT = 20,
1010
AGENT_STRIDE = 6, // x, y, vx, vy, targetIdx, state
1111
PACKET_COUNT = 20,
@@ -18,7 +18,7 @@ const
1818
* Uses a Zero-Allocation strategy (pre-allocated buffers) for high performance.
1919
*
2020
* **Node Buffer Layout (Float32Array):**
21-
* [x, y, vx, vy, radius, layer, parentId, phase]
21+
* [x, y, vx, vy, radius, layer, parentId, phase, energy]
2222
*
2323
* **Agent Buffer Layout (Float32Array):**
2424
* [x, y, vx, vy, targetIdx, state, ...]
@@ -274,14 +274,15 @@ class HomeCanvas extends Base {
274274
layer = buffer[idx + 5],
275275
parentId = buffer[idx + 6],
276276
phase = buffer[idx + 7],
277+
energy = buffer[idx + 8],
277278
pos = getPos(idx, layer);
278279

279280
let dx = pos.x - mx,
280281
dy = pos.y - my,
281282
dist = Math.sqrt(dx*dx + dy*dy),
282283
isHover = dist < 50;
283284

284-
// Shockwave Interaction (Nodes Pulse)
285+
// Shockwave Interaction
285286
if (me.shockwaves.length > 0) {
286287
me.shockwaves.forEach(wave => {
287288
let wDist = Math.sqrt((pos.x - wave.x)**2 + (pos.y - wave.y)**2),
@@ -293,20 +294,23 @@ class HomeCanvas extends Base {
293294
ctx.beginPath();
294295
let r = parentId === -1 ? radius * 1.5 : radius;
295296

296-
// Breathing Effect
297-
r *= 1 + Math.sin(me.time * 2 + phase) * 0.15;
297+
// Breathing + Energy
298+
r *= 1 + Math.sin(me.time * 2 + phase) * 0.15 + energy;
298299

299300
if (isHover) r *= 1.5;
300301

301302
ctx.arc(pos.x, pos.y, r, 0, Math.PI * 2);
302303

303-
if (layer === 2) {
304+
if (energy > 0.1) {
305+
// Energetic Node (Agent Scanned)
306+
ctx.fillStyle = HIGHLIGHT;
307+
ctx.globalAlpha = Math.min(1, 0.5 + energy);
308+
} else if (layer === 2) {
304309
ctx.fillStyle = isHover ? '#FFFFFF' : PRIMARY;
305310
ctx.globalAlpha = isHover ? 1 : 0.8;
306311
} else if (parentId === -2) {
307312
// Drifting Node Visual
308313
ctx.fillStyle = HIGHLIGHT;
309-
// Phase-based pulse for drifters
310314
ctx.globalAlpha = 0.6 + Math.sin(me.time * 10 + phase) * 0.3;
311315
} else if (layer === 1) {
312316
ctx.fillStyle = isHover ? HIGHLIGHT : SECONDARY;
@@ -494,6 +498,9 @@ class HomeCanvas extends Base {
494498

495499
// Phase (Breathing offset)
496500
buffer[idx + 7] = Math.random() * Math.PI * 2;
501+
502+
// Energy
503+
buffer[idx + 8] = 0;
497504
}
498505

499506
// 2. Assign Children to nearest Parent
@@ -720,14 +727,16 @@ class HomeCanvas extends Base {
720727
dist = Math.sqrt(dx*dx + dy*dy);
721728

722729
if (dist < 10) {
723-
agents[idx + 5] = 1; // Scan
724-
agents[idx + 2] *= 0.1;
730+
// Arrived! Scan.
731+
agents[idx + 5] = 1; // Scan state
732+
agents[idx + 2] *= 0.1; // Slow down
725733
agents[idx + 3] *= 0.1;
734+
735+
// Transfer Energy to Node
736+
nodes[nIdx + 8] = 1.0;
726737
} else {
727-
let force = 0.05;
728-
agents[idx + 2] += (dx / dist) * force;
729-
agents[idx + 3] += (dy / dist) * force;
730-
}
738+
// Steer towards target
739+
731740
} else if (state === 1) {
732741
if (Math.random() < 0.02) {
733742
agents[idx + 4] = -1;
@@ -871,6 +880,11 @@ class HomeCanvas extends Base {
871880
// 4. Physics
872881
buffer[idx + 2] *= 0.95; // Friction
873882
buffer[idx + 3] *= 0.95;
883+
884+
// Energy Decay
885+
buffer[idx + 8] *= 0.99;
886+
887+
let drift = isParent ? 0.02 : 0.01;
874888

875889
// 4. Ambient Drift / Flow Field
876890
if (isParent) {

0 commit comments

Comments
 (0)