doom-flywire / web /index.html
emiraran's picture
Fix motor_decoder torch.no_grad and continuous autonomous stream
c5471cb verified
Raw History Blame Contribute Delete
33.7 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOOM-FlyWire: 3D 139,248-Neuron Connectome Neural Activity Visualizer</title>
<meta name="description" content="A real-time whole-brain biological connectome simulation running the complete Drosophila melanogaster connectome (FlyWire Nature 2024) inside ViZDoom with an interactive 3D WebGL neural activity visualizer.">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600;700;800&family=Outfit:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<!-- Three.js + OrbitControls (Served locally for 100% reliability, zero CDN dependence) -->
<script src="/js/three.min.js"></script>
<script src="/js/OrbitControls.js"></script>
<style>
:root {
--bg-dark: #06080e;
--card-bg: rgba(13, 17, 28, 0.85);
--card-border: rgba(0, 245, 212, 0.2);
--accent-cyan: #00faff;
--accent-blue: #0d61ff;
--accent-purple: #b840ff;
--accent-pink: #ff2685;
--accent-amber: #ffbf1a;
--accent-emerald: #14ff33;
--accent-danger: #ef4444;
--text-main: #f8fafc;
--text-muted: #94a3b8;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-dark);
background-image:
radial-gradient(circle at 10% 20%, rgba(0, 245, 212, 0.06) 0%, transparent 45%),
radial-gradient(circle at 90% 80%, rgba(192, 132, 252, 0.07) 0%, transparent 50%),
radial-gradient(circle at 50% 50%, rgba(16, 185, 129, 0.03) 0%, transparent 60%);
color: var(--text-main);
font-family: 'Outfit', sans-serif;
min-height: 100vh;
overflow-x: hidden;
padding-bottom: 30px;
}
header {
padding: 14px 28px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid var(--card-border);
backdrop-filter: blur(14px);
background: rgba(6, 8, 14, 0.9);
position: sticky;
top: 0;
z-index: 100;
}
.brand-title {
display: flex;
align-items: center;
gap: 12px;
font-size: 1.35rem;
font-weight: 800;
letter-spacing: 0.5px;
}
.brand-badge {
background: linear-gradient(135deg, var(--accent-cyan), #00bb99);
color: #000;
font-family: 'JetBrains Mono', monospace;
font-size: 0.72rem;
padding: 3px 8px;
border-radius: 4px;
font-weight: 800;
letter-spacing: 0.5px;
}
.header-stats {
display: flex;
align-items: center;
gap: 16px;
font-family: 'JetBrains Mono', monospace;
font-size: 0.85rem;
}
.status-pill {
display: flex;
align-items: center;
gap: 6px;
padding: 4px 12px;
border-radius: 20px;
background: rgba(16, 185, 129, 0.15);
border: 1px solid rgba(16, 185, 129, 0.4);
color: var(--accent-emerald);
font-size: 0.75rem;
font-weight: 700;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--accent-emerald);
box-shadow: 0 0 10px var(--accent-emerald);
animation: pulse 1.8s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.4; transform: scale(0.85); }
}
.main-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
max-width: 1540px;
margin: 20px auto;
padding: 0 24px;
}
.glass-card {
background: var(--card-bg);
border: 1px solid var(--card-border);
border-radius: 14px;
padding: 20px;
backdrop-filter: blur(18px);
box-shadow: 0 10px 35px rgba(0, 0, 0, 0.5);
display: flex;
flex-direction: column;
}
.card-title {
font-size: 0.95rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1px;
color: var(--accent-cyan);
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 14px;
padding-bottom: 10px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
/* DOOM Viewport */
.viewport-container {
position: relative;
width: 100%;
aspect-ratio: 4 / 3;
background: #000;
border-radius: 10px;
overflow: hidden;
border: 2px solid rgba(0, 245, 212, 0.35);
box-shadow: 0 0 28px rgba(0, 245, 212, 0.15);
}
#doomCanvas {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.game-hud {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
margin-top: 14px;
}
.hud-box {
background: rgba(0, 0, 0, 0.55);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 8px;
padding: 8px 12px;
text-align: center;
font-family: 'JetBrains Mono', monospace;
}
.hud-label {
font-size: 0.68rem;
color: var(--text-muted);
text-transform: uppercase;
}
.hud-val {
font-size: 1.25rem;
font-weight: 800;
color: var(--accent-cyan);
margin-top: 2px;
}
/* Action Probabilities */
.action-bars {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 14px;
}
.action-item {
display: grid;
grid-template-columns: 110px 1fr 50px;
align-items: center;
gap: 12px;
font-family: 'JetBrains Mono', monospace;
font-size: 0.78rem;
}
.progress-track {
background: rgba(255, 255, 255, 0.08);
height: 10px;
border-radius: 5px;
overflow: hidden;
}
.progress-fill {
height: 100%;
border-radius: 5px;
transition: width 0.08s ease-out;
}
.fill-left { background: #0d61ff; }
.fill-right { background: #00faff; }
.fill-attack { background: #14ff33; }
/* 3D Brain Container */
.brain-canvas-container {
position: relative;
width: 100%;
aspect-ratio: 4 / 3;
background: radial-gradient(circle at 50% 50%, #0d1322 0%, #04060c 100%);
border-radius: 10px;
overflow: hidden;
border: 2px solid rgba(192, 132, 252, 0.35);
box-shadow: 0 0 30px rgba(192, 132, 252, 0.15);
cursor: grab;
}
.brain-canvas-container:active {
cursor: grabbing;
}
.brain-canvas-container canvas {
width: 100% !important;
height: 100% !important;
display: block;
position: absolute;
top: 0;
left: 0;
}
/* Brain Region Chips */
.region-chips-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
margin-top: 14px;
}
.region-chip {
background: rgba(0, 0, 0, 0.45);
border: 1px solid rgba(255, 255, 255, 0.07);
border-radius: 8px;
padding: 8px 10px;
font-family: 'JetBrains Mono', monospace;
font-size: 0.72rem;
display: flex;
flex-direction: column;
gap: 4px;
}
.chip-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.chip-title-wrap {
display: flex;
align-items: center;
gap: 6px;
}
.chip-dot {
width: 8px;
height: 8px;
border-radius: 50%;
box-shadow: 0 0 6px currentColor;
}
.chip-val {
font-weight: 800;
color: #fff;
}
.chip-sub {
font-size: 0.65rem;
color: var(--text-muted);
}
/* Controls Bar */
.controls-bar {
display: flex;
justify-content: space-between;
align-items: center;
gap: 10px;
margin-top: 14px;
}
.btn-action {
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.12);
color: var(--text-main);
padding: 6px 14px;
border-radius: 6px;
font-size: 0.78rem;
font-family: 'JetBrains Mono', monospace;
font-weight: 700;
cursor: pointer;
transition: all 0.2s;
}
.btn-action:hover {
background: var(--accent-cyan);
color: #000;
border-color: var(--accent-cyan);
box-shadow: 0 0 12px rgba(0, 245, 212, 0.4);
}
.speed-btn-group {
display: flex;
gap: 6px;
align-items: center;
}
@media (max-width: 1100px) {
.main-grid { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<div class="main-grid">
<!-- LEFT: DOOM Viewport & Actions -->
<div class="glass-card">
<div class="card-title">
<span>ViZDoom Perception-Action Viewport</span>
<span id="stepCounter" style="font-family:'JetBrains Mono',monospace; font-size:0.75rem; color:var(--text-muted);">Step: 0</span>
</div>
<div class="viewport-container">
<img id="doomCanvas" alt="Live DOOM Frame from Fruit Fly Connectome">
</div>
<div class="game-hud">
<div class="hud-box">
<div class="hud-label">Health</div>
<div id="hudHealth" class="hud-val">100</div>
</div>
<div class="hud-box">
<div class="hud-label">Ammo</div>
<div id="hudAmmo" class="hud-val" style="color:var(--accent-cyan); font-size:1.35rem;">∞</div >
</div>
<div class="hud-box">
<div class="hud-label">Round Kills</div>
<div id="hudKills" class="hud-val" style="color:var(--accent-emerald);">0</div>
</div>
<div class="hud-box">
<div class="hud-label">Total Reward</div>
<div id="hudScore" class="hud-val">0.0</div>
</div>
</div>
<!-- Action Probabilities from Descending Motor Neurons -->
<div class="action-bars">
<div class="action-item">
<span>TURN LEFT</span>
<div class="progress-track"><div id="barLeft" class="progress-fill fill-left" style="width:0%;"></div></div>
<span id="pctLeft">0%</span>
</div>
<div class="action-item">
<span>TURN RIGHT</span>
<div class="progress-track"><div id="barRight" class="progress-fill fill-right" style="width:0%;"></div></div>
<span id="pctRight">0%</span>
</div>
<div class="action-item">
<span>ATTACK</span>
<div class="progress-track"><div id="barAttack" class="progress-fill fill-attack" style="width:0%;"></div></div>
<span id="pctAttack">0%</span>
</div>
</div>
</div>
<!-- RIGHT: 3D FlyWire Connectome Neural Activity Visualizer -->
<div class="glass-card">
<div class="card-title">
<span>3D Connectome</span>
<span style="font-size:0.75rem; color:var(--accent-purple); font-family:'JetBrains Mono';">139,248 Neurons</span>
</div>
<!-- 3D WebGL Canvas Container -->
<div class="brain-canvas-container" id="brain3DContainer">
<div id="brain3DLoading" style="position:absolute; top:50%; left:50%; transform:translate(-50%, -50%); color:var(--accent-cyan); font-family:'JetBrains Mono'; font-size:0.85rem; pointer-events:none; z-index:5;">
Loading 139,248 neurons...
</div>
<div style="position:absolute; bottom:12px; left:14px; right:14px; display:flex; justify-content:space-between; align-items:center; z-index:10; pointer-events:none;">
<span style="font-family:'JetBrains Mono'; font-size:0.68rem; color:var(--text-muted); background:rgba(0,0,0,0.6); padding:3px 8px; border-radius:4px;">
Rotate • Scroll: Zoom • Right-Click: Pan
</span>
<div style="display:flex; gap:6px;">
<button id="btnResetView" style="pointer-events:auto; background:rgba(255,255,255,0.08); border:1px solid rgba(255,255,255,0.15); color:#fff; font-family:'JetBrains Mono'; font-size:0.68rem; padding:4px 10px; border-radius:4px; cursor:pointer;">
↺ Reset View
</button>
</div>
</div>
</div>
<!-- Color-coded Anatomical Circuit Legend -->
<div class="region-chips-grid">
<div id="chipOpticL" class="region-chip" style="border-left: 3px solid var(--accent-blue);">
<div class="chip-header">
<div class="chip-title-wrap">
<div class="chip-dot" style="background:var(--accent-blue);"></div>
<span>Optic Left</span>
</div>
<span class="chip-sub">39k</span>
</div>
</div>
<div id="chipOpticR" class="region-chip" style="border-left: 3px solid var(--accent-cyan);">
<div class="chip-header">
<div class="chip-title-wrap">
<div class="chip-dot" style="background:var(--accent-cyan);"></div>
<span>Optic Right</span>
</div>
<span class="chip-sub">39k</span>
</div>
</div>
<div id="chipCX" class="region-chip" style="border-left: 3px solid var(--accent-purple);">
<div class="chip-header">
<div class="chip-title-wrap">
<div class="chip-dot" style="background:var(--accent-purple);"></div>
<span>Central Complex</span>
</div>
<span class="chip-sub">2.8k</span>
</div>
</div>
<div id="chipKC" class="region-chip" style="border-left: 3px solid var(--accent-pink);">
<div class="chip-header">
<div class="chip-title-wrap">
<div class="chip-dot" style="background:var(--accent-pink);"></div>
<span>Mushroom Body</span>
</div>
<span class="chip-sub">5.1k</span>
</div>
</div>
<div id="chipDN" class="region-chip" style="border-left: 3px solid var(--accent-emerald);">
<div class="chip-header">
<div class="chip-title-wrap">
<div class="chip-dot" style="background:var(--accent-emerald);"></div>
<span>Descending Motor</span>
</div>
<span class="chip-sub">1.3k</span>
</div>
</div>
<div id="chipWhole" class="region-chip" style="border-left: 3px solid var(--accent-amber);">
<div class="chip-header">
<div class="chip-title-wrap">
<div class="chip-dot" style="background:var(--accent-amber);"></div>
<span>Whole Brain</span>
</div>
<span class="chip-sub">139k</span>
</div>
</div>
</div>
</div>
</div>
<script>
let ws = null;
const doomCanvas = document.getElementById("doomCanvas");
const container3D = document.getElementById("brain3DContainer");
// Real-time telemetry cache
let telemetry = {
optic_left: 0.25,
optic_right: 0.25,
central_complex: 0.20,
mushroom_body: 0.15,
descending_left: 0.22,
descending_right: 0.22,
whole_brain_firing: 0.23
};
// Regional Anatomy Colors (Hex & RGB)
const REGION_COLORS = {
optic_left: { hex: 0x0d61ff, str: "#0d61ff", label: "Optic Left" },
optic_right: { hex: 0x00faff, str: "#00faff", label: "Optic Right" },
central_complex: { hex: 0xb840ff, str: "#b840ff", label: "Central Complex" },
mushroom_body: { hex: 0xff2685, str: "#ff2685", label: "Mushroom Body" },
central_brain: { hex: 0xffbf1a, str: "#ffbf1a", label: "Central Brain" },
descending_left: { hex: 0x14ff33, str: "#14ff33", label: "Descending Left" },
descending_right: { hex: 0x14ff33, str: "#14ff33", label: "Descending Right" }
};
// Helper: Generate soft anti-aliased circular sprite for points (no harsh glare)
function createGlowSpriteTexture() {
const c = document.createElement("canvas");
c.width = 32;
c.height = 32;
const ctx = c.getContext("2d");
const grad = ctx.createRadialGradient(16, 16, 0, 16, 16, 16);
grad.addColorStop(0, "rgba(255, 255, 255, 0.9)");
grad.addColorStop(0.35, "rgba(255, 255, 255, 0.5)");
grad.addColorStop(1, "rgba(0, 0, 0, 0)");
ctx.fillStyle = grad;
ctx.fillRect(0, 0, 32, 32);
return new THREE.CanvasTexture(c);
}
// ==========================================
// Three.js 3D Interactive Scene Setup
// ==========================================
let scene, camera, renderer, controls;
let brainGroup = new THREE.Group();
let pointCloudObjects = {};
function init3DScene() {
const rect = container3D.getBoundingClientRect();
const w = container3D.clientWidth || 700;
const h = container3D.clientHeight || 525;
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x06080e, 0.003);
camera = new THREE.PerspectiveCamera(45, w / h, 1, 1000);
camera.position.set(0, 10, 135);
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(w, h);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
container3D.appendChild(renderer.domElement);
// OrbitControls for 360-degree mouse interaction (with defensive check)
if (typeof THREE !== "undefined" && THREE.OrbitControls) {
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.06;
controls.autoRotate = true;
controls.autoRotateSpeed = 0.6;
controls.maxDistance = 250;
controls.minDistance = 30;
} else {
console.warn("THREE.OrbitControls not found, using fallback controls");
controls = { update: () => {}, target: { set: () => {} }, autoRotate: false };
}
scene.add(brainGroup);
// Subtle lighting for crisp depth
const ambLight = new THREE.AmbientLight(0xffffff, 0.25);
scene.add(ambLight);
// Handle Resize
window.addEventListener("resize", onWindowResize);
// Reset View Button
const btnReset = document.getElementById("btnResetView");
if (btnReset) {
btnReset.onclick = () => {
camera.position.set(0, 10, 135);
if (controls && controls.target) controls.target.set(0, 0, 0);
if (controls && controls.update) controls.update();
};
}
}
function onWindowResize() {
if (!renderer || !camera || !container3D) return;
const w = container3D.clientWidth;
const h = container3D.clientHeight;
camera.aspect = w / h;
camera.updateProjectionMatrix();
renderer.setSize(w, h);
}
// 100% Deterministic 7-Region Drosophila Connectome - Pure Direct Model Readout (ZERO IF-ELSE!)
const regionPoints = {};
// Continuous 60fps tracking of the PyTorch FlyWire model firing rates
let targetOptL = 0.02, liveOptL = 0.02;
let targetOptR = 0.02, liveOptR = 0.02;
let targetCX = 0.02, liveCX = 0.02;
let targetKC = 0.02, liveKC = 0.02;
let targetDN = 0.02, liveDN = 0.02;
let targetWhole = 0.05, liveWhole = 0.05;
// Helper: generate soft circular radial glow sprite for point particles
function createGlowDotTexture() {
const canvas = document.createElement("canvas");
canvas.width = 64;
canvas.height = 64;
const ctx = canvas.getContext("2d");
const gradient = ctx.createRadialGradient(32, 32, 0, 32, 32, 32);
gradient.addColorStop(0.0, "rgba(255, 255, 255, 1.0)");
gradient.addColorStop(0.20, "rgba(255, 255, 255, 0.85)");
gradient.addColorStop(0.45, "rgba(255, 255, 255, 0.35)");
gradient.addColorStop(0.75, "rgba(255, 255, 255, 0.08)");
gradient.addColorStop(1.0, "rgba(255, 255, 255, 0.0)");
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 64, 64);
const texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
return texture;
}
function loadWholeConnectome139k() {
const ldr = document.getElementById("brain3DLoading");
Promise.all([
fetch("/brain_139k_pos.bin").then(r => {
if (!r.ok) throw new Error("Position binary fetch HTTP " + r.status);
return r.arrayBuffer();
}),
fetch("/brain_139k_col.bin").then(r => {
if (!r.ok) throw new Error("Color binary fetch HTTP " + r.status);
return r.arrayBuffer();
})
]).then(([posBuf, colBuf]) => {
const positions = new Float32Array(posBuf);
const rawColors = new Uint8Array(colBuf);
const nPoints = Math.floor(positions.length / 3);
// Pre-allocate typed arrays for all 7 anatomical circuits
const counts = {
opticL: 39063,
opticR: 38478,
cx: 2875,
kc: 5177,
dn: 1303,
cb: 24331,
in: 28021
};
const buffers = {
opticL: new Float32Array(counts.opticL * 3),
opticR: new Float32Array(counts.opticR * 3),
cx: new Float32Array(counts.cx * 3),
kc: new Float32Array(counts.kc * 3),
dn: new Float32Array(counts.dn * 3),
cb: new Float32Array(counts.cb * 3),
in: new Float32Array(counts.in * 3)
};
const ptrs = { opticL: 0, opticR: 0, cx: 0, kc: 0, dn: 0, cb: 0, in: 0 };
for (let i = 0; i < nPoints; i++) {
const p = i * 3;
const x = positions[p], y = positions[p + 1], z = positions[p + 2];
const r = rawColors[p], g = rawColors[p + 1], b = rawColors[p + 2];
let buf, key;
// 1. Optic Left: Sapphire Blue #0d61ff (RGB 0, 245, 212 in source binary)
if (r === 0 && g === 245 && b === 212) {
buf = buffers.opticL; key = "opticL";
}
// 2. Optic Right: Ice Cyan #00faff (RGB 56, 189, 248 in source binary)
else if (r === 56 && g === 189 && b === 248) {
buf = buffers.opticR; key = "opticR";
}
// 3. Central Complex: Cosmic Violet #b840ff (RGB 192, 132, 252 in source binary)
else if (r === 192 && g === 132 && b === 252) {
buf = buffers.cx; key = "cx";
}
// 4. Mushroom Body: Hot Magenta Pink #ff2685 (RGB 244, 63, 94 in source binary)
else if (r === 244 && g === 63 && b === 94) {
buf = buffers.kc; key = "kc";
}
// 5. Descending Motor: Laser Lime Green #14ff33 (RGB 16, 185, 129 in source binary)
else if (r === 16 && g === 185 && b === 129) {
buf = buffers.dn; key = "dn";
}
// 6. Central Brain: Golden Amber #ffbf1a (RGB 251, 191, 36 in source binary)
else if (r === 251 && g === 191 && b === 36) {
buf = buffers.cb; key = "cb";
}
// 7. Structural Interneurons: Deep Muted Slate #475569
else {
buf = buffers.in; key = "in";
}
const offset = ptrs[key];
buf[offset] = x;
buf[offset + 1] = y;
buf[offset + 2] = z;
ptrs[key] += 3;
}
const glowTex = createGlowDotTexture();
// Finely tuned Additive Blending: Transparent, glowing holographic baseline (No white blowout!)
const regionSpecs = [
{ key: "opticL", color: 0x0d61ff, size: 0.70, opacity: 0.05, buf: buffers.opticL }, // Sapphire Blue
{ key: "opticR", color: 0x00faff, size: 0.70, opacity: 0.05, buf: buffers.opticR }, // Ice Cyan
{ key: "cx", color: 0xb840ff, size: 1.15, opacity: 0.12, buf: buffers.cx }, // Cosmic Violet
{ key: "kc", color: 0xff2685, size: 1.10, opacity: 0.12, buf: buffers.kc }, // Magenta Pink
{ key: "dn", color: 0x14ff33, size: 1.25, opacity: 0.15, buf: buffers.dn }, // Laser Lime Green
{ key: "cb", color: 0xffd000, size: 0.85, opacity: 0.08, buf: buffers.cb }, // Warm Amber
{ key: "in", color: 0x475569, size: 0.50, opacity: 0.02, buf: buffers.in } // Slate Scaffold
];
regionSpecs.forEach(spec => {
const geom = new THREE.BufferGeometry();
geom.setAttribute("position", new THREE.BufferAttribute(spec.buf, 3));
const mat = new THREE.PointsMaterial({
color: spec.color,
size: spec.size,
map: glowTex,
transparent: true,
opacity: spec.opacity,
blending: THREE.AdditiveBlending,
depthWrite: false,
sizeAttenuation: true
});
const pts = new THREE.Points(geom, mat);
brainGroup.add(pts);
regionPoints[spec.key] = pts;
});
console.log("Loaded all 139,248 FlyWire neurons: Pure direct model visualizer ready!");
if (ldr) ldr.style.display = "none";
// Enable interactive click-to-test on region legend chips
const chipBinds = [
{ id: "chipOpticL", trigger: () => { targetOptL = 1.0; } },
{ id: "chipOpticR", trigger: () => { targetOptR = 1.0; } },
{ id: "chipCX", trigger: () => { targetCX = 1.0; } },
{ id: "chipKC", trigger: () => { targetKC = 1.0; } },
{ id: "chipDN", trigger: () => { targetDN = 1.0; } },
{ id: "chipWhole", trigger: () => { targetWhole = 1.0; } }
];
chipBinds.forEach(b => {
const el = document.getElementById(b.id);
if (el) {
el.style.cursor = "pointer";
el.onclick = b.trigger;
}
});
}).catch(err => {
console.error("Could not load 139k connectome binary", err);
if (ldr) {
ldr.innerText = "Connectome Load Failed: " + err.message;
ldr.style.color = "var(--accent-danger)";
}
});
}
// Pre-cached DOM element references (eliminates 15 expensive DOM queries per frame)
const elStep = document.getElementById("stepCounter");
const elHealth = document.getElementById("hudHealth");
const elAmmo = document.getElementById("hudAmmo");
const elKills = document.getElementById("hudKills");
const elScore = document.getElementById("hudScore");
const elBarLeft = document.getElementById("barLeft");
const elPctLeft = document.getElementById("pctLeft");
const elBarRight = document.getElementById("barRight");
const elPctRight = document.getElementById("pctRight");
const elBarAtk = document.getElementById("barAttack");
const elPctAtk = document.getElementById("pctAttack");
// Decoupled packet buffer for buttery-smooth 60fps rendering without layout thrashing
let latestPacket = null;
let hasNewPacket = false;
// 3D Animation Render Loop (60 FPS WebGL)
function animate3D() {
requestAnimationFrame(animate3D);
if (controls && controls.update) {
if (controls.autoRotate !== undefined) {
controls.autoRotate = false;
}
controls.update();
}
// Smooth 60fps exponential tracking of genuine PyTorch model activations
liveOptL += (targetOptL - liveOptL) * 0.25;
liveOptR += (targetOptR - liveOptR) * 0.25;
liveCX += (targetCX - liveCX) * 0.25;
liveKC += (targetKC - liveKC) * 0.25;
liveDN += (targetDN - liveDN) * 0.25;
liveWhole += (targetWhole - liveWhole) * 0.20;
// 3D Point Cloud Materials: Pure 1-to-1 analog readout of PyTorch model neurons (ZERO IF-ELSE!)
if (regionPoints.opticL) {
regionPoints.opticL.material.size = 0.70 + liveOptL * 1.5;
regionPoints.opticL.material.opacity = 0.05 + liveOptL * 0.32;
}
if (regionPoints.opticR) {
regionPoints.opticR.material.size = 0.70 + liveOptR * 1.5;
regionPoints.opticR.material.opacity = 0.05 + liveOptR * 0.32;
}
if (regionPoints.cx) {
regionPoints.cx.material.size = 1.15 + liveCX * 1.6;
regionPoints.cx.material.opacity = 0.12 + liveCX * 0.35;
}
if (regionPoints.kc) {
regionPoints.kc.material.size = 1.10 + liveKC * 1.5;
regionPoints.kc.material.opacity = 0.12 + liveKC * 0.35;
}
if (regionPoints.dn) {
regionPoints.dn.material.size = 1.25 + liveDN * 1.8;
regionPoints.dn.material.opacity = 0.15 + liveDN * 0.40;
}
if (regionPoints.cb) {
regionPoints.cb.material.size = 0.85 + liveWhole * 1.5;
regionPoints.cb.material.opacity = 0.08 + liveWhole * 0.35;
}
if (regionPoints.in) {
regionPoints.in.material.size = 0.55 + liveWhole * 0.6;
regionPoints.in.material.opacity = 0.02 + liveWhole * 0.10;
}
// Process latest game frame on browser display sync (avoids frame bursts and decoder stalls)
if (hasNewPacket && latestPacket) {
hasNewPacket = false;
const data = latestPacket;
if (data.frame_b64 && data.frame_b64.length > 50) {
doomCanvas.src = 'data:image/jpeg;base64,' + data.frame_b64;
}
if (elStep) elStep.innerText = "Step: " + (data.step || 0);
if (elHealth) elHealth.innerText = data.health || 0;
if (elAmmo) elAmmo.innerText = data.ammo || 0;
if (elKills) elKills.innerText = data.kills || 0;
if (elScore) elScore.innerText = (data.total_reward || 0).toFixed(1);
if (data.action_probs) {
const pLeft = Math.round(((data.action_probs.TURN_LEFT !== undefined ? data.action_probs.TURN_LEFT : data.action_probs.MOVE_LEFT) || 0) * 100);
const pRight = Math.round(((data.action_probs.TURN_RIGHT !== undefined ? data.action_probs.TURN_RIGHT : data.action_probs.MOVE_RIGHT) || 0) * 100);
const pAtk = Math.round((data.action_probs.ATTACK || 0) * 100);
if (elBarLeft) elBarLeft.style.width = pLeft + "%";
if (elPctLeft) elPctLeft.innerText = pLeft + "%";
if (elBarRight) elBarRight.style.width = pRight + "%";
if (elPctRight) elPctRight.innerText = pRight + "%";
if (elBarAtk) elBarAtk.style.width = pAtk + "%";
if (elPctAtk) elPctAtk.innerText = pAtk + "%";
}
if (data.telemetry) {
const optL = data.telemetry.optic_left || 0.02;
const optR = data.telemetry.optic_right || 0.02;
const cx = data.telemetry.central_complex || 0.02;
const kc = data.telemetry.mushroom_body || 0.02;
const dnL = data.telemetry.descending_left || 0.02;
const dnR = data.telemetry.descending_right || 0.02;
const dnAvg = (dnL + dnR) / 2;
const whole = data.telemetry.whole_brain_firing || 0.05;
targetOptL = optL;
targetOptR = optR;
targetCX = cx;
targetKC = kc;
targetDN = dnAvg;
targetWhole = whole;
}
}
if (renderer && scene && camera) {
renderer.render(scene, camera);
}
}
// Initialize 3D Engine & Load Connectome independently
try { init3DScene(); } catch (err) { console.error("init3DScene error:", err); }
try { loadWholeConnectome139k(); } catch (err) { console.error("loadWholeConnectome139k error:", err); }
try { animate3D(); } catch (err) { console.error("animate3D error:", err); }
// ==========================================
// Live WebSocket Stream (ViZDoom + Telemetry)
// ==========================================
function connectWebSocket() {
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
const wsUrl = protocol + "//" + window.location.host + "/ws/game";
ws = new WebSocket(wsUrl);
ws.onopen = () => {
const cs = document.getElementById("connStatus");
if (cs) {
cs.innerHTML = '<div class="status-dot"></div><span>LIVE CONNECTOME</span>';
cs.style.color = 'var(--accent-emerald)';
}
};
ws.onclose = () => {
const cs = document.getElementById("connStatus");
if (cs) {
cs.innerHTML = '<div class="status-dot" style="background:var(--accent-danger); box-shadow:none;"></div><span>RECONNECTING...</span>';
cs.style.color = 'var(--accent-danger)';
}
setTimeout(connectWebSocket, 2000);
};
ws.onmessage = (event) => {
try {
latestPacket = JSON.parse(event.data);
hasNewPacket = true;
} catch (err) {
console.error("Packet parse error:", err);
}
};
}
// Connect WebSocket stream immediately (zero delay, fully decoupled)
connectWebSocket();
</script>
</body>
</html>