Spaces:
Running on Zero
Running on Zero
Download web/index.html from emiraran/doom-flywire: direct link, hf CLI and curl.
- Browser
- Download file 33.7 kB
-
https://huggingface.co/spaces/emiraran/doom-flywire/resolve/main/web/index.html
- Command line
-
hf download hf://spaces/emiraran/doom-flywire/web/index.html
-
curl -L -o index.html https://huggingface.co/spaces/emiraran/doom-flywire/resolve/main/web/index.html
33.7 kB
| <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% ; | |
| height: 100% ; | |
| 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> | |