-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdynamical-systems.html
More file actions
469 lines (442 loc) · 74 KB
/
Copy pathdynamical-systems.html
File metadata and controls
469 lines (442 loc) · 74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="author" content="N. Apurva Ratan Murty">
<title>Dynamical Systems: Foundations · Computational Neuroscience · Georgia Tech</title>
<script>
MathJax = {
tex: { inlineMath: [['$','$'],['\\(','\\)']], displayMath: [['$$','$$'],['\\[','\\]']], tags:'none' },
options: { skipHtmlTags: ['script','noscript','style','textarea','canvas'] },
startup: {
ready() {
MathJax.startup.defaultReady();
MathJax.startup.promise.then(() => MathJax.typesetPromise());
}
}
};
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-chtml.min.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,300;0,6..72,400;0,6..72,500;1,6..72,300;1,6..72,400&family=Inter:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root {
--white: #ffffff; --off: #f9f9f8; --bg: #ffffff;
--border: #e6e6e4; --border-faint: #f0f0ee;
--text: #050504; --body: #0d0d0b; --secondary: #242420; --tertiary: #4a4a45;
--gold: #a97c10; --gold-vivid: #C29122;
--gold-bg: rgba(169,124,16,0.07); --gold-border: rgba(169,124,16,0.2);
--navy: #003057; --accent: #1a56db; --accent-bg: rgba(26,86,219,0.07);
--teal: #0c7a5e; --teal-bg: rgba(12,122,94,0.08);
--purple: #6d28d9; --purple-bg: rgba(109,40,217,0.07);
--red: #be3a2a; --red-bg: rgba(190,58,42,0.07);
--sans: 'Inter', system-ui, sans-serif;
--serif: 'Newsreader', Georgia, serif;
--mono: 'JetBrains Mono', monospace;
--col: 660px;
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body { background: var(--bg); color: var(--body); font-family: var(--serif); font-size: 1.18rem; line-height: 1.82; -webkit-font-smoothing: antialiased; }
mjx-container[jax="CHTML"][display="false"] { font-size: 0.95em !important; vertical-align: -0.1em; }
mjx-container[jax="CHTML"][display="true"] { margin: 0.5em 0 !important; overflow-x: auto; }
nav { position: fixed; top: 0; left: 0; right: 0; z-index: 200; height: 50px; background: rgba(255,255,255,0.95); backdrop-filter: blur(10px); border-bottom: 1px solid var(--border); display: flex; align-items: center; padding: 0 28px; }
.nav-mark { width: 28px; height: 28px; border-radius: 6px; background: var(--navy); display: flex; align-items: center; justify-content: center; font-family: Georgia,serif; font-size: 11px; font-weight: 700; color: var(--gold-vivid); flex-shrink: 0; margin-right: 10px; }
.nav-title { font-family: var(--sans); font-size: 0.78rem; font-weight: 600; color: var(--text); margin-right: 16px; }
.nav-sep { width: 1px; height: 16px; background: var(--border); margin-right: 16px; }
.nav-course { font-family: var(--sans); font-size: 0.7rem; color: var(--tertiary); }
.nav-links { margin-left: auto; display: flex; align-items: center; gap: 2px; overflow-x: auto; scrollbar-width: none; }
.nav-links::-webkit-scrollbar { display: none; }
.nav-links a { font-family: var(--sans); font-size: 0.7rem; font-weight: 500; color: var(--secondary); text-decoration: none; padding: 5px 9px; border-radius: 5px; white-space: nowrap; transition: background .1s, color .1s; }
.nav-links a:hover { background: var(--off); color: var(--text); }
@media (max-width: 700px) { .nav-links { display: none; } }
@media (max-width: 480px) { .nav-course { display: none; } .nav-sep { display: none; } }
.hero { position: relative; overflow: hidden; background: var(--navy); padding: 76px 28px 68px; }
#hero-cv { position: absolute; inset: 0; pointer-events: none; opacity: 0.14; }
.hero-in { position: relative; z-index: 1; max-width: var(--col); margin: 0 auto; }
.hero-chips { display: flex; align-items: center; gap: 10px; margin-bottom: 26px; flex-wrap: wrap; }
.chip { font-family: var(--sans); font-size: 0.65rem; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; padding: 4px 10px; border-radius: 4px; }
.chip-gold { color: var(--gold-vivid); background: rgba(194,145,34,0.12); border: 1px solid rgba(194,145,34,0.28); }
.chip-nav { font-family: var(--mono); font-size: 0.6rem; color: rgba(255,255,255,0.25); background: transparent; border: none; font-weight: 400; letter-spacing: .04em; padding: 0; }
h1 { font-family: var(--serif); font-weight: 300; font-size: clamp(2.1rem,5vw,3.4rem); line-height: 1.08; letter-spacing: -0.028em; color: #fff; margin-bottom: 16px; }
.hero-sub { font-family: var(--sans); font-weight: 400; font-size: 1rem; line-height: 1.7; color: rgba(255,255,255,0.78); max-width: 520px; margin-bottom: 36px; }
.hero-foot { padding-top: 28px; border-top: 1px solid rgba(255,255,255,0.18); }
.hero-by { font-family: var(--sans); font-size: 0.88rem; color: rgba(255,255,255,0.7); line-height: 1.65; }
.hero-by strong { color: #ffffff; font-weight: 600; font-size: 1rem; }
.page { padding-top: 50px; }
main { max-width: var(--col); margin: 0 auto; padding: 0 28px 100px; }
@media (max-width: 600px) { main { padding: 0 16px 80px; } .hero { padding: 52px 20px 48px; } nav { padding: 0 16px; } h1 { letter-spacing: -0.02em; } .lede { margin-top: 40px; } .section { margin-top: 52px; } }
.lede { margin-top: 60px; padding-bottom: 48px; border-bottom: 1px solid var(--border-faint); }
.lede p { font-size: 1.24rem; font-weight: 300; line-height: 1.78; }
.section { margin-top: 68px; }
.sec-ey { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; }
.sec-num { font-family: var(--mono); font-size: 0.58rem; color: var(--gold); font-weight: 500; letter-spacing: .04em; }
.sec-tag { font-family: var(--sans); font-size: 0.65rem; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--tertiary); }
.sec-line { flex: 1; height: 1px; background: var(--border-faint); }
h2 { font-family: var(--serif); font-size: clamp(1.55rem,3vw,2rem); font-weight: 400; line-height: 1.18; letter-spacing: -0.02em; color: var(--text); margin-bottom: 8px; }
.hook { font-family: var(--serif); font-size: 1.08rem; font-style: italic; font-weight: 300; color: var(--secondary); line-height: 1.6; margin-bottom: 24px; }
p { margin-bottom: 1.1em; }
p:last-child { margin-bottom: 0; }
.term { font-style: normal; font-weight: 500; color: var(--gold); background: var(--gold-bg); padding: 0 4px; border-radius: 3px; }
.eq-block { margin: 28px 0; padding: 18px 20px; -webkit-overflow-scrolling: touch; background: var(--off); border: 1px solid var(--border); border-left: 2px solid var(--navy); border-radius: 0 7px 7px 0; overflow-x: auto; }
.eq-lbl { font-family: var(--sans); font-size: 0.62rem; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; color: var(--tertiary); margin-bottom: 10px; }
.callout { margin: 26px 0; padding: 16px 20px; border-radius: 7px; border: 1px solid; }
.c-title { font-family: var(--sans); font-size: 0.68rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; margin-bottom: 7px; }
.callout p { font-family: var(--sans); font-size: 0.93rem; line-height: 1.62; color: var(--secondary); margin-bottom: 5px; }
.callout p:last-child { margin-bottom: 0; }
.callout strong { color: var(--body); font-weight: 600; }
.c-gold { background: var(--gold-bg); border-color: var(--gold-border); } .c-gold .c-title { color: var(--gold); }
.c-blue { background: var(--accent-bg); border-color: rgba(26,86,219,0.18); } .c-blue .c-title { color: var(--accent); }
.c-teal { background: var(--teal-bg); border-color: rgba(12,122,94,0.2); } .c-teal .c-title { color: var(--teal); }
.c-purple { background: var(--purple-bg); border-color: rgba(109,40,217,0.17); } .c-purple .c-title { color: var(--purple); }
.c-red { background: var(--red-bg); border-color: rgba(190,58,42,0.17); } .c-red .c-title { color: var(--red); }
.fig { margin: 38px -28px; border: 1px solid var(--border); border-radius: 9px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,0.05),0 3px 12px rgba(0,0,0,0.04); }
.fig-head { padding: 11px 16px; border-bottom: 1px solid var(--border-faint); display: flex; align-items: center; gap: 9px; background: var(--off); }
.f-title { font-family: var(--sans); font-size: 0.82rem; font-weight: 500; color: var(--secondary); flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.f-badge { font-family: var(--sans); font-size: 0.6rem; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; padding: 2px 8px; border-radius: 4px; flex-shrink: 0; }
.f-st { font-family: var(--mono); font-size: 0.6rem; color: var(--tertiary); flex-shrink: 0; }
.cv-wrap { background: #fff; height: 310px; position: relative; display: flex; align-items: stretch; }
canvas { display: block; width: 100% !important; }
.ctrl { padding: 10px 14px; border-top: 1px solid var(--border-faint); display: flex; flex-wrap: wrap; align-items: center; gap: 7px; background: var(--off); }
.btn { padding: 5px 13px; border-radius: 6px; border: 1px solid var(--border); background: var(--white); color: var(--body); font-family: var(--sans); font-size: 0.76rem; font-weight: 500; cursor: pointer; transition: border-color .12s; white-space: nowrap; }
.btn:hover { border-color: #999; }
.btn.p { color: #fff; border-color: transparent; font-weight: 600; }
.btn.p:hover { opacity: .87; }
.btn:disabled { opacity: .35; cursor: default; }
.sl-row { display: flex; align-items: center; gap: 7px; font-family: var(--sans); font-size: 0.74rem; color: var(--secondary); }
.sl-row input[type=range] { width: 88px; accent-color: var(--gold-vivid); cursor: pointer; }
.sl-val { font-family: var(--mono); font-size: 0.68rem; color: var(--gold); font-weight: 500; min-width: 34px; }
.f-cap { padding: 10px 16px; font-family: var(--sans); font-size: 0.82rem; color: var(--secondary); line-height: 1.58; border-top: 1px solid var(--border-faint); background: var(--off); }
@media (max-width: 600px) {
.fig { margin: 28px -16px; border-radius: 0; border-left: none; border-right: none; }
.cv-wrap { height: 240px !important; }
.ctrl { gap: 5px; padding: 8px 12px; }
.btn { padding: 5px 10px; font-size: 0.72rem; }
.sl-row input[type=range] { width: 70px; }
h1 { font-size: 2rem; }
.hero { padding: 48px 20px 52px; }
main { padding: 0 16px 60px; }
nav { padding: 0 16px; }
.eq-block { padding: 14px 16px; }
.callout { padding: 13px 14px; }
.summ-grid { grid-template-columns: 1fr 1fr; }
.cv-wrap { height: 260px; } .ctrl { padding: 10px 14px; gap: 8px; } .btn { padding: 7px 14px; font-size: 0.82rem; min-height: 38px; } .sl-row input[type=range] { width: 100px; } .fig-head { padding: 10px 14px; } .f-title { font-size: 0.72rem; } .f-badge { display: none; } }
.q-block { margin-bottom: 20px; padding: 20px 22px; border: 1px solid var(--border); border-radius: 8px; background: var(--white); }
.q-num { font-family: var(--mono); font-size: 0.6rem; color: var(--tertiary); margin-bottom: 6px; letter-spacing:.04em; }
.q-text { font-family: var(--sans); font-size: 0.96rem; font-weight: 500; color: var(--text); line-height: 1.5; margin-bottom: 14px; }
.q-opts { display: flex; flex-direction: column; gap: 7px; }
.q-opt { display: flex; align-items: flex-start; gap: 10px; padding: 11px 13px; border: 1px solid var(--border); border-radius: 6px; cursor: pointer; font-family: var(--sans); font-size: 0.88rem; color: var(--secondary); line-height: 1.45; transition: background .12s,border-color .12s,color .12s; background: var(--white); text-align: left; width: 100%; }
.q-opt:hover:not(:disabled) { background: var(--off); border-color: #bbb; color: var(--body); }
.q-opt:disabled { cursor: default; }
.q-opt.correct { background: rgba(12,122,94,0.07); border-color: rgba(12,122,94,0.35); color: var(--teal); }
.q-opt.wrong { background: rgba(190,58,42,0.07); border-color: rgba(190,58,42,0.3); color: var(--red); }
.q-opt.reveal { background: rgba(12,122,94,0.05); border-color: rgba(12,122,94,0.2); color: var(--teal); opacity: 0.75; }
.q-letter { font-family: var(--mono); font-size: 0.65rem; font-weight: 600; flex-shrink: 0; margin-top: 1px; opacity: 0.5; }
.q-feedback { margin-top: 10px; font-family: var(--sans); font-size: 0.8rem; line-height: 1.5; padding: 8px 12px; border-radius: 5px; display: none; }
.q-feedback.show { display: block; }
.q-feedback.correct { background: rgba(12,122,94,0.07); color: var(--teal); }
.q-feedback.wrong { background: rgba(190,58,42,0.07); color: var(--red); }
.summ { margin-top: 68px; padding-top: 48px; border-top: 1px solid var(--border); }
.summ-lbl { font-family: var(--sans); font-size: 0.65rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--tertiary); margin-bottom: 22px; }
.summ-grid { display: grid; grid-template-columns: repeat(auto-fill,minmax(160px,1fr)); gap: 1px; background: var(--border); border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.summ-card { background: var(--white); padding: 16px 18px; }
.summ-n { font-family: var(--mono); font-size: 0.56rem; font-weight: 500; color: var(--gold); letter-spacing: .05em; margin-bottom: 4px; }
.summ-card h4 { font-family: var(--sans); font-size: 0.92rem; font-weight: 600; color: var(--text); margin-bottom: 4px; line-height: 1.3; }
.summ-card p { font-family: var(--sans); font-size: 0.82rem; line-height: 1.55; color: var(--secondary); margin: 0; }
.summ-next { margin-top: 28px; font-family: var(--sans); font-size: 0.86rem; color: var(--secondary); line-height: 1.65; }
footer { border-top: 1px solid var(--border); padding: 24px 28px; background: var(--off); }
.foot-in { max-width: 800px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px; }
.foot-l { font-family: var(--sans); font-size: 0.72rem; color: var(--tertiary); line-height: 1.5; }
.foot-l strong { color: var(--secondary); font-weight: 500; }
.foot-r { display: flex; align-items: center; gap: 7px; font-family: var(--sans); font-size: 0.7rem; color: var(--tertiary); }
.foot-badge { width: 22px; height: 22px; border-radius: 4px; background: var(--navy); display: flex; align-items: center; justify-content: center; font-family: Georgia,serif; font-size: 9px; font-weight: 700; color: var(--gold-vivid); }
</style>
</head>
<body>
<nav>
<div class="nav-mark">GT</div>
<span class="nav-title">Computational Neuroscience</span>
<div class="nav-sep"></div>
<span class="nav-course">PSYC 3803 · PSYC 8805 · NEUR 4803</span>
<div class="nav-links">
<a href="#intro">Dynamical systems</a>
<a href="#state-space">State space</a>
<a href="#diffeq">Differential equations</a>
<a href="#fixed-points">Fixed points</a>
<a href="#stability">Stability</a>
<a href="#one-d">1D dynamics</a>
<a href="#phase-plane">Phase plane</a>
<a href="#concept-check">Concept check</a>
</div>
</nav>
<div class="page">
<div class="hero">
<canvas id="hero-cv"></canvas>
<div class="hero-in">
<div class="hero-chips">
<span class="chip chip-gold">Module 1 · Foundations</span>
<span class="chip chip-nav">Georgia Institute of Technology</span>
</div>
<h1>Dynamical Systems</h1>
<p class="hero-sub">A mathematical language for thinking about neural activity as something that unfolds in time — trajectories through state space, shaped by the structure of the underlying system.</p>
<div class="hero-foot">
<div class="hero-by">
<strong>N. Apurva Ratan Murty, PhD</strong><br>
Assistant Professor, School of Psychology · Georgia Tech<br>
<span style="margin-top:4px;display:inline-flex;align-items:center;gap:16px;">
<a href="https://murtylab.com" target="_blank" style="color:rgba(255,255,255,0.65);font-size:0.82rem;text-decoration:none;" onmouseover="this.style.color='#ffffff'" onmouseout="this.style.color='rgba(255,255,255,0.65)'">murtylab.com ↗</a>
<a href="#" onclick="window.location='mai'+'lto:ratan'+'@gatech.edu';return false;" style="color:rgba(255,255,255,0.65);font-size:0.82rem;text-decoration:none;" onmouseover="this.style.color='#ffffff'" onmouseout="this.style.color='rgba(255,255,255,0.65)'"><span class="__cf_email__" data-cfemail="bdcfdcc9dcd3fddadcc9d8ded593d8d9c8">[email protected]</span></a>
</span>
</div>
</div>
<div style="margin-top:20px;padding-top:20px;border-top:1px solid rgba(255,255,255,0.12);">
<p style="font-family:var(--sans);font-size:0.75rem;color:rgba(255,255,255,0.45);margin:0;">
Fact-checking assistance from
<span style="color:rgba(255,255,255,0.72);font-weight:500;">Mayukh Deb</span>,
<span style="color:rgba(255,255,255,0.72);font-weight:500;">Alish Dipani</span>, and
<span style="color:rgba(255,255,255,0.72);font-weight:500;">Nikolas McNeal</span>
</p>
</div>
</div>
</div>
<main>
<div class="lede">
<p>You have already seen what a <span class="term">neural manifold</span> looks like: a low-dimensional shape that emerges because neural activity patterns vary together in structured ways. But the important thing to remember is that the activity itself is dynamic. It does not simply sit on the manifold. As the animal perceives, remembers, decides, or acts, the population state moves through that space. Dynamical systems theory helps explain how and why the activity moves through it.</p>
<div class="section" id="intro">
<div class="sec-ey"><span class="sec-num">01</span><span class="sec-tag">Foundations</span><div class="sec-line"></div></div>
<h2>What is a dynamical system?</h2>
<p class="hook">A system whose state evolves over time according to a rule.</p>
<p>A dynamical system has two essential ingredients. The first is a <span class="term">state</span>: a set of variables that captures everything needed to describe the system at a given moment. The second is a <span class="term">rule of evolution</span>: a mathematical rule that determines how that state changes over time. Together, these two pieces define the system's behavior.</p>
<p>In neural systems, the state might be the membrane potential of a single neuron, the joint firing rates of a population, or a small set of latent variables that summarize where the activity sits on the manifold. The rule is the biophysics — synaptic currents, adaptation, recurrent connections, external drive — that governs how those variables change from moment to moment. Together, state and rule produce a <span class="term">trajectory</span>: the path the system traces through its space of possible configurations as time unfolds.</p>
<p>This is the central shift in perspective this module builds. Rather than asking what the neural activity looks like at a single moment, we ask how it flows: where does the trajectory go, what does it converge to, and what determines its shape? These are the questions that connect circuit structure to computation — and they are exactly the questions that dynamical systems theory is designed to answer.</p>
<div class="fig">
<div class="fig-head"><div class="f-title">Figure 1 — Three systems, same starting state, different rules</div><div class="f-badge" style="background:var(--gold-bg);color:var(--gold)">Interactive</div><div class="f-st" id="ds-status">Paused</div></div>
<div class="cv-wrap"><canvas id="cv-ds"></canvas></div>
<div class="ctrl"><button class="btn p" style="background:var(--gold-vivid)" onclick="dsReset()">↺ Restart</button><button class="btn" onclick="dsToggle()" id="ds-btn">▶ Play</button><span style="font-size:0.73rem;color:var(--secondary);font-family:var(--sans)">Decay · Oscillate · Grow</span></div>
<div class="f-cap">All three systems begin at $x = 0.5$ and evolve under $\dot{x} = f(x)$. <em>Decay</em> relaxes toward zero; <em>Oscillate</em> follows a sinusoidal rule; <em>Grow</em> is pushed toward a ceiling by logistic growth. Same structure, entirely different long-term behavior.</div>
</div>
<div class="callout c-gold"><div class="c-title">👉 Try it</div><p>Press <strong>Play</strong> to watch all three trajectories evolve from the same starting point. The system's <em>rule</em> — not its initial state — determines its long-term fate.</p></div>
</div>
<div class="section" id="state-space">
<div class="sec-ey"><span class="sec-num">02</span><span class="sec-tag">Geometry</span><div class="sec-line"></div></div>
<h2>State and state space</h2>
<p class="hook">The state is where the system is. State space is all the places it could be.</p>
<p>The <span class="term">state</span> of a system is a complete description of its condition at a given moment. The <span class="term">state space</span> is the set of all possible states the system can occupy. For a single neuron with membrane voltage $V$ and a recovery variable $w$, the state is the pair $(V, w)$. For a population model with $N$ firing rates, the state is a point in an $N$-dimensional space.</p>
<p>As the system evolves over time, that point moves through state space, tracing out a path called a <span class="term">trajectory</span>. This geometric viewpoint is central to computational neuroscience. Rather than focusing on the activity of individual neurons, we can ask where the system as a whole lies in state space, how it moves, and what kinds of structures organize that movement.</p>
<p>Ideas like <span class="term">attractors</span>, <span class="term">oscillations</span>, and <span class="term">bifurcations</span> are most naturally understood in these terms. An attractor is not a single neuron's property — it is a region of state space that trajectories flow toward. An oscillation is a closed loop in state space that the system traces repeatedly.</p>
<div class="fig">
<div class="fig-head"><div class="f-title">Figure 2 — 2D state space · click anywhere to launch a trajectory</div><div class="f-badge" style="background:var(--teal-bg);color:var(--teal)">Interactive</div><div class="f-st" id="ss-status">Click to explore</div></div>
<div class="cv-wrap" style="cursor:crosshair"><canvas id="cv-ss"></canvas></div>
<div class="ctrl"><button class="btn p" style="background:var(--teal)" onclick="ssClear()">Clear</button><button class="btn" onclick="ssAddRandom()">Add random</button><span style="font-size:0.73rem;color:var(--secondary);font-family:var(--sans)">Each click launches a new trajectory</span></div>
<div class="f-cap">A 2D state space with a single stable fixed point at the center. Trajectories from all initial conditions spiral inward toward it. Click anywhere to launch a new trajectory.</div>
</div>
<div class="callout c-teal"><div class="c-title">👉 Try it</div><p>Click anywhere in the state space to set an initial condition. The same system from different starting points always converges to the same long-term behavior — that convergence is what makes this a <strong>stable attractor</strong>.</p><p>Click <strong>Add random</strong> to populate many trajectories at once and see the global flow pattern.</p></div>
</div>
<div class="section" id="diffeq">
<div class="sec-ey"><span class="sec-num">03</span><span class="sec-tag">Equations</span><div class="sec-line"></div></div>
<h2>Reading a differential equation</h2>
<p class="hook">The equation tells you the rate of change. The solution tells you the trajectory.</p>
<p>Most models in computational neuroscience use equations of the form:</p>
<div class="eq-block"><div class="eq-lbl">General form</div>$$\tau \frac{dx}{dt} = -x + f(\text{input})$$</div>
<p>The left side is the rate of change of $x$, scaled by a <span class="term">time constant</span> $\tau$. The term $-x$ is a <span class="term">leak</span>: it pulls $x$ back toward zero. The term $f(\text{input})$ is the drive from external inputs or from other neurons. The time constant $\tau$ sets the timescale — a large $\tau$ means the system changes slowly. In a real neuron, $\tau$ is the <span class="term">membrane time constant</span>, typically 10–30 milliseconds.</p>
<p>If the input is zero, $x$ decays toward zero over a timescale $\tau$ — exponentially, with the shape $e^{-t/\tau}$. If the input is sustained, $x$ rises toward a <span class="term">steady state</span> where $dx/dt = 0$. That steady state is simply $x^* = f(\text{input})$.</p>
<div class="fig">
<div class="fig-head"><div class="f-title">Figure 3 — $\tau\,\dot{x} = -x + I$ · step response</div><div class="f-badge" style="background:var(--gold-bg);color:var(--gold)">Interactive</div><div class="f-st" id="eq-status">x = 0.00</div></div>
<div class="cv-wrap"><canvas id="cv-eq"></canvas></div>
<div class="ctrl"><button class="btn p" style="background:#b8870f" id="eq-pulse-btn" onclick="eqPulse()">▶ Turn input ON</button><button class="btn" onclick="eqReset()">↺ Reset</button><div class="sl-row"><span>τ:</span><input type="range" id="eq-tau" min="5" max="60" value="20" oninput="eqUpdate()"><span class="sl-val" id="eq-tau-val">20 ms</span></div><div class="sl-row"><span>Input:</span><input type="range" id="eq-inp" min="10" max="100" value="70" oninput="eqUpdate()"><span class="sl-val" id="eq-inp-val">70%</span></div></div>
<div class="f-cap">Top strip: the step input $I(t)$. Bottom: system response $x(t)$. The purple marker at $t = \tau$ shows exactly 63.2% of steady state — that is the definition of the time constant.</div>
</div>
<div class="callout c-gold"><div class="c-title">👉 Try it</div><p>Press <strong>Turn input ON</strong> to apply a sustained step. Watch $x(t)$ rise exponentially toward steady state. Adjust $\tau$ to see how it controls the speed — larger $\tau$ means slower, more gradual changes.</p></div>
</div>
<div class="section" id="fixed-points">
<div class="sec-ey"><span class="sec-num">04</span><span class="sec-tag">Fixed Points</span><div class="sec-line"></div></div>
<h2>Fixed points and steady states</h2>
<p class="hook">A fixed point is where $\dot{x} = 0$. The system does not move unless something pushes it.</p>
<p>A <span class="term">fixed point</span> is a state that does not change over time. For a system $\dot{x} = f(x)$, a fixed point $x^*$ satisfies:</p>
<div class="eq-block"><div class="eq-lbl">Fixed point condition</div>$$f(x^*) = 0$$</div>
<p>Think of a fixed point as a place where all forces acting on the system balance out. In one-dimensional systems, fixed points are the values of $x$ where the curve $f(x)$ crosses zero. In higher-dimensional systems, they are the points where all components of change are simultaneously zero.</p>
<p>In neuroscience, fixed points correspond to meaningful neural states. A neuron sitting at its <span class="term">resting membrane potential</span> is at a fixed point. A recurrent network continuing to fire after a brief input may be at a different fixed point — one corresponding to <span class="term">persistent activity</span>, the neural correlate of working memory. A decision circuit settled into one choice has also reached a fixed point. A system can have more than one fixed point, and where the system starts determines which one it reaches. This is how a network can make a binary choice, or how the same circuit can represent different memories.</p>
<div class="fig">
<div class="fig-head"><div class="f-title">Figure 4 — Flow on a line · fixed points where $f(x) = 0$</div><div class="f-badge" style="background:var(--purple-bg);color:var(--purple)">Interactive · drag ball</div><div class="f-st" id="fp-status">Drag the ball</div></div>
<div class="cv-wrap" style="cursor:grab"><canvas id="cv-fp"></canvas></div>
<div class="ctrl"><button class="btn p" style="background:var(--purple)" onclick="fpReset()">↺ Reset</button><div class="sl-row"><span>Input $I$:</span><input type="range" id="fp-inp" min="-80" max="80" value="0" oninput="fpUpdate()"><span class="sl-val" id="fp-inp-val">0</span></div></div>
<div class="f-cap">The curve is $f(x) = -x^3 + x + I$. Fixed points are where it crosses zero. Green = stable, red = unstable. Drag the ball to any starting position. Shift $I$ to move fixed points and watch them appear, merge, and disappear.</div>
</div>
<div class="callout c-purple"><div class="c-title">👉 Try it</div><p>Move the <strong>Input $I$</strong> slider. At $I = 0$, there are three fixed points: two stable and one unstable. As $|I|$ increases, two fixed points approach each other, collide, and disappear — a <strong>bifurcation</strong>.</p></div>
</div>
<div class="section" id="stability">
<div class="sec-ey"><span class="sec-num">05</span><span class="sec-tag">Stability</span><div class="sec-line"></div></div>
<h2>Does the system return?</h2>
<p class="hook">A stable fixed point pulls nearby trajectories back in. An unstable one pushes them away.</p>
<p>Once we find a fixed point, the next question is just as important: what happens if the system is pushed slightly away? A <span class="term">stable fixed point</span> pulls nearby trajectories back toward it. An <span class="term">unstable fixed point</span> does the opposite: even a tiny perturbation is amplified, and the state moves farther away. Stability is really about how the system responds to small <span class="term">perturbations</span>.</p>
<p>For a one-dimensional system $\dot{x} = f(x)$, a fixed point $x^*$ is stable when the slope of $f$ is negative there: $f'(x^*) < 0$. A perturbation to the right makes $\dot{x}$ negative (pushes back left); a perturbation to the left makes $\dot{x}$ positive (pushes back right). If $f'(x^*) > 0$, perturbations are amplified rather than corrected.</p>
<p>This distinction is fundamental in neuroscience because neural systems must often operate reliably in the presence of noise. A stable fixed point can support a robust neural state — a resting potential, a maintained memory. An unstable fixed point often acts as a <span class="term">decision boundary</span> separating two stable choice states, so that once activity crosses it, the dynamics carry the system strongly toward one outcome.</p>
<div class="fig">
<div class="fig-head"><div class="f-title">Figure 5 — Stability landscape · stable valleys, unstable peaks</div><div class="f-badge" style="background:var(--red-bg);color:var(--red)">Interactive · drag ball</div><div class="f-st" id="stab-status">Drag the ball</div></div>
<div class="cv-wrap" style="cursor:grab"><canvas id="cv-stab"></canvas></div>
<div class="ctrl"><button class="btn p" style="background:var(--red)" onclick="stabReset()">↺ Reset</button><button class="btn" onclick="stabPerturb()">Perturb near unstable point</button></div>
<div class="f-cap">Energy landscape $E(x) = \tfrac{1}{4}x^4 - \tfrac{1}{2}x^2$. The two valleys (green) are stable fixed points; the peak (red) is unstable. The dashed line is the <em>basin boundary</em> — the unstable point separating which valley the ball rolls into.</div>
</div>
<div class="callout c-red"><div class="c-title">👉 Try it</div><p>Drop the ball at the ridge near $x = 0$ and watch it roll away. Try <strong>Perturb near unstable point</strong> to see how a tiny push sends the system strongly toward one stable state.</p></div>
</div>
<div class="section" id="one-d">
<div class="sec-ey"><span class="sec-num">06</span><span class="sec-tag">1D Dynamics</span><div class="sec-line"></div></div>
<h2>One-dimensional dynamics</h2>
<p class="hook">Start simple: one variable, one equation, one axis.</p>
<p>A simple and biologically important nonlinear model is a firing-rate equation with recurrent feedback:</p>
<div class="eq-block"><div class="eq-lbl">Rate model with recurrent feedback</div>$$\tau \frac{dr}{dt} = -r + \phi(wr + I)$$</div>
<p>Here $r$ is the <span class="term">firing rate</span>, $\phi(\cdot)$ is a saturating nonlinearity such as $\tanh$, $I$ is external input, and $w$ controls the strength of <span class="term">recurrent feedback</span>. The <span class="term">leak term</span> $-r$ pulls activity toward zero; the recurrent and external terms push it up.</p>
<p>When recurrent feedback is weak ($w < 1$), the system has a single stable fixed point — it is <span class="term">monostable</span>. As $w$ grows past a critical threshold, a second stable fixed point appears, separated by an unstable one. The system becomes <span class="term">bistable</span>. This transition is a <span class="term">bifurcation</span>. Many working memory and decision models operate near exactly such a bifurcation, and neuromodulators like dopamine are thought to shift the system's operating point across it.</p>
<div class="fig">
<div class="fig-head"><div class="f-title">Figure 6 — 1D flow diagram · arrows show direction of change</div><div class="f-badge" style="background:var(--accent-bg);color:var(--accent)">Interactive</div><div class="f-st" id="od-status">Monostable</div></div>
<div class="cv-wrap"><canvas id="cv-od"></canvas></div>
<div class="ctrl"><button class="btn p" style="background:var(--accent)" onclick="odReset()">↺ Reset</button><div class="sl-row"><span>Gain $g$:</span><input type="range" id="od-gain" min="5" max="25" value="8" oninput="odUpdate()"><span class="sl-val" id="od-gain-val">0.8</span></div><div class="sl-row"><span>Input $I$:</span><input type="range" id="od-inp" min="-30" max="30" value="0" oninput="odUpdate()"><span class="sl-val" id="od-inp-val">0</span></div></div>
<div class="f-cap">The curve is $f(x) = -x + g\tanh(x + I)$. Arrows show the direction of flow. When $g > 1$, the curve develops two additional zero-crossings and the system becomes bistable.</div>
</div>
<div class="callout c-blue"><div class="c-title">👉 Try it</div><p>Increase <strong>Gain $g$</strong> past $g = 1.0$. Watch two new stable fixed points emerge symmetrically. Then change $I$ to break the symmetry — at large enough $|I|$, one stable point disappears (a <em>saddle-node bifurcation</em>).</p></div>
</div>
<div class="section" id="phase-plane">
<div class="sec-ey"><span class="sec-num">07</span><span class="sec-tag">Phase Plane</span><div class="sec-line"></div></div>
<h2>The two-dimensional phase plane</h2>
<p class="hook">When a system has two variables, its state is a point in a plane. The phase plane shows you everything.</p>
<p>Consider a system with two variables: $x$, a fast variable like membrane voltage, and $y$, a slow variable like adaptation or gating. At every point in the plane, the equations give you the velocity of the state. Drawing these as arrows everywhere gives the <span class="term">vector field</span>.</p>
<p>Two special curves cut through this field. The <span class="term">$\dot{x} = 0$ nullcline</span> is where $x$ has no tendency to change — on this curve, the state can only move vertically. The <span class="term">$\dot{y} = 0$ nullcline</span> is where $y$ is momentarily constant — motion there is purely horizontal. Fixed points are exactly where the two nullclines intersect.</p>
<p>Once you have the nullclines and the vector field, you can reason about trajectories without computing anything. The entire qualitative story — where the system goes, what it converges to, what separates different outcomes — is visible geometrically. This is the real power of the phase plane: it transforms a system of equations into a picture you can read at a glance.</p>
<div class="fig">
<div class="fig-head"><div class="f-title">Figure 7 — Phase plane · build it up layer by layer</div><div class="f-badge" style="background:var(--teal-bg);color:var(--teal)">Step-by-step</div><div class="f-st" id="pp-status">Step 1 of 4</div></div>
<div class="cv-wrap" style="cursor:crosshair"><canvas id="cv-pp"></canvas></div>
<div class="ctrl"><button class="btn p" style="background:var(--teal)" id="pp-step-btn" onclick="ppNextStep()">Next: add x-nullcline →</button><button class="btn" onclick="ppClear()">Clear trajectories</button><button class="btn" onclick="ppRestart()">↺ Start over</button><span style="font-size:0.73rem;color:var(--secondary);font-family:var(--sans)" id="pp-hint"></span></div>
<div class="f-cap">A FitzHugh–Nagumo–style system with two stable fixed points (green) and one unstable (red). Step through the reveal to see how nullclines and the vector field together explain the system's behavior without solving anything analytically.</div>
</div>
<div class="callout c-teal"><div class="c-title">👉 Try it — step by step</div><p>Use <strong>Next</strong> to add each layer: vector field, then the $\dot{x}=0$ nullcline (blue), then the $\dot{y}=0$ nullcline (gold). Once all layers are visible, click anywhere in the plane to launch a trajectory.</p></div>
</div>
<div id="concept-check" style="margin-top:68px;padding-top:48px;border-top:1px solid var(--border);">
<div style="font-family:var(--sans);font-size:0.65rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--tertiary);margin-bottom:6px;">Concept Check</div>
<h2 style="font-size:1.4rem;margin-bottom:8px;">Test your understanding</h2>
<p style="font-family:var(--sans);font-size:0.88rem;color:var(--secondary);margin-bottom:28px;line-height:1.55;">Answer these questions to test your concepts.</p>
<div id="quiz-container"></div>
<div id="quiz-score" style="display:none;margin-top:32px;padding:20px 24px;background:var(--off);border:1px solid var(--border);border-radius:8px;font-family:var(--sans);">
<div style="font-size:0.65rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--tertiary);margin-bottom:6px;">Your score</div>
<div id="quiz-score-val" style="font-size:1.8rem;font-weight:600;color:var(--text);line-height:1;margin-bottom:6px;"></div>
<div id="quiz-score-msg" style="font-size:0.85rem;color:var(--secondary);line-height:1.55;"></div>
<button onclick="quizReset()" style="margin-top:14px;padding:6px 16px;border-radius:6px;border:1px solid var(--border);background:var(--white);font-family:var(--sans);font-size:0.78rem;font-weight:500;cursor:pointer;">↻ Try again</button>
</div>
</div>
<div class="summ">
<div class="summ-lbl">Module 1 complete · vocabulary checklist</div>
<div class="summ-grid">
<div class="summ-card"><div class="summ-n">01 · Dynamical system</div><h4>State + rule</h4><p>A system whose state evolves over time according to a differential equation.</p></div>
<div class="summ-card"><div class="summ-n">02 · State space</div><h4>All possible states</h4><p>Evolution traces a trajectory $\mathbf{x}(t)$ through the space of all possible configurations.</p></div>
<div class="summ-card"><div class="summ-n">03 · Differential equation</div><h4>$\tau\dot{x} = -x + I$</h4><p>Leak pulls toward zero; input drives away. $\tau$ sets the timescale of everything.</p></div>
<div class="summ-card"><div class="summ-n">04 · Fixed points</div><h4>Where $\dot{x} = 0$</h4><p>Resting states, persistent activity, decisions. The system does not leave them without a push.</p></div>
<div class="summ-card"><div class="summ-n">05 · Stability</div><h4>$f'(x^*) < 0$ or $> 0$?</h4><p>Stable fixed points attract nearby trajectories. Unstable ones repel and separate outcomes.</p></div>
<div class="summ-card"><div class="summ-n">06 · 1D flow</div><h4>Arrows on a line</h4><p>Sigmoidal feedback creates bistability. A bifurcation is a qualitative change in fixed point structure.</p></div>
<div class="summ-card"><div class="summ-n">07 · Phase plane</div><h4>Nullclines + vector field</h4><p>Nullclines intersect at fixed points. The vector field gives the full qualitative behavior at a glance.</p></div>
</div>
<p class="summ-next">Module 2 builds directly on these ideas. Every attractor network type is a specific configuration of fixed points and flow fields in a high-dimensional state space. The vocabulary developed here — state, trajectory, fixed point, stability, nullcline — will be the language of the whole course.</p>
</div>
</main>
<footer>
<div class="foot-in">
<div class="foot-l"><strong>Computational Neuroscience · Georgia Institute of Technology</strong><br>PSYC 3803 / PSYC 8805 / NEUR 4803 · Module 1: Dynamical Systems Foundations · N. Apurva Ratan Murty, PhD</div>
<div class="foot-r"><div class="foot-badge">GT</div>Georgia Tech</div>
</div>
</footer>
</div>
<script data-cfasync="false">
function themeC2(){return '#ffffff';} function themeRule(){return '#e6e6e4';} function themeMuted(){return '#9b9b96';} function clamp(v,a,b){return Math.max(a,Math.min(b,v));}
function setupCanvas(id){var cv=document.getElementById(id);var wrap=cv.parentElement;function resize(){var r=wrap.getBoundingClientRect();var dpr=window.devicePixelRatio||1;cv.style.width=r.width+'px';cv.style.height=r.height+'px';cv.width=~~(r.width*dpr);cv.height=~~(r.height*dpr);cv._W=r.width;cv._H=r.height;cv._dpr=dpr;}resize();window.addEventListener('resize',resize);return cv;}
function drawDotGrid(g,W,H){var sp=24;g.fillStyle='rgba(0,0,0,0.03)';for(var x=sp;x<W;x+=sp)for(var y=sp;y<H;y+=sp){g.beginPath();g.arc(x,y,1,0,Math.PI*2);g.fill();}}
(function(){var cv=document.getElementById('hero-cv');if(!cv)return;function resize(){cv.width=window.innerWidth;cv.height=cv.parentElement.offsetHeight;cv.style.width=cv.width+'px';cv.style.height=cv.height+'px';}resize();window.addEventListener('resize',resize);var t=0;function draw(){requestAnimationFrame(draw);t+=0.005;var W=cv.width,H=cv.height;var g=cv.getContext('2d');g.clearRect(0,0,W,H);var step=58;for(var x=step;x<W;x+=step)for(var y=step;y<H;y+=step){var nx=(x/W)*4-2,ny=(y/H)*3-1.5;var dx=nx-nx*nx*nx/3-ny+0.3+0.1*Math.sin(t*0.7);var dy=(nx-2.5*ny+0.1+0.1*Math.cos(t*0.5))/8;var mag=Math.hypot(dx,dy)||1;var sc=14/mag;var ex=x+dx*sc,ey=y-dy*sc;var alpha=0.22+0.06*Math.sin(t+x*0.02+y*0.015);g.strokeStyle='rgba(194,145,34,'+alpha+')';g.lineWidth=1;g.beginPath();g.moveTo(x,y);g.lineTo(ex,ey);g.stroke();var ang=Math.atan2(ey-y,ex-x);g.fillStyle='rgba(194,145,34,'+alpha+')';g.beginPath();g.moveTo(ex,ey);g.lineTo(ex-5*Math.cos(ang-0.5),ey-5*Math.sin(ang-0.5));g.lineTo(ex-5*Math.cos(ang+0.5),ey-5*Math.sin(ang+0.5));g.closePath();g.fill();}};draw();})();
var ds={t:0,running:false,systems:[{name:'Decay',color:'#a97c10',x:0.5,hist:[],fn:function(x){return -x*1.5;}},{name:'Oscillate',color:'#0c7a5e',x:0.5,hist:[],fn:function(x,t){return Math.sin(t*2)*0.8;}},{name:'Grow',color:'#be3a2a',x:0.5,hist:[],fn:function(x){return x*(1-x)*2.5;}}]};
function dsReset(){ds.t=0;ds.systems.forEach(function(s){s.x=0.5;s.hist=[];});ds.running=false;document.getElementById('ds-btn').textContent='\u25b6 Play';}
function dsToggle(){ds.running=!ds.running;document.getElementById('ds-btn').textContent=ds.running?'\u23f8 Pause':'\u25b6 Play';}
function dsDraw(){var cv=document.getElementById('cv-ds');if(!cv._W)setupCanvas('cv-ds');var W=cv._W,H=cv._H,dpr=cv._dpr||1;var g=cv.getContext('2d');g.save();g.scale(dpr,dpr);g.fillStyle='#fff';g.fillRect(0,0,W,H);drawDotGrid(g,W,H);if(ds.running){ds.t+=0.018;ds.systems.forEach(function(s){var dx=s.fn(s.x,ds.t)*0.018;s.x=clamp(s.x+dx,0,1);s.hist.push(s.x);if(s.hist.length>300)s.hist.shift();});}var pad=44,tw=W-pad*2,th=H-pad*2-30;g.strokeStyle=themeRule();g.lineWidth=1;g.beginPath();g.moveTo(pad,pad);g.lineTo(pad,pad+th);g.lineTo(pad+tw,pad+th);g.stroke();g.fillStyle=themeMuted();g.font='11px Inter,sans-serif';g.textAlign='center';g.fillText('Time',pad+tw/2,H-6);g.save();g.translate(15,pad+th/2);g.rotate(-Math.PI/2);g.fillText('State x',0,0);g.restore();ds.systems.forEach(function(s){if(s.hist.length<2){var px=pad,py=pad+th-s.x*th;g.beginPath();g.arc(px,py,5,0,Math.PI*2);g.fillStyle=s.color;g.fill();g.fillStyle=s.color;g.font='bold 11px Inter,sans-serif';g.textAlign='left';g.fillText(s.name,pad+tw+6,py+4);return;}g.beginPath();s.hist.forEach(function(v,i){var px=pad+(i/299)*tw,py=pad+th-v*th;i===0?g.moveTo(px,py):g.lineTo(px,py);});g.strokeStyle=s.color;g.lineWidth=2.2;g.stroke();var ly=pad+th-s.x*th;g.fillStyle=s.color;g.font='bold 11px Inter,sans-serif';g.textAlign='left';g.fillText(s.name,pad+tw+6,ly+4);});if(!ds.running&&ds.systems[0].hist.length===0){g.fillStyle=themeMuted();g.font='13px Inter,sans-serif';g.textAlign='center';g.fillText('All three start at x = 0.5 \u2014 press Play to evolve',pad+tw/2,pad+th/2);}document.getElementById('ds-status').textContent=ds.running?'Running':'Paused';g.restore();requestAnimationFrame(dsDraw);}
dsDraw();
var ss={trails:[]};
function ssVelocity(x,y){var cx=0.5,cy=0.5,dx=x-cx,dy=y-cy;return{vx:-dx*0.8-dy*1.5,vy:-dy*0.8+dx*1.5};}
function ssClear(){ss.trails=[];}
function ssAddRandom(){for(var i=0;i<5;i++)ssLaunch(Math.random()*0.8+0.1,Math.random()*0.8+0.1);}
function ssLaunch(x,y){ss.trails.push({pts:[{x:x,y:y}],done:false,color:'hsl('+(Math.random()*300+40)+',60%,48%)'});}
function ssDraw(){var cv=document.getElementById('cv-ss');if(!cv._W)setupCanvas('cv-ss');var W=cv._W,H=cv._H,dpr=cv._dpr||1;var g=cv.getContext('2d');g.save();g.scale(dpr,dpr);g.fillStyle='#fff';g.fillRect(0,0,W,H);drawDotGrid(g,W,H);ss.trails.forEach(function(tr){if(!tr.done){var last=tr.pts[tr.pts.length-1];var v=ssVelocity(last.x,last.y);var nx=last.x+v.vx*0.010,ny=last.y+v.vy*0.010;tr.pts.push({x:clamp(nx,0.01,0.99),y:clamp(ny,0.01,0.99)});if(tr.pts.length>800)tr.done=true;}if(tr.pts.length<2)return;g.beginPath();tr.pts.forEach(function(p,i){i===0?g.moveTo(p.x*W,p.y*H):g.lineTo(p.x*W,p.y*H);});g.strokeStyle=tr.color;g.lineWidth=1.7;g.stroke();var last=tr.pts[tr.pts.length-1];g.beginPath();g.arc(last.x*W,last.y*H,4,0,Math.PI*2);g.fillStyle=tr.color;g.fill();});g.beginPath();g.arc(0.5*W,0.5*H,8,0,Math.PI*2);g.fillStyle='rgba(26,86,219,0.13)';g.fill();g.strokeStyle='#1a56db';g.lineWidth=2;g.stroke();g.fillStyle='#1a56db';g.font='11px Inter,sans-serif';g.textAlign='left';g.fillText('Fixed point',0.5*W+11,0.5*H+4);g.fillStyle=themeMuted();g.font='11px Inter,sans-serif';g.textAlign='center';g.fillText('\u2190 x\u2081 \u2192',W/2,H-5);g.save();g.translate(12,H/2);g.rotate(-Math.PI/2);g.fillText('\u2190 x\u2082 \u2192',0,0);g.restore();document.getElementById('ss-status').textContent=ss.trails.length+' trajectory'+(ss.trails.length===1?'':'s');g.restore();requestAnimationFrame(ssDraw);}
document.getElementById('cv-ss').addEventListener('click',function(e){var r=e.target.getBoundingClientRect();ssLaunch((e.clientX-r.left)/r.width,(e.clientY-r.top)/r.height);});
document.getElementById('cv-ss').addEventListener('touchstart',function(e){e.preventDefault();var r=e.target.getBoundingClientRect(),t=e.touches[0];ssLaunch((t.clientX-r.left)/r.width,(t.clientY-r.top)/r.height);},{passive:false});
for(var _ii=0;_ii<6;_ii++)ssLaunch(Math.random()*0.8+0.1,Math.random()*0.8+0.1);
ssDraw();
var eq={x:0,tau:20,inp:0.7,inputOn:false,hist:[],maxT:300};
function eqUpdate(){eq.tau=parseInt(document.getElementById('eq-tau').value);eq.inp=parseInt(document.getElementById('eq-inp').value)/100;document.getElementById('eq-tau-val').textContent=eq.tau+' ms';document.getElementById('eq-inp-val').textContent=document.getElementById('eq-inp').value+'%';}
function eqReset(){eq.x=0;eq.hist=[];eq.inputOn=false;document.getElementById('eq-pulse-btn').textContent='\u25b6 Turn input ON';}
function eqPulse(){eq.inputOn=!eq.inputOn;document.getElementById('eq-pulse-btn').textContent=eq.inputOn?'\u2b1b Turn input OFF':'\u25b6 Turn input ON';}
function eqDraw(){var cv=document.getElementById('cv-eq');if(!cv._W)setupCanvas('cv-eq');var W=cv._W,H=cv._H,dpr=cv._dpr||1;var g=cv.getContext('2d');g.save();g.scale(dpr,dpr);g.fillStyle='#fff';g.fillRect(0,0,W,H);var target=eq.inputOn?eq.inp:0;eq.x=clamp(eq.x+(-eq.x+target)/eq.tau,0,1);eq.hist.push({x:eq.x,on:eq.inputOn});if(eq.hist.length>eq.maxT)eq.hist.shift();var pL=56,pR=20,pT=24,iH=40,gap=16,tT=pT+iH+gap,tH=H-tT-46,tw=W-pL-pR;g.fillStyle='rgba(245,245,243,0.7)';g.fillRect(pL,pT,tw,iH);g.strokeStyle=themeRule();g.lineWidth=1;g.strokeRect(pL,pT,tw,iH);if(eq.hist.length>1){g.beginPath();eq.hist.forEach(function(h,i){var px=pL+(i/(eq.maxT-1))*tw,py=h.on?pT+7:pT+iH-7;i===0?g.moveTo(px,py):g.lineTo(px,py);});g.strokeStyle='#a97c10';g.lineWidth=2.5;g.stroke();g.beginPath();eq.hist.forEach(function(h,i){var px=pL+(i/(eq.maxT-1))*tw,py=h.on?pT+7:pT+iH-7;i===0?g.moveTo(px,py):g.lineTo(px,py);});g.lineTo(pL+tw,pT+iH);g.lineTo(pL,pT+iH);g.closePath();g.fillStyle='rgba(169,124,16,0.07)';g.fill();}g.fillStyle=themeMuted();g.font='11px Inter,sans-serif';g.textAlign='right';g.fillText('Input I(t)',pL-6,pT+iH/2+4);[0,0.25,0.5,0.75,1].forEach(function(v){var py=tT+tH-v*tH;g.strokeStyle=themeRule();g.lineWidth=0.6;g.globalAlpha=0.6;g.beginPath();g.moveTo(pL,py);g.lineTo(pL+tw,py);g.stroke();g.globalAlpha=1;g.fillStyle=themeMuted();g.font='10px JetBrains Mono,monospace';g.textAlign='right';g.fillText(v.toFixed(2),pL-5,py+4);});var ssY=tT+tH-eq.inp*tH;g.strokeStyle='rgba(169,124,16,0.3)';g.lineWidth=1.2;g.setLineDash([5,5]);g.beginPath();g.moveTo(pL,ssY);g.lineTo(pL+tw,ssY);g.stroke();g.setLineDash([]);g.fillStyle='rgba(169,124,16,0.5)';g.font='10px Inter,sans-serif';g.textAlign='left';g.fillText('steady state = '+eq.inp.toFixed(2),pL+4,ssY-5);if(eq.hist.length>1){g.beginPath();eq.hist.forEach(function(h,i){var px=pL+(i/(eq.maxT-1))*tw,py=tT+tH-h.x*tH;i===0?g.moveTo(px,py):g.lineTo(px,py);});g.strokeStyle='#1a56db';g.lineWidth=2.5;g.stroke();var lx=eq.hist[eq.hist.length-1].x,dotPy=tT+tH-lx*tH;var grd=g.createRadialGradient(pL+tw,dotPy,0,pL+tw,dotPy,7);grd.addColorStop(0,'rgba(26,86,219,0.9)');grd.addColorStop(1,'rgba(26,86,219,0)');g.beginPath();g.arc(pL+tw,dotPy,7,0,Math.PI*2);g.fillStyle=grd;g.fill();g.beginPath();g.arc(pL+tw,dotPy,4,0,Math.PI*2);g.fillStyle='#7aabff';g.fill();}var ri=eq.hist.findIndex(function(h,i){return i>0&&!eq.hist[i-1].on&&h.on;});if(ri>0&&eq.hist.length>ri+eq.tau){var ti=Math.min(ri+eq.tau,eq.hist.length-1),tPx=pL+(ti/(eq.maxT-1))*tw,tPy=tT+tH-0.632*eq.inp*tH;g.strokeStyle='rgba(109,40,217,0.45)';g.lineWidth=1.5;g.setLineDash([4,3]);g.beginPath();g.moveTo(tPx,tT);g.lineTo(tPx,tT+tH);g.stroke();g.setLineDash([]);g.beginPath();g.arc(tPx,tPy,5,0,Math.PI*2);g.fillStyle='#6d28d9';g.fill();g.fillStyle='#6d28d9';g.font='bold 10px Inter,sans-serif';g.textAlign='left';g.fillText('t = \u03c4 \u2192 63.2%',tPx+6,tPy-6);}g.strokeStyle=themeRule();g.lineWidth=1.5;g.beginPath();g.moveTo(pL,pT);g.lineTo(pL,tT+tH);g.lineTo(pL+tw,tT+tH);g.stroke();g.fillStyle=themeMuted();g.font='11px Inter,sans-serif';g.textAlign='center';g.fillText('\u2190 Time (ms) \u2192',pL+tw/2,H-5);g.save();g.translate(14,tT+tH/2);g.rotate(-Math.PI/2);g.fillText('x(t)',0,0);g.restore();document.getElementById('eq-status').textContent='x = '+eq.x.toFixed(3)+' | input '+(eq.inputOn?'ON':'OFF');g.restore();requestAnimationFrame(eqDraw);}
eqDraw();
var fp={bx:0.8,bv:0,dragging:false,inp:0};
function fpRawF(x){return -x*x*x+x+fp.inp/80;}
function fpUpdate(){fp.inp=parseInt(document.getElementById('fp-inp').value);document.getElementById('fp-inp-val').textContent=fp.inp;}
function fpReset(){fp.bx=0.8;fp.bv=0;}
function fpDraw(){var cv=document.getElementById('cv-fp');if(!cv._W)setupCanvas('cv-fp');var W=cv._W,H=cv._H,dpr=cv._dpr||1;var g=cv.getContext('2d');g.save();g.scale(dpr,dpr);g.fillStyle='#fff';g.fillRect(0,0,W,H);drawDotGrid(g,W,H);var pad=50,tw=W-pad*2,mid=H*0.5;var xR=[-1.6,1.6];function xS(x){return pad+(x-xR[0])/(xR[1]-xR[0])*tw;}function fS(f){return mid-f/1.2*mid*0.7;}g.strokeStyle=themeRule();g.lineWidth=1.5;g.beginPath();g.moveTo(pad,mid);g.lineTo(pad+tw,mid);g.stroke();for(var i=0;i<=22;i++){var x=xR[0]+i/22*(xR[1]-xR[0]),f=fpRawF(x),sx=xS(x),sy=mid+28,al=clamp(f*18,-20,20);if(Math.abs(al)<0.5)continue;var col=f>0?'rgba(12,122,94,0.7)':'rgba(190,58,42,0.7)';g.strokeStyle=col;g.fillStyle=col;g.lineWidth=1.5;g.beginPath();g.moveTo(sx,sy);g.lineTo(sx+al,sy);g.stroke();var d=Math.sign(al);g.beginPath();g.moveTo(sx+al,sy);g.lineTo(sx+al-d*5,sy-4);g.lineTo(sx+al-d*5,sy+4);g.closePath();g.fill();}g.beginPath();for(var j=0;j<=200;j++){var xj=xR[0]+j/200*(xR[1]-xR[0]),fj=fpRawF(xj);j===0?g.moveTo(xS(xj),fS(fj)):g.lineTo(xS(xj),fS(fj));}g.strokeStyle='#6d28d9';g.lineWidth=2.5;g.stroke();var fpts=[];for(var k=0;k<200;k++){var x0=xR[0]+k/200*(xR[1]-xR[0]),x1=xR[0]+(k+1)/200*(xR[1]-xR[0]);if(fpRawF(x0)*fpRawF(x1)<0){var xfp=(x0+x1)/2,slope=(fpRawF(xfp+0.01)-fpRawF(xfp-0.01))/0.02;fpts.push({x:xfp,stable:slope<0});}}fpts.forEach(function(p){var sx=xS(p.x);g.beginPath();g.arc(sx,mid,8,0,Math.PI*2);g.fillStyle=p.stable?'#0c7a5e':'#be3a2a';g.fill();g.strokeStyle='#fff';g.lineWidth=2;g.stroke();g.fillStyle=p.stable?'#0c7a5e':'#be3a2a';g.font='10px Inter,sans-serif';g.textAlign='center';g.fillText(p.stable?'stable':'unstable',sx,mid+22);});if(!fp.dragging){var ff=fpRawF(fp.bx);fp.bv+=ff*0.08;fp.bv*=0.84;fp.bx=clamp(fp.bx+fp.bv*0.04,xR[0]+0.05,xR[1]-0.05);}var bsx=xS(fp.bx);g.beginPath();g.arc(bsx,mid-22,12,0,Math.PI*2);var bg=g.createRadialGradient(bsx-3,mid-25,2,bsx,mid-22,12);bg.addColorStop(0,'#ffe090');bg.addColorStop(1,'#e67e22');g.fillStyle=bg;g.fill();g.strokeStyle='rgba(255,255,255,0.4)';g.lineWidth=1.5;g.stroke();g.fillStyle=themeMuted();g.font='11px Inter,sans-serif';g.textAlign='center';g.fillText('\u2190 x \u2192',pad+tw/2,H-8);g.save();g.translate(14,mid);g.rotate(-Math.PI/2);g.fillText('f(x) = dx/dt',0,0);g.restore();g.fillStyle='#6d28d9';g.font='11px Inter,sans-serif';g.textAlign='right';g.fillText('f(x) = -x\u00b3 + x + I',pad+tw,pad+14);var near=fpts.find(function(p){return Math.abs(fp.bx-p.x)<0.08;});document.getElementById('fp-status').textContent=near?'At '+(near.stable?'stable':'unstable')+' fixed point':'Rolling\u2026';g.restore();requestAnimationFrame(fpDraw);}
document.getElementById('cv-fp').addEventListener('mousedown',function(e){var r=e.target.getBoundingClientRect();fp.bx=-1.6+(e.clientX-r.left)/((e.target._W||600)-100)*3.2;fp.bv=0;fp.dragging=true;});
document.getElementById('cv-fp').addEventListener('touchstart',function(e){e.preventDefault();var r=e.target.getBoundingClientRect(),t=e.touches[0];fp.bx=-1.6+(t.clientX-r.left)/((e.target._W||600)-100)*3.2;fp.bv=0;fp.dragging=true;},{passive:false});
document.addEventListener('mousemove',function(e){if(!fp.dragging)return;var cv=document.getElementById('cv-fp'),r=cv.getBoundingClientRect();fp.bx=clamp(-1.6+(e.clientX-r.left)/((cv._W||600)-100)*3.2,-1.55,1.55);});
document.addEventListener('touchmove',function(e){if(!fp.dragging)return;e.preventDefault();var cv=document.getElementById('cv-fp'),r=cv.getBoundingClientRect(),t=e.touches[0];fp.bx=clamp(-1.6+(t.clientX-r.left)/((cv._W||600)-100)*3.2,-1.55,1.55);},{passive:false});
document.addEventListener('mouseup',function(){fp.dragging=false;});
document.addEventListener('touchend',function(){fp.dragging=false;});
fpDraw();
var stab={bx:0,bv:0,dragging:false};
function stabE(x){return 0.25*x*x*x*x-0.5*x*x;}
function stabReset(){stab.bx=0.02;stab.bv=0;}
function stabPerturb(){stab.bx=0.05+Math.random()*0.1;stab.bv=(Math.random()-0.5)*0.5;}
function stabDraw(){var cv=document.getElementById('cv-stab');if(!cv._W)setupCanvas('cv-stab');var W=cv._W,H=cv._H,dpr=cv._dpr||1;var g=cv.getContext('2d');g.save();g.scale(dpr,dpr);g.fillStyle='#fff';g.fillRect(0,0,W,H);drawDotGrid(g,W,H);var pad=40,tw=W-pad*2,xR=[-1.6,1.6],mn=Infinity,mx=-Infinity;for(var i=0;i<=200;i++){var x=xR[0]+i/200*(xR[1]-xR[0]),e=stabE(x);if(e<mn)mn=e;if(e>mx)mx=e;}function xS(x){return pad+(x-xR[0])/(xR[1]-xR[0])*tw;}function eS(e){return H*0.85-(e-mn)/(mx-mn)*(H*0.68);}var path=new Path2D();for(var j=0;j<=200;j++){var xj=xR[0]+j/200*(xR[1]-xR[0]),ej=stabE(xj);j===0?path.moveTo(xS(xj),eS(ej)):path.lineTo(xS(xj),eS(ej));}path.lineTo(xS(xR[1]),H);path.lineTo(xS(xR[0]),H);path.closePath();var gr=g.createLinearGradient(0,0,0,H);gr.addColorStop(0,'rgba(26,86,219,0.12)');gr.addColorStop(1,'rgba(26,86,219,0.02)');g.fillStyle=gr;g.fill(path);g.strokeStyle='rgba(26,86,219,0.6)';g.lineWidth=2.5;g.stroke(path);[-1,1].forEach(function(x){var sx=xS(x),sy=eS(stabE(x));g.beginPath();g.arc(sx,sy,6,0,Math.PI*2);g.fillStyle='rgba(12,122,94,0.35)';g.fill();g.fillStyle='#0c7a5e';g.font='bold 10px Inter,sans-serif';g.textAlign='center';g.fillText('stable',sx,sy+18);});var usx=xS(0),usy=eS(stabE(0));g.beginPath();g.arc(usx,usy,6,0,Math.PI*2);g.fillStyle='rgba(190,58,42,0.35)';g.fill();g.fillStyle='#be3a2a';g.font='bold 10px Inter,sans-serif';g.textAlign='center';g.fillText('unstable',usx,usy-12);g.strokeStyle='rgba(190,58,42,0.22)';g.lineWidth=1;g.setLineDash([4,4]);g.beginPath();g.moveTo(usx,0);g.lineTo(usx,H);g.stroke();g.setLineDash([]);g.fillStyle='rgba(190,58,42,0.32)';g.font='10px Inter,sans-serif';g.textAlign='center';g.fillText('basin boundary',usx,H-8);if(!stab.dragging){var eps=0.01,dEdx=(stabE(stab.bx+eps)-stabE(stab.bx-eps))/(2*eps);stab.bv+=-dEdx*2.5;stab.bv*=0.83;stab.bx=clamp(stab.bx+stab.bv*0.04,xR[0]+0.05,xR[1]-0.05);}var bsx=xS(stab.bx),bsy=eS(stabE(stab.bx))-14;g.beginPath();g.ellipse(bsx,bsy+14,9,3.5,0,0,Math.PI*2);g.fillStyle='rgba(0,0,0,0.1)';g.fill();var bg2=g.createRadialGradient(bsx-3,bsy-3,2,bsx,bsy,12);bg2.addColorStop(0,'#ffe090');bg2.addColorStop(1,'#e67e22');g.beginPath();g.arc(bsx,bsy,12,0,Math.PI*2);g.fillStyle=bg2;g.fill();g.strokeStyle='rgba(255,255,255,0.4)';g.lineWidth=1.5;g.stroke();var atS=Math.abs(Math.abs(stab.bx)-1)<0.12,atU=Math.abs(stab.bx)<0.08;document.getElementById('stab-status').textContent=atS?'At stable fixed point (x \u2248 '+(stab.bx>0?'+1':'-1')+')':atU?'Near unstable \u2014 will diverge':'Rolling\u2026';g.restore();requestAnimationFrame(stabDraw);}
document.getElementById('cv-stab').addEventListener('mousedown',function(e){stab.dragging=true;stab.bv=0;var r=e.target.getBoundingClientRect();stab.bx=-1.6+(e.clientX-r.left)/((e.target._W||600)-80)*3.2;});
document.getElementById('cv-stab').addEventListener('touchstart',function(e){e.preventDefault();stab.dragging=true;stab.bv=0;var r=e.target.getBoundingClientRect(),t=e.touches[0];stab.bx=-1.6+(t.clientX-r.left)/((e.target._W||600)-80)*3.2;},{passive:false});
document.addEventListener('mousemove',function(e){if(!stab.dragging)return;var cv=document.getElementById('cv-stab'),r=cv.getBoundingClientRect();stab.bx=clamp(-1.6+(e.clientX-r.left)/((cv._W||600)-80)*3.2,-1.55,1.55);});
document.addEventListener('touchmove',function(e){if(!stab.dragging)return;e.preventDefault();var cv=document.getElementById('cv-stab'),r=cv.getBoundingClientRect(),t=e.touches[0];stab.bx=clamp(-1.6+(t.clientX-r.left)/((cv._W||600)-80)*3.2,-1.55,1.55);},{passive:false});
document.addEventListener('mouseup',function(){stab.dragging=false;});
document.addEventListener('touchend',function(){stab.dragging=false;});
stabDraw();
var od={gain:8,inp:0};
function odF(x){return -x+(od.gain/10)*Math.tanh(x+od.inp*0.08);}
function odUpdate(){od.gain=parseInt(document.getElementById('od-gain').value);od.inp=parseInt(document.getElementById('od-inp').value);document.getElementById('od-gain-val').textContent=(od.gain/10).toFixed(1);document.getElementById('od-inp-val').textContent=od.inp>0?'+'+od.inp:od.inp;}
function odReset(){od.gain=8;od.inp=0;document.getElementById('od-gain').value=8;document.getElementById('od-inp').value=0;odUpdate();}
function odDraw(){var cv=document.getElementById('cv-od');if(!cv._W)setupCanvas('cv-od');var W=cv._W,H=cv._H,dpr=cv._dpr||1;var g=cv.getContext('2d');g.save();g.scale(dpr,dpr);g.fillStyle='#fff';g.fillRect(0,0,W,H);drawDotGrid(g,W,H);var pad=54,tw=W-pad*2,mid=H*0.48,xR=[-2.2,2.2],fSc=1.4;function xS(x){return pad+(x-xR[0])/(xR[1]-xR[0])*tw;}function fS(f){return mid-(f/fSc)*(mid-pad*0.8);}g.strokeStyle=themeRule();g.lineWidth=1.5;g.beginPath();g.moveTo(pad,mid);g.lineTo(pad+tw,mid);g.stroke();g.beginPath();g.moveTo(xS(0),pad*0.6);g.lineTo(xS(0),H-pad*0.8);g.stroke();g.fillStyle=themeMuted();g.font='10px JetBrains Mono,monospace';[-2,-1,0,1,2].forEach(function(v){var sx=xS(v);g.textAlign='center';g.fillText(v,sx,mid+14);g.strokeStyle=themeRule();g.lineWidth=0.5;g.globalAlpha=0.3;g.beginPath();g.moveTo(sx,pad*0.6);g.lineTo(sx,H-pad*0.8);g.stroke();g.globalAlpha=1;});var N=400,curve=[];for(var i=0;i<=N;i++){var x=xR[0]+i/N*(xR[1]-xR[0]);curve.push({x:x,f:odF(x)});}curve.forEach(function(pt,i){if(i===0)return;var prev=curve[i-1];var sy=fS(clamp(pt.f,-fSc,fSc)),psy=fS(clamp(prev.f,-fSc,fSc));g.fillStyle=pt.f>0?'rgba(12,122,94,0.05)':'rgba(190,58,42,0.05)';g.beginPath();g.moveTo(xS(prev.x),mid);g.lineTo(xS(prev.x),psy);g.lineTo(xS(pt.x),sy);g.lineTo(xS(pt.x),mid);g.closePath();g.fill();});g.beginPath();curve.forEach(function(pt,i){var sy=fS(clamp(pt.f,-fSc,fSc));i===0?g.moveTo(xS(pt.x),sy):g.lineTo(xS(pt.x),sy);});g.strokeStyle='#1a56db';g.lineWidth=2.8;g.stroke();var aY=mid+36;for(var j=0;j<22;j++){var x=xR[0]+0.1+j/22*(xR[1]-xR[0]-0.2),f=odF(x),sx=xS(x),len=clamp(f*28,-24,24);if(Math.abs(len)<1)continue;var col=f>0?'rgba(12,122,94,0.8)':'rgba(190,58,42,0.8)';g.strokeStyle=col;g.fillStyle=col;g.lineWidth=1.8;g.beginPath();g.moveTo(sx,aY);g.lineTo(sx+len,aY);g.stroke();var d=Math.sign(len);g.beginPath();g.moveTo(sx+len,aY);g.lineTo(sx+len-d*6,aY-4);g.lineTo(sx+len-d*6,aY+4);g.closePath();g.fill();}var fps2=[];for(var k=0;k<N;k++){var x0=xR[0]+k/N*(xR[1]-xR[0]),x1=xR[0]+(k+1)/N*(xR[1]-xR[0]);if(odF(x0)*odF(x1)<0){var lo=x0,hi=x1;for(var m=0;m<20;m++){var md=(lo+hi)/2;odF(md)*odF(lo)<0?hi=md:lo=md;}var xfp=(lo+hi)/2,sl=(odF(xfp+0.005)-odF(xfp-0.005))/0.01;fps2.push({x:xfp,stable:sl<0});}}fps2.forEach(function(pt){var sx=xS(pt.x);g.beginPath();g.arc(sx,mid,9,0,Math.PI*2);g.fillStyle=pt.stable?'rgba(12,122,94,0.15)':'rgba(190,58,42,0.15)';g.fill();g.strokeStyle=pt.stable?'#0c7a5e':'#be3a2a';g.lineWidth=2.5;g.stroke();g.fillStyle=pt.stable?'#0c7a5e':'#be3a2a';g.font='bold 10px Inter,sans-serif';g.textAlign='center';g.fillText(pt.stable?'stable':'unstable',sx,pt.stable?mid+24:mid-14);});g.fillStyle=themeMuted();g.font='11px Inter,sans-serif';g.textAlign='center';g.fillText('\u2190 x (activity) \u2192',pad+tw/2,H-8);g.save();g.translate(14,mid);g.rotate(-Math.PI/2);g.fillText('f(x) = dx/dt',0,0);g.restore();var gV=(od.gain/10).toFixed(1),bis=fps2.filter(function(p){return p.stable;}).length>=2;g.fillStyle=bis?'#0c7a5e':'#a97c10';g.font='bold 11px Inter,sans-serif';g.textAlign='right';g.fillText('g = '+gV+' '+(bis?'(bistable)':'(monostable)'),pad+tw,pad+20);document.getElementById('od-status').textContent=bis?'Bistable \u2014 '+fps2.length+' fixed points':fps2.length===1?'Monostable \u2014 1 fixed point':fps2.length+' fixed points';g.restore();requestAnimationFrame(odDraw);}
odDraw();
var ppP={s:2.5,d:0.105,tau:8.0,I:0.3};
function ppDx(x,y){return x-x*x*x/3-y+ppP.I;}
function ppDy(x,y){return (x-ppP.s*y+ppP.d)/ppP.tau;}
var pp={step:1,trails:[]};
var ppSL=['','Next: add x-nullcline \u2192','Next: add y-nullcline \u2192','Next: click to add trajectories \u2192','Complete \u2014 click anywhere to explore'];
var ppSH=['','The vector field shows the velocity at every point in the plane.','Blue curve: where x-dot = 0. The state only moves vertically on this curve.','Gold line: where y-dot = 0. Nullclines cross at three fixed points \u2014 two stable (green) and one unstable (red).','Click anywhere to launch a trajectory. Notice which stable fixed point it flows toward.'];
var ppSS=['','Step 1 / 4 \u2014 vector field','Step 2 / 4 \u2014 x-nullcline','Step 3 / 4 \u2014 y-nullcline','Step 4 / 4 \u2014 click to add trajectories'];
function ppNextStep(){if(pp.step<4)pp.step++;var btn=document.getElementById('pp-step-btn');btn.textContent=pp.step<4?ppSL[pp.step]:ppSL[4];btn.disabled=pp.step===4;document.getElementById('pp-hint').textContent=ppSH[pp.step];}
function ppClear(){pp.trails=[];}
function ppRestart(){pp.step=1;pp.trails=[];var btn=document.getElementById('pp-step-btn');btn.textContent=ppSL[1];btn.disabled=false;document.getElementById('pp-hint').textContent=ppSH[1];}
function ppLaunch(wx,wy){if(pp.step<4)return;pp.trails.push({pts:[{x:wx,y:wy}],done:false,color:'hsl('+(Math.random()*260+40)+',60%,48%)'});}
function ppDraw(){var cv=document.getElementById('cv-pp');if(!cv._W)setupCanvas('cv-pp');var W=cv._W,H=cv._H,dpr=cv._dpr||1;var g=cv.getContext('2d');g.save();g.scale(dpr,dpr);g.fillStyle='#fff';g.fillRect(0,0,W,H);drawDotGrid(g,W,H);var pad=48,xR=[-2.2,2.2],yR=[-1.0,1.3];function toS(x,y){return[(x-xR[0])/(xR[1]-xR[0])*(W-2*pad)+pad,H-pad-(y-yR[0])/(yR[1]-yR[0])*(H-2*pad)];}var grd2=14;for(var i=0;i<=grd2;i++)for(var j=0;j<=grd2;j++){var x=xR[0]+i/grd2*(xR[1]-xR[0]),y=yR[0]+j/grd2*(yR[1]-yR[0]),dx=ppDx(x,y),dy=ppDy(x,y),mag=Math.hypot(dx,dy);if(mag<0.001)continue;var sc=0.1/mag,s=toS(x,y),ex=s[0]+dx*sc*(W-2*pad)/(xR[1]-xR[0]),ey=s[1]-dy*sc*(H-2*pad)/(yR[1]-yR[0]),br=clamp(mag/1.5,0.1,0.6);g.strokeStyle='rgba(0,48,87,'+(br*0.4)+')';g.lineWidth=1.1;g.beginPath();g.moveTo(s[0],s[1]);g.lineTo(ex,ey);g.stroke();var ang=Math.atan2(ey-s[1],ex-s[0]);g.fillStyle='rgba(0,48,87,'+(br*0.4)+')';g.beginPath();g.moveTo(ex,ey);g.lineTo(ex-5*Math.cos(ang-0.4),ey-5*Math.sin(ang-0.4));g.lineTo(ex-5*Math.cos(ang+0.4),ey-5*Math.sin(ang+0.4));g.closePath();g.fill();}
if(pp.step>=2){g.beginPath();var s1=false;for(var i=0;i<=300;i++){var x=xR[0]+i/300*(xR[1]-xR[0]),y=x-x*x*x/3+ppP.I;if(y<yR[0]||y>yR[1]){s1=false;continue;}var s=toS(x,y);s1?g.lineTo(s[0],s[1]):(g.moveTo(s[0],s[1]),s1=true);}g.strokeStyle='#1a56db';g.lineWidth=2.8;g.stroke();g.fillStyle='#1a56db';g.font='bold 10px Inter,sans-serif';g.textAlign='left';g.fillText('x-nullcline',pad+4,pad+17);g.fillStyle='rgba(26,86,219,0.38)';g.font='9px Inter,sans-serif';g.fillText('\u2190 vertical motion',pad+4,pad+30);}
if(pp.step>=3){g.beginPath();var s2=false;for(var i=0;i<=300;i++){var x=xR[0]+i/300*(xR[1]-xR[0]),y=(x+ppP.d)/ppP.s;if(y<yR[0]||y>yR[1]){s2=false;continue;}var s=toS(x,y);s2?g.lineTo(s[0],s[1]):(g.moveTo(s[0],s[1]),s2=true);}g.strokeStyle='#a97c10';g.lineWidth=2.8;g.stroke();g.fillStyle='#a97c10';g.font='bold 10px Inter,sans-serif';g.textAlign='left';g.fillText('y-nullcline',pad+4,pad+48);g.fillStyle='rgba(169,124,16,0.5)';g.font='9px Inter,sans-serif';g.fillText('\u2190 horizontal motion',pad+4,pad+61);var fps3=[];for(var i=0;i<2000;i++){var x0=xR[0]+i/2000*(xR[1]-xR[0]),x1=xR[0]+(i+1)/2000*(xR[1]-xR[0]);var d0=(x0-x0*x0*x0/3+ppP.I)-(x0+ppP.d)/ppP.s,d1=(x1-x1*x1*x1/3+ppP.I)-(x1+ppP.d)/ppP.s;if(d0*d1<0){var lo=x0,hi=x1;for(var k=0;k<20;k++){var m=(lo+hi)/2;var dm=(m-m*m*m/3+ppP.I)-(m+ppP.d)/ppP.s;dm*d0<0?hi=m:lo=m;}var xfp=(lo+hi)/2,yfp=(xfp+ppP.d)/ppP.s,ds=(1-xfp*xfp)-1/ppP.s;fps3.push({x:xfp,y:yfp,stable:ds<0});}}fps3.forEach(function(fp2){var s=toS(fp2.x,fp2.y),col=fp2.stable?'#0c7a5e':'#be3a2a';var gw=g.createRadialGradient(s[0],s[1],0,s[0],s[1],18);gw.addColorStop(0,fp2.stable?'rgba(12,122,94,0.2)':'rgba(190,58,42,0.2)');gw.addColorStop(1,'rgba(0,0,0,0)');g.beginPath();g.arc(s[0],s[1],18,0,Math.PI*2);g.fillStyle=gw;g.fill();g.beginPath();g.arc(s[0],s[1],7,0,Math.PI*2);g.fillStyle='#fff';g.fill();g.strokeStyle=col;g.lineWidth=2.5;g.stroke();g.fillStyle='#fff';g.font='bold 9px Inter,sans-serif';g.textAlign='left';g.fillText(fp2.stable?'stable':'unstable',s[0]+11,s[1]+4);});}
if(pp.step>=4){pp.trails.forEach(function(tr){if(!tr.done){for(var s=0;s<4;s++){var last=tr.pts[tr.pts.length-1],dx=ppDx(last.x,last.y),dy=ppDy(last.x,last.y),nx=last.x+dx*0.02,ny=last.y+dy*0.02;if(nx<xR[0]-0.3||nx>xR[1]+0.3||ny<yR[0]-0.3||ny>yR[1]+0.3){tr.done=true;break;}tr.pts.push({x:nx,y:ny});if(Math.hypot(dx,dy)<0.004){tr.done=true;break;}}}if(tr.pts.length<2)return;g.beginPath();tr.pts.forEach(function(p,i){var s=toS(p.x,p.y);i===0?g.moveTo(s[0],s[1]):g.lineTo(s[0],s[1]);});g.strokeStyle=tr.color;g.lineWidth=2.2;g.stroke();var tip=tr.pts[tr.pts.length-1],s=toS(tip.x,tip.y);g.beginPath();g.arc(s[0],s[1],5,0,Math.PI*2);g.fillStyle=tr.color;g.fill();g.strokeStyle='rgba(255,255,255,0.5)';g.lineWidth=1;g.stroke();});if(pp.trails.length===0){g.fillStyle='rgba(12,122,94,0.4)';g.font='13px Inter,sans-serif';g.textAlign='center';g.fillText('Click anywhere to launch a trajectory',W/2,H/2);}}
var os=toS(0,0);g.strokeStyle=themeRule();g.lineWidth=1;g.beginPath();g.moveTo(pad,os[1]);g.lineTo(W-pad,os[1]);g.stroke();g.beginPath();g.moveTo(os[0],pad);g.lineTo(os[0],H-pad);g.stroke();g.fillStyle=themeMuted();g.font='11px Inter,sans-serif';g.textAlign='center';g.fillText('\u2190 x (fast) \u2192',W/2,H-5);g.save();g.translate(12,H/2);g.rotate(-Math.PI/2);g.fillText('\u2190 y (slow) \u2192',0,0);g.restore();document.getElementById('pp-status').textContent=ppSS[pp.step];g.restore();requestAnimationFrame(ppDraw);}
document.getElementById('cv-pp').addEventListener('click',function(e){var r=e.target.getBoundingClientRect(),pad=48,xR=[-2.2,2.2],yR=[-1.0,1.3],W=r.width,H=r.height;ppLaunch(xR[0]+(e.clientX-r.left-pad)/(W-2*pad)*(xR[1]-xR[0]),yR[0]+(H-pad-(e.clientY-r.top))/(H-2*pad)*(yR[1]-yR[0]));});
document.getElementById('cv-pp').addEventListener('touchstart',function(e){e.preventDefault();var r=e.target.getBoundingClientRect(),t=e.touches[0],pad=48,xR=[-2.2,2.2],yR=[-1.0,1.3],W=r.width,H=r.height;ppLaunch(xR[0]+(t.clientX-r.left-pad)/(W-2*pad)*(xR[1]-xR[0]),yR[0]+(H-pad-(t.clientY-r.top))/(H-2*pad)*(yR[1]-yR[0]));},{passive:false});
document.getElementById('pp-hint').textContent=ppSH[1];
ppDraw();
var quizData=[
{q:"What are the two essential ingredients of a dynamical system?",opts:["An input signal and an output measurement","A state and a rule of evolution","A differential equation and a time constant","A fixed point and a trajectory"],ans:1,fb:"Correct. A dynamical system is fully defined by its state and a rule of evolution that says how that state changes over time.",fb_w:"Not quite. The two ingredients are a state (a complete description of the system's condition) and a rule of evolution (how that state changes over time)."},
{q:"A trajectory in state space represents:",opts:["The set of all possible states the system could occupy","The value of a single variable over time","The path traced by the system's state as it evolves over time","The location of a fixed point"],ans:2,fb:"Correct. As the system evolves, its state moves continuously through state space, tracing out a path called a trajectory.",fb_w:"Not quite. A trajectory is the path traced by the system's state point as it moves through state space over time."},
{q:"In the equation tau dx/dt = -x + I, what is the role of the term -x?",opts:["It amplifies the input signal","It sets the steady-state value","It is a leak term that pulls x back toward zero","It defines the time constant"],ans:2,fb:"Correct. The -x term is a leak: it opposes the current value of x, always pulling it back toward zero.",fb_w:"Not quite. The -x term is a leak that pulls x back toward zero, opposing any deviation from rest."},
{q:"What does the time constant tau control?",opts:["The steady-state value that x converges to","The number of fixed points the system has","The timescale on which the system responds and decays","The stability of the fixed point"],ans:2,fb:"Correct. Tau sets the timescale: how quickly the system rises toward steady state and decays. At t = tau, the response is at 63.2% of its final value.",fb_w:"Not quite. Tau sets the timescale of the system's dynamics."},
{q:"A fixed point x* of the system x-dot = f(x) is defined by:",opts:["f-prime(x*) = 0","f(x*) = 0","x* = tau times I","f(x*) > 0"],ans:1,fb:"Correct. A fixed point is where the rate of change is zero: f(x*) = 0.",fb_w:"Not quite. A fixed point satisfies f(x*) = 0: the rate of change is zero."},
{q:"A fixed point is stable when:",opts:["f-prime(x*) > 0","The system oscillates nearby","Small perturbations cause the system to move away","f-prime(x*) < 0, so small perturbations are corrected"],ans:3,fb:"Correct. When f-prime(x*) < 0, perturbations in either direction are opposed by the dynamics and the system returns.",fb_w:"Not quite. A fixed point is stable when f-prime(x*) < 0."},
{q:"In neuroscience, an unstable fixed point often acts as:",opts:["A resting membrane potential","A persistent activity state for working memory","A decision boundary separating two stable states","A neural time constant"],ans:2,fb:"Correct. Unstable fixed points serve as boundaries between basins of attraction of two stable states.",fb_w:"Not quite. Unstable fixed points commonly act as decision boundaries."},
{q:"What is a bifurcation?",opts:["A trajectory that spirals toward a fixed point","A qualitative change in the number or stability of fixed points","The value of x where f(x) is at its maximum","A second-order differential equation"],ans:1,fb:"Correct. A bifurcation is a qualitative change in fixed point structure as a parameter is varied.",fb_w:"Not quite. A bifurcation is a qualitative change in the number or stability of fixed points."},
{q:"In a 2D phase plane, fixed points occur:",opts:["Where the vector field arrows are longest","Where the x-nullcline crosses the axes","At the intersections of the x-nullcline and y-nullcline","Where the trajectory has the highest curvature"],ans:2,fb:"Correct. Fixed points are exactly where the x-nullcline and y-nullcline intersect.",fb_w:"Not quite. Fixed points occur at the intersection of the two nullclines."},
{q:"A recurrent firing-rate model becomes bistable when:",opts:["The time constant tau is very large","The input I is set to zero","The recurrent gain w exceeds a critical threshold (approximately w > 1)","The nonlinearity is replaced by a linear function"],ans:2,fb:"Correct. When recurrent gain w exceeds a critical value, the system bifurcates into two stable fixed points separated by an unstable one.",fb_w:"Not quite. Bistability emerges when the recurrent gain w exceeds approximately w > 1."}
];
var qAnswered=new Array(quizData.length).fill(false),qScore=0;
function buildQuiz(){var c=document.getElementById('quiz-container');c.innerHTML='';qAnswered=new Array(quizData.length).fill(false);qScore=0;document.getElementById('quiz-score').style.display='none';var letters=['A','B','C','D'];quizData.forEach(function(item,qi){var block=document.createElement('div');block.className='q-block';block.id='q-'+qi;var optsHtml=item.opts.map(function(opt,oi){return '<button class="q-opt" onclick="quizAnswer('+qi+','+oi+')" id="q-'+qi+'-opt-'+oi+'"><span class="q-letter">'+letters[oi]+'</span><span>'+opt+'</span></button>';}).join('');block.innerHTML='<div class="q-num">Question '+(qi+1)+' of '+quizData.length+'</div><div class="q-text">'+item.q+'</div><div class="q-opts">'+optsHtml+'</div><div class="q-feedback" id="q-'+qi+'-fb"></div>';c.appendChild(block);});}
function quizAnswer(qi,chosen){if(qAnswered[qi])return;qAnswered[qi]=true;var item=quizData[qi],correct=chosen===item.ans;if(correct)qScore++;item.opts.forEach(function(_,oi){var btn=document.getElementById('q-'+qi+'-opt-'+oi);btn.disabled=true;if(oi===item.ans)btn.classList.add(correct?'correct':'reveal');if(oi===chosen&&!correct)btn.classList.add('wrong');});var fb=document.getElementById('q-'+qi+'-fb');fb.textContent=(correct?'\u2713 ':'\u2717 ')+(correct?item.fb:item.fb_w);fb.className='q-feedback show '+(correct?'correct':'wrong');if(qAnswered.every(Boolean)){var sd=document.getElementById('quiz-score');document.getElementById('quiz-score-val').textContent=qScore+' / '+quizData.length;document.getElementById('quiz-score-msg').textContent=qScore>=8?"Great work \u2014 you're ready for Module 2.":"Please go through the entire text again carefully and make sure you follow each concept before moving on.";sd.style.display='block';sd.scrollIntoView({behavior:'smooth',block:'nearest'});}}
function quizReset(){buildQuiz();window.scrollTo({top:document.getElementById('concept-check').offsetTop-80,behavior:'smooth'});}
buildQuiz();
</script>
</body>
</html>