Explain ECG in detail


QTc = QT / √RR (RR interval in seconds)
| Axis | Unit | Value |
|---|---|---|
| Horizontal (time) | Small box (1 mm) | 0.04 seconds (40 msec) |
| Horizontal (time) | Large box (5 mm) | 0.20 seconds (200 msec) |
| Vertical (voltage) | Small box (1 mm) | 0.1 mV |
| Vertical (voltage) | Large box (5 mm) | 0.5 mV |
| Standard speed | - | 25 mm/sec |
| Standard gain | - | 10 mm = 1 mV |

| Lead | Position | Normal QRS |
|---|---|---|
| V1 | 4th intercostal space, right sternal border | Small r, deep S (rS pattern) |
| V2 | 4th intercostal space, left sternal border | Small r, deep S |
| V3 | Between V2 and V4 | Transitional |
| V4 | 5th intercostal space, midclavicular line | Transitional (R wave = S wave) |
| V5 | Anterior axillary line | Tall R, small S |
| V6 | Midaxillary line | Tall R, no S (qR pattern) |
| Axis | Degrees |
|---|---|
| Normal | -30° to +90° |
| Left axis deviation (LAD) | -30° to -90° |
| Right axis deviation (RAD) | +90° to +180° |
| Extreme (northwest) axis | -90° to ±180° |
| Parameter | Normal Range |
|---|---|
| Heart rate | 60-100 bpm |
| PR interval | 120-200 msec (3-5 small boxes) |
| QRS duration | 70-110 msec (< 3 small boxes) |
| QTc | < 440 msec (men), < 460 msec (women) |
| QRS axis | -30° to +90° |
| R wave transition | V3-V4 |
Create a presentation on ECG interpretation and common pathologic patterns
mkdir -p /home/daytona/workspace/ecg-presentation && ls /home/daytona/workspace/
node /home/daytona/skills/shared/scripts/fetch_images.js \ "https://cdn.orris.care/cdss_images/ed3e36266afd563652d7495682c022c3508452257126640a6ab3375b0b0a7e46.png" \ "https://cdn.orris.care/cdss_images/ef815eeb06c673b405e3c7f9112d1e9ff6fbea96a00a2723b233d384156386bd.png" \ "https://cdn.orris.care/cdss_images/8b26161d5b113aef98766f605c7bba4c915e78cd2023db7dbdc80f9dd92dc48a.png" \ "https://cdn.orris.care/cdss_images/980d49638880f123e60f04d038c1af30751f3ad125962f137d98b37cf6e5d83e.png" \ "https://cdn.orris.care/cdss_images/c77d6d597bfe808053a402fa9624538fe9360e78c43eb10a56369a082b8f37a6.png" \ 2>/dev/null | node -e "const d=require('fs');const chunks=[];process.stdin.on('data',c=>chunks.push(c));process.stdin.on('end',()=>{const arr=JSON.parse(Buffer.concat(chunks).toString());arr.forEach((img,i)=>{if(!img.error){const b64=img.base64.split(',')[1];d.writeFileSync('/home/daytona/workspace/ecg-presentation/img'+i+'.b64',img.base64);}else{console.log('Error img'+i+':',img.error);}});console.log('Done, saved',arr.filter(x=>!x.error).length,'images');})"
~/ecg-presentation/build.js
const pptxgen = require("pptxgenjs");
const fs = require("fs");
// Load pre-fetched images
const img0 = fs.readFileSync("/home/daytona/workspace/ecg-presentation/img0.b64", "utf8"); // conduction system
const img1 = fs.readFileSync("/home/daytona/workspace/ecg-presentation/img1.b64", "utf8"); // ECG waveform labeled
const img2 = fs.readFileSync("/home/daytona/workspace/ecg-presentation/img2.b64", "utf8"); // precordial leads
const img3 = fs.readFileSync("/home/daytona/workspace/ecg-presentation/img3.b64", "utf8"); // Einthoven's triangle
const img4 = fs.readFileSync("/home/daytona/workspace/ecg-presentation/img4.b64", "utf8"); // normal 12-lead ECG
// ─── Color Palette ───────────────────────────────────────────────
const DARK_BG = "0D1B2A"; // deep navy (dominant)
const MID_BG = "1B2A3B"; // mid navy
const CARD_BG = "162230"; // card background
const ACCENT = "E63946"; // bold red-crimson (ECG line color)
const ACCENT2 = "26C6DA"; // teal (labels, highlights)
const GOLD = "FFB703"; // amber (key values)
const WHITE = "FFFFFF";
const LIGHT_GRAY = "B0C4D8";
const MUTED = "6E8BA3";
let pres = new pptxgen();
pres.layout = "LAYOUT_16x9";
pres.author = "Orris Medical";
pres.title = "ECG Interpretation & Common Pathologic Patterns";
// ─────────────────────────────────────────────────────────────────
// Helper: dark slide background
function darkBg(slide) {
slide.background = { color: DARK_BG };
}
// Helper: accent bar at top
function topBar(slide, color = ACCENT) {
slide.addShape(pres.shapes.RECTANGLE, {
x: 0, y: 0, w: 10, h: 0.08, fill: { color }, line: { type: "none" }
});
}
// Helper: section heading
function sectionHead(slide, title, subtitle = "") {
slide.addText(title, {
x: 0.45, y: 0.18, w: 9.1, h: 0.55,
fontSize: 22, bold: true, color: WHITE, fontFace: "Calibri",
margin: 0
});
if (subtitle) {
slide.addText(subtitle, {
x: 0.45, y: 0.72, w: 9.1, h: 0.3,
fontSize: 11, color: ACCENT2, fontFace: "Calibri", italic: true, margin: 0
});
}
}
// Helper: divider line
function divider(slide, y = 0.65, color = ACCENT, opacity = 0.6) {
slide.addShape(pres.shapes.RECTANGLE, {
x: 0.45, y, w: 9.1, h: 0.025,
fill: { color, transparency: Math.round((1 - opacity) * 100) },
line: { type: "none" }
});
}
// Helper: card (rounded rect)
function card(slide, x, y, w, h, fillColor = CARD_BG) {
slide.addShape(pres.shapes.ROUNDED_RECTANGLE, {
x, y, w, h,
fill: { color: fillColor },
line: { color: ACCENT2, pt: 0.5, transparency: 60 },
rectRadius: 0.12,
shadow: { type: "outer", color: "000000", blur: 8, offset: 2, angle: 135, opacity: 0.25 }
});
}
// ─────────────────────────────────────────────────────────────────
// SLIDE 1: Title
// ─────────────────────────────────────────────────────────────────
{
const slide = pres.addSlide();
darkBg(slide);
// ECG-line decorative band at top
slide.addShape(pres.shapes.RECTANGLE, {
x: 0, y: 0, w: 10, h: 0.55, fill: { color: ACCENT }, line: { type: "none" }
});
slide.addShape(pres.shapes.RECTANGLE, {
x: 0, y: 0.55, w: 10, h: 0.06, fill: { color: GOLD }, line: { type: "none" }
});
// Title
slide.addText("ECG Interpretation", {
x: 0.4, y: 1.0, w: 9.2, h: 0.9,
fontSize: 46, bold: true, color: WHITE, fontFace: "Calibri",
align: "center", charSpacing: 2
});
slide.addText("& Common Pathologic Patterns", {
x: 0.4, y: 1.85, w: 9.2, h: 0.6,
fontSize: 26, color: ACCENT2, fontFace: "Calibri",
align: "center", italic: true
});
// Subtitle bar
slide.addShape(pres.shapes.RECTANGLE, {
x: 3.0, y: 2.6, w: 4.0, h: 0.04, fill: { color: GOLD }, line: { type: "none" }
});
// Decorative ECG waveform image (labeled waveform)
slide.addImage({ data: img1, x: 1.2, y: 3.0, w: 7.6, h: 1.9, transparency: 18 });
// Footer
slide.addText("Goldman-Cecil Medicine · Guyton & Hall Physiology", {
x: 0, y: 5.2, w: 10, h: 0.35,
fontSize: 9, color: MUTED, align: "center", fontFace: "Calibri", italic: true
});
}
// ─────────────────────────────────────────────────────────────────
// SLIDE 2: Table of Contents
// ─────────────────────────────────────────────────────────────────
{
const slide = pres.addSlide();
darkBg(slide);
topBar(slide, GOLD);
sectionHead(slide, "Overview", "What this presentation covers");
divider(slide, 0.65, GOLD);
const topics = [
["01", "The Cardiac Conduction System", "Anatomy of electrical activation"],
["02", "ECG Waveforms & Intervals", "P, QRS, T, U · PR, QT, QRS durations"],
["03", "The 12-Lead System", "Limb leads, augmented leads, precordial leads"],
["04", "Heart Rate & Axis", "Rate calculation · frontal plane axis"],
["05", "Conduction Abnormalities", "Bundle branch blocks · fascicular blocks · AV blocks"],
["06", "Ischemia & Infarction", "ST changes · Q waves · territory recognition"],
["07", "Hypertrophy & Enlargement", "LVH · RVH · atrial enlargement criteria"],
["08", "Electrolyte & Drug Effects", "Hypo/hyperkalaemia · hypercalcaemia · QTc drugs"],
];
const colX = [0.4, 4.85];
topics.forEach((t, i) => {
const row = i % 4;
const col = Math.floor(i / 4);
const x = colX[col];
const y = 0.9 + row * 1.12;
card(slide, x, y, 4.22, 0.96);
slide.addText(t[0], {
x: x + 0.12, y: y + 0.08, w: 0.48, h: 0.36,
fontSize: 14, bold: true, color: ACCENT, fontFace: "Calibri", align: "center"
});
slide.addText(t[1], {
x: x + 0.62, y: y + 0.06, w: 3.5, h: 0.36,
fontSize: 11.5, bold: true, color: WHITE, fontFace: "Calibri", margin: 0
});
slide.addText(t[2], {
x: x + 0.62, y: y + 0.44, w: 3.5, h: 0.3,
fontSize: 9.5, color: LIGHT_GRAY, fontFace: "Calibri", italic: true, margin: 0
});
});
}
// ─────────────────────────────────────────────────────────────────
// SLIDE 3: Cardiac Conduction System
// ─────────────────────────────────────────────────────────────────
{
const slide = pres.addSlide();
darkBg(slide);
topBar(slide, ACCENT);
sectionHead(slide, "The Cardiac Conduction System", "Normal sequence of electrical activation");
divider(slide);
// Image: conduction anatomy
card(slide, 0.4, 0.85, 4.4, 4.4, "0A1520");
slide.addImage({ data: img0, x: 0.55, y: 0.95, w: 4.1, h: 4.15 });
// Steps
const steps = [
{ num: "1", label: "SA Node", desc: "Right atrium near SVC. Dominant pacemaker (60-100 bpm). Not visible on surface ECG." },
{ num: "2", label: "Atria", desc: "Wavefront spreads R→L via Bachmann bundle. Generates P wave." },
{ num: "3", label: "AV Node", desc: "Only bridge between atria & ventricles. Physiologic delay allows ventricular filling." },
{ num: "4", label: "Bundle of His", desc: "Exits AV node into fast-conducting tissue at the ventricular septum." },
{ num: "5", label: "Bundle Branches", desc: "Right bundle branch + Left bundle branch (anterior & posterior fascicles)." },
{ num: "6", label: "Purkinje Fibers", desc: "Rapid subendocardial distribution → ventricular depolarization → QRS complex." },
];
steps.forEach((s, i) => {
const y = 0.87 + i * 0.74;
slide.addShape(pres.shapes.ELLIPSE, {
x: 5.0, y: y + 0.04, w: 0.32, h: 0.32,
fill: { color: ACCENT }, line: { type: "none" }
});
slide.addText(s.num, {
x: 5.0, y: y + 0.04, w: 0.32, h: 0.32,
fontSize: 9, bold: true, color: WHITE, align: "center", valign: "middle", margin: 0
});
slide.addText(s.label, {
x: 5.38, y: y + 0.03, w: 4.2, h: 0.22,
fontSize: 11, bold: true, color: ACCENT2, fontFace: "Calibri", margin: 0
});
slide.addText(s.desc, {
x: 5.38, y: y + 0.26, w: 4.2, h: 0.38,
fontSize: 9, color: LIGHT_GRAY, fontFace: "Calibri", margin: 0
});
});
}
// ─────────────────────────────────────────────────────────────────
// SLIDE 4: ECG Waveforms & Intervals
// ─────────────────────────────────────────────────────────────────
{
const slide = pres.addSlide();
darkBg(slide);
topBar(slide, ACCENT2);
sectionHead(slide, "ECG Waveforms & Intervals", "Every deflection has a physiological meaning");
divider(slide, 0.65, ACCENT2);
// Large waveform image
card(slide, 0.4, 0.82, 4.2, 4.5, "080F18");
slide.addImage({ data: img1, x: 0.5, y: 0.9, w: 4.0, h: 4.3 });
// Wave table
const waves = [
{ wave: "P Wave", color: ACCENT2, normal: "< 120ms, < 0.3mV", meaning: "Atrial depolarization" },
{ wave: "PR Interval", color: GOLD, normal: "120–200 ms", meaning: "AV conduction time" },
{ wave: "QRS Complex", color: ACCENT, normal: "< 110 ms; 1.0–1.5 mV", meaning: "Ventricular depolarization" },
{ wave: "ST Segment", color: WHITE, normal: "Isoelectric (±0.5mm)", meaning: "Plateau phase (no net current)" },
{ wave: "T Wave", color: GOLD, normal: "0.2–0.3 mV; concordant", meaning: "Ventricular repolarization" },
{ wave: "QT Interval", color: ACCENT2, normal: "QTc < 440ms ♂ / 460ms ♀", meaning: "Total ventricular recovery" },
{ wave: "U Wave", color: MUTED, normal: "Small, positive", meaning: "Purkinje / M-cell repolarization" },
];
waves.forEach((w, i) => {
const y = 0.85 + i * 0.675;
card(slide, 4.82, y, 4.75, 0.62, MID_BG);
slide.addText(w.wave, {
x: 4.96, y: y + 0.05, w: 1.65, h: 0.26,
fontSize: 10.5, bold: true, color: w.color, fontFace: "Calibri", margin: 0
});
slide.addText(w.normal, {
x: 4.96, y: y + 0.32, w: 1.65, h: 0.22,
fontSize: 8.5, color: LIGHT_GRAY, fontFace: "Calibri", margin: 0, italic: true
});
slide.addText(w.meaning, {
x: 6.65, y: y + 0.15, w: 2.78, h: 0.3,
fontSize: 9.5, color: WHITE, fontFace: "Calibri", margin: 0
});
});
}
// ─────────────────────────────────────────────────────────────────
// SLIDE 5: The 12-Lead System
// ─────────────────────────────────────────────────────────────────
{
const slide = pres.addSlide();
darkBg(slide);
topBar(slide, GOLD);
sectionHead(slide, "The 12-Lead ECG System", "12 views of the heart's electrical activity");
divider(slide, 0.65, GOLD);
// Einthoven image
card(slide, 0.4, 0.85, 3.2, 2.35, "0A1520");
slide.addImage({ data: img3, x: 0.5, y: 0.88, w: 3.0, h: 2.28 });
// Precordial image
card(slide, 0.4, 3.3, 3.2, 2.0, "0A1520");
slide.addImage({ data: img2, x: 0.5, y: 3.38, w: 3.0, h: 1.85 });
// Lead groups
const groups = [
{
title: "Bipolar Limb Leads",
color: ACCENT,
items: [
"Lead I — LA(+) vs RA(−) → lateral wall",
"Lead II — LL(+) vs RA(−) → inferior wall",
"Lead III — LL(+) vs LA(−) → inferior wall",
"Einthoven's law: I + III = II",
]
},
{
title: "Augmented Limb Leads",
color: ACCENT2,
items: [
"aVR — right shoulder view (inverted in normal)",
"aVL — left shoulder view (lateral)",
"aVF — inferior view",
"Normal: upright P & QRS in I, II, III, aVF",
]
},
{
title: "Precordial (Chest) Leads V1–V6",
color: GOLD,
items: [
"V1/V2: right side — rS pattern (negative QRS)",
"V3/V4: transition zone (R = S at V3 or V4)",
"V5/V6: left side — tall R wave (positive QRS)",
"R wave progression: V1→V5 R grows, S shrinks",
]
},
];
groups.forEach((g, i) => {
const x = 3.82;
const y = 0.83 + i * 1.55;
card(slide, x, y, 5.75, 1.42, CARD_BG);
slide.addShape(pres.shapes.RECTANGLE, {
x, y, w: 0.12, h: 1.42, fill: { color: g.color }, line: { type: "none" }
});
slide.addText(g.title, {
x: x + 0.2, y: y + 0.06, w: 5.4, h: 0.28,
fontSize: 11, bold: true, color: g.color, fontFace: "Calibri", margin: 0
});
const bullets = g.items.map((t, j) => ({
text: t, options: { bullet: { code: "2022" }, color: j === 3 ? GOLD : LIGHT_GRAY, fontSize: 9, breakLine: j < g.items.length - 1 }
}));
slide.addText(bullets, {
x: x + 0.22, y: y + 0.36, w: 5.4, h: 0.98,
fontFace: "Calibri", margin: 0
});
});
}
// ─────────────────────────────────────────────────────────────────
// SLIDE 6: Heart Rate & Axis
// ─────────────────────────────────────────────────────────────────
{
const slide = pres.addSlide();
darkBg(slide);
topBar(slide, ACCENT);
sectionHead(slide, "Heart Rate & Electrical Axis", "Two fundamental parameters in every ECG read");
divider(slide);
// Heart rate card
card(slide, 0.4, 0.85, 4.5, 2.2, CARD_BG);
slide.addShape(pres.shapes.RECTANGLE, {
x: 0.4, y: 0.85, w: 0.12, h: 2.2, fill: { color: ACCENT }, line: { type: "none" }
});
slide.addText("Heart Rate Calculation", {
x: 0.62, y: 0.9, w: 4.1, h: 0.32,
fontSize: 13, bold: true, color: ACCENT, fontFace: "Calibri", margin: 0
});
slide.addText([
{ text: "Regular rhythm:", options: { bold: true, color: WHITE, fontSize: 10 } },
{ text: " 300 ÷ (large boxes between R-R peaks)", options: { color: LIGHT_GRAY, fontSize: 10 } },
{ text: "\n or 1500 ÷ (small boxes between R-R peaks)", options: { color: LIGHT_GRAY, fontSize: 10, italic: true } },
], { x: 0.62, y: 1.28, w: 4.1, h: 0.45, fontFace: "Calibri", margin: 0 });
slide.addText([
{ text: "Irregular rhythm:", options: { bold: true, color: WHITE, fontSize: 10 } },
{ text: " Count QRS in 10 sec strip × 6", options: { color: LIGHT_GRAY, fontSize: 10 } },
], { x: 0.62, y: 1.8, w: 4.1, h: 0.3, fontFace: "Calibri", margin: 0 });
const rateValues = [
{ label: "Bradycardia", val: "< 60", col: ACCENT2 },
{ label: "Normal", val: "60-100", col: GOLD },
{ label: "Tachycardia", val: "> 100", col: ACCENT },
];
rateValues.forEach((r, i) => {
card(slide, 0.62 + i * 1.46, 2.26, 1.36, 0.65, MID_BG);
slide.addText(r.val + " bpm", { x: 0.62 + i * 1.46, y: 2.29, w: 1.36, h: 0.28, fontSize: 12, bold: true, color: r.col, align: "center", fontFace: "Calibri", margin: 0 });
slide.addText(r.label, { x: 0.62 + i * 1.46, y: 2.56, w: 1.36, h: 0.22, fontSize: 8.5, color: MUTED, align: "center", fontFace: "Calibri", margin: 0 });
});
// Axis card
card(slide, 0.4, 3.22, 4.5, 2.05, CARD_BG);
slide.addShape(pres.shapes.RECTANGLE, {
x: 0.4, y: 3.22, w: 0.12, h: 2.05, fill: { color: GOLD }, line: { type: "none" }
});
slide.addText("QRS Axis (Frontal Plane)", {
x: 0.62, y: 3.26, w: 4.1, h: 0.3,
fontSize: 13, bold: true, color: GOLD, fontFace: "Calibri", margin: 0
});
const axes = [
{ range: "−30° to +90°", label: "Normal axis", color: GOLD },
{ range: "−30° to −90°", label: "Left axis deviation (LAD)", color: ACCENT2 },
{ range: "+90° to +180°", label: "Right axis deviation (RAD)", color: ACCENT },
{ range: "±180° / −90°", label: "Extreme (NW) axis", color: LIGHT_GRAY },
];
axes.forEach((a, i) => {
const y = 3.62 + i * 0.38;
slide.addText(a.range, { x: 0.62, y, w: 1.6, h: 0.3, fontSize: 9.5, bold: true, color: a.color, fontFace: "Calibri", margin: 0 });
slide.addText(a.label, { x: 2.25, y, w: 2.5, h: 0.3, fontSize: 9.5, color: WHITE, fontFace: "Calibri", margin: 0 });
});
// Right side: 12-lead normal ECG image
card(slide, 5.15, 0.85, 4.45, 4.45, "060D14");
slide.addImage({ data: img4, x: 5.22, y: 0.9, w: 4.3, h: 4.35 });
slide.addText("Normal 12-Lead ECG", {
x: 5.15, y: 5.3, w: 4.45, h: 0.2,
fontSize: 8, color: MUTED, align: "center", fontFace: "Calibri", italic: true
});
}
// ─────────────────────────────────────────────────────────────────
// SLIDE 7: Conduction Abnormalities
// ─────────────────────────────────────────────────────────────────
{
const slide = pres.addSlide();
darkBg(slide);
topBar(slide, ACCENT2);
sectionHead(slide, "Conduction Abnormalities", "Bundle branch blocks · fascicular blocks · AV blocks");
divider(slide, 0.65, ACCENT2);
const blocks = [
{
title: "LBBB — Left Bundle Branch Block",
color: ACCENT,
criteria: [
"QRS ≥ 120 msec (broad, ≥ 3 small boxes)",
"Broad notched R ('M shape') in I, V5, V6",
"Deep S or QS in V1 — no r wave",
"No septal q waves in lateral leads",
"Discordant ST-T changes (opposite to QRS)",
"Isolated new LBBB → rule out STEMI (Sgarbossa criteria)",
]
},
{
title: "RBBB — Right Bundle Branch Block",
color: ACCENT2,
criteria: [
"QRS ≥ 120 msec",
"RSR' ('rabbit ears') pattern in V1/V2",
"Wide, slurred S wave in I, V5, V6",
"ST depression & T-wave inversion in V1-V3",
"Incomplete RBBB: same morphology, QRS 100-119 ms",
"RBBB ± LAD → bifascicular block (high-risk)",
]
},
{
title: "AV Blocks",
color: GOLD,
criteria: [
"1st degree: PR > 200 ms, every P conducts — benign",
"2nd Mobitz I (Wenckebach): Progressive PR ↑ then dropped beat",
"2nd Mobitz II: Fixed PR then sudden non-conducted P — dangerous",
"3rd degree (complete): P waves & QRS independent — no relationship",
"3rd degree: narrow escape = junctional (40-60); wide = ventricular (<40)",
"Mobitz II & 3rd degree → urgent pacemaker evaluation",
]
},
];
blocks.forEach((b, i) => {
const y = 0.85 + i * 1.58;
card(slide, 0.4, y, 9.2, 1.44, CARD_BG);
slide.addShape(pres.shapes.RECTANGLE, {
x: 0.4, y, w: 0.14, h: 1.44, fill: { color: b.color }, line: { type: "none" }
});
slide.addText(b.title, {
x: 0.65, y: y + 0.07, w: 8.8, h: 0.28,
fontSize: 12, bold: true, color: b.color, fontFace: "Calibri", margin: 0
});
const leftItems = b.criteria.slice(0, 3);
const rightItems = b.criteria.slice(3);
const makeBullets = (arr) => arr.map((t, j) => ({
text: t, options: { bullet: { code: "25B8" }, color: LIGHT_GRAY, fontSize: 9.5, breakLine: j < arr.length - 1 }
}));
slide.addText(makeBullets(leftItems), { x: 0.65, y: y + 0.4, w: 4.35, h: 0.92, fontFace: "Calibri", margin: 0 });
slide.addText(makeBullets(rightItems), { x: 5.1, y: y + 0.4, w: 4.4, h: 0.92, fontFace: "Calibri", margin: 0 });
});
}
// ─────────────────────────────────────────────────────────────────
// SLIDE 8: Ischemia & Infarction
// ─────────────────────────────────────────────────────────────────
{
const slide = pres.addSlide();
darkBg(slide);
topBar(slide, ACCENT);
sectionHead(slide, "Ischemia & Myocardial Infarction", "ST changes, Q waves, and territory recognition");
divider(slide);
// ST changes table
const stChanges = [
{ title: "STEMI (Acute MI)", color: ACCENT, bg: "1A0A0A",
points: [
"ST elevation ≥ 1mm (limb leads) or ≥ 2mm (chest leads)",
"Convex ('tombstone') or flat ST elevation in ≥ 2 contiguous leads",
"Reciprocal ST depression in opposite leads (supports diagnosis)",
"Early: hyperacute tall T waves (first minutes)",
"Evolves to: Q waves + T inversion + ST returns to baseline",
]
},
{ title: "NSTEMI / Unstable Angina", color: GOLD, bg: "1A1500",
points: [
"Horizontal or downsloping ST depression ≥ 0.5mm",
"T-wave inversion (symmetric, deep) in ischemic territory",
"No pathological Q waves (no transmural necrosis)",
"Differentiate NSTEMI from UAP by troponin elevation",
"Posterior MI: ST depression V1-V3 + tall R in V1 (mirror image)",
]
},
];
stChanges.forEach((s, i) => {
const y = 0.85 + i * 2.1;
card(slide, 0.4, y, 5.5, 1.92, s.bg);
slide.addShape(pres.shapes.RECTANGLE, { x: 0.4, y, w: 0.14, h: 1.92, fill: { color: s.color }, line: { type: "none" } });
slide.addText(s.title, { x: 0.64, y: y + 0.07, w: 5.1, h: 0.3, fontSize: 12, bold: true, color: s.color, fontFace: "Calibri", margin: 0 });
const bullets = s.points.map((t, j) => ({ text: t, options: { bullet: { code: "2022" }, color: LIGHT_GRAY, fontSize: 9, breakLine: j < s.points.length - 1 } }));
slide.addText(bullets, { x: 0.64, y: y + 0.42, w: 5.1, h: 1.38, fontFace: "Calibri", margin: 0 });
});
// Territory table
card(slide, 6.1, 0.85, 3.5, 4.2, CARD_BG);
slide.addText("Territory Localization", {
x: 6.22, y: 0.91, w: 3.26, h: 0.3,
fontSize: 11, bold: true, color: ACCENT2, fontFace: "Calibri", margin: 0
});
const territories = [
{ leads: "II, III, aVF", artery: "RCA", wall: "Inferior" },
{ leads: "V1–V4", artery: "LAD", wall: "Anterior" },
{ leads: "V1–V2", artery: "LAD/Sep", wall: "Septal" },
{ leads: "I, aVL, V5–V6", artery: "LCx", wall: "Lateral" },
{ leads: "V1–V4 + I,aVL", artery: "LAD", wall: "Anterolateral" },
{ leads: "V1–V3 mirror", artery: "LCx", wall: "Posterior" },
{ leads: "V3R–V4R", artery: "RCA", wall: "Right Ventricle" },
];
const tableRows = [
[
{ text: "Leads", options: { bold: true, fill: { color: "2A1515" }, color: ACCENT, fontSize: 9 } },
{ text: "Artery", options: { bold: true, fill: { color: "2A1515" }, color: ACCENT, fontSize: 9 } },
{ text: "Wall", options: { bold: true, fill: { color: "2A1515" }, color: ACCENT, fontSize: 9 } },
],
...territories.map(t => [
{ text: t.leads, options: { color: ACCENT2, fontSize: 8.5, fill: { color: CARD_BG } } },
{ text: t.artery, options: { color: GOLD, fontSize: 8.5, fill: { color: CARD_BG } } },
{ text: t.wall, options: { color: LIGHT_GRAY, fontSize: 8.5, fill: { color: CARD_BG } } },
])
];
slide.addTable(tableRows, {
x: 6.22, y: 1.26, w: 3.26, h: 3.65,
border: { pt: 0.5, color: "2A3A4A" },
rowH: 0.38,
colW: [1.35, 0.82, 1.09],
fontFace: "Calibri"
});
}
// ─────────────────────────────────────────────────────────────────
// SLIDE 9: Hypertrophy & Enlargement
// ─────────────────────────────────────────────────────────────────
{
const slide = pres.addSlide();
darkBg(slide);
topBar(slide, GOLD);
sectionHead(slide, "Hypertrophy & Chamber Enlargement", "Voltage and morphology changes from increased wall stress");
divider(slide, 0.65, GOLD);
const conditions = [
{
title: "Left Ventricular Hypertrophy (LVH)",
color: ACCENT,
criteria: [
"Sokolov-Lyon: S(V1) + R(V5 or V6) ≥ 35mm",
"Cornell: R(aVL) + S(V3) ≥ 28mm (♂) / ≥ 20mm (♀)",
"R in aVL ≥ 11mm alone",
"ST depression + T inversion in V5-V6, I, aVL (strain pattern)",
"Left axis deviation common",
"Cause: hypertension, aortic stenosis, HCM",
]
},
{
title: "Right Ventricular Hypertrophy (RVH)",
color: ACCENT2,
criteria: [
"Dominant R wave in V1 (R > S in V1)",
"R/S ratio > 1 in V1 with R/S ratio < 1 in V6",
"Right axis deviation (> +90°)",
"ST depression + T inversion in V1-V3 (right strain)",
"Cause: pulmonary HTN, pulmonary stenosis, cor pulmonale",
"P pulmonale in II (tall, peaked P > 2.5mm) suggests RAE",
]
},
{
title: "Atrial Enlargement",
color: GOLD,
criteria: [
"LAE (P mitrale): P wave > 120ms; bifid (notched) P in II; biphasic P in V1 with deep negative terminal",
"RAE (P pulmonale): P wave ≥ 2.5mm tall in II, III, or aVF; peaked (not broad)",
"Biatrial: features of both LAE + RAE simultaneously",
"Causes of LAE: mitral stenosis, LV failure, hypertension",
"Causes of RAE: COPD, pulmonary hypertension, tricuspid disease",
"Atrial enlargement increases AF risk",
]
},
];
conditions.forEach((c, i) => {
const y = 0.85 + i * 1.58;
card(slide, 0.4, y, 9.2, 1.44, CARD_BG);
slide.addShape(pres.shapes.RECTANGLE, { x: 0.4, y, w: 0.14, h: 1.44, fill: { color: c.color }, line: { type: "none" } });
slide.addText(c.title, { x: 0.64, y: y + 0.07, w: 8.8, h: 0.28, fontSize: 12, bold: true, color: c.color, fontFace: "Calibri", margin: 0 });
const left = c.criteria.slice(0, 3).map((t, j) => ({ text: t, options: { bullet: { code: "25B8" }, color: LIGHT_GRAY, fontSize: 9.5, breakLine: j < 2 } }));
const right = c.criteria.slice(3).map((t, j) => ({ text: t, options: { bullet: { code: "25B8" }, color: LIGHT_GRAY, fontSize: 9.5, breakLine: j < 2 } }));
slide.addText(left, { x: 0.64, y: y + 0.4, w: 4.35, h: 0.92, fontFace: "Calibri", margin: 0 });
slide.addText(right, { x: 5.1, y: y + 0.4, w: 4.4, h: 0.92, fontFace: "Calibri", margin: 0 });
});
}
// ─────────────────────────────────────────────────────────────────
// SLIDE 10: Electrolyte & Drug Effects
// ─────────────────────────────────────────────────────────────────
{
const slide = pres.addSlide();
darkBg(slide);
topBar(slide, ACCENT2);
sectionHead(slide, "Electrolyte & Drug Effects on the ECG", "Recognizing systemic disturbances from the trace");
divider(slide, 0.65, ACCENT2);
const effects = [
{ condition: "Hypokalaemia (K⁺ ↓)", color: ACCENT2, changes: ["Prominent U waves (after T)", "Flat or inverted T waves", "ST depression", "Prolonged QU interval (appears as long QT)", "Risk: polymorphic VT (Torsades)"] },
{ condition: "Hyperkalaemia (K⁺ ↑)", color: ACCENT, changes: ["Peaked, tall, narrow (tent-like) T waves — earliest sign", "Widened QRS → sine wave pattern (severe)", "Flattened / absent P waves", "PR prolongation", "Risk: VF / asystole"] },
{ condition: "Hypocalcaemia (Ca²⁺ ↓)", color: GOLD, changes: ["Prolonged QT interval (long ST segment)", "T wave may be normal height", "Risk: Torsades de pointes"] },
{ condition: "Hypercalcaemia (Ca²⁺ ↑)", color: WHITE, changes: ["Short QT interval (shortened ST)", "J-wave (Osborn) occasionally", "Bradycardia, PR prolongation in severe cases"] },
{ condition: "Digoxin effect", color: MUTED, changes: ["Down-sloping ST depression ('reverse tick' or 'hockey stick')", "T wave flattening/inversion", "PR prolongation (AV conduction slowing)", "Short QT; does not indicate toxicity alone"] },
{ condition: "QT-prolonging Drugs", color: ACCENT, changes: ["Class Ia antiarrhythmics (quinidine, procainamide)", "Class III antiarrhythmics (amiodarone, sotalol)", "Macrolide antibiotics (azithromycin, clarithromycin)", "Antipsychotics (haloperidol, quetiapine)", "Monitor QTc; risk of Torsades de pointes"] },
];
const positions = [
{ x: 0.4, y: 0.9 }, { x: 3.42, y: 0.9 }, { x: 6.45, y: 0.9 },
{ x: 0.4, y: 3.2 }, { x: 3.42, y: 3.2 }, { x: 6.45, y: 3.2 },
];
effects.forEach((e, i) => {
const { x, y } = positions[i];
card(slide, x, y, 2.88, 2.12, CARD_BG);
slide.addShape(pres.shapes.RECTANGLE, { x, y, w: 2.88, h: 0.08, fill: { color: e.color }, line: { type: "none" } });
slide.addText(e.condition, {
x: x + 0.1, y: y + 0.14, w: 2.68, h: 0.3,
fontSize: 10, bold: true, color: e.color, fontFace: "Calibri", margin: 0
});
const bullets = e.changes.map((t, j) => ({ text: t, options: { bullet: { code: "2022" }, color: LIGHT_GRAY, fontSize: 8.5, breakLine: j < e.changes.length - 1 } }));
slide.addText(bullets, {
x: x + 0.1, y: y + 0.48, w: 2.68, h: 1.5,
fontFace: "Calibri", margin: 0
});
});
}
// ─────────────────────────────────────────────────────────────────
// SLIDE 11: Normal Intervals Quick Reference Table
// ─────────────────────────────────────────────────────────────────
{
const slide = pres.addSlide();
darkBg(slide);
topBar(slide, GOLD);
sectionHead(slide, "Quick Reference: Normal ECG Values", "Memorize these before reading any ECG");
divider(slide, 0.65, GOLD);
const tableData = [
[
{ text: "Parameter", options: { bold: true, fill: { color: "2A2000" }, color: GOLD, fontSize: 11 } },
{ text: "Normal Range", options: { bold: true, fill: { color: "2A2000" }, color: GOLD, fontSize: 11 } },
{ text: "Abnormal If…", options: { bold: true, fill: { color: "2A2000" }, color: GOLD, fontSize: 11 } },
{ text: "Clinical Significance", options: { bold: true, fill: { color: "2A2000" }, color: GOLD, fontSize: 11 } },
],
["Heart Rate", "60–100 bpm", "< 60 bradycardia; > 100 tachycardia", "Pacemaker rate, autonomic tone, medications"],
["PR Interval", "120–200 ms (3–5 small boxes)", "> 200 ms = 1st-degree AV block; < 120 ms = preexcitation", "AV nodal conduction; WPW if short"],
["QRS Duration", "70–110 ms (< 3 small boxes)", "≥ 120 ms = bundle branch block", "Ventricular conduction; wide → BBB or aberrancy"],
["QTc", "< 440ms ♂ / < 460ms ♀", "> 500ms = high Torsades risk", "Drug review; electrolytes; congenital LQTS"],
["QRS Axis", "−30° to +90°", "< −30° LAD; > +90° RAD", "Fascicular block; hypertrophy; COPD"],
["P-wave duration", "< 120 ms", "> 120ms = left atrial enlargement", "Atrial disease; mitral pathology"],
["P-wave amplitude", "< 2.5 mm (limb leads)", "> 2.5mm in II = RAE (P pulmonale)", "Pulmonary hypertension; COPD"],
["R progression", "Transition at V3 or V4", "Poor R progression → anterior MI", "Anterior wall infarction; LBBB"],
["ST segment", "Isoelectric (±0.5mm)", "Elevation or depression", "Ischemia, pericarditis, Brugada, LV aneurysm"],
["T wave direction", "Concordant with QRS", "Inversion = ischemia / strain / RBBB", "Follow for dynamic ischemia"],
];
const formattedRows = tableData.map((row, i) => {
if (i === 0) return row;
const rowColor = i % 2 === 0 ? CARD_BG : MID_BG;
return row.map((cell, j) => ({
text: cell,
options: {
fill: { color: rowColor },
color: j === 0 ? WHITE : (j === 1 ? ACCENT2 : (j === 2 ? GOLD : LIGHT_GRAY)),
fontSize: 9.5,
}
}));
});
slide.addTable(formattedRows, {
x: 0.4, y: 0.88, w: 9.2, h: 4.5,
border: { pt: 0.5, color: "2A3A4A" },
rowH: 0.41,
colW: [1.65, 1.92, 2.68, 2.95],
fontFace: "Calibri"
});
}
// ─────────────────────────────────────────────────────────────────
// SLIDE 12: Systematic Approach to ECG Reading
// ─────────────────────────────────────────────────────────────────
{
const slide = pres.addSlide();
darkBg(slide);
topBar(slide, ACCENT);
sectionHead(slide, "Systematic 10-Step ECG Approach", "Never miss a finding — use the same order every time");
divider(slide);
const steps = [
{ n: "1", title: "Calibration", desc: "Check paper speed (25mm/s) and gain (10mm=1mV). Look for artifacts." },
{ n: "2", title: "Rate", desc: "300 ÷ large boxes (regular) or count × 6 over 10 seconds (irregular)." },
{ n: "3", title: "Rhythm", desc: "Is it regular? Are all P waves identical? Is P:QRS ratio 1:1?" },
{ n: "4", title: "P Wave", desc: "Present? Normal morphology? PR interval 120-200ms? Every P followed by QRS?" },
{ n: "5", title: "PR Interval", desc: "Fixed or variable? Prolonged (>200ms) or short (<120ms)?" },
{ n: "6", title: "QRS Duration", desc: "Narrow (<110ms) or wide (≥120ms)? Bundle branch block morphology?" },
{ n: "7", title: "Axis", desc: "Check leads I and aVF. Calculate or estimate frontal plane axis." },
{ n: "8", title: "ST Segment", desc: "Elevation? Depression? Contiguous leads? Reciprocal changes?" },
{ n: "9", title: "T Wave & QTc", desc: "Inverted T waves? Peaked T waves? Calculate QTc with Bazett's formula." },
{ n: "10", title: "Overall Impression", desc: "Integrate all findings. Correlate with clinical context." },
];
const cols = [steps.slice(0, 5), steps.slice(5)];
cols.forEach((col, ci) => {
col.forEach((s, si) => {
const x = 0.4 + ci * 4.85;
const y = 0.88 + si * 0.93;
card(slide, x, y, 4.55, 0.82, CARD_BG);
slide.addShape(pres.shapes.ELLIPSE, {
x: x + 0.1, y: y + 0.12, w: 0.45, h: 0.45,
fill: { color: ci === 0 ? ACCENT : ACCENT2 }, line: { type: "none" }
});
slide.addText(s.n, {
x: x + 0.1, y: y + 0.12, w: 0.45, h: 0.45,
fontSize: 12, bold: true, color: WHITE, align: "center", valign: "middle", margin: 0
});
slide.addText(s.title, {
x: x + 0.65, y: y + 0.07, w: 3.75, h: 0.26,
fontSize: 11, bold: true, color: WHITE, fontFace: "Calibri", margin: 0
});
slide.addText(s.desc, {
x: x + 0.65, y: y + 0.35, w: 3.75, h: 0.38,
fontSize: 8.5, color: LIGHT_GRAY, fontFace: "Calibri", margin: 0
});
});
});
}
// ─────────────────────────────────────────────────────────────────
// SLIDE 13: Summary / Key Takeaways
// ─────────────────────────────────────────────────────────────────
{
const slide = pres.addSlide();
darkBg(slide);
slide.addShape(pres.shapes.RECTANGLE, {
x: 0, y: 0, w: 10, h: 0.55, fill: { color: ACCENT }, line: { type: "none" }
});
slide.addShape(pres.shapes.RECTANGLE, {
x: 0, y: 0.55, w: 10, h: 0.06, fill: { color: GOLD }, line: { type: "none" }
});
slide.addText("Key Takeaways", {
x: 0.4, y: 0.7, w: 9.2, h: 0.55,
fontSize: 28, bold: true, color: WHITE, fontFace: "Calibri", align: "center"
});
const takeaways = [
{ icon: "→", color: ACCENT, text: "Every ECG wave has a direct physiological correlate — P (atrial depol), QRS (ventricular depol), T (ventricular repol)." },
{ icon: "→", color: ACCENT2, text: "Normal intervals: PR 120-200ms · QRS <110ms · QTc <440ms (♂) / <460ms (♀) · axis -30° to +90°." },
{ icon: "→", color: GOLD, text: "Wide QRS (≥120ms) = bundle branch block or ventricular rhythm. LBBB obstructs MI diagnosis — apply Sgarbossa criteria." },
{ icon: "→", color: ACCENT, text: "ST elevation in contiguous leads with reciprocal depression = STEMI until proven otherwise. Localise by territory." },
{ icon: "→", color: ACCENT2, text: "Peaked T waves = hyperkalaemia (tent sign); prominent U waves = hypokalaemia; short QT = hypercalcaemia." },
{ icon: "→", color: GOLD, text: "Always use a systematic 10-step approach. ECG findings must be interpreted in clinical context — never in isolation." },
];
takeaways.forEach((t, i) => {
const y = 1.42 + i * 0.67;
card(slide, 0.4, y, 9.2, 0.58, i % 2 === 0 ? CARD_BG : MID_BG);
slide.addShape(pres.shapes.RECTANGLE, { x: 0.4, y, w: 0.12, h: 0.58, fill: { color: t.color }, line: { type: "none" } });
slide.addText(t.text, {
x: 0.64, y: y + 0.1, w: 8.82, h: 0.38,
fontSize: 10, color: WHITE, fontFace: "Calibri", margin: 0
});
});
slide.addText("Sources: Goldman-Cecil Medicine · Guyton & Hall Textbook of Medical Physiology · Rosen's Emergency Medicine", {
x: 0, y: 5.3, w: 10, h: 0.25,
fontSize: 8, color: MUTED, align: "center", fontFace: "Calibri", italic: true
});
}
// ─────────────────────────────────────────────────────────────────
// SAVE
// ─────────────────────────────────────────────────────────────────
pres.writeFile({ fileName: "/home/daytona/workspace/ecg-presentation/ECG_Interpretation.pptx" })
.then(() => console.log("SUCCESS: ECG_Interpretation.pptx saved"))
.catch(e => { console.error("ERROR:", e); process.exit(1); });
cd /home/daytona/workspace/ecg-presentation && node build.js 2>&1
node -e "const p=require('pptxgenjs'); const pres=new p(); console.log(Object.keys(pres.shapes).filter(k=>k.toLowerCase().includes('ell')||k.toLowerCase().includes('circ')))"
node -e "const p=require('pptxgenjs'); const pres=new p(); console.log(Object.keys(pres.shapes))" | tr ',' '\n' | grep -i oval
node -e "const p=require('pptxgenjs'); const pres=new p(); const keys=Object.keys(pres.shapes); console.log(keys.join('\n'))" 2>&1 | head -40
node -e "const p=require('pptxgenjs'); const pres=new p(); const keys=Object.keys(pres.shapes); console.log(keys.join('\n'))" 2>&1 | grep -E "OVAL|oval"
~/ecg-presentation/build.js
Successfully replaced 2 instance(s) of the string in '/home/daytona/workspace/ecg-presentation/build.js'
cd /home/daytona/workspace/ecg-presentation && node build.js 2>&1
ls -lh /home/daytona/workspace/ecg-presentation/ECG_Interpretation.pptx
ECG Interpretation
Presentation · PPTX
| # | Slide | Highlights |
|---|---|---|
| 1 | Title | Full-bleed ECG waveform backdrop, crimson/gold header band |
| 2 | Table of Contents | 8 topic cards with section numbers |
| 3 | Cardiac Conduction System | Textbook anatomy diagram + 6-step activation sequence |
| 4 | ECG Waveforms & Intervals | Labeled waveform image + 7-row wave reference (normal values + meaning) |
| 5 | The 12-Lead System | Einthoven triangle image + precordial lead image + lead group descriptions |
| 6 | Heart Rate & Axis | Rate calculation formulas, bradycardia/normal/tachy cards, axis table + real 12-lead ECG |
| 7 | Conduction Abnormalities | LBBB, RBBB, and all AV blocks with criteria |
| 8 | Ischemia & Infarction | STEMI vs NSTEMI criteria + territory localization table (leads → artery → wall) |
| 9 | Hypertrophy & Enlargement | LVH (Sokolov-Lyon, Cornell), RVH, atrial enlargement criteria |
| 10 | Electrolyte & Drug Effects | 6-panel grid: hypo/hyperkalaemia, hypo/hypercalcaemia, digoxin, QT-prolonging drugs |
| 11 | Quick Reference Table | 10-row normal values table with "abnormal if" and clinical significance columns |
| 12 | Systematic 10-Step Approach | Step-by-step reading method in numbered card layout |
| 13 | Key Takeaways | 6 high-yield points for rapid review |