Here is a comprehensive guide to 10 common heart diseases with their characteristic ECG changes and illustrative ECG images.
| Territory | Culprit Artery | Leads with ST Elevation |
|---|---|---|
| Anterior | LAD | V1-V4, I, aVL |
| Inferior | RCA or LCx | II, III, aVF |
| Lateral | LCx | I, aVL, V5-V6 |
| Posterior | RCA or LCx | Reciprocal changes V1-V3 (tall R, ST depression) |
| Right ventricular | Proximal RCA | V1, V4R |






| Stage | Timing | ECG Finding |
|---|---|---|
| 1 | Hours of onset | Diffuse ST elevation (concave, saddle-shaped) in all leads except aVR and V1; PR depression in most leads; PR elevation in aVR ("knuckle sign") |
| 2 | Days | ST and PR segments return to baseline |
| 3 | Days-weeks | Diffuse T-wave inversions (without Q waves) |
| 4 | Weeks-months | ECG normalizes |


| Feature | Finding | Mechanism |
|---|---|---|
| Short PR interval | <120 ms | Accessory pathway bypasses AV nodal delay |
| Delta wave | Slurred initial QRS upstroke | Early ventricular activation via accessory pathway |
| Wide QRS | Slightly prolonged (fused complex) | Combination of accessory + normal activation |
| Condition | Key ECG Finding(s) | Hallmark Feature |
|---|---|---|
| STEMI | Focal convex ST elevation + reciprocal ST depression | Territory-specific; evolves to Q waves |
| Atrial Fibrillation | No P waves, irregularly irregular RR | Chaotic baseline, no two RR intervals equal |
| Atrial Flutter | Sawtooth flutter waves ~300 bpm | Regular 2:1 rate ~150 bpm |
| 1st-degree AV block | PR >200 ms, all beats conduct | Prolonged PR, no dropped beats |
| 2nd-degree Mobitz I | Progressive PR lengthening then dropped beat | Group beating, Wenckebach cycles |
| 2nd-degree Mobitz II | Constant PR then sudden dropped beat | Abrupt block, often with BBB |
| 3rd-degree AV block | Complete AV dissociation | P and QRS march independently |
| Acute Pericarditis | Diffuse concave ST elevation + PR depression | Saddle-shaped, all leads except aVR |
| Ventricular Tachycardia | Wide QRS tachycardia + AV dissociation | Fusion/capture beats pathognomonic |
| Ventricular Fibrillation | Chaotic, no QRS, immediate cardiac arrest | No organized waveform of any kind |
| WPW | Short PR + delta wave + wide QRS | Pre-excitation triad in sinus rhythm |
| HCM | LVH + deep narrow lateral Q waves | "Dagger" Q waves without prior MI |
| Pulmonary Embolism | Sinus tachycardia; S1Q3T3; T inversions V1-V4 | Right heart strain pattern |
Make a quick-reference PDF card of ECG changes
ls /home/daytona/workspace/ 2>/dev/null || echo "workspace empty or missing"
mkdir -p /home/daytona/workspace/ecg-reference && python3 -c "import reportlab; print('reportlab ok')" && python3 -c "from reportlab.lib import colors; print('colors ok')"
~/ecg-reference/build_ecg_card.py
"""
ECG Quick-Reference Card Generator
Two-page A4 landscape PDF
"""
from reportlab.lib.pagesizes import A4, landscape
from reportlab.lib import colors
from reportlab.lib.units import mm
from reportlab.platypus import (
SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer,
HRFlowable, KeepTogether
)
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
import os
OUTPUT = "/home/daytona/workspace/ecg-reference/ECG_Quick_Reference.pdf"
# ── Colour palette ──────────────────────────────────────────────────────────
NAVY = colors.HexColor("#0D2B55")
CRIMSON = colors.HexColor("#C0392B")
TEAL = colors.HexColor("#117A8B")
AMBER = colors.HexColor("#D4860B")
GREEN = colors.HexColor("#1A7A4A")
PURPLE = colors.HexColor("#6C3483")
SLATE = colors.HexColor("#2C3E50")
LTGREY = colors.HexColor("#F0F3F6")
MIDGREY = colors.HexColor("#D5D8DC")
WHITE = colors.white
BLACK = colors.black
# ── Styles ───────────────────────────────────────────────────────────────────
def make_styles():
base = {"fontName": "Helvetica", "leading": 10}
return {
"title": ParagraphStyle("title", fontName="Helvetica-Bold",
fontSize=17, textColor=WHITE,
alignment=TA_CENTER, leading=22),
"subtitle": ParagraphStyle("subtitle", fontName="Helvetica",
fontSize=8.5, textColor=colors.HexColor("#A9CCE3"),
alignment=TA_CENTER, leading=11),
"section_hdr": ParagraphStyle("section_hdr", fontName="Helvetica-Bold",
fontSize=9, textColor=WHITE,
alignment=TA_CENTER, leading=11),
"condition": ParagraphStyle("condition", fontName="Helvetica-Bold",
fontSize=8.2, textColor=NAVY, leading=10),
"body": ParagraphStyle("body", fontName="Helvetica",
fontSize=7.2, textColor=SLATE, leading=9.5),
"bold_body": ParagraphStyle("bold_body", fontName="Helvetica-Bold",
fontSize=7.2, textColor=SLATE, leading=9.5),
"highlight": ParagraphStyle("highlight", fontName="Helvetica-BoldOblique",
fontSize=7, textColor=CRIMSON, leading=9),
"footer": ParagraphStyle("footer", fontName="Helvetica",
fontSize=6.5, textColor=colors.grey,
alignment=TA_CENTER, leading=9),
"col_hdr": ParagraphStyle("col_hdr", fontName="Helvetica-Bold",
fontSize=7.5, textColor=WHITE,
alignment=TA_CENTER, leading=9),
}
S = make_styles()
def p(text, style="body"):
return Paragraph(text, S[style])
def bullet(items, style="body"):
"""Render a list of strings as bullet paragraphs joined by line breaks."""
lines = "<br/>".join(f"• {i}" for i in items)
return Paragraph(lines, S[style])
# ── Data ─────────────────────────────────────────────────────────────────────
CONDITIONS = [
{
"no": "01",
"name": "STEMI",
"full": "ST-Elevation MI",
"color": CRIMSON,
"rhythm": "Sinus (any rate)",
"pr": "Normal",
"qrs": "Normal (narrow)",
"st_t": "Focal convex ST↑; hyperacute T; reciprocal ST↓",
"other": "Pathologic Q waves evolve within hours",
"hallmark": "Territory-specific ST↑ with mirror-image ST↓",
"leads": "V1–V4 (ant) · II,III,aVF (inf) · I,aVL (lat)",
},
{
"no": "02",
"name": "NSTEMI / UA",
"full": "Non-ST-Elevation ACS",
"color": colors.HexColor("#A93226"),
"rhythm": "Sinus ± tachycardia",
"pr": "Normal",
"qrs": "Normal (narrow)",
"st_t": "ST depression ± T-wave inversions; no ST elevation",
"other": "Wellens sign: deep symmetric T↓ V1–V4 → critical LAD stenosis",
"hallmark": "ST↓ in ≥2 contiguous leads (≥0.5 mm)",
"leads": "V1–V6, I, aVL",
},
{
"no": "03",
"name": "Atrial Fibrillation",
"full": "AF",
"color": TEAL,
"rhythm": "Irregularly irregular, 60–170 bpm (uncontrolled >170)",
"pr": "Absent (no P waves)",
"qrs": "Narrow (unless BBB/WPW)",
"st_t": "No specific changes",
"other": "Fibrillatory baseline (chaotic) best in V1",
"hallmark": "Absent P waves + irregularly irregular RR intervals",
"leads": "All leads",
},
{
"no": "04",
"name": "Atrial Flutter",
"full": "AFL",
"color": colors.HexColor("#148F77"),
"rhythm": "Regular 150 bpm (2:1); 100 bpm (3:1)",
"pr": "Not measurable (flutter waves)",
"qrs": "Narrow (unless BBB)",
"st_t": "No specific changes",
"other": "Atrial rate 250–350 bpm; 2:1 block commonest",
"hallmark": "Sawtooth flutter waves in II, III, aVF, V1",
"leads": "II, III, aVF, V1",
},
{
"no": "05",
"name": "1° AV Block",
"full": "First-Degree Heart Block",
"color": PURPLE,
"rhythm": "Regular sinus",
"pr": "Prolonged >200 ms; every P conducts",
"qrs": "Normal (narrow)",
"st_t": "Normal",
"other": "Usually benign; may occur with digoxin, inferior MI, vagal tone",
"hallmark": "PR >200 ms, no dropped beats",
"leads": "All leads",
},
{
"no": "06",
"name": "2° AV Block",
"full": "Mobitz I & II",
"color": colors.HexColor("#7D3C98"),
"rhythm": "Regularly irregular (group beating in Mobitz I)",
"pr": "Mobitz I: progressively lengthens → dropped QRS\nMobitz II: fixed PR → sudden dropped QRS",
"qrs": "Normal (Mobitz I); may be wide with BBB (Mobitz II)",
"st_t": "Normal",
"other": "Mobitz II → high risk of progression to complete block",
"hallmark": "Mobitz I: Wenckebach cycles · Mobitz II: abrupt dropped beat",
"leads": "Rhythm strip (II or V1)",
},
{
"no": "07",
"name": "3° AV Block",
"full": "Complete Heart Block",
"color": colors.HexColor("#512E5F"),
"rhythm": "P rate > QRS rate; complete AV dissociation",
"pr": "Variable (no relationship between P and QRS)",
"qrs": "Narrow (junctional escape 40–60 bpm)\nor Wide (ventricular escape 20–40 bpm)",
"st_t": "Normal (unless underlying ischemia)",
"other": "Fusion/capture beats absent (unlike VT). Syncope/Stokes-Adams attacks",
"hallmark": "P waves and QRS march independently",
"leads": "All leads",
},
{
"no": "08",
"name": "Acute Pericarditis",
"full": "Pericarditis (Stage 1–4)",
"color": AMBER,
"rhythm": "Sinus tachycardia (pain-driven)",
"pr": "PR depression in most leads; PR elevation in aVR ('knuckle sign')",
"qrs": "Normal",
"st_t": "Diffuse concave (saddle-shaped) ST↑ in ALL leads except aVR\nStage 3: diffuse T-wave inversions",
"other": "No reciprocal ST↓; no Q waves; ST elevation <5 mm",
"hallmark": "Diffuse concave ST↑ + PR depression (≠ focal STEMI)",
"leads": "I, II, III, aVF, V2–V6 (spares aVR/V1)",
},
{
"no": "09",
"name": "Ventricular Tachycardia",
"full": "VT (Monomorphic)",
"color": colors.HexColor("#CB4335"),
"rhythm": "Regular, 100–250 bpm",
"pr": "Dissociated (P waves unrelated to QRS)",
"qrs": "Wide ≥120 ms (usually >140 ms); LBBB or RBBB morphology",
"st_t": "Discordant (opposite to QRS main deflection)",
"other": "AV dissociation + fusion/capture beats = diagnostic of VT\nConcordance in precordial leads strongly favours VT",
"hallmark": "Wide QRS tachycardia + AV dissociation",
"leads": "All leads; aVR, V1 most diagnostic",
},
{
"no": "10",
"name": "Ventricular Fibrillation",
"full": "VF",
"color": colors.HexColor("#922B21"),
"rhythm": "Absent — chaotic undulations",
"pr": "None",
"qrs": "None — no organised complexes",
"st_t": "None",
"other": "May be fine (low amplitude) or coarse (higher amplitude)\nImmediate defibrillation + CPR",
"hallmark": "Completely chaotic waveform; no P, QRS or T",
"leads": "All leads",
},
{
"no": "11",
"name": "WPW Syndrome",
"full": "Wolff-Parkinson-White",
"color": GREEN,
"rhythm": "Sinus (normal rate in baseline)",
"pr": "Short PR <120 ms",
"qrs": "Slightly wide; slurred delta wave at QRS onset",
"st_t": "Discordant ST-T changes (opposite to delta/QRS direction)\nDelta waves may mimic Q-wave MI",
"other": "AVOID adenosine/β-blockers/CCBs/digoxin in AF+WPW (risk of VF)",
"hallmark": "Short PR + delta wave + wide QRS (pre-excitation triad)",
"leads": "Most prominent in leads with dominant QRS",
},
{
"no": "12",
"name": "Hypertrophic CM",
"full": "HCM",
"color": colors.HexColor("#1A5276"),
"rhythm": "Sinus; may have AF",
"pr": "Normal (may be short with LVH)",
"qrs": "Tall voltage (LVH criteria); deep narrow 'dagger' Q waves lateral leads",
"st_t": "Lateral ST depression + T-wave inversions\nApical HCM: massive T inversions V2–V5",
"other": "LVH voltage: S(V1) + R(V5/V6) >35 mm\nLeft atrial enlargement (bifid P); left axis deviation",
"hallmark": "LVH voltage + deep narrow Q waves lateral leads (not infarction)",
"leads": "I, aVL, V4–V6 (Q waves); V1–V4 (voltage)",
},
{
"no": "13",
"name": "Pulmonary Embolism",
"full": "Acute PE",
"color": colors.HexColor("#117864"),
"rhythm": "Sinus tachycardia (most common finding)",
"pr": "Normal",
"qrs": "Possible new RBBB (complete or incomplete)",
"st_t": "T-wave inversions V1–V4 (RV strain); S1Q3T3 pattern (~20%)",
"other": "Right axis deviation; P pulmonale (tall P in II)\nNormal ECG does not exclude PE",
"hallmark": "Sinus tachycardia ± S1Q3T3 ± T inversions V1–V4 (RV strain)",
"leads": "I, III, V1–V4",
},
]
# ── Column widths ─────────────────────────────────────────────────────────────
W = landscape(A4)
H = landscape(A4)[1]
PAGE_W = W - 20*mm # usable width after margins
# proportional column widths (total = 1.0):
# No | Condition | Rhythm | PR | QRS | ST/T | Other | Hallmark | Leads
COL_FRACTIONS = [0.028, 0.085, 0.115, 0.095, 0.115, 0.175, 0.185, 0.155, 0.047]
COL_WIDTHS = [PAGE_W * f for f in COL_FRACTIONS]
def build_header_row():
hdrs = ["#", "Condition", "Rhythm", "PR Interval",
"QRS Complex", "ST / T Changes", "Additional Features",
"Hallmark Finding", "Key Leads"]
return [p(h, "col_hdr") for h in hdrs]
def condition_cell(c):
return [
p(f"<b>{c['no']}</b>", "condition"),
p(f"<b>{c['name']}</b>", "condition"),
p(f"<i>{c['full']}</i>", "body"),
]
def build_data_row(c):
def wrap(txt):
return Paragraph(txt.replace("\n", "<br/>"), S["body"])
return [
p(f"<b>{c['no']}</b>", "bold_body"),
[p(f"<b>{c['name']}</b>", "condition"),
p(f"<i>{c['full']}</i>", "body")],
wrap(c["rhythm"]),
wrap(c["pr"]),
wrap(c["qrs"]),
wrap(c["st_t"]),
wrap(c["other"]),
Paragraph(f"<b>{c['hallmark']}</b>", S["highlight"]),
wrap(c["leads"]),
]
def row_style(i, color):
"""Return TableStyle commands for a single data row i (0-indexed, row 1 = first data row)."""
row = i + 1 # +1 for header
bg = LTGREY if i % 2 == 0 else WHITE
return [
("BACKGROUND", (0, row), (0, row), color),
("BACKGROUND", (1, row), (-1, row), bg),
("TEXTCOLOR", (0, row), (0, row), WHITE),
("FONTNAME", (0, row), (0, row), "Helvetica-Bold"),
]
def build_table(conditions):
rows = [build_header_row()] + [build_data_row(c) for c in conditions]
# Base style
style_cmds = [
# Header
("BACKGROUND", (0, 0), (-1, 0), NAVY),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, 0), 8),
("TEXTCOLOR", (0, 0), (-1, 0), WHITE),
("ALIGN", (0, 0), (-1, 0), "CENTER"),
("VALIGN", (0, 0), (-1, -1), "TOP"),
("FONTSIZE", (0, 1), (-1, -1), 7.2),
("ROWBACKGROUND", (0, 1), (-1, -1), [LTGREY, WHITE]),
("GRID", (0, 0), (-1, -1), 0.35, MIDGREY),
("LINEBELOW", (0, 0), (-1, 0), 1.2, NAVY),
("TOPPADDING", (0, 0), (-1, -1), 3),
("BOTTOMPADDING", (0, 0), (-1, -1), 3.5),
("LEFTPADDING", (0, 0), (-1, -1), 3),
("RIGHTPADDING", (0, 0), (-1, -1), 3),
# Hallmark column emphasis
("FONTNAME", (7, 1), (7, -1), "Helvetica-BoldOblique"),
("TEXTCOLOR", (7, 1), (7, -1), CRIMSON),
# Alternate row backgrounds
*[("BACKGROUND", (1, i+1), (-1, i+1), LTGREY if i % 2 == 0 else WHITE)
for i in range(len(conditions))],
# Color strip in column 0
*[("BACKGROUND", (0, i+1), (0, i+1), conditions[i]["color"])
for i in range(len(conditions))],
("TEXTCOLOR", (0, 1), (0, -1), WHITE),
("ALIGN", (0, 1), (0, -1), "CENTER"),
("FONTNAME", (0, 1), (0, -1), "Helvetica-Bold"),
]
t = Table(rows, colWidths=COL_WIDTHS, repeatRows=1)
t.setStyle(TableStyle(style_cmds))
return t
def build_legend():
"""Small footnote legend."""
text = (
"<b>Abbreviations:</b> "
"ACS = Acute Coronary Syndrome · AF = Atrial Fibrillation · AFL = Atrial Flutter · "
"AV = Atrioventricular · BBB = Bundle Branch Block · CCB = Calcium Channel Blocker · "
"CM = Cardiomyopathy · CPR = Cardiopulmonary Resuscitation · HCM = Hypertrophic Cardiomyopathy · "
"LAD = Left Anterior Descending · LBBB = Left Bundle Branch Block · LCx = Left Circumflex · "
"LVH = Left Ventricular Hypertrophy · MI = Myocardial Infarction · NSTEMI = Non-ST-Elevation MI · "
"PE = Pulmonary Embolism · RBBB = Right Bundle Branch Block · RCA = Right Coronary Artery · "
"RV = Right Ventricular · STEMI = ST-Elevation MI · UA = Unstable Angina · "
"VF = Ventricular Fibrillation · VT = Ventricular Tachycardia · WPW = Wolff-Parkinson-White | "
"<b>Sources:</b> Harrison's 22e · Tintinalli's 9e · Braunwald's Heart Disease · "
"Fuster & Hurst's The Heart 15e · Washington Manual · Goldman-Cecil Medicine"
)
return Paragraph(text, S["footer"])
def build_title_block():
title_data = [[
Paragraph("ECG QUICK-REFERENCE CARD", S["title"]),
]]
title_style = TableStyle([
("BACKGROUND", (0, 0), (-1, -1), NAVY),
("TOPPADDING", (0, 0), (-1, -1), 7),
("BOTTOMPADDING", (0, 0), (-1, -1), 4),
("LEFTPADDING", (0, 0), (-1, -1), 8),
("RIGHTPADDING", (0, 0), (-1, -1), 8),
])
subtitle_data = [[
Paragraph(
"13 Common Cardiac Conditions · Rhythm · PR · QRS · ST/T Changes · Hallmark Findings · Key Leads",
S["subtitle"]
)
]]
subtitle_style = TableStyle([
("BACKGROUND", (0, 0), (-1, -1), NAVY),
("TOPPADDING", (0, 0), (-1, -1), 0),
("BOTTOMPADDING", (0, 0), (-1, -1), 8),
("LEFTPADDING", (0, 0), (-1, -1), 8),
("RIGHTPADDING", (0, 0), (-1, -1), 8),
])
t1 = Table(title_data, colWidths=[PAGE_W])
t1.setStyle(title_style)
t2 = Table(subtitle_data, colWidths=[PAGE_W])
t2.setStyle(subtitle_style)
return [t1, t2]
# ── Build PDF ────────────────────────────────────────────────────────────────
def build():
doc = SimpleDocTemplate(
OUTPUT,
pagesize=landscape(A4),
leftMargin=10*mm, rightMargin=10*mm,
topMargin=8*mm, bottomMargin=8*mm,
)
story = []
story += build_title_block()
story.append(Spacer(1, 3*mm))
story.append(build_table(CONDITIONS))
story.append(Spacer(1, 2.5*mm))
story.append(HRFlowable(width="100%", thickness=0.5, color=MIDGREY))
story.append(Spacer(1, 1*mm))
story.append(build_legend())
doc.build(story)
print(f"PDF written to: {OUTPUT}")
if __name__ == "__main__":
build()
cd /home/daytona/workspace/ecg-reference && python3 build_ecg_card.py 2>&1
~/ecg-reference/build_ecg_card.py
Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/ecg-reference/build_ecg_card.py'
cd /home/daytona/workspace/ecg-reference && python3 build_ecg_card.py 2>&1
ls -lh /home/daytona/workspace/ecg-reference/ECG_Quick_Reference.pdf
ECG Quick Reference
PDF Document · PDF