diff --git a/app.js b/app.js
index e94f10b0..da340c86 100644
--- a/app.js
+++ b/app.js
@@ -30,10 +30,13 @@ let mouseDown = false;
let batcaveTerminals = [];
let portals = []; // Registry of active portals
let visionPoints = []; // Registry of vision points
+let agents = []; // Registry of agent presences
let activePortal = null; // Portal currently in proximity
let activeVisionPoint = null; // Vision point currently in proximity
let portalOverlayActive = false;
let visionOverlayActive = false;
+let thoughtStreamMesh;
+let harnessPulseMesh;
let particles, dustParticles;
let debugOverlay;
let frameCount = 0, lastFPSTime = 0, fps = 0;
@@ -116,6 +119,9 @@ async function init() {
createDustParticles();
updateLoad(85);
createAmbientStructures();
+ createAgentPresences();
+ createThoughtStream();
+ createHarnessPulse();
updateLoad(90);
composer = new EffectComposer(renderer);
@@ -430,6 +436,113 @@ function createTerminalPanel(parent, x, y, rot, title, color, lines) {
batcaveTerminals.push({ group, scanMat, borderMat });
}
+// ═══ AGENT PRESENCE SYSTEM ═══
+function createAgentPresences() {
+ const agentData = [
+ { id: 'timmy', name: 'TIMMY', color: NEXUS.colors.primary, pos: { x: -4, z: -4 } },
+ { id: 'kimi', name: 'KIMI', color: NEXUS.colors.secondary, pos: { x: 4, z: -4 } },
+ { id: 'claude', name: 'CLAUDE', color: NEXUS.colors.gold, pos: { x: 0, z: -6 } },
+ ];
+
+ agentData.forEach(data => {
+ const group = new THREE.Group();
+ group.position.set(data.pos.x, 0, data.pos.z);
+
+ const color = new THREE.Color(data.color);
+
+ // Agent Orb
+ const orbGeo = new THREE.SphereGeometry(0.4, 32, 32);
+ const orbMat = new THREE.MeshPhysicalMaterial({
+ color: color,
+ emissive: color,
+ emissiveIntensity: 2,
+ roughness: 0,
+ metalness: 1,
+ transmission: 0.8,
+ thickness: 0.5,
+ });
+ const orb = new THREE.Mesh(orbGeo, orbMat);
+ orb.position.y = 3;
+ group.add(orb);
+
+ // Halo
+ const haloGeo = new THREE.TorusGeometry(0.6, 0.02, 16, 64);
+ const haloMat = new THREE.MeshBasicMaterial({ color: color, transparent: true, opacity: 0.4 });
+ const halo = new THREE.Mesh(haloGeo, haloMat);
+ halo.position.y = 3;
+ halo.rotation.x = Math.PI / 2;
+ group.add(halo);
+
+ // Label
+ const canvas = document.createElement('canvas');
+ canvas.width = 256;
+ canvas.height = 64;
+ const ctx = canvas.getContext('2d');
+ ctx.font = 'bold 24px "Orbitron", sans-serif';
+ ctx.fillStyle = '#' + color.getHexString();
+ ctx.textAlign = 'center';
+ ctx.fillText(data.name, 128, 40);
+ const tex = new THREE.CanvasTexture(canvas);
+ const mat = new THREE.MeshBasicMaterial({ map: tex, transparent: true, side: THREE.DoubleSide });
+ const label = new THREE.Mesh(new THREE.PlaneGeometry(2, 0.5), mat);
+ label.position.y = 3.8;
+ group.add(label);
+
+ scene.add(group);
+ agents.push({ id: data.id, group, orb, halo, color });
+ });
+}
+
+function createThoughtStream() {
+ const geo = new THREE.CylinderGeometry(8, 8, 12, 32, 1, true);
+ const mat = new THREE.ShaderMaterial({
+ transparent: true,
+ side: THREE.BackSide,
+ depthWrite: false,
+ uniforms: {
+ uTime: { value: 0 },
+ uColor: { value: new THREE.Color(NEXUS.colors.primary) },
+ },
+ vertexShader: `
+ varying vec2 vUv;
+ void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }
+ `,
+ fragmentShader: `
+ uniform float uTime;
+ uniform vec3 uColor;
+ varying vec2 vUv;
+
+ float hash(vec2 p) { return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453); }
+
+ void main() {
+ float flow = fract(vUv.y - uTime * 0.1);
+ float lines = step(0.98, fract(vUv.x * 20.0 + uTime * 0.05));
+ float dots = step(0.99, hash(vUv * 50.0 + floor(uTime * 10.0) * 0.01));
+
+ float alpha = (lines * 0.1 + dots * 0.5) * smoothstep(0.0, 0.2, vUv.y) * smoothstep(1.0, 0.8, vUv.y);
+ gl_FragColor = vec4(uColor, alpha * 0.3);
+ }
+ `,
+ });
+ thoughtStreamMesh = new THREE.Mesh(geo, mat);
+ thoughtStreamMesh.position.y = 6;
+ scene.add(thoughtStreamMesh);
+}
+
+function createHarnessPulse() {
+ const geo = new THREE.RingGeometry(0.1, 0.2, 64);
+ const mat = new THREE.MeshBasicMaterial({
+ color: NEXUS.colors.primary,
+ transparent: true,
+ opacity: 0,
+ side: THREE.DoubleSide,
+ });
+ harnessPulseMesh = new THREE.Mesh(geo, mat);
+ harnessPulseMesh.rotation.x = -Math.PI / 2;
+ harnessPulseMesh.position.y = 0.1;
+ scene.add(harnessPulseMesh);
+}
+
// ═══ VISION SYSTEM ═══
function createVisionPoints(data) {
data.forEach(config => {
@@ -1042,11 +1155,31 @@ function closeVisionOverlay() {
}
// ═══ GAME LOOP ═══
+let lastThoughtTime = 0;
+let pulseTimer = 0;
+
function gameLoop() {
requestAnimationFrame(gameLoop);
const delta = Math.min(clock.getDelta(), 0.1);
const elapsed = clock.elapsedTime;
+ // Agent Thought Simulation
+ if (elapsed - lastThoughtTime > 4) {
+ lastThoughtTime = elapsed;
+ simulateAgentThought();
+ }
+
+ // Harness Pulse
+ pulseTimer += delta;
+ if (pulseTimer > 8) {
+ pulseTimer = 0;
+ triggerHarnessPulse();
+ }
+ if (harnessPulseMesh) {
+ harnessPulseMesh.scale.addScalar(delta * 15);
+ harnessPulseMesh.material.opacity = Math.max(0, harnessPulseMesh.material.opacity - delta * 0.5);
+ }
+
const mode = NAV_MODES[navModeIdx];
const chatActive = document.activeElement === document.getElementById('chat-input');
@@ -1152,6 +1285,19 @@ function gameLoop() {
vp.light.intensity = 1 + Math.sin(elapsed * 3) * 0.3;
});
+ // Animate Agents
+ agents.forEach((agent, i) => {
+ agent.orb.position.y = 3 + Math.sin(elapsed * 2 + i) * 0.15;
+ agent.halo.rotation.z = elapsed * 0.5;
+ agent.halo.scale.setScalar(1 + Math.sin(elapsed * 3 + i) * 0.1);
+ agent.orb.material.emissiveIntensity = 2 + Math.sin(elapsed * 4 + i) * 1;
+ });
+
+ if (thoughtStreamMesh) {
+ thoughtStreamMesh.material.uniforms.uTime.value = elapsed;
+ thoughtStreamMesh.rotation.y = elapsed * 0.05;
+ }
+
if (particles?.material?.uniforms) {
particles.material.uniforms.uTime.value = elapsed;
}
@@ -1203,4 +1349,63 @@ function onResize() {
composer.setSize(w, h);
}
+// ═══ AGENT SIMULATION ═══
+function simulateAgentThought() {
+ const agentIds = ['timmy', 'kimi', 'claude'];
+ const agentId = agentIds[Math.floor(Math.random() * agentIds.length)];
+ const thoughts = {
+ timmy: [
+ 'Analyzing portal stability...',
+ 'Sovereign nodes synchronized.',
+ 'Memory stream optimization complete.',
+ 'Scanning for external interference...',
+ 'The harness is humming beautifully.',
+ ],
+ kimi: [
+ 'Processing linguistic patterns...',
+ 'Context window expanded.',
+ 'Synthesizing creative output...',
+ 'Awaiting user prompt sequence.',
+ 'Neural weights adjusted.',
+ ],
+ claude: [
+ 'Reasoning through complex logic...',
+ 'Ethical guardrails verified.',
+ 'Refining thought architecture...',
+ 'Connecting disparate data points.',
+ 'Deep analysis in progress.',
+ ]
+ };
+
+ const thought = thoughts[agentId][Math.floor(Math.random() * thoughts[agentId].length)];
+ addAgentLog(agentId, thought);
+}
+
+function addAgentLog(agentId, text) {
+ const container = document.getElementById('agent-log-content');
+ if (!container) return;
+
+ const entry = document.createElement('div');
+ entry.className = 'agent-log-entry';
+ entry.innerHTML = `[${agentId.toUpperCase()}]${text}`;
+
+ container.prepend(entry);
+ if (container.children.length > 6) {
+ container.lastElementChild.remove();
+ }
+}
+
+function triggerHarnessPulse() {
+ if (!harnessPulseMesh) return;
+ harnessPulseMesh.scale.setScalar(0.1);
+ harnessPulseMesh.material.opacity = 0.8;
+
+ // Flash the core
+ const core = scene.getObjectByName('nexus-core');
+ if (core) {
+ core.material.emissiveIntensity = 10;
+ setTimeout(() => { if (core) core.material.emissiveIntensity = 2; }, 200);
+ }
+}
+
init();