Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>Neuraxon v2.0 Network Builder β By David Vivancos for Qubic Open Science - Artificiology Research </title> | |
| <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600;700&family=DM+Sans:wght@400;500;600;700&display=swap" rel="stylesheet"> | |
| <style> | |
| :root { | |
| --bg-deep: #06090f; | |
| --bg-panel: #0c1220; | |
| --bg-card: #111a2e; | |
| --bg-card-hover: #162040; | |
| --border: #1e2d4a; | |
| --border-active: #2e5daa; | |
| --text: #c8d6e5; | |
| --text-dim: #5a6f8a; | |
| --text-bright: #e8f0fe; | |
| --accent: #6fd0ff; | |
| --accent2: #7affff; | |
| --accent-glow: rgba(79,172,254,0.25); | |
| --success: #00e676; | |
| --danger: #ff5252; | |
| --warning: #ffc107; | |
| --purple: #d4a3ff; | |
| --orange: #ff9100; | |
| --pink: #ff7ab8; | |
| --teal: #4fffd1; | |
| --radius: 10px; | |
| --font-mono: 'JetBrains Mono', monospace; | |
| --font-body: 'DM Sans', sans-serif; | |
| } | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| html, body { | |
| width: 100%; height: 100%; | |
| font-family: var(--font-body); | |
| background: var(--bg-deep); | |
| color: var(--text); | |
| overflow: hidden; | |
| } | |
| ::-webkit-scrollbar { width: 6px; } | |
| ::-webkit-scrollbar-track { background: var(--bg-deep); } | |
| ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } | |
| ::-webkit-scrollbar-thumb:hover { background: var(--accent); } | |
| #main-container { width: 100%; height: 100%; display: flex; flex-direction: column; } | |
| /* ββ Config Panel ββ */ | |
| #config-panel { | |
| flex: 1; overflow-y: auto; | |
| background: var(--bg-deep); | |
| padding: 2em 3em; | |
| } | |
| #config-panel.hidden { display: none; } | |
| .config-header { | |
| text-align: center; padding: 1.5em 0 2em; | |
| border-bottom: 1px solid var(--border); | |
| margin-bottom: 2em; | |
| } | |
| .config-header h1 { | |
| font-size: 2.2em; font-weight: 700; | |
| background: linear-gradient(135deg, var(--accent), var(--accent2)); | |
| -webkit-background-clip: text; -webkit-text-fill-color: transparent; | |
| letter-spacing: -0.02em; | |
| } | |
| .config-header h1 a { -webkit-text-fill-color: inherit; text-decoration: none; } | |
| .config-header .subtitle { | |
| font-size: 0.95em; color: var(--text-dim); margin-top: 0.4em; | |
| } | |
| .config-header .subtitle a { color: var(--accent); text-decoration: none; } | |
| .config-header .subtitle a:hover { text-decoration: underline; } | |
| .config-header .version-badge { | |
| display: inline-block; margin-top: 0.6em; | |
| padding: 0.25em 0.8em; border-radius: 20px; | |
| background: rgba(79,172,254,0.12); border: 1px solid rgba(79,172,254,0.25); | |
| font-family: var(--font-mono); font-size: 0.75em; color: var(--accent); | |
| letter-spacing: 0.05em; | |
| } | |
| .pipeline-banner { | |
| display: flex; align-items: center; justify-content: center; | |
| gap: 0.5em; padding: 0.8em 1.5em; margin-bottom: 2em; | |
| background: linear-gradient(135deg, rgba(79,172,254,0.06), rgba(0,242,254,0.04)); | |
| border: 1px solid rgba(79,172,254,0.12); border-radius: var(--radius); | |
| font-family: var(--font-mono); font-size: 0.78em; color: var(--text-dim); | |
| } | |
| .pipeline-banner .step { color: var(--accent); font-weight: 600; } | |
| .pipeline-banner .arrow { color: var(--text-dim); opacity: 0.5; } | |
| /* ββ Section cards ββ */ | |
| .param-section { | |
| background: var(--bg-card); | |
| border: 1px solid var(--border); | |
| border-radius: var(--radius); | |
| padding: 1.5em; | |
| margin-bottom: 1.2em; | |
| transition: border-color 0.2s; | |
| } | |
| .param-section:hover { border-color: var(--border-active); } | |
| .section-header { | |
| display: flex; align-items: center; gap: 0.6em; | |
| margin-bottom: 1em; cursor: pointer; user-select: none; | |
| } | |
| .section-header .icon { | |
| width: 32px; height: 32px; border-radius: 8px; | |
| display: flex; align-items: center; justify-content: center; | |
| font-size: 1em; flex-shrink: 0; | |
| } | |
| .section-header h2 { | |
| font-size: 1em; font-weight: 600; color: var(--text-bright); | |
| letter-spacing: -0.01em; | |
| } | |
| .section-header .toggle-arrow { | |
| margin-left: auto; font-size: 0.7em; color: var(--text-dim); | |
| transition: transform 0.2s; | |
| } | |
| .section-content { display: grid; gap: 0.8em; } | |
| .section-content.collapsed { display: none; } | |
| /* ββ Param rows ββ */ | |
| .param-group { } | |
| .param-label { | |
| display: flex; justify-content: space-between; align-items: baseline; | |
| margin-bottom: 0.3em; | |
| } | |
| .param-label label { | |
| font-size: 0.82em; font-weight: 500; color: var(--text); | |
| } | |
| .param-label .value-display { | |
| font-family: var(--font-mono); font-size: 0.78em; font-weight: 600; | |
| color: var(--accent); background: rgba(79,172,254,0.08); | |
| padding: 0.15em 0.5em; border-radius: 4px; | |
| min-width: 50px; text-align: center; | |
| } | |
| .param-description { | |
| font-size: 0.72em; color: var(--text-dim); margin-bottom: 0.3em; | |
| } | |
| input[type="range"] { | |
| width: 100%; height: 4px; border-radius: 2px; | |
| background: var(--border); outline: none; | |
| -webkit-appearance: none; margin: 0.3em 0; | |
| } | |
| input[type="range"]::-webkit-slider-thumb { | |
| -webkit-appearance: none; width: 14px; height: 14px; | |
| border-radius: 50%; background: var(--accent); | |
| cursor: pointer; box-shadow: 0 0 8px var(--accent-glow); | |
| transition: box-shadow 0.15s; | |
| } | |
| input[type="range"]::-webkit-slider-thumb:hover { | |
| box-shadow: 0 0 16px var(--accent-glow); | |
| } | |
| input[type="text"], input[type="number"] { | |
| width: 100%; padding: 0.5em 0.7em; | |
| background: var(--bg-deep); border: 1px solid var(--border); | |
| border-radius: 6px; color: var(--text-bright); | |
| font-family: var(--font-body); font-size: 0.88em; | |
| } | |
| input[type="text"]:focus, input[type="number"]:focus { | |
| outline: none; border-color: var(--accent); | |
| box-shadow: 0 0 0 2px var(--accent-glow); | |
| } | |
| /* ββ Toggle switches ββ */ | |
| .toggle-row { | |
| display: flex; align-items: center; justify-content: space-between; | |
| padding: 0.4em 0; | |
| } | |
| .toggle-row label { font-size: 0.82em; font-weight: 500; } | |
| .toggle-switch { | |
| position: relative; width: 38px; height: 20px; flex-shrink: 0; | |
| } | |
| .toggle-switch input { opacity: 0; width: 0; height: 0; } | |
| .toggle-slider { | |
| position: absolute; top: 0; left: 0; right: 0; bottom: 0; | |
| background: var(--border); border-radius: 20px; | |
| cursor: pointer; transition: 0.2s; | |
| } | |
| .toggle-slider::before { | |
| content: ''; position: absolute; height: 14px; width: 14px; | |
| left: 3px; bottom: 3px; background: var(--text-dim); | |
| border-radius: 50%; transition: 0.2s; | |
| } | |
| .toggle-switch input:checked + .toggle-slider { background: var(--accent); } | |
| .toggle-switch input:checked + .toggle-slider::before { | |
| transform: translateX(18px); background: #fff; | |
| } | |
| /* ββ Two-col grid for sub params ββ */ | |
| .two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 0.8em 1.2em; } | |
| /* ββ Buttons ββ */ | |
| button, .file-upload-label { | |
| padding: 0.6em 1.2em; border: none; border-radius: 6px; | |
| font-family: var(--font-body); font-weight: 600; font-size: 0.85em; | |
| cursor: pointer; transition: all 0.15s; display: inline-flex; | |
| align-items: center; gap: 0.4em; | |
| } | |
| .btn-primary { | |
| background: linear-gradient(135deg, var(--accent), var(--accent2)); | |
| color: #000; box-shadow: 0 4px 15px rgba(79,172,254,0.3); | |
| } | |
| .btn-primary:hover { transform: translateY(-1px); box-shadow: 0 6px 20px rgba(79,172,254,0.4); } | |
| .btn-success { background: var(--success); color: #000; } | |
| .btn-danger { background: var(--danger); color: #fff; } | |
| .btn-warning { background: var(--warning); color: #000; } | |
| .btn-secondary { background: var(--bg-card); color: var(--text); border: 1px solid var(--border); } | |
| .btn-secondary:hover { border-color: var(--accent); color: var(--accent); } | |
| .btn-ghost { background: transparent; color: var(--text-dim); border: 1px solid var(--border); } | |
| .btn-ghost:hover { border-color: var(--accent); color: var(--accent); } | |
| #build-network-btn { | |
| width: 100%; padding: 1em; font-size: 1.1em; margin-top: 1.5em; | |
| border-radius: var(--radius); | |
| } | |
| .button-row { display: flex; flex-wrap: wrap; gap: 0.5em; margin-top: 0.8em; } | |
| .preset-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 0.5em; margin-top: 0.8em; } | |
| .preset-btn { font-size: 0.78em; padding: 0.5em 0.8em; } | |
| .file-upload-label { | |
| background: var(--warning); color: #000; | |
| } | |
| .file-upload-label:hover { opacity: 0.9; } | |
| /* ββ Viz Container ββ */ | |
| #viz-container { display: none; flex: 1; flex-direction: row; } | |
| #viz-container.visible { display: flex; } | |
| #canvas-container { | |
| flex: 1; position: relative; | |
| background: radial-gradient(ellipse at center, #0f1f38 0%, #06090f 85%); | |
| min-width: 0; | |
| } | |
| #networkCanvas { display: block; width: 100%; height: 100%; } | |
| #controls { | |
| position: relative; flex: 0 1 360px; min-width: 280px; max-width: 500px; | |
| background: var(--bg-panel); padding: 1.2em; | |
| overflow-y: auto; border-left: 1px solid var(--border); | |
| } | |
| #controls h2 { | |
| font-size: 0.85em; font-weight: 600; color: var(--text-dim); | |
| text-transform: uppercase; letter-spacing: 0.08em; | |
| margin: 1em 0 0.5em; padding-bottom: 0.3em; | |
| border-bottom: 1px solid var(--border); | |
| } | |
| /* ββ HUD overlay ββ */ | |
| #info { | |
| position: absolute; top: 12px; left: 12px; | |
| background: rgba(6,9,15,0.85); backdrop-filter: blur(8px); | |
| color: var(--text); padding: 12px 16px; border-radius: var(--radius); | |
| border: 1px solid var(--border); font-family: var(--font-mono); | |
| font-size: 11px; max-width: 340px; z-index: 2; | |
| line-height: 1.7; | |
| } | |
| #info strong { color: var(--accent); } | |
| #info a { color: var(--accent); text-decoration: none; } | |
| #info .stat-label { color: var(--text-dim); } | |
| #info .stat-value { color: var(--text-bright); font-weight: 600; } | |
| #info .energy-bar { | |
| display: inline-block; width: 60px; height: 4px; background: var(--border); | |
| border-radius: 2px; vertical-align: middle; margin-left: 4px; | |
| overflow: hidden; | |
| } | |
| #info .energy-fill { | |
| height: 100%; background: linear-gradient(90deg, var(--accent), var(--accent2)); | |
| border-radius: 2px; transition: width 0.3s; | |
| } | |
| /* ββ Pipeline indicator on viz ββ */ | |
| #pipeline-indicator { | |
| position: absolute; bottom: 12px; left: 12px; | |
| display: flex; gap: 4px; z-index: 2; | |
| } | |
| .pip-step { | |
| padding: 3px 8px; border-radius: 4px; font-family: var(--font-mono); | |
| font-size: 9px; font-weight: 600; letter-spacing: 0.03em; | |
| background: rgba(6,9,15,0.7); border: 1px solid var(--border); | |
| color: var(--text-dim); transition: all 0.2s; | |
| } | |
| .pip-step.active { border-color: var(--accent); color: var(--accent); background: rgba(79,172,254,0.1); } | |
| /* ββ Legend ββ */ | |
| .legend { | |
| background: var(--bg-card); border: 1px solid var(--border); | |
| padding: 0.8em; border-radius: var(--radius); margin-top: 1em; | |
| } | |
| .legend strong { font-size: 0.8em; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.06em; } | |
| .legend-item { display: flex; align-items: center; margin: 0.35em 0; font-size: 0.78em; } | |
| .legend-color { width: 14px; height: 14px; margin-right: 0.5em; border-radius: 3px; flex-shrink: 0; } | |
| /* ββ Input buttons ββ */ | |
| .input-states { display: grid; grid-template-columns: repeat(auto-fill, minmax(60px, 1fr)); gap: 0.4em; margin: 0.5em 0; } | |
| .input-btn { | |
| padding: 0.5em; border-radius: 6px; border: 1px solid var(--border); | |
| background: var(--bg-card); color: var(--text); | |
| cursor: pointer; font-weight: 600; font-size: 0.78em; | |
| font-family: var(--font-mono); transition: all 0.12s; | |
| } | |
| .input-btn.state-excitatory { background: rgba(0,230,118,0.15); border-color: var(--success); color: var(--success); } | |
| .input-btn.state-inhibitory { background: rgba(255,82,82,0.15); border-color: var(--danger); color: var(--danger); } | |
| .input-btn.state-neutral { background: var(--bg-card); border-color: var(--border); color: var(--text-dim); } | |
| .simulation-running { background: rgba(0,230,118,0.2) ; border-color: var(--success) ; } | |
| /* ββ Neuromod live sliders ββ */ | |
| .neuromod-group { margin-bottom: 0.5em; } | |
| .neuromod-label { | |
| display: flex; align-items: center; gap: 0.4em; | |
| font-size: 0.78em; font-weight: 500; margin-bottom: 0.2em; | |
| } | |
| .neuromod-label .nm-icon { font-size: 1.1em; } | |
| .neuromod-label .nm-val { | |
| margin-left: auto; font-family: var(--font-mono); font-size: 0.85em; | |
| color: var(--accent); font-weight: 600; | |
| } | |
| /* ββ Receptor display ββ */ | |
| .receptor-grid { | |
| display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; | |
| margin-top: 0.5em; | |
| } | |
| .receptor-chip { | |
| padding: 3px 6px; border-radius: 4px; font-family: var(--font-mono); | |
| font-size: 9px; text-align: center; | |
| background: var(--bg-deep); border: 1px solid var(--border); | |
| color: var(--text-dim); | |
| } | |
| .receptor-chip .r-name { font-weight: 600; color: var(--text); display: block; } | |
| .receptor-chip .r-val { color: var(--accent); } | |
| /* ββ Oscillator viz ββ */ | |
| .osc-bars { | |
| display: flex; align-items: flex-end; gap: 3px; height: 30px; margin-top: 0.5em; | |
| } | |
| .osc-bar { | |
| flex: 1; border-radius: 2px 2px 0 0; | |
| background: linear-gradient(to top, var(--accent), var(--accent2)); | |
| transition: height 0.15s; min-height: 2px; opacity: 0.7; | |
| } | |
| /* ββ Loading overlay ββ */ | |
| #loading-overlay { | |
| display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; | |
| background: rgba(6,9,15,0.92); z-index: 9999; | |
| justify-content: center; align-items: center; flex-direction: column; | |
| } | |
| #loading-overlay.visible { display: flex; } | |
| .spinner { | |
| width: 50px; height: 50px; border: 3px solid var(--border); | |
| border-top: 3px solid var(--accent); border-radius: 50%; | |
| animation: spin 0.8s linear infinite; | |
| } | |
| @keyframes spin { to { transform: rotate(360deg); } } | |
| .loading-text { color: var(--text-dim); font-size: 0.9em; margin-top: 1em; font-family: var(--font-mono); } | |
| @media (max-width: 900px) { | |
| #config-panel { padding: 1em; } | |
| #viz-container { flex-direction: column; } | |
| #canvas-container { height: 50vh; } | |
| #controls { flex: 0 0 auto; width: 100% ; max-width: 100%; height: 50vh; } | |
| .two-col { grid-template-columns: 1fr; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="main-container"> | |
| <!-- βββββββββββββββ CONFIGURATION PANEL βββββββββββββββ --> | |
| <div id="config-panel"> | |
| <div class="config-header"> | |
| <h1><a href="https://www.researchgate.net/publication/400868863_Neuraxon_V20_A_New_Neural_Growth_Computation_Blueprint" target="_blank">π§ Neuraxon v2.0</a></h1> | |
| <div class="subtitle"> | |
| By <a href="https://vivancos.com/" target="_blank">David Vivancos</a> & | |
| <a href="https://josesanchezgarcia.com/" target="_blank">Jose Sanchez</a> β | |
| <a href="https://qubic.org/" target="_blank">Qubic Open Science</a> & | |
| Artificiology Research, UNIR | |
| </div> | |
| <div class="version-badge">Build Your Bio-Inspired Neural Network based on Neuraxon 2.0 paper & Qubic's Aigarth Intelligent Tissue</div> | |
| </div> | |
| <div class="pipeline-banner"> | |
| <span class="step">β Time Warping</span><span class="arrow">β</span> | |
| <span class="step">β‘ Dynamic Decay</span><span class="arrow">β</span> | |
| <span class="step">β’ CTSN</span><span class="arrow">β</span> | |
| <span class="step">β£ AGMP</span> | |
| </div> | |
| <!-- NETWORK IDENTITY & SAVE/LOAD --> | |
| <div class="param-section"> | |
| <div class="section-header" onclick="toggleSection(this)"> | |
| <div class="icon" style="background:rgba(79,172,254,0.12);color:var(--accent);">πΎ</div> | |
| <h2>Network Identity & Management</h2> | |
| <span class="toggle-arrow">βΌ</span> | |
| </div> | |
| <div class="section-content"> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Network Name</label></div> | |
| <input type="text" id="network_name" value="Neuraxon v2.0 Net"> | |
| </div> | |
| <div class="button-row"> | |
| <button class="btn-success" onclick="saveParameters()">πΎ Save Params</button> | |
| <label for="load-file" class="file-upload-label">π€ Load Params</label> | |
| <input type="file" id="load-file" accept=".json" style="display:none" onchange="loadParameters(this)"> | |
| <button class="btn-warning" onclick="saveFullModel()">π¦ Save Model</button> | |
| <label for="load-model-file" class="file-upload-label" style="background:var(--orange);">π₯ Load Model</label> | |
| <input type="file" id="load-model-file" accept=".json" style="display:none" onchange="loadFullModel(this)"> | |
| </div> | |
| <div class="preset-grid" style="margin-top:0.8em;"> | |
| <button class="btn-ghost preset-btn" onclick="loadPreset('default')">π Default</button> | |
| <button class="btn-ghost preset-btn" onclick="loadPreset('small')">π¬ Small</button> | |
| <button class="btn-ghost preset-btn" onclick="loadPreset('large')">π Large</button> | |
| <button class="btn-ghost preset-btn" onclick="loadPreset('chrono')">β± Chrono</button> | |
| <button class="btn-ghost preset-btn" onclick="loadPreset('homeostatic')">β Homeostatic</button> | |
| <button class="btn-ghost preset-btn" onclick="loadPreset('fast')">β‘ Fast</button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- NETWORK ARCHITECTURE --> | |
| <div class="param-section"> | |
| <div class="section-header" onclick="toggleSection(this)"> | |
| <div class="icon" style="background:rgba(0,242,254,0.12);color:var(--accent2);">π</div> | |
| <h2>Network Architecture</h2> | |
| <span class="toggle-arrow">βΌ</span> | |
| </div> | |
| <div class="section-content"> | |
| <div class="two-col"> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Input Neurons</label><span class="value-display" id="val-num_input_neurons">5</span></div> | |
| <input type="range" id="num_input_neurons" min="1" max="10" value="5" step="1"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Hidden Neurons</label><span class="value-display" id="val-num_hidden_neurons">20</span></div> | |
| <input type="range" id="num_hidden_neurons" min="1" max="100" value="20" step="1"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Output Neurons</label><span class="value-display" id="val-num_output_neurons">5</span></div> | |
| <input type="range" id="num_output_neurons" min="1" max="10" value="5" step="1"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Dendritic Branches</label><span class="value-display" id="val-num_dendritic_branches">3</span></div> | |
| <input type="range" id="num_dendritic_branches" min="1" max="8" value="3" step="1"> | |
| </div> | |
| </div> | |
| <div class="two-col"> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Dendritic Spike Threshold</label><span class="value-display" id="val-dendritic_spike_threshold">0.40</span></div> | |
| <input type="range" id="dendritic_spike_threshold" min="0.1" max="1.0" value="0.4" step="0.05"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Supralinear Gamma</label><span class="value-display" id="val-dendritic_supralinear_gamma">1.30</span></div> | |
| <input type="range" id="dendritic_supralinear_gamma" min="1.0" max="2.5" value="1.3" step="0.05"> | |
| </div> | |
| </div> | |
| <h3 style="font-size:0.82em;color:var(--text-dim);margin-top:0.8em;margin-bottom:0.4em;">Watts-Strogatz Small-World Topology</h3> | |
| <div class="two-col"> | |
| <div class="param-group"> | |
| <div class="param-label"><label>WS k (neighbors)</label><span class="value-display" id="val-ws_k">6</span></div> | |
| <input type="range" id="ws_k" min="2" max="20" value="6" step="1"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>WS Ξ² (rewire prob)</label><span class="value-display" id="val-ws_beta">0.30</span></div> | |
| <input type="range" id="ws_beta" min="0" max="1" value="0.3" step="0.05"> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- NEURON PARAMETERS --> | |
| <div class="param-section"> | |
| <div class="section-header" onclick="toggleSection(this)"> | |
| <div class="icon" style="background:rgba(179,136,255,0.12);color:var(--purple);">β‘</div> | |
| <h2>Neuron Parameters</h2> | |
| <span class="toggle-arrow">βΌ</span> | |
| </div> | |
| <div class="section-content"> | |
| <div class="two-col"> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Membrane Ο (ms)</label><span class="value-display" id="val-membrane_time_constant">20.0</span></div> | |
| <input type="range" id="membrane_time_constant" min="5" max="50" value="20" step="0.5"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Excitatory ΞΈ</label><span class="value-display" id="val-firing_threshold_excitatory">0.40</span></div> | |
| <input type="range" id="firing_threshold_excitatory" min="0.1" max="2.0" value="0.4" step="0.05"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Inhibitory ΞΈ</label><span class="value-display" id="val-firing_threshold_inhibitory">-0.40</span></div> | |
| <input type="range" id="firing_threshold_inhibitory" min="-2.0" max="-0.1" value="-0.4" step="0.05"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Adaptation Ο</label><span class="value-display" id="val-adaptation_tau">100.0</span></div> | |
| <input type="range" id="adaptation_tau" min="10" max="500" value="100" step="5"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Autoreceptor Ο</label><span class="value-display" id="val-autoreceptor_tau">200.0</span></div> | |
| <input type="range" id="autoreceptor_tau" min="50" max="1000" value="200" step="10"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Spontaneous Rate</label><span class="value-display" id="val-spontaneous_firing_rate">0.020</span></div> | |
| <input type="range" id="spontaneous_firing_rate" min="0" max="0.1" value="0.02" step="0.005"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Health Decay</label><span class="value-display" id="val-neuron_health_decay">0.0010</span></div> | |
| <input type="range" id="neuron_health_decay" min="0" max="0.01" value="0.001" step="0.0001"> | |
| </div> | |
| </div> | |
| <h3 style="font-size:0.82em;color:var(--text-dim);margin-top:0.8em;margin-bottom:0.4em;">Homeostatic Plasticity (Eq 1)</h3> | |
| <div class="two-col"> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Target Firing Rate</label><span class="value-display" id="val-target_firing_rate">0.200</span></div> | |
| <input type="range" id="target_firing_rate" min="0.01" max="0.5" value="0.2" step="0.01"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Homeostatic Rate</label><span class="value-display" id="val-homeostatic_rate">0.00050</span></div> | |
| <input type="range" id="homeostatic_rate" min="0" max="0.005" value="0.0005" step="0.00005"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Firing Rate Ξ±</label><span class="value-display" id="val-firing_rate_alpha">0.010</span></div> | |
| <input type="range" id="firing_rate_alpha" min="0.001" max="0.1" value="0.01" step="0.001"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Threshold Mod k</label><span class="value-display" id="val-threshold_mod_k">0.30</span></div> | |
| <input type="range" id="threshold_mod_k" min="0" max="1" value="0.3" step="0.05"> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- MSTH --> | |
| <div class="param-section"> | |
| <div class="section-header" onclick="toggleSection(this)"> | |
| <div class="icon" style="background:rgba(29,233,182,0.12);color:var(--teal);">π</div> | |
| <h2>MSTH β Multi-Scale Temporal Homeostasis</h2> | |
| <span class="toggle-arrow">βΈ</span> | |
| </div> | |
| <div class="section-content collapsed"> | |
| <div class="param-description" style="margin-bottom:0.6em;">4 regulatory loops: Ultrafast (~5ms) β Fast (~2s) β Medium (~5min) β Slow (~1h)</div> | |
| <div class="two-col"> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Ultrafast Ο (ms)</label><span class="value-display" id="val-msth_ultrafast_tau">5.0</span></div> | |
| <input type="range" id="msth_ultrafast_tau" min="1" max="20" value="5" step="0.5"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Ultrafast Ceiling</label><span class="value-display" id="val-msth_ultrafast_ceiling">2.00</span></div> | |
| <input type="range" id="msth_ultrafast_ceiling" min="0.5" max="5" value="2" step="0.1"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Fast Ο (ms)</label><span class="value-display" id="val-msth_fast_tau">2000</span></div> | |
| <input type="range" id="msth_fast_tau" min="500" max="5000" value="2000" step="100"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Fast Gain</label><span class="value-display" id="val-msth_fast_gain">0.100</span></div> | |
| <input type="range" id="msth_fast_gain" min="0.01" max="0.5" value="0.1" step="0.01"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Medium Ο (ms)</label><span class="value-display" id="val-msth_medium_tau">300000</span></div> | |
| <input type="range" id="msth_medium_tau" min="50000" max="1000000" value="300000" step="10000"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Medium Gain</label><span class="value-display" id="val-msth_medium_gain">0.0010</span></div> | |
| <input type="range" id="msth_medium_gain" min="0.0001" max="0.01" value="0.001" step="0.0001"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Slow Ο (ms)</label><span class="value-display" id="val-msth_slow_tau">3600000</span></div> | |
| <input type="range" id="msth_slow_tau" min="1000000" max="10000000" value="3600000" step="100000"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Slow Gain</label><span class="value-display" id="val-msth_slow_gain">0.00010</span></div> | |
| <input type="range" id="msth_slow_gain" min="0.00001" max="0.001" value="0.0001" step="0.00001"> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- DSN + CTSN --> | |
| <div class="param-section"> | |
| <div class="section-header" onclick="toggleSection(this)"> | |
| <div class="icon" style="background:rgba(255,64,129,0.12);color:var(--pink);">π§¬</div> | |
| <h2>DSN Dynamic Decay & CTSN Complement</h2> | |
| <span class="toggle-arrow">βΈ</span> | |
| </div> | |
| <div class="section-content collapsed"> | |
| <h3 style="font-size:0.82em;color:var(--text-dim);margin-bottom:0.4em;">DSN β Dynamic Decay (Ξ±_t via causal conv)</h3> | |
| <div class="toggle-row"> | |
| <label>DSN Enabled</label> | |
| <label class="toggle-switch"><input type="checkbox" id="dsn_enabled" checked><span class="toggle-slider"></span></label> | |
| </div> | |
| <div class="two-col"> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Kernel Size</label><span class="value-display" id="val-dsn_kernel_size">4</span></div> | |
| <input type="range" id="dsn_kernel_size" min="2" max="16" value="4" step="1"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>DSN Bias</label><span class="value-display" id="val-dsn_bias">0.00</span></div> | |
| <input type="range" id="dsn_bias" min="-1" max="1" value="0" step="0.05"> | |
| </div> | |
| </div> | |
| <h3 style="font-size:0.82em;color:var(--text-dim);margin-top:0.8em;margin-bottom:0.4em;">CTSN β Complemented Trinary State (sΜ = s + h)</h3> | |
| <div class="toggle-row"> | |
| <label>CTSN Enabled</label> | |
| <label class="toggle-switch"><input type="checkbox" id="ctsn_enabled" checked><span class="toggle-slider"></span></label> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>CTSN Ο</label><span class="value-display" id="val-ctsn_rho">0.90</span></div> | |
| <input type="range" id="ctsn_rho" min="0" max="1" value="0.9" step="0.01"> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- SYNAPSE PARAMETERS --> | |
| <div class="param-section"> | |
| <div class="section-header" onclick="toggleSection(this)"> | |
| <div class="icon" style="background:rgba(0,230,118,0.12);color:var(--success);">π</div> | |
| <h2>Synapse Parameters</h2> | |
| <span class="toggle-arrow">βΈ</span> | |
| </div> | |
| <div class="section-content collapsed"> | |
| <div class="two-col"> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Ο Fast (ms)</label><span class="value-display" id="val-tau_fast">5.0</span></div> | |
| <input type="range" id="tau_fast" min="1" max="20" value="5" step="0.5"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Ο Slow (ms)</label><span class="value-display" id="val-tau_slow">50.0</span></div> | |
| <input type="range" id="tau_slow" min="10" max="200" value="50" step="5"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Ο Meta (ms)</label><span class="value-display" id="val-tau_meta">1000</span></div> | |
| <input type="range" id="tau_meta" min="200" max="5000" value="1000" step="50"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Ο STDP (ms)</label><span class="value-display" id="val-tau_stdp">20.0</span></div> | |
| <input type="range" id="tau_stdp" min="5" max="100" value="20" step="1"> | |
| </div> | |
| </div> | |
| <h3 style="font-size:0.82em;color:var(--text-dim);margin-top:0.6em;margin-bottom:0.4em;">Weight Init Ranges</h3> | |
| <div class="two-col"> | |
| <div class="param-group"> | |
| <div class="param-label"><label>W_fast min</label><span class="value-display" id="val-w_fast_init_min">-0.80</span></div> | |
| <input type="range" id="w_fast_init_min" min="-1" max="0" value="-0.8" step="0.05"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>W_fast max</label><span class="value-display" id="val-w_fast_init_max">0.80</span></div> | |
| <input type="range" id="w_fast_init_max" min="0" max="1" value="0.8" step="0.05"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>W_slow min</label><span class="value-display" id="val-w_slow_init_min">-0.40</span></div> | |
| <input type="range" id="w_slow_init_min" min="-1" max="0" value="-0.4" step="0.05"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>W_slow max</label><span class="value-display" id="val-w_slow_init_max">0.40</span></div> | |
| <input type="range" id="w_slow_init_max" min="0" max="1" value="0.4" step="0.05"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>W_meta min</label><span class="value-display" id="val-w_meta_init_min">-0.30</span></div> | |
| <input type="range" id="w_meta_init_min" min="-0.5" max="0" value="-0.3" step="0.05"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>W_meta max</label><span class="value-display" id="val-w_meta_init_max">0.30</span></div> | |
| <input type="range" id="w_meta_init_max" min="0" max="0.5" value="0.3" step="0.05"> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- CHRONOPLASTICITY --> | |
| <div class="param-section"> | |
| <div class="section-header" onclick="toggleSection(this)"> | |
| <div class="icon" style="background:rgba(255,145,0,0.12);color:var(--orange);">β±</div> | |
| <h2>ChronoPlasticity β Synaptic Time Warping</h2> | |
| <span class="toggle-arrow">βΈ</span> | |
| </div> | |
| <div class="section-content collapsed"> | |
| <div class="param-description" style="margin-bottom:0.6em;">Eqs 5-7: Learned Ο_t controls temporal memory horizon</div> | |
| <div class="toggle-row"> | |
| <label>ChronoPlasticity Enabled</label> | |
| <label class="toggle-switch"><input type="checkbox" id="chrono_enabled" checked><span class="toggle-slider"></span></label> | |
| </div> | |
| <div class="two-col"> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Ξ±_fast</label><span class="value-display" id="val-chrono_alpha_f">0.950</span></div> | |
| <input type="range" id="chrono_alpha_f" min="0.8" max="0.999" value="0.95" step="0.005"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Ξ±_slow</label><span class="value-display" id="val-chrono_alpha_s">0.990</span></div> | |
| <input type="range" id="chrono_alpha_s" min="0.9" max="0.9999" value="0.99" step="0.001"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Ξ»_fast</label><span class="value-display" id="val-chrono_lambda_f">0.150</span></div> | |
| <input type="range" id="chrono_lambda_f" min="0" max="0.5" value="0.15" step="0.01"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Ξ»_slow</label><span class="value-display" id="val-chrono_lambda_s">0.080</span></div> | |
| <input type="range" id="chrono_lambda_s" min="0" max="0.5" value="0.08" step="0.01"> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- AGMP --> | |
| <div class="param-section"> | |
| <div class="section-header" onclick="toggleSection(this)"> | |
| <div class="icon" style="background:rgba(255,64,129,0.12);color:var(--pink);">π</div> | |
| <h2>AGMP β Astrocyte-Gated Plasticity</h2> | |
| <span class="toggle-arrow">βΈ</span> | |
| </div> | |
| <div class="section-content collapsed"> | |
| <div class="param-description" style="margin-bottom:0.6em;">Eqs 8-10: Eligibility Γ Dopamine Γ Astrocyte</div> | |
| <div class="toggle-row"> | |
| <label>AGMP Enabled</label> | |
| <label class="toggle-switch"><input type="checkbox" id="agmp_enabled" checked><span class="toggle-slider"></span></label> | |
| </div> | |
| <div class="two-col"> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Ξ»_eligibility</label><span class="value-display" id="val-agmp_lambda_e">0.950</span></div> | |
| <input type="range" id="agmp_lambda_e" min="0.8" max="0.999" value="0.95" step="0.005"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Ξ»_astrocyte</label><span class="value-display" id="val-agmp_lambda_a">0.9990</span></div> | |
| <input type="range" id="agmp_lambda_a" min="0.99" max="0.9999" value="0.999" step="0.0001"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Ξ· (learning rate)</label><span class="value-display" id="val-agmp_eta">0.0050</span></div> | |
| <input type="range" id="agmp_eta" min="0.001" max="0.05" value="0.005" step="0.001"> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- PLASTICITY --> | |
| <div class="param-section"> | |
| <div class="section-header" onclick="toggleSection(this)"> | |
| <div class="icon" style="background:rgba(0,230,118,0.12);color:var(--success);">π±</div> | |
| <h2>Plasticity & Structural</h2> | |
| <span class="toggle-arrow">βΈ</span> | |
| </div> | |
| <div class="section-content collapsed"> | |
| <div class="two-col"> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Learning Rate</label><span class="value-display" id="val-learning_rate">0.010</span></div> | |
| <input type="range" id="learning_rate" min="0" max="0.1" value="0.01" step="0.001"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>STDP Window (ms)</label><span class="value-display" id="val-stdp_window">20.0</span></div> | |
| <input type="range" id="stdp_window" min="5" max="100" value="20" step="1"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Associative Ξ±</label><span class="value-display" id="val-associative_alpha">0.0050</span></div> | |
| <input type="range" id="associative_alpha" min="0" max="0.05" value="0.005" step="0.001"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Synapse Integrity ΞΈ</label><span class="value-display" id="val-synapse_integrity_threshold">0.10</span></div> | |
| <input type="range" id="synapse_integrity_threshold" min="0" max="0.5" value="0.1" step="0.01"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Formation Prob</label><span class="value-display" id="val-synapse_formation_prob">0.050</span></div> | |
| <input type="range" id="synapse_formation_prob" min="0" max="0.2" value="0.05" step="0.005"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Death Prob</label><span class="value-display" id="val-synapse_death_prob">0.010</span></div> | |
| <input type="range" id="synapse_death_prob" min="0" max="0.1" value="0.01" step="0.001"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Neuron Death ΞΈ</label><span class="value-display" id="val-neuron_death_threshold">0.10</span></div> | |
| <input type="range" id="neuron_death_threshold" min="0" max="0.5" value="0.1" step="0.01"> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- NEUROMODULATORS --> | |
| <div class="param-section"> | |
| <div class="section-header" onclick="toggleSection(this)"> | |
| <div class="icon" style="background:rgba(255,193,7,0.12);color:var(--warning);">π§ͺ</div> | |
| <h2>Neuromodulator System</h2> | |
| <span class="toggle-arrow">βΈ</span> | |
| </div> | |
| <div class="section-content collapsed"> | |
| <div class="two-col"> | |
| <div class="param-group"> | |
| <div class="param-label"><label>π― Dopamine Baseline</label><span class="value-display" id="val-dopamine_baseline">0.15</span></div> | |
| <input type="range" id="dopamine_baseline" min="0" max="1" value="0.15" step="0.01"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>π Serotonin Baseline</label><span class="value-display" id="val-serotonin_baseline">0.15</span></div> | |
| <input type="range" id="serotonin_baseline" min="0" max="1" value="0.15" step="0.01"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>π‘ Acetylcholine Baseline</label><span class="value-display" id="val-acetylcholine_baseline">0.15</span></div> | |
| <input type="range" id="acetylcholine_baseline" min="0" max="1" value="0.15" step="0.01"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>β‘ Norepinephrine Baseline</label><span class="value-display" id="val-norepinephrine_baseline">0.15</span></div> | |
| <input type="range" id="norepinephrine_baseline" min="0" max="1" value="0.15" step="0.01"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Ο Tonic (ms)</label><span class="value-display" id="val-tau_tonic">5000</span></div> | |
| <input type="range" id="tau_tonic" min="1000" max="20000" value="5000" step="500"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Ο Phasic (ms)</label><span class="value-display" id="val-tau_phasic">200</span></div> | |
| <input type="range" id="tau_phasic" min="50" max="1000" value="200" step="10"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Release Rate</label><span class="value-display" id="val-neuromod_release_rate">0.020</span></div> | |
| <input type="range" id="neuromod_release_rate" min="0" max="0.1" value="0.02" step="0.005"> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- OSCILLATOR --> | |
| <div class="param-section"> | |
| <div class="section-header" onclick="toggleSection(this)"> | |
| <div class="icon" style="background:rgba(79,172,254,0.12);color:var(--accent);">γ°</div> | |
| <h2>Oscillator Bank</h2> | |
| <span class="toggle-arrow">βΈ</span> | |
| </div> | |
| <div class="section-content collapsed"> | |
| <div class="param-description" style="margin-bottom:0.6em;">Multi-band: Infraslow (0.05Hz) β Slow (0.5Hz) β Theta (6Hz) β Alpha (10Hz) β Gamma (40Hz) with PAC</div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Coupling Strength</label><span class="value-display" id="val-oscillator_coupling">0.10</span></div> | |
| <input type="range" id="oscillator_coupling" min="0" max="0.5" value="0.1" step="0.01"> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- AIGARTH --> | |
| <div class="param-section"> | |
| <div class="section-header" onclick="toggleSection(this)"> | |
| <div class="icon" style="background:rgba(29,233,182,0.12);color:var(--teal);">π§«</div> | |
| <h2>Aigarth Hybridization</h2> | |
| <span class="toggle-arrow">βΈ</span> | |
| </div> | |
| <div class="section-content collapsed"> | |
| <div class="param-description" style="margin-bottom:0.6em;">Evolutionary ITU: population + mutation + selection</div> | |
| <div class="two-col"> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Population Size</label><span class="value-display" id="val-aigarth_pop_size">10</span></div> | |
| <input type="range" id="aigarth_pop_size" min="2" max="50" value="10" step="1"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>ITU Size</label><span class="value-display" id="val-aigarth_itu_size">12</span></div> | |
| <input type="range" id="aigarth_itu_size" min="4" max="50" value="12" step="1"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Tick Cap</label><span class="value-display" id="val-aigarth_tick_cap">20</span></div> | |
| <input type="range" id="aigarth_tick_cap" min="5" max="100" value="20" step="1"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>W_fast Mutation P</label><span class="value-display" id="val-aigarth_mutation_wf_prob">0.30</span></div> | |
| <input type="range" id="aigarth_mutation_wf_prob" min="0" max="1" value="0.3" step="0.05"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>W_slow Mutation P</label><span class="value-display" id="val-aigarth_mutation_ws_prob">0.10</span></div> | |
| <input type="range" id="aigarth_mutation_ws_prob" min="0" max="1" value="0.1" step="0.05"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>W_meta Mutation P</label><span class="value-display" id="val-aigarth_mutation_wm_prob">0.05</span></div> | |
| <input type="range" id="aigarth_mutation_wm_prob" min="0" max="1" value="0.05" step="0.01"> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- SIMULATION --> | |
| <div class="param-section"> | |
| <div class="section-header" onclick="toggleSection(this)"> | |
| <div class="icon" style="background:rgba(79,172,254,0.12);color:var(--accent);">β±</div> | |
| <h2>Simulation</h2> | |
| <span class="toggle-arrow">βΌ</span> | |
| </div> | |
| <div class="section-content"> | |
| <div class="two-col"> | |
| <div class="param-group"> | |
| <div class="param-label"><label>dt (ms)</label><span class="value-display" id="val-dt">1.0</span></div> | |
| <input type="range" id="dt" min="0.1" max="10" value="1" step="0.1"> | |
| </div> | |
| <div class="param-group"> | |
| <div class="param-label"><label>Steps</label><span class="value-display" id="val-simulation_steps">2000</span></div> | |
| <input type="range" id="simulation_steps" min="1" max="100000" value="10000" step="10"> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <button id="build-network-btn" class="btn-primary"> | |
| π Build Neuraxon v2.0 Network | |
| </button> | |
| </div> | |
| <!-- βββββββββββββββ VISUALIZATION βββββββββββββββ --> | |
| <div id="viz-container"> | |
| <div id="canvas-container"> | |
| <canvas id="networkCanvas"></canvas> | |
| <div id="info"> | |
| <strong><a href="https://www.researchgate.net/publication/400868863_Neuraxon_V20_A_New_Neural_Growth_Computation_Blueprint" target="_blank">Qubic Open Science Neuraxon v2.0</a></strong><br> | |
| <span class="stat-label">Name:</span> <span class="stat-value" id="display-network-name"></span><br> | |
| <span class="stat-label">Neurons:</span> <span class="stat-value" id="neuron-count">0</span> | |
| <span class="stat-label" style="margin-left:8px;">Synapses:</span> <span class="stat-value" id="synapse-count">0</span><br> | |
| <span class="stat-label">Time:</span> <span class="stat-value" id="time">0</span>ms | |
| <span class="stat-label" style="margin-left:8px;">Steps:</span> <span class="stat-value" id="steps">0</span><br> | |
| <span class="stat-label">Energy:</span> <span class="stat-value" id="energy">0.000</span> | |
| <span class="energy-bar"><span class="energy-fill" id="energy-fill" style="width:0%"></span></span><br> | |
| <span class="stat-label">Active:</span> <span class="stat-value" id="active-count">0</span> | |
| <span class="stat-label" style="margin-left:8px;">Mods:</span> <span class="stat-value" id="modulator-count">0</span> | |
| </div> | |
| <div id="pipeline-indicator"> | |
| <div class="pip-step" id="pip-chrono">CHRONO</div> | |
| <div class="pip-step" id="pip-dsn">DSN</div> | |
| <div class="pip-step" id="pip-ctsn">CTSN</div> | |
| <div class="pip-step" id="pip-agmp">AGMP</div> | |
| </div> | |
| </div> | |
| <div id="controls"> | |
| <div style="display:flex;align-items:center;gap:0.5em;margin-bottom:1em;"> | |
| <button class="btn-ghost" style="flex:1;" onclick="backToConfig()">β Config</button> | |
| <button class="btn-warning" onclick="saveFullModel()" style="font-size:0.78em;">π¦ Save</button> | |
| </div> | |
| <h2>Input Neurons</h2> | |
| <div class="input-states" id="input-states"></div> | |
| <h2>Simulation</h2> | |
| <div class="param-group" style="margin-bottom:0.5em;"> | |
| <input type="number" id="sim-steps" value="2000" min="1" max="100000" style="font-size:0.85em;"> | |
| </div> | |
| <div style="display:flex;gap:0.4em;"> | |
| <button class="btn-success" id="btn-simulate" style="flex:1;font-size:0.82em;">βΆ Run</button> | |
| <button class="btn-warning" id="btn-stop" style="flex:1;font-size:0.82em;">βΈ Stop</button> | |
| <button class="btn-danger" id="btn-reset" style="flex:1;font-size:0.82em;">β» Reset</button> | |
| </div> | |
| <h2>Neuromodulators (Tonic)</h2> | |
| <div class="neuromod-group"> | |
| <div class="neuromod-label"><span class="nm-icon">π―</span>Dopamine<span class="nm-val" id="val-dopamine-live">0.15</span></div> | |
| <input type="range" id="dopamine-live" min="0" max="1" step="0.01" value="0.15"> | |
| </div> | |
| <div class="neuromod-group"> | |
| <div class="neuromod-label"><span class="nm-icon">π</span>Serotonin<span class="nm-val" id="val-serotonin-live">0.15</span></div> | |
| <input type="range" id="serotonin-live" min="0" max="1" step="0.01" value="0.15"> | |
| </div> | |
| <div class="neuromod-group"> | |
| <div class="neuromod-label"><span class="nm-icon">π‘</span>Acetylcholine<span class="nm-val" id="val-acetylcholine-live">0.15</span></div> | |
| <input type="range" id="acetylcholine-live" min="0" max="1" step="0.01" value="0.15"> | |
| </div> | |
| <div class="neuromod-group"> | |
| <div class="neuromod-label"><span class="nm-icon">β‘</span>Norepinephrine<span class="nm-val" id="val-norepinephrine-live">0.15</span></div> | |
| <input type="range" id="norepinephrine-live" min="0" max="1" step="0.01" value="0.15"> | |
| </div> | |
| <h2>Receptor Activations</h2> | |
| <div class="receptor-grid" id="receptor-grid"> | |
| <div class="receptor-chip"><span class="r-name">D1</span><span class="r-val" id="r-D1">0.00</span></div> | |
| <div class="receptor-chip"><span class="r-name">D2</span><span class="r-val" id="r-D2">0.00</span></div> | |
| <div class="receptor-chip"><span class="r-name">5HT1A</span><span class="r-val" id="r-5HT1A">0.00</span></div> | |
| <div class="receptor-chip"><span class="r-name">5HT2A</span><span class="r-val" id="r-5HT2A">0.00</span></div> | |
| <div class="receptor-chip"><span class="r-name">5HT4</span><span class="r-val" id="r-5HT4">0.00</span></div> | |
| <div class="receptor-chip"><span class="r-name">M1</span><span class="r-val" id="r-M1">0.00</span></div> | |
| <div class="receptor-chip"><span class="r-name">M2</span><span class="r-val" id="r-M2">0.00</span></div> | |
| <div class="receptor-chip"><span class="r-name">Ξ²1</span><span class="r-val" id="r-beta1">0.00</span></div> | |
| <div class="receptor-chip"><span class="r-name">Ξ±2</span><span class="r-val" id="r-alpha2">0.00</span></div> | |
| </div> | |
| <h2>Oscillator Bank</h2> | |
| <div class="osc-bars" id="osc-bars"> | |
| <div class="osc-bar" id="osc-infraslow" title="Infraslow"></div> | |
| <div class="osc-bar" id="osc-slow" title="Slow"></div> | |
| <div class="osc-bar" id="osc-theta" title="Theta"></div> | |
| <div class="osc-bar" id="osc-alpha" title="Alpha"></div> | |
| <div class="osc-bar" id="osc-gamma" title="Gamma"></div> | |
| </div> | |
| <div style="display:flex;justify-content:space-between;font-size:9px;font-family:var(--font-mono);color:var(--text-dim);margin-top:2px;"> | |
| <span>Infra</span><span>Slow</span><span>ΞΈ</span><span>Ξ±</span><span>Ξ³</span> | |
| </div> | |
| <div class="legend"> | |
| <strong>Legend</strong> | |
| <div class="legend-item"><div class="legend-color" style="background:#4facfe;"></div><span>Input (Box)</span></div> | |
| <div class="legend-item"><div class="legend-color" style="background:#b388ff;"></div><span>Hidden (Sphere)</span></div> | |
| <div class="legend-item"><div class="legend-color" style="background:#ff4081;"></div><span>Output (Cone)</span></div> | |
| <div class="legend-item"><div class="legend-color" style="background:#00e676;height:4px;"></div><span>Excitatory Synapse</span></div> | |
| <div class="legend-item"><div class="legend-color" style="background:#ff5252;height:4px;"></div><span>Inhibitory Synapse</span></div> | |
| <div class="legend-item"><div class="legend-color" style="background:#ffc107;height:4px;border-radius:50%;width:14px;"></div><span>Neuromodulator Particles</span></div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Loading Overlay --> | |
| <div id="loading-overlay"> | |
| <div class="spinner"></div> | |
| <div class="loading-text">Building Neuraxon v2.0 Network...</div> | |
| </div> | |
| </div> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script> | |
| <script> | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // PARAMETER SYSTEM | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const sliderParams = [ | |
| 'num_input_neurons','num_hidden_neurons','num_output_neurons','num_dendritic_branches', | |
| 'dendritic_spike_threshold','dendritic_supralinear_gamma','ws_k','ws_beta', | |
| 'membrane_time_constant','firing_threshold_excitatory','firing_threshold_inhibitory', | |
| 'adaptation_tau','autoreceptor_tau','spontaneous_firing_rate','neuron_health_decay', | |
| 'target_firing_rate','homeostatic_rate','firing_rate_alpha','threshold_mod_k', | |
| 'msth_ultrafast_tau','msth_ultrafast_ceiling','msth_fast_tau','msth_fast_gain', | |
| 'msth_medium_tau','msth_medium_gain','msth_slow_tau','msth_slow_gain', | |
| 'dsn_kernel_size','dsn_bias','ctsn_rho', | |
| 'tau_fast','tau_slow','tau_meta','tau_stdp', | |
| 'w_fast_init_min','w_fast_init_max','w_slow_init_min','w_slow_init_max', | |
| 'w_meta_init_min','w_meta_init_max', | |
| 'chrono_alpha_f','chrono_alpha_s','chrono_lambda_f','chrono_lambda_s', | |
| 'agmp_lambda_e','agmp_lambda_a','agmp_eta', | |
| 'learning_rate','stdp_window','associative_alpha', | |
| 'synapse_integrity_threshold','synapse_formation_prob','synapse_death_prob','neuron_death_threshold', | |
| 'dopamine_baseline','serotonin_baseline','acetylcholine_baseline','norepinephrine_baseline', | |
| 'tau_tonic','tau_phasic','neuromod_release_rate','oscillator_coupling', | |
| 'aigarth_pop_size','aigarth_itu_size','aigarth_tick_cap', | |
| 'aigarth_mutation_wf_prob','aigarth_mutation_ws_prob','aigarth_mutation_wm_prob', | |
| 'dt','simulation_steps' | |
| ]; | |
| const toggleParams = ['dsn_enabled','ctsn_enabled','chrono_enabled','agmp_enabled']; | |
| function getStepDecimals(el) { | |
| const s = el.step || '1'; | |
| return s.includes('.') ? s.split('.')[1].length : 0; | |
| } | |
| sliderParams.forEach(id => { | |
| const el = document.getElementById(id); | |
| if (!el) return; | |
| const disp = document.getElementById(`val-${id}`); | |
| if (!disp) return; | |
| el.addEventListener('input', () => { | |
| disp.textContent = parseFloat(el.value).toFixed(getStepDecimals(el)); | |
| }); | |
| }); | |
| function toggleSection(header) { | |
| const content = header.nextElementSibling; | |
| const arrow = header.querySelector('.toggle-arrow'); | |
| content.classList.toggle('collapsed'); | |
| arrow.textContent = content.classList.contains('collapsed') ? 'βΈ' : 'βΌ'; | |
| } | |
| function getParameters() { | |
| const p = {}; | |
| p.network_name = document.getElementById('network_name').value; | |
| sliderParams.forEach(id => { | |
| const el = document.getElementById(id); | |
| if (el) p[id] = parseFloat(el.value); | |
| }); | |
| toggleParams.forEach(id => { | |
| const el = document.getElementById(id); | |
| if (el) p[id] = el.checked; | |
| }); | |
| return p; | |
| } | |
| function setParameters(params) { | |
| if (params.network_name !== undefined) document.getElementById('network_name').value = params.network_name; | |
| sliderParams.forEach(id => { | |
| if (params[id] !== undefined) { | |
| const el = document.getElementById(id); | |
| if (el) { el.value = params[id]; el.dispatchEvent(new Event('input')); } | |
| } | |
| }); | |
| toggleParams.forEach(id => { | |
| if (params[id] !== undefined) { | |
| const el = document.getElementById(id); | |
| if (el) el.checked = params[id]; | |
| } | |
| }); | |
| } | |
| // ββ Presets ββ | |
| const presets = { | |
| default: { network_name:"Neuraxon v2.0 Net", num_input_neurons:5, num_hidden_neurons:20, num_output_neurons:5 }, | |
| small: { network_name:"Small Net", num_input_neurons:3, num_hidden_neurons:8, num_output_neurons:3, ws_k:4 }, | |
| large: { network_name:"Large Net", num_input_neurons:10, num_hidden_neurons:80, num_output_neurons:10, ws_k:8, ws_beta:0.2 }, | |
| chrono: { network_name:"Chrono-Heavy", chrono_alpha_f:0.98, chrono_alpha_s:0.995, chrono_lambda_f:0.3, chrono_lambda_s:0.2, chrono_enabled:true }, | |
| homeostatic: { network_name:"Homeostatic Focus", target_firing_rate:0.15, homeostatic_rate:0.002, msth_fast_gain:0.2 }, | |
| fast: { network_name:"Fast Dynamics", tau_fast:2, tau_slow:20, membrane_time_constant:10, dt:0.5 }, | |
| }; | |
| function loadPreset(name) { setParameters(presets[name]); } | |
| // ββ Save/Load ββ | |
| function saveParameters() { | |
| const p = getParameters(); | |
| const fn = (p.network_name||'neuraxon').replace(/[^a-z0-9]/gi,'_').toLowerCase()+'_params.json'; | |
| const b = new Blob([JSON.stringify(p,null,2)],{type:'application/json'}); | |
| const a = document.createElement('a'); a.href = URL.createObjectURL(b); a.download = fn; | |
| document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(a.href); | |
| } | |
| function loadParameters(input) { | |
| const f = input.files[0]; if(!f)return; | |
| const r = new FileReader(); | |
| r.onload = e => { try { setParameters(JSON.parse(e.target.result)); } catch(err) { alert('Error: '+err.message); } }; | |
| r.readAsText(f); input.value=''; | |
| } | |
| function saveFullModel() { | |
| if(!networkData) return alert("Build a network first"); | |
| const full = { | |
| version: "2.0-full", | |
| timestamp: Date.now(), | |
| parameters: networkData.parameters, | |
| neurons: { | |
| input: networkData.neurons.input.map(n => deepCloneNeuron(n)), | |
| hidden: networkData.neurons.hidden.map(n => deepCloneNeuron(n)), | |
| output: networkData.neurons.output.map(n => deepCloneNeuron(n)) | |
| }, | |
| synapses: networkData.synapses.map(s => deepCloneSynapse(s)), | |
| neuromodulator_system: networkData.neuromodulator_system, | |
| oscillators: networkData.oscillator_phases, | |
| time: networkData.time, | |
| step_count: networkData.step_count, | |
| energy_usage: networkData.energy_usage | |
| }; | |
| const blob = new Blob([JSON.stringify(full, null, 2)], {type:"application/json"}); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement("a"); a.href=url; a.download="neuraxon_v2_full.json"; a.click(); | |
| URL.revokeObjectURL(url); | |
| } | |
| function deepCloneNeuron(n) { | |
| return { | |
| ...n, | |
| dsn_input_buffer: [...(n.dsn_input_buffer||[])], | |
| dsn_kernel_weights: [...(n.dsn_kernel_weights||[])], | |
| ctsn_phi_gain: n.ctsn_phi_gain, | |
| ctsn_phi_bias: n.ctsn_phi_bias, | |
| msth: {...(n.msth||{})}, | |
| state_history: [...(n.state_history||[])], | |
| potential_history: [...(n.potential_history||[])] | |
| }; | |
| } | |
| function deepCloneSynapse(s) { | |
| return { | |
| ...s, | |
| branch_index: s.branch_index, | |
| eligibility: s.eligibility, | |
| chrono_fast_trace: s.chrono_fast_trace, | |
| chrono_slow_trace: s.chrono_slow_trace, | |
| chrono_omega: s.chrono_omega | |
| }; | |
| } | |
| function loadFullModel(input) { | |
| const f = input.files[0]; if(!f)return; | |
| const r = new FileReader(); | |
| r.onload = e => { | |
| try { | |
| const data = JSON.parse(e.target.result); | |
| if(data.parameters) setParameters(data.parameters); | |
| networkData = data; | |
| networkData.step_count = data.step_count || 0; | |
| networkData.time = data.time || 0; | |
| networkData.energy_usage = data.energy_usage || 0; | |
| // ensure neuromods | |
| if(!networkData.neuromodulators) { | |
| networkData.neuromodulators = { | |
| dopamine: data.parameters.dopamine_baseline || 0.15, | |
| serotonin: data.parameters.serotonin_baseline || 0.15, | |
| acetylcholine: data.parameters.acetylcholine_baseline || 0.15, | |
| norepinephrine: data.parameters.norepinephrine_baseline || 0.15 | |
| }; | |
| } | |
| if(!networkData.receptor_activations) { | |
| networkData.receptor_activations = {D1:0,D2:0,'5HT1A':0,'5HT2A':0,'5HT4':0,M1:0,M2:0,beta1:0,alpha2:0}; | |
| } | |
| if(!networkData.oscillator_phases) { | |
| networkData.oscillator_phases = {infraslow:0,slow:0,theta:0,alpha:0,gamma:0}; | |
| } | |
| document.getElementById('config-panel').classList.add('hidden'); | |
| document.getElementById('viz-container').classList.add('visible'); | |
| document.getElementById('display-network-name').textContent = data.parameters.network_name||'Loaded'; | |
| initThreeJS(); | |
| initVisualization(); | |
| alert('Model loaded!'); | |
| } catch(err) { alert('Error: '+err.message); } | |
| }; | |
| r.readAsText(f); input.value=''; | |
| } | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // NETWORK DATA & SIMULATION | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| let networkData = null; | |
| let neuronMeshes = {}; | |
| let synapseMeshes = []; | |
| let modulators = []; | |
| let isSimulating = false; | |
| let simulationInterval = null; | |
| let scene, camera, renderer, orbit, networkGroup; | |
| function buildNetwork() { | |
| const params = getParameters(); | |
| document.getElementById('loading-overlay').classList.add('visible'); | |
| setTimeout(() => { | |
| networkData = generateNetwork(params); | |
| document.getElementById('config-panel').classList.add('hidden'); | |
| document.getElementById('viz-container').classList.add('visible'); | |
| document.getElementById('loading-overlay').classList.remove('visible'); | |
| document.getElementById('display-network-name').textContent = params.network_name || "Neuraxon v2.0"; | |
| initThreeJS(); | |
| initVisualization(); | |
| }, 600); | |
| } | |
| function generateNetwork(p) { | |
| const neurons = { input:[], hidden:[], output:[] }; | |
| let id = 0; | |
| const B = Math.round(p.num_dendritic_branches) || 3; | |
| for(let i=0;i<p.num_input_neurons;i++) neurons.input.push(makeNeuron(id++,'input',p)); | |
| for(let i=0;i<p.num_hidden_neurons;i++) neurons.hidden.push(makeNeuron(id++,'hidden',p)); | |
| for(let i=0;i<p.num_output_neurons;i++) neurons.output.push(makeNeuron(id++,'output',p)); | |
| const all = [...neurons.input,...neurons.hidden,...neurons.output]; | |
| const N = all.length; | |
| const k = Math.min(Math.round(p.ws_k)||6, N-1); | |
| const halfK = Math.max(Math.floor(k/2),1); | |
| const beta = p.ws_beta || 0.3; | |
| const edges = new Set(); | |
| // Watts-Strogatz ring lattice | |
| for(let i=0;i<N;i++){ | |
| for(let j=1;j<=halfK;j++){ | |
| for(const t of [(i+j)%N,(i-j+N)%N]){ | |
| if(all[i].type==='output'&&all[t].type==='input') continue; | |
| if(i!==t) edges.add(`${i}-${t}`); | |
| } | |
| } | |
| } | |
| // Rewire | |
| const edgeArr = [...edges]; | |
| for(const e of edgeArr){ | |
| if(Math.random()<beta){ | |
| edges.delete(e); | |
| const [pi] = e.split('-').map(Number); | |
| const ni = Math.floor(Math.random()*N); | |
| if(ni!==pi && !edges.has(`${pi}-${ni}`) && !(all[pi].type==='output'&&all[ni].type==='input')){ | |
| edges.add(`${pi}-${ni}`); | |
| } else { edges.add(e); } | |
| } | |
| } | |
| const synapses = []; | |
| for(const e of edges){ | |
| const [pi,po] = e.split('-').map(Number); | |
| synapses.push(makeSynapse(all[pi].id, all[po].id, B, p)); | |
| } | |
| return { | |
| version: '2.0', | |
| parameters: p, | |
| neurons, synapses, | |
| neuromodulators: { | |
| dopamine: p.dopamine_baseline, serotonin: p.serotonin_baseline, | |
| acetylcholine: p.acetylcholine_baseline, norepinephrine: p.norepinephrine_baseline, | |
| }, | |
| neuromod_tonic: { DA: p.dopamine_baseline, '5HT': p.serotonin_baseline, ACh: p.acetylcholine_baseline, NA: p.norepinephrine_baseline }, | |
| neuromod_phasic: { DA:0, '5HT':0, ACh:0, NA:0 }, | |
| receptor_activations: { D1:0,D2:0,'5HT1A':0,'5HT2A':0,'5HT4':0,M1:0,M2:0,beta1:0,alpha2:0 }, | |
| oscillator_phases: { infraslow: Math.random()*2*Math.PI, slow: Math.random()*2*Math.PI, theta: Math.random()*2*Math.PI, alpha: Math.random()*2*Math.PI, gamma: Math.random()*2*Math.PI }, | |
| time: 0, step_count: 0, energy_usage: 0, | |
| }; | |
| } | |
| function makeNeuron(id, type, p) { | |
| return { | |
| id, type, trinary_state:0, membrane_potential:0, state_tilde:0, | |
| complement_h:0, adaptation:0, autoreceptor:0, | |
| health:1.0, is_active:true, firing_rate_avg: p.target_firing_rate||0.2, | |
| astrocyte_state:0, | |
| msth: { ultrafast_activity:0, fast_excitability:0, medium_gain:1, slow_structural:0 }, | |
| dsn_buffer: new Array(Math.round(p.dsn_kernel_size)||4).fill(0), | |
| dsn_alpha: 0.5, | |
| branch_potentials: new Array(Math.round(p.num_dendritic_branches)||3).fill(0), | |
| }; | |
| } | |
| function makeSynapse(preId, postId, B, p) { | |
| return { | |
| pre_id:preId, post_id:postId, | |
| branch_id: Math.floor(Math.random()*B), | |
| w_fast: rng(p.w_fast_init_min, p.w_fast_init_max), | |
| w_slow: rng(p.w_slow_init_min, p.w_slow_init_max), | |
| w_meta: rng(p.w_meta_init_min, p.w_meta_init_max), | |
| is_silent: Math.random()<0.1, is_modulatory: Math.random()<0.2, | |
| integrity: 1.0, | |
| pre_trace:0, post_trace:0, recent_delta_w:0, | |
| chrono_fast_trace:0, chrono_slow_trace:0, chrono_omega:0.5, | |
| eligibility:0, | |
| }; | |
| } | |
| function rng(a,b){return a+(b-a)*Math.random();} | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // NEURAXON v2.0 SIMULATION ENGINE (JavaScript port) | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function simulateStep() { | |
| const p = networkData.parameters; | |
| const dt = p.dt || 1; | |
| const all = [...networkData.neurons.input,...networkData.neurons.hidden,...networkData.neurons.output]; | |
| const N = all.length; | |
| // --- Activity stats --- | |
| const active = all.filter(n=>n.is_active); | |
| const meanAct = active.length ? active.reduce((s,n)=>s+Math.abs(n.trinary_state),0)/active.length : 0; | |
| const excFrac = active.length ? active.filter(n=>n.trinary_state===1).length/active.length : 0; | |
| const changeRate = active.length ? active.filter(n=>n.trinary_state!==n._prev_state).length/active.length : 0; | |
| // --- Neuromodulator system update --- | |
| updateNeuromodulators(p, dt, meanAct, excFrac, changeRate); | |
| const R = computeReceptorActivations(); | |
| networkData.receptor_activations = R; | |
| // --- Oscillator update --- | |
| const osc = networkData.oscillator_phases; | |
| const oscFreqs = {infraslow:0.05,slow:0.5,theta:6,alpha:10,gamma:40}; | |
| for(const band in osc) osc[band] = (osc[band]+2*Math.PI*oscFreqs[band]*dt/1000) % (2*Math.PI); | |
| // --- Pipeline indicator --- | |
| animatePipeline(); | |
| // --- Collect synaptic inputs by branch --- | |
| const branchInputs = {}; | |
| const modInputs = {}; | |
| all.forEach(n => { branchInputs[n.id]={}; modInputs[n.id]=[]; for(let b=0;b<(p.num_dendritic_branches||3);b++) branchInputs[n.id][b]=[]; }); | |
| for(const syn of networkData.synapses) { | |
| if(syn.integrity<=0) continue; | |
| const pre = all.find(n=>n.id===syn.pre_id); | |
| if(!pre||!pre.is_active) continue; | |
| // Step 1: ChronoPlasticity trace update | |
| if(p.chrono_enabled) { | |
| const sPre = Math.abs(pre.trinary_state)===1?1:0; | |
| const raw = 2*sPre + 1.5*syn.chrono_slow_trace - 1; | |
| syn.chrono_omega = 1/(1+Math.exp(-raw)); | |
| syn.chrono_fast_trace = (p.chrono_alpha_f||0.95)*syn.chrono_fast_trace + sPre; | |
| const alphaWarped = Math.pow(p.chrono_alpha_s||0.99, syn.chrono_omega); | |
| syn.chrono_slow_trace = alphaWarped*syn.chrono_slow_trace + sPre; | |
| } | |
| // Compute input with chrono | |
| let inp; | |
| if(syn.is_silent) { inp=0; } | |
| else { | |
| const wTotal = syn.w_fast + syn.w_slow; | |
| inp = wTotal * pre.trinary_state; | |
| if(p.chrono_enabled) { | |
| inp += (p.chrono_lambda_f||0.15)*syn.w_fast*syn.chrono_fast_trace | |
| + (p.chrono_lambda_s||0.08)*syn.w_slow*syn.chrono_slow_trace; | |
| } | |
| } | |
| if(branchInputs[syn.post_id] && branchInputs[syn.post_id][syn.branch_id]!==undefined) | |
| branchInputs[syn.post_id][syn.branch_id].push(inp); | |
| if(syn.is_modulatory && syn.w_meta!==0) modInputs[syn.post_id].push(syn.w_meta); | |
| } | |
| // --- Update neurons --- | |
| for(const neuron of all) { | |
| if(!neuron.is_active) continue; | |
| neuron._prev_state = neuron.trinary_state; | |
| if(neuron.type==='input') continue; // inputs hold | |
| // Dendritic integration (supralinear) | |
| let D = 0; | |
| const branches = branchInputs[neuron.id] || {}; | |
| const B = p.num_dendritic_branches || 3; | |
| for(let b=0;b<B;b++){ | |
| const bi = branches[b]||[]; | |
| const sigma = bi.reduce((s,v)=>s+v,0); | |
| if(Math.abs(sigma)>(p.dendritic_spike_threshold||0.4)){ | |
| D += Math.sign(sigma)*Math.pow(Math.abs(sigma), p.dendritic_supralinear_gamma||1.3); | |
| } else { D += sigma; } | |
| } | |
| // Oscillator drive | |
| const phi = 2*Math.PI*neuron.id/Math.max(N,1); | |
| const thetaP = osc.theta; const gammaP = osc.gamma; | |
| const gateTheta = Math.max(0,Math.cos(thetaP+phi)); | |
| const oscDrive = (p.oscillator_coupling||0.1)*(gateTheta*Math.sin(gammaP+2*phi) + 0.5*Math.sin(osc.slow+0.3*phi) + 0.3*Math.sin(osc.infraslow)); | |
| // Homeostatic rate | |
| neuron.firing_rate_avg += (p.firing_rate_alpha||0.01)*(Math.abs(neuron.trinary_state)-neuron.firing_rate_avg)*dt; | |
| // MSTH | |
| const msth = neuron.msth; | |
| const curAbs = Math.abs(neuron.trinary_state); | |
| const ufA = dt/(p.msth_ultrafast_tau||5); | |
| msth.ultrafast_activity = (1-ufA)*msth.ultrafast_activity + ufA*curAbs; | |
| const ufSuppress = msth.ultrafast_activity > (p.msth_ultrafast_ceiling||2); | |
| const fA = dt/(p.msth_fast_tau||2000); | |
| msth.fast_excitability = (1-fA)*msth.fast_excitability + fA*curAbs; | |
| const fastShift = (p.msth_fast_gain||0.1)*(msth.fast_excitability-(p.target_firing_rate||0.2)); | |
| const mA = dt/(p.msth_medium_tau||300000); | |
| const tgtDev = curAbs-(p.target_firing_rate||0.2); | |
| msth.medium_gain += mA*(-(p.msth_medium_gain||0.001)*tgtDev*msth.medium_gain); | |
| msth.medium_gain = Math.max(0.5,Math.min(2,msth.medium_gain)); | |
| const sA = dt/(p.msth_slow_tau||3600000); | |
| msth.slow_structural = (1-sA)*msth.slow_structural + sA*Math.abs(tgtDev); | |
| // NA gain + spontaneous | |
| const b1 = R.beta1||0; const a2 = R.alpha2||0; | |
| const gNA = 1+0.5*b1+0.2*a2; | |
| let spontaneous = 0; | |
| if(Math.random()<((p.spontaneous_firing_rate||0.02)+0.3*a2)*dt) spontaneous = (Math.random()-0.5)*0.6; | |
| // Step 2: DSN Dynamic Decay | |
| const totalInpMag = Math.abs(D); | |
| let alpha_t = 0.5; | |
| if(p.dsn_enabled) { | |
| const buf = neuron.dsn_buffer; | |
| buf.shift(); buf.push(totalInpMag); | |
| const kSize = buf.length; | |
| const weights = []; for(let i=0;i<kSize;i++) weights.push((i+1)/kSize); | |
| let conv = 0; for(let i=0;i<kSize;i++) conv += weights[i]*buf[i]; | |
| conv /= Math.max(weights.reduce((a,b)=>a+b,0),1e-6); | |
| alpha_t = 1/(1+Math.exp(-5*(conv-0.2+(p.dsn_bias||0)))); | |
| neuron.dsn_alpha = alpha_t; | |
| } | |
| const DScaled = D*msth.medium_gain; | |
| const rawInput = gNA*DScaled + oscDrive - neuron.adaptation + spontaneous; | |
| neuron.membrane_potential = alpha_t*neuron.membrane_potential + (1-alpha_t)*rawInput; | |
| if(ufSuppress) neuron.membrane_potential *= 0.5; | |
| // Step 3: CTSN complement | |
| if(p.ctsn_enabled) { | |
| const rho = p.ctsn_rho||0.9; | |
| const phiC = Math.tanh(totalInpMag*0.5); | |
| neuron.complement_h = rho*neuron.complement_h + (1-rho)*phiC; | |
| } else { neuron.complement_h = 0; } | |
| neuron.state_tilde = neuron.membrane_potential + neuron.complement_h; | |
| // Threshold modulation | |
| const rawMod = (modInputs[neuron.id]||[]).reduce((s,v)=>s+v,0) | |
| + 0.3*(R.M1||0) - 0.2*(R.M2||0); | |
| const dThMeta = (p.threshold_mod_k||0.3)*Math.tanh(rawMod); | |
| const dThHomeo = (p.homeostatic_rate||0.0005)*(neuron.firing_rate_avg-(p.target_firing_rate||0.2)); | |
| const thE = (p.firing_threshold_excitatory||0.4) - dThMeta + dThHomeo + fastShift - 0.1*neuron.autoreceptor; | |
| const thI = (p.firing_threshold_inhibitory||-0.4) - dThMeta + dThHomeo + fastShift + 0.1*neuron.autoreceptor; | |
| // Step 4: Trinary readout on s_tilde | |
| if(neuron.state_tilde > thE) neuron.trinary_state = 1; | |
| else if(neuron.state_tilde < thI) neuron.trinary_state = -1; | |
| else neuron.trinary_state = 0; | |
| // Adaptation & autoreceptor | |
| neuron.adaptation += dt/(p.adaptation_tau||100)*(-neuron.adaptation+0.1*Math.abs(neuron.trinary_state)); | |
| neuron.autoreceptor += dt/(p.autoreceptor_tau||200)*(-neuron.autoreceptor+0.2*neuron.trinary_state); | |
| // AGMP astrocyte | |
| if(p.agmp_enabled) { | |
| const la = p.agmp_lambda_a||0.999; | |
| neuron.astrocyte_state = la*neuron.astrocyte_state + (1-la)*Math.abs(neuron.state_tilde); | |
| } | |
| if (p.ctsn_enabled && Math.abs(neuron.complement_h) > 0.68 && Math.random()<0.45) spawnFeatureParticles(neuron, 'ctsn'); | |
| if (p.agmp_enabled && neuron.astrocyte_state > 0.72 && Math.random()<0.4) spawnFeatureParticles(neuron, 'agmp'); | |
| if (p.dsn_enabled && neuron.dsn_alpha < 0.48 && Math.random()<0.3) spawnFeatureParticles(neuron, 'dsn'); | |
| if (p.chrono_enabled && Math.abs(neuron.trinary_state)===1 && Math.random()<0.25) spawnFeatureParticles(neuron, 'chrono'); | |
| if (neuron.trinary_state !== neuron._prev_state && Math.abs(neuron.trinary_state)===1) spawnFeatureParticles(neuron, 'fire'); | |
| // Health | |
| if(Math.abs(neuron.membrane_potential)/2<0.01) neuron.health -= (p.neuron_health_decay||0.001)*dt; | |
| else neuron.health = Math.min(1, neuron.health+0.0005*dt); | |
| if(neuron.type==='hidden'&&neuron.health<(p.neuron_death_threshold||0.1)&&Math.random()<0.001) neuron.is_active=false; | |
| } | |
| // --- Synapse update (STDP + AGMP) --- | |
| const branchSynapses = {}; | |
| for(const syn of networkData.synapses) { | |
| const key = `${syn.post_id}-${syn.branch_id}`; | |
| if(!branchSynapses[key]) branchSynapses[key]=[]; | |
| branchSynapses[key].push(syn); | |
| } | |
| for(const syn of networkData.synapses) { | |
| if(syn.integrity<=0) continue; | |
| const pre = all.find(n=>n.id===syn.pre_id); | |
| const post = all.find(n=>n.id===syn.post_id); | |
| if(!pre||!post||!pre.is_active||!post.is_active) continue; | |
| const tau = p.tau_stdp||20; | |
| syn.pre_trace += (-syn.pre_trace/tau + (pre.trinary_state===1?1:0))*dt; | |
| syn.post_trace += (-syn.post_trace/tau + (post.trinary_state===1?1:0))*dt; | |
| const Ap = syn.pre_trace*(post.trinary_state===1?1:0); | |
| const Am = syn.post_trace*(pre.trinary_state===1?1:0); | |
| const d1 = R.D1||0.5; const d2 = R.D2||0.5; | |
| let dw = (p.learning_rate||0.01)*Ap*d1 - (p.learning_rate||0.01)*Am*d2; | |
| if(pre.trinary_state===1&&post.trinary_state===1) dw+=(p.learning_rate||0.01)*0.5*d1; | |
| if(pre.trinary_state===1&&post.trinary_state===-1) dw-=(p.learning_rate||0.01)*0.5*d2; | |
| if(pre.trinary_state===0&&post.trinary_state===0) dw*=0.1; | |
| // Neighbour associative | |
| const key = `${syn.post_id}-${syn.branch_id}`; | |
| const neighbours = branchSynapses[key]||[]; | |
| for(const n of neighbours){ | |
| if(n===syn) continue; | |
| dw += (p.associative_alpha||0.005)*n.recent_delta_w; | |
| } | |
| syn.recent_delta_w = dw; | |
| syn.w_fast += dt/(p.tau_fast||5)*(-syn.w_fast*0.01+0.3*dw); | |
| syn.w_fast = Math.max(-1,Math.min(1,syn.w_fast)); | |
| syn.w_slow += dt/(p.tau_slow||50)*(-syn.w_slow*0.01+0.1*dw); | |
| syn.w_slow = Math.max(-1,Math.min(1,syn.w_slow)); | |
| const ht2a=R['5HT2A']||0; const ht1a=R['5HT1A']||0; | |
| const sf = 0.5*ht2a+0.1*(1-ht1a); | |
| syn.w_meta += dt/(p.tau_meta||1000)*(-syn.w_meta*0.01+0.05*dw*sf); | |
| syn.w_meta = Math.max(-0.5,Math.min(0.5,syn.w_meta)); | |
| if(Math.abs(syn.w_fast)<0.01&&Math.abs(syn.w_slow)<0.01) syn.integrity-=(p.synapse_death_prob||0.01)*dt; | |
| else syn.integrity = Math.min(1,syn.integrity+0.001*dt); | |
| if(syn.is_silent&&pre.trinary_state===1&&post.trinary_state===1&&Math.random()<0.05) syn.is_silent=false; | |
| // AGMP | |
| if(p.agmp_enabled) { | |
| let psi=0; | |
| if(pre.trinary_state===1&&post.trinary_state===1) psi=1; | |
| else if(pre.trinary_state===1&&post.trinary_state===-1) psi=-0.5; | |
| else if(pre.trinary_state===-1&&post.trinary_state===1) psi=-0.3; | |
| syn.eligibility = (p.agmp_lambda_e||0.95)*syn.eligibility + psi; | |
| const mt = networkData.neuromod_phasic.DA||0; | |
| const at = post.astrocyte_state||0; | |
| const dagmp = (p.agmp_eta||0.005)*mt*at*syn.eligibility; | |
| syn.w_fast = Math.max(-1,Math.min(1,syn.w_fast+dagmp*0.3)); | |
| syn.w_slow = Math.max(-1,Math.min(1,syn.w_slow+dagmp*0.1)); | |
| } | |
| } | |
| // Homeostatic scaling | |
| for(const neuron of all) { | |
| if(!neuron.is_active||neuron.type==='input') continue; | |
| const scale = 1+(p.homeostatic_rate||0.0005)*((p.target_firing_rate||0.2)-neuron.firing_rate_avg)*(neuron.msth.medium_gain); | |
| for(const syn of networkData.synapses){ | |
| if(syn.post_id===neuron.id&&syn.integrity>0){ | |
| syn.w_fast = Math.max(-1,Math.min(1,syn.w_fast*scale)); | |
| syn.w_slow = Math.max(-1,Math.min(1,syn.w_slow*scale)); | |
| } | |
| } | |
| } | |
| // Structural plasticity | |
| networkData.synapses = networkData.synapses.filter(s=>s.integrity>(p.synapse_integrity_threshold||0.1)); | |
| if(Math.random()<(p.synapse_formation_prob||0.05)){ | |
| const activeN = all.filter(n=>n.is_active); | |
| if(activeN.length>=2){ | |
| const pr = activeN[Math.floor(Math.random()*activeN.length)]; | |
| const po = activeN[Math.floor(Math.random()*activeN.length)]; | |
| if(pr.id!==po.id&&!(pr.type==='output'&&po.type==='input')&&!networkData.synapses.some(s=>s.pre_id===pr.id&&s.post_id===po.id)){ | |
| networkData.synapses.push(makeSynapse(pr.id,po.id,p.num_dendritic_branches||3,p)); | |
| } | |
| } | |
| } | |
| // Energy | |
| const activeCount = all.filter(n=>n.is_active&&n.trinary_state!==0).length; | |
| networkData.energy_usage += 0.01*activeCount*dt; | |
| networkData.time += dt; | |
| networkData.step_count++; | |
| spawnModulators(); | |
| updateVisualization(); | |
| updateLivePanels(); | |
| Object.values(neuronMeshes).forEach(m => { if(m.userData.spawnCooldown>0) m.userData.spawnCooldown--; }); | |
| } | |
| // ββ Neuromodulator system ββ | |
| function updateNeuromodulators(p, dt, meanAct, excFrac, changeRate) { | |
| const T = networkData.neuromod_tonic; | |
| const Ph = networkData.neuromod_phasic; | |
| const tauT = p.tau_tonic||5000; const tauP = p.tau_phasic||200; | |
| const rr = p.neuromod_release_rate||0.02; | |
| for(const [key,bl] of [['DA','dopamine_baseline'],['5HT','serotonin_baseline'],['ACh','acetylcholine_baseline'],['NA','norepinephrine_baseline']]){ | |
| T[key] += dt/tauT*((p[bl]||0.15)-T[key]); | |
| Ph[key] += dt/tauP*(0-Ph[key]); | |
| } | |
| Ph.DA += rr*changeRate*dt; | |
| T['5HT'] += rr*meanAct*dt; | |
| Ph.ACh += rr*excFrac*dt; | |
| Ph.NA += rr*changeRate*dt; | |
| Ph.ACh *= Math.max(0,1-0.1*Ph.DA); | |
| T['5HT'] += 0.02*(T.NA+Ph.NA)*dt; | |
| for(const k of ['DA','5HT','ACh','NA']){ | |
| T[k]=Math.max(0,Math.min(2,T[k])); Ph[k]=Math.max(0,Math.min(2,Ph[k])); | |
| } | |
| networkData.neuromodulators = { | |
| dopamine: T.DA+Ph.DA, serotonin: T['5HT']+Ph['5HT'], | |
| acetylcholine: T.ACh+Ph.ACh, norepinephrine: T.NA+Ph.NA, | |
| }; | |
| } | |
| function computeReceptorActivations() { | |
| const T = networkData.neuromod_tonic; | |
| const Ph = networkData.neuromod_phasic; | |
| const receptors = { | |
| D1:{parent:'DA',th:0.35,gain:1,tonic:false}, D2:{parent:'DA',th:0.25,gain:1,tonic:true}, | |
| '5HT1A':{parent:'5HT',th:0.05,gain:1,tonic:true}, '5HT2A':{parent:'5HT',th:0.30,gain:1,tonic:false}, | |
| '5HT4':{parent:'5HT',th:0.20,gain:1,tonic:false}, | |
| M1:{parent:'ACh',th:0.30,gain:1,tonic:false}, M2:{parent:'ACh',th:0.10,gain:1,tonic:true}, | |
| beta1:{parent:'NA',th:0.20,gain:1,tonic:false}, alpha2:{parent:'NA',th:0.08,gain:1,tonic:true}, | |
| }; | |
| const R = {}; | |
| for(const [name,r] of Object.entries(receptors)){ | |
| const conc = r.tonic ? T[r.parent] : (T[r.parent]+Ph[r.parent]); | |
| const k = r.tonic ? 20 : 10; | |
| const exp = Math.max(-50,Math.min(50,-k*(conc-r.th))); | |
| R[name] = r.gain/(1+Math.exp(exp)); | |
| } | |
| return R; | |
| } | |
| let pipelineStep = 0; | |
| function animatePipeline() { | |
| pipelineStep = (pipelineStep+1)%4; | |
| const ids = ['pip-chrono','pip-dsn','pip-ctsn','pip-agmp']; | |
| ids.forEach((id,i)=>{ | |
| document.getElementById(id).classList.toggle('active', i===pipelineStep); | |
| }); | |
| } | |
| function updateLivePanels() { | |
| const R = networkData.receptor_activations; | |
| for(const name of ['D1','D2','5HT1A','5HT2A','5HT4','M1','M2','beta1','alpha2']){ | |
| const el = document.getElementById('r-'+name); | |
| if(el) el.textContent = (R[name]||0).toFixed(2); | |
| } | |
| // Oscillator bars | |
| const osc = networkData.oscillator_phases; | |
| for(const band of ['infraslow','slow','theta','alpha','gamma']){ | |
| const el = document.getElementById('osc-'+band); | |
| if(el) el.style.height = (5+25*Math.abs(Math.sin(osc[band]||0)))+'px'; | |
| } | |
| } | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // THREE.JS VISUALIZATION | |
| // βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function initThreeJS() { | |
| const canvas = document.getElementById('networkCanvas'); | |
| scene = new THREE.Scene(); | |
| scene.background = new THREE.Color(0x06090f); | |
| scene.fog = null; /* no darkening when moving backwards (camera zoom/orbit) */ | |
| // --- NEW: softer biological lighting --- | |
| const hemi = new THREE.HemisphereLight(0x112244, 0x0a0f1f, 0.65); | |
| scene.add(hemi); | |
| camera = new THREE.PerspectiveCamera(65, canvas.clientWidth/canvas.clientHeight, 0.1, 600); | |
| camera.position.set(0, 12, 72); | |
| renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true }); | |
| renderer.setSize(canvas.clientWidth, canvas.clientHeight, false); | |
| renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); | |
| renderer.toneMapping = THREE.ACESFilmicToneMapping; | |
| renderer.toneMappingExposure = 1.2; | |
| orbit = new THREE.OrbitControls(camera, renderer.domElement); | |
| orbit.target.set(0, 0, 0); | |
| orbit.enableDamping = true; | |
| orbit.dampingFactor = 0.06; | |
| orbit.autoRotate = true; | |
| orbit.autoRotateSpeed = 0.25; | |
| orbit.update(); | |
| const ambient = new THREE.AmbientLight(0x334466, 0.35); | |
| scene.add(ambient); | |
| const dir = new THREE.DirectionalLight(0xa0c4ff, 0.75); // brighter | |
| dir.position.set(35, 45, 35); | |
| scene.add(dir); | |
| const point1 = new THREE.PointLight(0xff6ba3, 0.45, 140); | |
| point1.position.set(-35, 25, -25); | |
| scene.add(point1); | |
| const point2 = new THREE.PointLight(0x5ce1e6, 0.45, 140); | |
| point2.position.set(25, -20, 35); | |
| scene.add(point2); | |
| networkGroup = new THREE.Group(); | |
| scene.add(networkGroup); | |
| createEmojiTextures(); | |
| animate(performance.now()); | |
| } | |
| function createNeuronMesh(neuron) { | |
| let geo, colorHex, scale = 1; | |
| if (neuron.type === 'input') { | |
| geo = new THREE.BoxGeometry(1.5, 1.5, 1.5); | |
| colorHex = 0x00ccff; // PURE bright blue base for inputs | |
| scale = 1.25; | |
| } else if (neuron.type === 'output') { | |
| geo = new THREE.ConeGeometry(0.9, 2.2, 8); | |
| colorHex = 0xff3366; // PURE vivid red base for outputs | |
| scale = 1.25; | |
| } else { | |
| geo = new THREE.SphereGeometry(0.85, 24, 24); | |
| colorHex = 0xbb77ff; // vibrant purple for hidden | |
| } | |
| const base = new THREE.Color(colorHex); | |
| const mat = new THREE.MeshPhongMaterial({ | |
| color: base, | |
| emissive: base, | |
| emissiveIntensity: 0.85, // even stronger base glow | |
| transparent: true, opacity: 0.98, shininess: 95, | |
| }); | |
| const mesh = new THREE.Mesh(geo, mat); | |
| mesh.scale.set(scale, scale, scale); | |
| mesh.userData = { id: neuron.id, neuronRef: neuron, spawnCooldown: 0, baseColor: base, basePosition: mesh.position.clone(), velocity: new THREE.Vector3() }; | |
| return mesh; | |
| } | |
| // ββ NEW: store base positions for drifting ββ | |
| function layoutNeuronsSphere() { | |
| const all = [...networkData.neurons.input,...networkData.neurons.hidden,...networkData.neurons.output]; | |
| const radius = 22 + all.length * 0.18; | |
| const golden = Math.PI * (3 - Math.sqrt(5)); | |
| all.forEach((neuron, i) => { | |
| const y = 1 - (i / Math.max(1, all.length - 1)) * 2; | |
| const r = Math.sqrt(Math.max(0, 1 - y * y)); | |
| const theta = golden * i; | |
| const mesh = createNeuronMesh(neuron); | |
| mesh.position.set(Math.cos(theta)*r*radius, y*radius, Math.sin(theta)*r*radius); | |
| networkGroup.add(mesh); | |
| neuronMeshes[neuron.id] = mesh; | |
| mesh.userData.basePosition = mesh.position.clone(); | |
| }); | |
| } | |
| function makeCurve(start, end) { | |
| const dir = new THREE.Vector3().subVectors(end, start); | |
| const len = Math.max(0.001, dir.length()); | |
| const dirN = dir.clone().normalize(); | |
| let up = new THREE.Vector3(0, 1, 0); | |
| if (Math.abs(dirN.dot(up)) > 0.95) up = new THREE.Vector3(1, 0, 0); | |
| const normal = new THREE.Vector3().crossVectors(up, dirN).normalize(); | |
| const binorm = new THREE.Vector3().crossVectors(dirN, normal).normalize(); | |
| const amp = len * 0.18; | |
| const p1 = start.clone().add(dirN.clone().multiplyScalar(len*0.28)).add(normal.clone().multiplyScalar((Math.random()-0.5)*amp)); | |
| const p2 = start.clone().add(dirN.clone().multiplyScalar(len*0.72)).add(binorm.clone().multiplyScalar((Math.random()-0.5)*amp)); | |
| const curve = new THREE.CatmullRomCurve3([start.clone(), p1, p2, end.clone()]); | |
| curve.userData = { phase: Math.random()*Math.PI*2, chronoPhase: 0 }; | |
| return curve; | |
| } | |
| function createSynapseMesh(syn) { | |
| const pre = neuronMeshes[syn.pre_id]; | |
| const post = neuronMeshes[syn.post_id]; | |
| if(!pre||!post) return; | |
| const tw = syn.w_fast + syn.w_slow; | |
| const strength = Math.min(Math.abs(tw), 1.0); | |
| const c = tw > 0.02 ? 0x00ff88 : (tw < -0.02 ? 0xff4488 : 0x445577); | |
| const r = 0.018 + 0.062 * strength; // slightly thicker but softer | |
| const curve = makeCurve(pre.position, post.position); | |
| const geo = new THREE.TubeGeometry(curve, 24, r, 8, false); | |
| const mat = new THREE.MeshPhongMaterial({ | |
| color: c, | |
| emissive: new THREE.Color(c), | |
| emissiveIntensity: 0.52, // toned down | |
| transparent: true, | |
| opacity: 0.90, | |
| shininess: 70 | |
| }); | |
| const mesh = new THREE.Mesh(geo, mat); | |
| syn.post = post.userData.neuronRef; // for updateAllSynapses DSN check | |
| mesh.userData = { synapse: syn, curve, baseRadius: r, baseColor: new THREE.Color(c) }; | |
| networkGroup.add(mesh); | |
| synapseMeshes.push(mesh); | |
| } | |
| function updateNeuronVisual(mesh) { | |
| const n = mesh.userData.neuronRef; | |
| const base = mesh.userData.baseColor; | |
| const mat = mesh.material; | |
| // === PURE STATE COLORS === | |
| let targetColor = base.clone(); | |
| let ei = 0.85; | |
| if (n.trinary_state === 1) { | |
| ei = 1.35; | |
| if (n.type === 'input') targetColor.setHex(0x00eeff); // PURE cyan-blue +1 | |
| else if (n.type === 'output') targetColor.setHex(0xff1144); // FULL vivid red +1 | |
| else targetColor.setHex(0xdd44ff); // bright magenta hidden | |
| } | |
| else if (n.trinary_state === -1) { | |
| ei = 0.95; | |
| if (n.type === 'input') targetColor.setHex(0x4477ff); // deep blue -1 | |
| else if (n.type === 'output') targetColor.setHex(0xaa0033); // deep crimson -1 | |
| else targetColor.setHex(0x8844cc); // darker purple hidden | |
| } | |
| else { | |
| targetColor.setHex(0x88aaff); // neutral soft blue | |
| ei = 0.55; | |
| } | |
| mat.color.copy(targetColor); | |
| mat.emissive.copy(targetColor); | |
| mat.emissiveIntensity = n.is_active ? ei : 0.18; | |
| mesh.scale.setScalar((n.type==='input'||n.type==='output'?1.28:1) * (1 + 0.18*Math.abs(n.trinary_state))); | |
| // CTSN ring β keep subtle | |
| if (!mesh.userData.extra) mesh.userData.extra = {}; | |
| const extra = mesh.userData.extra; | |
| if (!extra.ctsnRing) { | |
| extra.ctsnRing = new THREE.Mesh(new THREE.RingGeometry(1.6, 1.8, 32), | |
| new THREE.MeshBasicMaterial({color:0x00ffff, transparent:true, opacity:0.3, side:THREE.DoubleSide})); | |
| mesh.add(extra.ctsnRing); | |
| } | |
| if (extra.ctsnRing) { | |
| extra.ctsnRing.scale.setScalar(1.12 + 0.55 * Math.abs(n.complement_h||0)); | |
| extra.ctsnRing.material.opacity = 0.18; | |
| } | |
| // AGMP vol β keep small | |
| if (!extra.agmpVol) { | |
| extra.agmpVol = new THREE.Mesh(new THREE.SphereGeometry(1.4, 32, 32), new THREE.MeshBasicMaterial({color:0x34d399, transparent:true, opacity:0, blending:THREE.AdditiveBlending})); | |
| mesh.add(extra.agmpVol); | |
| } | |
| if (extra.agmpVol) extra.agmpVol.material.opacity = 0.16 * (n.astrocyte_state||0); | |
| } | |
| // ββ NEW: dynamic neuron drifting (called every frame) ββ | |
| function updateDynamicPositions() { | |
| const all = [...networkData.neurons.input,...networkData.neurons.hidden,...networkData.neurons.output]; | |
| for (const n of all) { | |
| const mesh = neuronMeshes[n.id]; | |
| if (!mesh || !n.is_active) continue; | |
| const activity = Math.abs(n.trinary_state) + 0.3 * Math.abs(n.membrane_potential); | |
| const target = mesh.userData.basePosition; | |
| // gentle spring + activity-driven breathing | |
| const spring = target.clone().sub(mesh.position).multiplyScalar(0.008); | |
| mesh.userData.velocity.add(spring); | |
| // excitatory pull / inhibitory push between connected neurons | |
| networkData.synapses.forEach(s => { | |
| if (s.post_id === n.id && neuronMeshes[s.pre_id]) { | |
| const other = neuronMeshes[s.pre_id].position; | |
| const d = other.clone().sub(mesh.position); | |
| const dist = d.length(); | |
| if (dist > 0.01) { | |
| const dir = d.normalize(); | |
| const force = dir.multiplyScalar((s.w_fast + s.w_slow) * 0.012); | |
| mesh.userData.velocity.add(force); | |
| } | |
| } | |
| }); | |
| mesh.userData.velocity.multiplyScalar(0.89); // damping | |
| mesh.position.add(mesh.userData.velocity); | |
| // clamp drift | |
| const maxDrift = 2.8; | |
| if (mesh.position.distanceTo(target) > maxDrift) { | |
| mesh.position.lerp(target, 0.12); | |
| } | |
| } | |
| } | |
| // ββ NEW: dynamic synapse curves (fluctuating splines) ββ | |
| function updateAllSynapses() { | |
| synapseMeshes.forEach(mesh => { | |
| if (!mesh.visible) return; | |
| const syn = mesh.userData.synapse; | |
| if (!syn) return; | |
| const strength = Math.min(Math.abs(syn.w_fast + syn.w_slow), 1.0); | |
| const curve = mesh.userData.curve; | |
| const points = curve.getPoints(32); | |
| const time = performance.now() / 700; | |
| const omega = syn.chrono_omega || 0.5; | |
| for (let i = 1; i < points.length - 1; i++) { | |
| const wave = Math.sin(time * 3 + i * 1.2 + curve.userData.phase) * 0.45 * omega; | |
| points[i].add(new THREE.Vector3(wave*0.6, wave*0.4, wave*0.7)); | |
| } | |
| // extra DSN compression when decay is high | |
| if (syn.post && syn.post.dsn_alpha && syn.post.dsn_alpha < 0.65) { | |
| for (let i = 1; i < points.length - 1; i++) { | |
| points[i].multiplyScalar(0.92); | |
| } | |
| } | |
| mesh.geometry.dispose(); | |
| mesh.geometry = new THREE.TubeGeometry(new THREE.CatmullRomCurve3(points), 24, mesh.userData.baseRadius * (0.9 + strength*0.45), 8, false); | |
| }); | |
| } | |
| // ββ NEW: feature fly-out particles (the big visual win) ββ | |
| let featureParticles = []; | |
| function spawnFeatureParticles(neuron, type) { | |
| const mesh = neuronMeshes[neuron.id]; | |
| if (!mesh || mesh.userData.spawnCooldown > 0) return; | |
| mesh.userData.spawnCooldown = 18; // cooldown frames | |
| const colors = {ctsn:0x22ddff, agmp:0x34d399, dsn:0xffd700, chrono:0xc026d3, fire:0xa5f3fc}; | |
| const count = (type === 'fire' || type === 'ctsn' || type === 'agmp') ? 2 : 3; // only 2-3 max | |
| for (let i = 0; i < count; i++) { | |
| const sprite = new THREE.Sprite(new THREE.SpriteMaterial({ | |
| map: emojiTextures[type] || emojiTextures.dopamine, | |
| color: colors[type], | |
| transparent: true, | |
| opacity: 0.95, | |
| depthTest: false | |
| })); | |
| sprite.scale.set(0.85, 0.85, 0.85); | |
| sprite.position.copy(mesh.position); | |
| networkGroup.add(sprite); | |
| featureParticles.push({ | |
| sprite, | |
| velocity: new THREE.Vector3( | |
| (Math.random()-0.5)*0.45, | |
| (Math.random()-0.5)*0.55 + 0.3, | |
| (Math.random()-0.5)*0.45 | |
| ), | |
| life: 24, | |
| type | |
| }); | |
| } | |
| // ONE BIGGER inner burst on strong CTSN/AGMP | |
| if ((type === 'ctsn' || type === 'agmp') && Math.random() < 0.35) { | |
| const ring = new THREE.Mesh( | |
| new THREE.RingGeometry(1.1, 1.8, 48), | |
| new THREE.MeshBasicMaterial({color: colors[type], transparent:true, opacity:0.45, side:THREE.DoubleSide, blending:THREE.AdditiveBlending}) | |
| ); | |
| ring.position.copy(mesh.position); | |
| networkGroup.add(ring); | |
| setTimeout(() => { if(ring.parent) networkGroup.remove(ring); ring.geometry.dispose(); ring.material.dispose(); }, 420); | |
| } | |
| } | |
| function updateFeatureParticles() { | |
| for (let i = featureParticles.length - 1; i >= 0; i--) { | |
| const p = featureParticles[i]; | |
| p.sprite.position.add(p.velocity); | |
| p.velocity.multiplyScalar(0.94); | |
| p.life--; | |
| p.sprite.material.opacity = p.life / 32; | |
| if (p.life <= 0) { | |
| networkGroup.remove(p.sprite); | |
| p.sprite.material.dispose(); | |
| featureParticles.splice(i, 1); | |
| } | |
| } | |
| } | |
| function updateVisualization() { | |
| Object.values(neuronMeshes).forEach(updateNeuronVisual); | |
| // ββ NEW: Dynamic synapse visuals (this is why they never changed before) ββ | |
| synapseMeshes.forEach(updateSynapseVisual); | |
| const all = [...networkData.neurons.input,...networkData.neurons.hidden,...networkData.neurons.output]; | |
| document.getElementById('neuron-count').textContent = all.length; | |
| document.getElementById('synapse-count').textContent = networkData.synapses.length; | |
| document.getElementById('modulator-count').textContent = modulators.length; | |
| document.getElementById('time').textContent = networkData.time.toFixed(1); | |
| document.getElementById('steps').textContent = networkData.step_count; | |
| document.getElementById('energy').textContent = networkData.energy_usage.toFixed(3); | |
| const eFill = Math.min(100, networkData.energy_usage * 2); | |
| document.getElementById('energy-fill').style.width = eFill + '%'; | |
| document.getElementById('active-count').textContent = all.filter(n=>n.is_active&&n.trinary_state!==0).length+'/'+all.length; | |
| } | |
| function updateSynapseVisual(mesh) { | |
| const syn = mesh.userData.synapse; | |
| if(!syn || syn.integrity <= 0) { | |
| mesh.visible = false; | |
| return; | |
| } | |
| mesh.visible = true; | |
| const tw = syn.w_fast + syn.w_slow; | |
| const strength = Math.min(Math.abs(tw), 1.0); | |
| // === PURE VIVID COLORS WHEN FULL ACTIVE === | |
| let targetColor; | |
| if (tw > 0.35) targetColor = new THREE.Color(0x00ff55); // PURE electric green excitatory | |
| else if (tw > 0.08) targetColor = new THREE.Color(0x44ffaa); // bright green | |
| else if (tw < -0.35) targetColor = new THREE.Color(0xff2255); // PURE vivid red inhibitory | |
| else if (tw < -0.08) targetColor = new THREE.Color(0xff6688); // bright rose-red | |
| else targetColor = new THREE.Color(0x77ddff); // soft cyan neutral | |
| mesh.material.color.lerp(targetColor, 0.35); // faster transition | |
| mesh.material.emissive.copy(mesh.material.color); | |
| // Dynamic thickness (pulses with strength + ChronoPlasticity) | |
| const chronoBoost = syn.chrono_omega ? (syn.chrono_omega - 0.5) * 0.4 : 0; | |
| const newRadius = mesh.userData.baseRadius * (0.95 + strength * 0.35 + chronoBoost); | |
| mesh.geometry.dispose(); | |
| mesh.geometry = new THREE.TubeGeometry( | |
| mesh.userData.curve, 32, newRadius, 8, false | |
| ); | |
| // brighter when active | |
| mesh.material.emissiveIntensity = 0.95 + 0.95 * strength; | |
| mesh.material.opacity = 0.96 + 0.04 * strength; | |
| } | |
| function initVisualization() { | |
| layoutNeuronsSphere(); | |
| networkData.synapses.forEach(createSynapseMesh); | |
| updateVisualization(); | |
| createInputControls(); | |
| setupLiveControls(); | |
| startModulatorSpawning(); | |
| } | |
| function createInputControls() { | |
| const c = document.getElementById('input-states'); c.innerHTML = ''; | |
| networkData.neurons.input.forEach((n, i) => { | |
| const btn = document.createElement('button'); | |
| btn.className = 'input-btn'; btn.textContent = `I${i}`; | |
| btn.dataset.neuronId = n.id; btn.dataset.state = n.trinary_state; | |
| styleInputButton(btn, n.trinary_state); | |
| btn.onclick = () => { | |
| let ns = (parseInt(btn.dataset.state)+2)%3-1; | |
| btn.dataset.state = ns; n.trinary_state = ns; | |
| n.membrane_potential = ns * (networkData.parameters.firing_threshold_excitatory||0.4); | |
| n.state_tilde = n.membrane_potential + n.complement_h; | |
| styleInputButton(btn, ns); | |
| if(neuronMeshes[n.id]) updateNeuronVisual(neuronMeshes[n.id]); | |
| }; | |
| c.appendChild(btn); | |
| }); | |
| } | |
| function styleInputButton(btn, s) { | |
| btn.classList.remove('state-excitatory','state-inhibitory','state-neutral'); | |
| const lbl = btn.textContent.split(' ')[0]; | |
| if(s===1){btn.classList.add('state-excitatory');btn.textContent=lbl+' +1';} | |
| else if(s===-1){btn.classList.add('state-inhibitory');btn.textContent=lbl+' -1';} | |
| else{btn.classList.add('state-neutral');btn.textContent=lbl+' 0';} | |
| } | |
| function setupLiveControls() { | |
| ['dopamine','serotonin','acetylcholine','norepinephrine'].forEach(mod=>{ | |
| const sl = document.getElementById(`${mod}-live`); | |
| const disp = document.getElementById(`val-${mod}-live`); | |
| sl.value = networkData.neuromodulators[mod]; | |
| disp.textContent = parseFloat(sl.value).toFixed(2); | |
| sl.addEventListener('input', e => { | |
| const v = parseFloat(e.target.value); | |
| disp.textContent = v.toFixed(2); | |
| networkData.neuromodulators[mod] = v; | |
| // Also update tonic | |
| const map = {dopamine:'DA',serotonin:'5HT',acetylcholine:'ACh',norepinephrine:'NA'}; | |
| if(networkData.neuromod_tonic) networkData.neuromod_tonic[map[mod]] = v; | |
| }); | |
| }); | |
| document.getElementById('btn-simulate').addEventListener('click', () => { | |
| if(isSimulating) return; | |
| isSimulating = true; | |
| const btn = document.getElementById('btn-simulate'); | |
| btn.classList.add('simulation-running'); btn.textContent = 'β΅ Running...'; | |
| const steps = parseInt(document.getElementById('sim-steps').value,10); | |
| let cur = 0; | |
| simulationInterval = setInterval(() => { | |
| simulateStep(); | |
| if(++cur>=steps) document.getElementById('btn-stop').click(); | |
| }, 80); | |
| }); | |
| document.getElementById('btn-stop').addEventListener('click', () => { | |
| if(simulationInterval) clearInterval(simulationInterval); | |
| simulationInterval = null; isSimulating = false; | |
| const btn = document.getElementById('btn-simulate'); | |
| btn.classList.remove('simulation-running'); btn.textContent = 'βΆ Run'; | |
| }); | |
| document.getElementById('btn-reset').addEventListener('click', () => { | |
| document.getElementById('btn-stop').click(); | |
| [...networkData.neurons.input,...networkData.neurons.hidden,...networkData.neurons.output].forEach(n=>{ | |
| n.trinary_state=0; n.membrane_potential=0; n.state_tilde=0; n.complement_h=0; | |
| n.adaptation=0; n.autoreceptor=0; n.astrocyte_state=0; | |
| }); | |
| networkData.time=0; networkData.step_count=0; networkData.energy_usage=0; | |
| updateVisualization(); createInputControls(); | |
| }); | |
| } | |
| // ββ Neuromodulator emoji particles ββ | |
| const MOD_EMOJI = {dopamine:'π―',serotonin:'π',acetylcholine:'π‘',norepinephrine:'β‘'}; | |
| const FEATURE_EMOJI = {ctsn:'π',agmp:'πΏ',dsn:'β¬',chrono:'β³',fire:'β¨'}; | |
| const emojiTextures = {}; | |
| function createEmojiTextures() { | |
| Object.entries(MOD_EMOJI).forEach(([key,emoji])=>{ | |
| const c = document.createElement('canvas'); c.width=64; c.height=64; | |
| const ctx = c.getContext('2d'); ctx.font='48px serif'; ctx.textAlign='center'; ctx.textBaseline='middle'; | |
| ctx.fillText(emoji,32,32); | |
| emojiTextures[key] = new THREE.CanvasTexture(c); | |
| }); | |
| Object.entries(FEATURE_EMOJI).forEach(([key,emoji])=>{ | |
| const c = document.createElement('canvas'); c.width=64; c.height=64; | |
| const ctx = c.getContext('2d'); ctx.font='48px serif'; ctx.textAlign='center'; ctx.textBaseline='middle'; | |
| ctx.fillText(emoji,32,32); | |
| emojiTextures[key] = new THREE.CanvasTexture(c); | |
| }); | |
| } | |
| function spawnModulators() { | |
| if(!networkData) return; | |
| Object.entries(networkData.neuromodulators).forEach(([key,level])=>{ | |
| const count = Math.floor(level*2+Math.random()); | |
| for(let i=0;i<count;i++){ | |
| if(!synapseMeshes.length) break; | |
| const s = synapseMeshes[Math.floor(Math.random()*synapseMeshes.length)]; | |
| if(!s.userData.curve) continue; | |
| const sp = new THREE.Sprite(new THREE.SpriteMaterial({map:emojiTextures[key],transparent:true,opacity:0.85})); | |
| sp.scale.set(1.2,1.2,1.2); | |
| networkGroup.add(sp); | |
| modulators.push({sprite:sp,curve:s.userData.curve,progress:Math.random()*0.3,speed:0.005+0.02*level+Math.random()*0.005}); | |
| } | |
| }); | |
| } | |
| function updateModulators(delta) { | |
| const rem = []; | |
| modulators.forEach((m,i)=>{ | |
| m.progress += m.speed*(delta/16.67); | |
| if(m.progress>=1) rem.push(i); | |
| else m.sprite.position.copy(m.curve.getPoint(m.progress)); | |
| }); | |
| for(let i=rem.length-1;i>=0;i--){ | |
| networkGroup.remove(modulators[rem[i]].sprite); | |
| modulators[rem[i]].sprite.material.dispose(); | |
| modulators.splice(rem[i],1); | |
| } | |
| } | |
| let lastTs = performance.now(); | |
| function animate(ts) { | |
| requestAnimationFrame(animate); | |
| const delta = ts - lastTs; lastTs = ts; | |
| updateModulators(delta); | |
| updateFeatureParticles(); | |
| updateDynamicPositions(); | |
| updateAllSynapses(); // living curves | |
| orbit.update(); | |
| const canvas = document.getElementById('networkCanvas'); | |
| const rect = canvas.getBoundingClientRect(); | |
| const w = Math.max(1,Math.floor(rect.width)); const h = Math.max(1,Math.floor(rect.height)); | |
| if(canvas.width!==w||canvas.height!==h){ camera.aspect=w/h; camera.updateProjectionMatrix(); renderer.setSize(w,h,false); } | |
| renderer.render(scene, camera); | |
| } | |
| let modulatorSpawnTimer = null; | |
| function startModulatorSpawning() { | |
| if(modulatorSpawnTimer) return; | |
| spawnModulators(); | |
| modulatorSpawnTimer = setInterval(() => { if(!isSimulating && networkData) spawnModulators(); }, 2500); | |
| } | |
| function backToConfig() { | |
| document.getElementById('viz-container').classList.remove('visible'); | |
| document.getElementById('config-panel').classList.remove('hidden'); | |
| if(simulationInterval){clearInterval(simulationInterval);simulationInterval=null;isSimulating=false;} | |
| if(modulatorSpawnTimer){clearInterval(modulatorSpawnTimer);modulatorSpawnTimer=null;} | |
| if(networkGroup){while(networkGroup.children.length>0)networkGroup.remove(networkGroup.children[0]);} | |
| modulators.forEach(m=>{if(m.sprite&&m.sprite.material)m.sprite.material.dispose();}); | |
| modulators=[]; neuronMeshes={}; synapseMeshes=[]; networkData=null; | |
| } | |
| document.getElementById('build-network-btn').addEventListener('click', buildNetwork); | |
| window.addEventListener('resize', () => { | |
| if(camera&&renderer){ | |
| const c = document.getElementById('networkCanvas'); const r = c.getBoundingClientRect(); | |
| camera.aspect=r.width/r.height; camera.updateProjectionMatrix(); renderer.setSize(r.width,r.height,false); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |