Neuraxon / index.html
DavidVivancos's picture
Upload index.html
40c6f84 verified
<!DOCTYPE html>
<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) !important; border-color: var(--success) !important; }
/* ── 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% !important; 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>