1. Stack
| Concern | Choice |
|---|---|
| Build | Vite 5 + TypeScript (strict). Same toolchain as BrainSurgery, and it works with Node 18 on the server |
| Traces | Canvas 2D: paper grid, sweep (monitor) and scroll (paper) modes, 25/50 mm/s, gain 5/10/20 mm/mV, 1 mV calibration pulse |
| Teaching diagrams | SVG: annotated beats, interval brackets, hexaxial axis wheel, territory maps |
| Audio | Web Audio synthesis only: QRS beep, IEC-style alarms, S1/S2/S3/S4, rub, murmurs |
| State | localStorage with a versioned schema (ecg.v1) and a migration chain |
| Language | English only. All clinical text lives in the JSON data files, not in code |
| UI | Plain DOM + CSS (no framework). Monitor dark mode and pink/red ECG-paper mode |
2. Source layout
src/
renderer/ grid, trace canvas (sweep/scroll), 12-lead layout, calipers, overlays, territory highlight
waveforms/ beat synthesis, lead projection, rhythm engine (event timeline), noise/artifact, measurements
teach/ waveform breakdown, systematic framework, deep-dives, mimics & traps, condition pages
diagnosis/ live arena, timer/scoring, vitals + CLN scenarios, MCQ engine, spaced repetition
ui/ shell, start/disclaimer, HUD readouts, hint drawer, analytics dashboard, settings
audio/ monitor beep, alarms, auscultation synth, audio-rules gate
data/ conditions/{arr,cnd,pcd,isc,met,str,axs,cln}.json (incl. names, explanations, pearls),
mcq/*.json, schema.ts (validation)
3. Waveform engine — the core design
Morphology and rhythm are generated separately, so any rhythm can be combined with any morphology (e.g. AF + LBBB, or sinus + hyperkalaemia stage 3).
Beat morphology. A beat is a sum of shaped components: P, Q, R, S, R′, T, U, delta, J/Osborn and epsilon. Each component has an amplitude (mV), a centre (ms), a width, and a skew (asymmetric Gaussian or raised-cosine). ST is modelled as a segment with elevation at the J point, a slope and a shape (straight / concave / convex / coved / scooped / downsloping).
Twelve leads.
- A frontal-plane QRS/P/T vector (axis) is projected onto the hexaxial limb leads, so I, II, III, aVR, aVL and aVF stay mathematically consistent (III = II − I, aVR = −(I+II)/2).
- Precordial leads use a V1→V6 R/S progression template.
- Each condition then patches specific leads or named lead groups (
inferior,anterior,lateral,septal,highLateral,right,posterior) with overrides such as ST shift, T amplitude/inversion, Q waves, RSR′ or notching. - Extra leads V4R and V7–V9 are derived from the same model.
Rhythm engine. Produces an event timeline: atrial events, conduction (PR, blocked or conducted), and ventricular events (normal, aberrant, PVC, paced, escape, fusion). It supports:
- stochastic RR intervals
- Wenckebach PR growth
- independent atrial and ventricular clocks for complete heart block
- flutter at 300/min with a block ratio
- AF f-waves (band-limited noise) with irregular RR
- VF (drifting multi-sine with amplitude envelope)
- torsades (amplitude-modulated twisting)
- pacing spikes with capture/sense logic
Measurements. HR, PR, QRS, QT and QTc (Bazett and Fridericia) are computed from the generated timeline, not stored separately, so the readouts always match what is drawn.
4. Condition database (JSON, one file per category)
All IDs exactly as in the spec: ARR-01…24, CND-01…12, PCD-01…03, ISC-01…13, MET-01…10, STR-01…16, AXS-01…04, CLN-01…02. Each entry has:
{
"id": "CND-07",
"category": "CND",
"rhythm": { "type": "sinus", "rate": [60, 90] },
"morphology": { "qrs": { "width": 140 }, "leads": { "V1": { "pattern": "rsR'" }, "lateral": { "sSlur": true } } },
"criteria": [{ "key": "qrs_ge_120", "threshold": "QRS ≥ 120 ms" }],
"discriminators": ["..."],
"mimics": ["CND-12", "ARR-23"],
"audio": "none", // or "split_S2", "S4", "rub", ...
"vitals": { "pulse": true }
}
Each entry also carries its own name, clinical explanation, board pearls and rationale text. A schema validator runs at build time, so a malformed condition fails the build instead of breaking at runtime. Adding a condition means adding JSON only.
5. Part 1 — Teach
- Waveform breakdown: an interactive annotated beat on the grid. Hovering a wave shows its normal duration and voltage limits. Includes a QTc explorer (Bazett vs Fridericia) that shows Bazett's error at extreme heart rates.
- Systematic framework: a 9-step walkthrough (rate → rhythm → axis → P → PR → QRS → ST/T → QT/QTc → other), applied step by step to any condition.
- Morphological deep-dives: highlight overlays for the delta wave, digoxin scoop, sine wave, coved Brugada ST, epsilon wave, Osborn J wave and electrical alternans.
- Mimics & traps: side-by-side comparisons:
- STEMI vs pericarditis vs early repolarisation vs LV aneurysm
- VT vs SVT with aberrancy (Brugada and Vereckei aVR algorithms as interactive flowcharts)
- 2:1 block ambiguity
- fine VF vs asystole (gain demo)
- Condition pages: live strip + 12-lead, criteria with thresholds, discriminators, mimics, pearls, and an audio toggle that is gated by the audio rules.
6. Part 2 — Live Diagnosis & MCQ Arena
- Live mode: scrolling Lead II strip with an optional 12-lead view, and a countdown timer. Pick the diagnosis from a searchable list. The optional vitals panel (BP, SpO₂, pulse present/absent) drives the CLN scenarios (PEA, pulseless VT vs VT with a pulse).
- MCQ engine: board-style stems with 4 options, instant feedback, rationale and "trap" notes.
Includes management questions (stable vs unstable, pulseless vs pulse present). Uses a hand-written
bank plus templated questions generated from condition metadata, with distractors drawn from each
condition's
mimics. - Analytics: accuracy by category, time to answer, confusion matrix of mistaken pairs, and spaced-repetition (SM-2-style) flagging of missed patterns.
7. Tools & HUD
- Calipers: drag to measure ms and mV. March-out mode repeats the interval to check P–P and R–R regularity.
- 12-lead switcher: with territory highlighting (septal, anterior, lateral, inferior, posterior, RV).
- Display: paper mode (pink/red) vs dark monitor mode, grid on/off, and a gain/speed selector.
- Hint drawer: tiered hints (hint 1 → hint 2 → full explanation).
- Readouts: HR, PR, QRS, QT/QTc and axis in monospace.
- English only (no language toggle).
8. Audio rules (enforced in one gate module)
- Monitor beep on each QRS. High-priority alarm for VF, VT, asystole and pulseless states; advisory tone for brady/tachycardia.
- No heart sounds for VF, pulseless VT, asystole or PEA; the alarm plays only.
- Rhythm correlates:
- AF: irregular S1 with varying intensity
- complete heart block: variable S1 intensity (occasional "cannon" loud S1)
- tachycardias: rate-matched S1/S2
- Structural correlates:
- pericarditis: friction rub
- LVH: S4
- HCM: systolic ejection murmur that increases with Valsalva (Valsalva toggle)
- tamponade: muffled sounds
9. Milestones (each runnable and deployed to ecg.myneetpg.com)
- M1: grid, renderer (sweep/scroll, speed, gain, calibration), beat synthesis, lead projection, rhythm engine. NSR, AF, VF working with live readouts. Placeholder shell.
- M2: Teach framework: waveform breakdown, systematic framework, annotation overlays, condition page template, first ~25 conditions authored in JSON.
- M3: calipers and march-out, 12-lead viewer incl. V4R and V7–V9, territory highlighting, audio engine + rules gate.
- M4: Live arena (scrolling sim, timer, vitals, CLN scenarios), remaining condition database completed (all IDs), mimics & traps module.
- M5: MCQ engine, feedback/scoring, rationale database (~150 questions + templates), hint drawer.
- M6: analytics (category accuracy, speed, confusion matrix, spaced repetition), versioned localStorage, polish, README.
After each milestone: run the dev server, fix errors, deploy, and tell you what to test.
10. Honest limits
- Synthetic, rule-based waveforms: morphologically faithful for teaching, not digitised patient ECGs.
- Clarity analytics is not included unless you want it (as on BrainSurgery).