Give me 10 ecg changes with ecg example

Reading File
Finding Sources
Searching the Web

ECG changes examples with images STEMI LBBB hyperkalemia LVH atrial fibrillation

Finding Sources
Searching the Web

ECG examples site:litfl.com OR site:ecglibrary.com 10 classic ECG patterns images

Reading File
Reading Web Page

https://litfl.com/ecg-library

Loading Image
Loading Image
Loading Image
Loading Image
Loading Image
Searching the Web

site:litfl.com ECG atrial fibrillation STEMI hyperkalemia WPW QT prolongation images

Loading Image
Loading Image
Loading Image
Loading Image
Loading Image
I have the killer rhythms infographic and comprehensive knowledge. Now I'll compose the full answer with the infographic plus detailed descriptions for each ECG change.

Here are 10 classic ECG changes with explanations and visual examples:

ECG Killer Rhythms Overview

LITFL Killer ECG Rhythms Infographic
(Infographic from LITFL ECG Library showing 8 killer patterns; full 10 covered below)

1. ST-Elevation Myocardial Infarction (STEMI)

What to look for:
  • ST elevation ≥1 mm in ≥2 contiguous limb leads, or ≥2 mm in ≥2 contiguous precordial leads
  • Reciprocal ST depression in opposite leads
  • Hyperacute T waves (early sign) - broad, tall, symmetrical T waves
  • Q waves develop over hours (sign of necrosis)
Territory clues:
  • Inferior STEMI: ST elevation in II, III, aVF (right coronary artery)
  • Anterior STEMI: ST elevation in V1-V4 (LAD)
  • Lateral STEMI: ST elevation in I, aVL, V5-V6
ECG Example:
Inferior STEMI ECG
Clinical significance: Time-critical - door-to-balloon <90 min. - Tintinalli's Emergency Medicine

2. Atrial Fibrillation (AF)

What to look for:
  • Absent P waves (replaced by fibrillatory baseline - "irregularly irregular" f-waves)
  • Irregularly irregular R-R intervals (no two consecutive intervals the same)
  • Ventricular rate typically 100-160 bpm if uncontrolled
  • Narrow QRS complex (unless aberrant conduction or pre-existing bundle branch block)
ECG Example (from LITFL):
LeadFinding
All leadsNo discernible P waves
RhythmCompletely irregular
V1Fibrillatory baseline most visible
Clinical significance: Risk of thromboembolism (stroke). CHA₂DS₂-VASc score guides anticoagulation.

3. Left Bundle Branch Block (LBBB)

What to look for:
  • QRS duration ≥120 ms (broad complex)
  • Broad notched ("M-shaped") R waves in lateral leads I, aVL, V5, V6
  • Deep S waves in V1 (rS or QS pattern)
  • Absence of septal Q waves in leads I, V5, V6
  • Appropriate ST/T wave discordance (ST and T wave go opposite to main QRS deflection)
Memory aid: WiLLiaM - W shape in V1, M shape in V6 for LBBB
ECG Example: Broad notched R in V6, deep S in V1, QRS >120 ms - see LITFL LBBB examples
Clinical significance: New LBBB with chest pain warrants urgent evaluation. Apply Sgarbossa criteria to detect underlying MI.

4. Hyperkalemia

What to look for (progressive with K⁺ level):
K⁺ LevelECG Change
5.5-6.5 mmol/LTall, peaked ("tented") T waves - narrow base, symmetric
6.5-7.0 mmol/LProlonged PR interval, widened QRS
7.0-8.0 mmol/LLoss of P waves, further QRS widening
>8.0 mmol/LSine wave pattern, VF, asystole
As shown in the infographic above: tented T waves + QRS widening + prolonged PR are the hallmarks.
Clinical significance: Life-threatening above 7 mmol/L. Give calcium gluconate for cardiac membrane stabilization.

5. Wolff-Parkinson-White (WPW) Syndrome

What to look for:
  • Short PR interval (<120 ms)
  • Delta wave - slurred upstroke at the beginning of the QRS
  • Broad QRS (>110 ms) due to fusion of delta wave and normal conduction
  • Secondary ST/T wave changes (discordant from delta wave)
As shown in the infographic above: delta wave + short PR interval are the classic features.
Clinical significance: Risk of rapid conduction during AF - can precipitate VF. Avoid AV nodal blocking agents (adenosine, verapamil) in AF with WPW.

6. Pulmonary Embolism (PE)

What to look for:
  • Sinus tachycardia (most common finding)
  • S1Q3T3 pattern: deep S in lead I, Q wave + T wave inversion in lead III
  • New right bundle branch block (RBBB) or incomplete RBBB
  • T wave inversions in V1-V4 (right heart strain)
  • Right axis deviation
  • P pulmonale (peaked P waves in lead II >2.5 mm)
Clinical significance: ECG is neither sensitive nor specific for PE, but the combination of sinus tachycardia + S1Q3T3 + anterior T inversions supports the diagnosis. - Tintinalli's Emergency Medicine

7. Complete Heart Block (3rd Degree AV Block)

What to look for:
  • P waves present but completely dissociated from QRS complexes
  • P rate > QRS rate (atria and ventricles beat independently)
  • Narrow QRS escape rhythm (junctional, rate 40-60 bpm) or wide QRS escape rhythm (ventricular, rate 20-40 bpm)
  • Regular P-P intervals, regular R-R intervals, but no fixed PR interval
Clinical significance: Often requires emergency transvenous pacing. Causes include inferior MI (usually transient), Lyme disease, drug toxicity.

8. Brugada Syndrome

What to look for:
  • Coved-type (Type 1 - diagnostic): J wave/ST elevation ≥2 mm with downsloping ST segment + T wave inversion in V1-V3
  • Saddle-back type (Type 2/3 - not diagnostic alone)
  • Appears spontaneously or unmasked by fever, sodium channel blockers, or vagotonic states
As shown in the infographic above: coved ST elevation >2 mm + negative T wave in V1-3 is the defining pattern.
Clinical significance: Risk of VF and sudden cardiac death. ICD is the main treatment for high-risk patients.

9. QT Prolongation / Torsades de Pointes

What to look for:
  • Corrected QT interval (QTc) >440 ms (men), >460 ms (women) using Bazett's formula
  • Giant T wave inversions can accompany QTc prolongation (seen in intracranial hemorrhage)
  • Torsades de Pointes: polymorphic VT with QRS complexes that "twist" around the isoelectric baseline
Common causes:
  • Drugs: antipsychotics (haloperidol, quetiapine), antibiotics (azithromycin, fluoroquinolones), antiarrhythmics (amiodarone, sotalol)
  • Electrolyte disturbances: hypokalemia, hypomagnesemia, hypocalcemia
  • Congenital Long QT syndrome
As shown in the infographic above: QT prolongation + giant T wave inversion point to intracranial pathology or drug toxicity.
Clinical significance: Torsades can degenerate to VF. Treat with IV magnesium sulfate 2g.

10. Left Ventricular Hypertrophy (LVH)

What to look for:
  • Sokolov-Lyon criteria: S in V1 + R in V5 or V6 >35 mm (most used)
  • Cornell criteria: R in aVL + S in V3 >28 mm (men), >20 mm (women)
  • ST depression and T wave inversion in lateral leads (I, aVL, V5-V6) - "strain pattern"
  • Left axis deviation
  • Broad, notched P wave in lead II (P mitrale) - suggests left atrial enlargement
Clinical significance: Marker of end-organ damage from hypertension. Associated with increased risk of arrhythmias, heart failure, and MI.

Summary Table

#ECG ChangeKey FeatureCause
1STEMIST elevation + reciprocal depressionCoronary occlusion
2Atrial FibrillationNo P waves, irregularly irregularMultiple atrial foci
3LBBBWiLLiaM pattern, QRS >120 msConduction disease, MI
4HyperkalemiaPeaked T waves → sine waveRenal failure, drugs
5WPWDelta wave, short PRAccessory pathway
6Pulmonary EmbolismS1Q3T3, sinus tachycardiaRV strain
73rd Degree AV BlockP-QRS dissociationIschemia, Lyme, drugs
8Brugada SyndromeCoved ST V1-V3Sodium channel mutation
9QT Prolongation/TdPQTc >440 ms, twisting VTDrugs, electrolytes
10LVHTall QRS voltages + strainHypertension, aortic stenosis

For interactive ECG strips with real patient tracings, the LITFL ECG Library is the gold-standard free resource, covering all 10 patterns above with multiple annotated examples per diagnosis.

Create a visual ECG reference card (PDF) with all 10 patterns

Reading File
Running Command

mkdir -p /home/daytona/workspace/ecg-reference-card && ls /home/daytona/workspace/

Writing File

~/ecg-reference-card/generate_ecg_card.py

"""
ECG Reference Card PDF - 10 Classic ECG Patterns
Uses ReportLab to create a professional A4 reference card with hand-drawn ECG waveforms.
"""

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm, cm
from reportlab.platypus import SimpleDocTemplate, Spacer
from reportlab.pdfgen import canvas
from reportlab.lib.colors import HexColor, white, black
import math

W, H = A4  # 595 x 842 pts

# ── Color palette ────────────────────────────────────────────────────────────
BG_DARK     = HexColor("#0D1B2A")   # deep navy background
BG_CARD     = HexColor("#112233")   # card background
GRID_COLOR  = HexColor("#1A3A5C")   # ECG grid lines
ECG_GREEN   = HexColor("#00FF88")   # ECG trace (bright green like monitors)
ECG_YELLOW  = HexColor("#FFD700")   # highlight / accent
ECG_RED     = HexColor("#FF4560")   # danger/alert labels
ECG_BLUE    = HexColor("#00B4D8")   # info labels
ECG_ORANGE  = HexColor("#FF8C00")   # annotation
TEXT_WHITE  = HexColor("#FFFFFF")
TEXT_LIGHT  = HexColor("#B0C4DE")
TEXT_DIM    = HexColor("#607B8B")
HEADER_BG   = HexColor("#001F3F")
ACCENT      = HexColor("#00FF88")

MARGIN      = 14*mm
COLS        = 2
ROWS        = 5
HEADER_H    = 28*mm
FOOTER_H    = 10*mm
GRID_W      = (W - 2*MARGIN - 4*mm) / COLS
GRID_H      = (H - HEADER_H - FOOTER_H - 2*MARGIN - (ROWS-1)*3*mm) / ROWS


def draw_ecg_grid(c, x, y, w, h):
    """Draw ECG grid background."""
    c.saveState()
    c.setFillColor(BG_CARD)
    c.rect(x, y, w, h, fill=1, stroke=0)
    # Fine grid (1mm equivalent in points = ~2.83 pts)
    step = 5
    c.setStrokeColor(GRID_COLOR)
    c.setLineWidth(0.3)
    xi = x
    while xi <= x + w:
        c.line(xi, y, xi, y + h)
        xi += step
    yi = y
    while yi <= y + h:
        c.line(x, yi, x + w, yi)
        yi += step
    # Bold grid every 5 steps
    c.setLineWidth(0.7)
    xi = x
    while xi <= x + w:
        c.line(xi, y, xi, y + h)
        xi += step * 5
    yi = y
    while yi <= y + h:
        c.line(x, yi, x + w, yi)
        yi += step * 5
    c.restoreState()


def draw_label_box(c, x, y, w, h, title, color, subtitle=""):
    """Draw card header with colored title."""
    c.saveState()
    c.setFillColor(color)
    c.rect(x, y + h - 14, w, 14, fill=1, stroke=0)
    c.setFillColor(BG_DARK)
    c.setFont("Helvetica-Bold", 8)
    c.drawCentredString(x + w/2, y + h - 10, title)
    if subtitle:
        c.setFillColor(TEXT_DIM)
        c.setFont("Helvetica", 6)
        c.drawCentredString(x + w/2, y + h - 20, subtitle)
    c.restoreState()


def draw_annotation(c, x, y, text, color=ECG_YELLOW, size=5.5):
    c.saveState()
    c.setFillColor(color)
    c.setFont("Helvetica-Bold", size)
    c.drawString(x, y, text)
    c.restoreState()


def draw_arrow_label(c, x1, y1, x2, y2, text, color=ECG_YELLOW):
    """Draw an arrow pointing to ECG feature with label."""
    c.saveState()
    c.setStrokeColor(color)
    c.setFillColor(color)
    c.setLineWidth(0.7)
    c.line(x1, y1, x2, y2)
    # arrowhead
    angle = math.atan2(y2 - y1, x2 - x1)
    al = 4
    c.line(x2, y2,
           x2 - al * math.cos(angle - 0.4),
           y2 - al * math.sin(angle - 0.4))
    c.line(x2, y2,
           x2 - al * math.cos(angle + 0.4),
           y2 - al * math.sin(angle + 0.4))
    c.setFont("Helvetica-Bold", 5)
    c.drawString(x1 - 2, y1 + 2, text)
    c.restoreState()


def trace(c, points, color=ECG_GREEN, lw=1.2):
    """Draw ECG trace from list of (x,y) points."""
    c.saveState()
    c.setStrokeColor(color)
    c.setLineWidth(lw)
    p = c.beginPath()
    p.moveTo(points[0][0], points[0][1])
    for pt in points[1:]:
        p.lineTo(pt[0], pt[1])
    c.drawPath(p, stroke=1, fill=0)
    c.restoreState()


def ecg_baseline(x, y, w, count=3):
    """Return baseline points for 'count' repeating patterns across width w."""
    return [(x, y), (x + w, y)]


# ─────────────────────────────────────────────────────────────────────────────
# ECG PATTERN DRAWING FUNCTIONS
# Each takes (c, x, y, w, h) where x,y = bottom-left of card area
# ─────────────────────────────────────────────────────────────────────────────

def draw_normal_ecg(c, x, y, w, h):
    """Normal sinus rhythm for reference."""
    mid = y + h * 0.45
    seg = w / 3
    def beat(ox):
        return [
            (ox, mid),
            (ox + seg*0.10, mid),
            (ox + seg*0.13, mid + 3),    # P wave up
            (ox + seg*0.17, mid - 3),    # P wave down
            (ox + seg*0.20, mid),
            (ox + seg*0.24, mid),
            (ox + seg*0.26, mid - 5),    # Q
            (ox + seg*0.30, mid + 22),   # R peak
            (ox + seg*0.34, mid - 8),    # S
            (ox + seg*0.40, mid),
            (ox + seg*0.50, mid + 3),    # T wave
            (ox + seg*0.60, mid),
            (ox + seg*1.0, mid),
        ]
    pts = beat(x + 2)
    pts2 = beat(x + 2 + seg)
    pts3 = beat(x + 2 + 2*seg)
    trace(c, pts)
    trace(c, pts2)
    trace(c, pts3)


def draw_stemi(c, x, y, w, h):
    mid = y + h * 0.42
    seg = w / 2.8
    def beat(ox):
        return [
            (ox, mid),
            (ox + seg*0.10, mid),
            (ox + seg*0.13, mid + 4),    # P wave
            (ox + seg*0.18, mid),
            (ox + seg*0.22, mid),
            (ox + seg*0.25, mid - 4),    # Q
            (ox + seg*0.30, mid + 28),   # Tall R
            (ox + seg*0.35, mid + 10),   # ST elevation start
            (ox + seg*0.55, mid + 10),   # Flat ST elevation
            (ox + seg*0.70, mid + 6),    # T wave peak
            (ox + seg*0.80, mid),
            (ox + seg*1.00, mid),
        ]
    for i in range(2):
        pts = beat(x + 4 + i * seg)
        trace(c, pts, ECG_RED, 1.4)
    # Annotations
    draw_annotation(c, x + seg*0.55 + 4, mid + 12, "ST↑", ECG_RED, 6)
    draw_annotation(c, x + 8, mid + 31, "Hyperacute T", ECG_YELLOW, 5)
    # Reciprocal depression hint
    mid2 = y + h * 0.25
    rec = [
        (x + 4, mid2), (x + seg*0.5 + 4, mid2),
        (x + seg*0.55 + 4, mid2 - 7),
        (x + seg*0.7 + 4, mid2 - 7),
        (x + seg*0.85 + 4, mid2),
        (x + seg + 4, mid2),
    ]
    trace(c, rec, ECG_ORANGE, 0.9)
    draw_annotation(c, x + seg*0.55 + 4, mid2 - 12, "Reciprocal↓", ECG_ORANGE, 4.5)


def draw_af(c, x, y, w, h):
    mid = y + h * 0.45
    import random
    random.seed(42)
    # Irregular baseline (fibrillatory)
    pts = [(x + 3, mid)]
    bx = x + 3
    while bx < x + w - 10:
        bx += random.uniform(1.5, 3.5)
        dy = random.uniform(-3, 3)
        pts.append((bx, mid + dy))
    trace(c, pts, HexColor("#607B8B"), 0.6)
    # Irregularly irregular QRS - no P waves
    beats = [x + 18, x + 42, x + 58, x + 82, x + 102, x + 120, x + 148]
    for bx in beats:
        qrs = [
            (bx, mid),
            (bx + 2, mid - 4),   # Q
            (bx + 4, mid + 22),  # R
            (bx + 6, mid - 6),   # S
            (bx + 10, mid),
            (bx + 16, mid + 2),  # T wave small
            (bx + 22, mid),
        ]
        trace(c, qrs, ECG_GREEN, 1.3)
    draw_annotation(c, x + 5, y + h - 26, "No P waves", ECG_RED, 5.5)
    draw_annotation(c, x + 5, y + h - 34, "Irregular R-R", ECG_YELLOW, 5.5)


def draw_lbbb(c, x, y, w, h):
    mid = y + h * 0.45
    seg = w / 2.5
    def beat(ox):
        return [
            (ox, mid),
            (ox + seg*0.10, mid),
            (ox + seg*0.13, mid + 3),    # P wave
            (ox + seg*0.18, mid),
            (ox + seg*0.22, mid),
            # Broad notched M-shape R in V6 (no Q wave)
            (ox + seg*0.25, mid + 8),    # first R hump
            (ox + seg*0.32, mid + 4),    # notch
            (ox + seg*0.40, mid + 20),   # second R hump (broad)
            (ox + seg*0.48, mid),
            (ox + seg*0.60, mid - 6),    # ST depression (discordant)
            (ox + seg*0.75, mid - 8),    # T wave inverted (discordant)
            (ox + seg*0.85, mid),
            (ox + seg*1.00, mid),
        ]
    for i in range(2):
        pts = beat(x + 4 + i * seg)
        trace(c, pts, ECG_GREEN, 1.3)
    draw_annotation(c, x + 8, y + h - 26, "Broad notched R (M-shape)", ECG_YELLOW, 5)
    draw_annotation(c, x + 8, y + h - 34, "QRS >120ms | No septal Q", ECG_ORANGE, 5)
    # QRS width arrow
    c.saveState()
    c.setStrokeColor(ECG_BLUE)
    c.setLineWidth(0.7)
    bx = x + 4 + seg*0.22
    ex = x + 4 + seg*0.48
    by = mid - 12
    c.line(bx, by, ex, by)
    c.line(bx, by - 2, bx, by + 2)
    c.line(ex, by - 2, ex, by + 2)
    c.setFillColor(ECG_BLUE)
    c.setFont("Helvetica-Bold", 5)
    c.drawCentredString((bx + ex)/2, by - 8, ">120ms")
    c.restoreState()


def draw_hyperkalemia(c, x, y, w, h):
    mid = y + h * 0.45
    seg = w / 2.8
    def beat(ox, k_level="high"):
        if k_level == "moderate":
            # Peaked T, widened QRS, flat P
            return [
                (ox, mid),
                (ox + seg*0.10, mid),
                (ox + seg*0.13, mid + 1),  # Flat P wave
                (ox + seg*0.18, mid),
                (ox + seg*0.22, mid),
                (ox + seg*0.25, mid - 4),  # Q
                (ox + seg*0.30, mid + 18), # R
                (ox + seg*0.36, mid - 5),  # S
                (ox + seg*0.42, mid),
                # Tall narrow peaked T
                (ox + seg*0.50, mid + 2),
                (ox + seg*0.56, mid + 20), # PEAKED T
                (ox + seg*0.62, mid + 2),
                (ox + seg*0.70, mid),
                (ox + seg*1.00, mid),
            ]
        else:
            # Severe: wide QRS, sine wave
            return [
                (ox, mid),
                (ox + seg*0.05, mid + 6),
                (ox + seg*0.20, mid + 22),  # broad R
                (ox + seg*0.35, mid),
                (ox + seg*0.45, mid - 10),  # broad S
                (ox + seg*0.60, mid + 4),
                (ox + seg*0.80, mid + 18),  # sine T
                (ox + seg*1.00, mid + 2),
            ]
    # Two moderate beats
    for i in range(2):
        pts = beat(x + 4 + i * seg, "moderate")
        trace(c, pts, ECG_GREEN, 1.2)
    # One severe beat
    pts2 = beat(x + 4 + 2*seg, "high")
    trace(c, pts2, ECG_RED, 1.3)

    draw_annotation(c, x + 8, y + h - 26, "Peaked T waves → Sine wave", ECG_YELLOW, 5)
    draw_annotation(c, x + 8, y + h - 34, "Wide QRS | Flat/absent P", ECG_RED, 5)
    # Label peaked T
    tx = x + 4 + seg*0.56
    ty = mid + 22
    c.saveState()
    c.setStrokeColor(ECG_ORANGE)
    c.setFillColor(ECG_ORANGE)
    c.setLineWidth(0.6)
    c.line(tx, ty + 2, tx, ty + 8)
    c.setFont("Helvetica-Bold", 5)
    c.drawCentredString(tx, ty + 10, "Peaked T")
    c.restoreState()


def draw_wpw(c, x, y, w, h):
    mid = y + h * 0.45
    seg = w / 2.8
    def beat(ox):
        return [
            (ox, mid),
            (ox + seg*0.08, mid),
            (ox + seg*0.10, mid + 3),    # P wave
            (ox + seg*0.15, mid),
            # Short PR then delta wave
            (ox + seg*0.17, mid),        # short PR
            (ox + seg*0.20, mid + 3),    # delta wave slur start
            (ox + seg*0.28, mid + 22),   # R peak (broad QRS)
            (ox + seg*0.35, mid - 6),    # S
            (ox + seg*0.42, mid),
            (ox + seg*0.52, mid + 4),    # T wave
            (ox + seg*0.62, mid),
            (ox + seg*1.00, mid),
        ]
    for i in range(2):
        pts = beat(x + 4 + i * seg)
        trace(c, pts, ECG_GREEN, 1.3)
    draw_annotation(c, x + 8, y + h - 26, "Short PR (<120ms)", ECG_YELLOW, 5)
    draw_annotation(c, x + 8, y + h - 34, "Delta wave | Broad QRS", ECG_ORANGE, 5)
    # Delta wave annotation
    dx = x + 4 + seg*0.20
    dy = mid + 3
    c.saveState()
    c.setStrokeColor(ECG_RED)
    c.setFillColor(ECG_RED)
    c.setLineWidth(0.7)
    c.line(dx, dy + 2, dx - 4, dy + 12)
    c.setFont("Helvetica-Bold", 5)
    c.drawString(dx - 16, dy + 14, "δ wave")
    # PR bracket
    c.setStrokeColor(ECG_BLUE)
    p_start = x + 4 + seg*0.10
    p_end = x + 4 + seg*0.17
    c.line(p_start, mid - 8, p_end, mid - 8)
    c.line(p_start, mid - 6, p_start, mid - 10)
    c.line(p_end, mid - 6, p_end, mid - 10)
    c.setFillColor(ECG_BLUE)
    c.setFont("Helvetica-Bold", 5)
    c.drawCentredString((p_start + p_end)/2, mid - 16, "Short PR")
    c.restoreState()


def draw_pe(c, x, y, w, h):
    mid = y + h * 0.50
    seg = w / 2.8
    # Lead I: S wave
    def beat_I(ox):
        return [
            (ox, mid), (ox + seg*0.08, mid),
            (ox + seg*0.11, mid + 3),    # P
            (ox + seg*0.16, mid),
            (ox + seg*0.20, mid),
            (ox + seg*0.23, mid - 2),
            (ox + seg*0.26, mid + 16),   # R
            (ox + seg*0.30, mid - 10),   # Deep S in I
            (ox + seg*0.40, mid),
            (ox + seg*0.50, mid + 3),
            (ox + seg*0.60, mid),
            (ox + seg*1.00, mid),
        ]
    # Lead III: Q wave + T inversion
    def beat_III(ox):
        return [
            (ox, mid), (ox + seg*0.08, mid),
            (ox + seg*0.11, mid + 2),    # P
            (ox + seg*0.16, mid),
            (ox + seg*0.20, mid - 6),    # Q wave in III
            (ox + seg*0.24, mid + 14),   # R
            (ox + seg*0.29, mid - 2),
            (ox + seg*0.35, mid),
            (ox + seg*0.45, mid - 5),    # T inversion in III
            (ox + seg*0.60, mid),
            (ox + seg*1.00, mid),
        ]
    # Two strips: top = I, bottom = III
    mid_I = y + h * 0.65
    mid_III = y + h * 0.30
    for i in range(2):
        trace(c, beat_I(x + 4 + i*seg), ECG_GREEN, 1.1)
    for i in range(2):
        pts3 = beat_III(x + 4 + i*seg)
        pts3_shifted = [(px, py - (mid - mid_III)) for px, py in pts3]
        trace(c, pts3_shifted, HexColor("#88DDFF"), 1.1)
    draw_annotation(c, x + 5, mid_I + 8, "Lead I: Deep S", ECG_GREEN, 5)
    draw_annotation(c, x + 5, mid_III + 8, "Lead III: Q+T inv", ECG_BLUE, 5)
    draw_annotation(c, x + 5, y + h - 26, "S1Q3T3 Pattern", ECG_YELLOW, 5.5)
    draw_annotation(c, x + 5, y + h - 34, "Sinus tachycardia | RV strain", ECG_ORANGE, 5)


def draw_chb(c, x, y, w, h):
    """Complete (3rd degree) heart block."""
    mid = y + h * 0.5
    p_mid = y + h * 0.72    # P wave level
    qrs_mid = y + h * 0.40  # QRS level
    seg_p = (w - 8) / 5     # P waves more frequent
    seg_qrs = (w - 8) / 3   # QRS slower
    # P waves (atrial rate ~75)
    for i in range(5):
        ox = x + 4 + i * seg_p
        pw = [
            (ox, p_mid), (ox + 3, p_mid + 4), (ox + 6, p_mid), (ox + seg_p, p_mid)
        ]
        trace(c, pw, ECG_ORANGE, 1.0)
    # QRS complexes (ventricular escape rate ~35)
    for i in range(3):
        ox = x + 4 + i * seg_qrs
        qrs = [
            (ox, qrs_mid),
            (ox + 4, qrs_mid),
            (ox + 6, qrs_mid - 5),
            (ox + 9, qrs_mid + 18),
            (ox + 13, qrs_mid - 6),
            (ox + 18, qrs_mid),
            (ox + 26, qrs_mid + 5),   # T wave
            (ox + 36, qrs_mid),
            (ox + seg_qrs, qrs_mid),
        ]
        trace(c, qrs, ECG_GREEN, 1.3)
    draw_annotation(c, x + 5, y + h - 22, "P waves (orange): regular ~75bpm", ECG_ORANGE, 5)
    draw_annotation(c, x + 5, y + h - 30, "QRS (green): regular ~35bpm", ECG_GREEN, 5)
    draw_annotation(c, x + 5, y + h - 38, "No fixed PR - Complete dissociation", ECG_RED, 5)


def draw_brugada(c, x, y, w, h):
    mid = y + h * 0.45
    seg = w / 2.8
    def beat(ox):
        return [
            (ox, mid),
            (ox + seg*0.08, mid),
            (ox + seg*0.11, mid + 3),    # P
            (ox + seg*0.16, mid),
            (ox + seg*0.20, mid),
            (ox + seg*0.22, mid - 3),    # small Q
            (ox + seg*0.26, mid + 20),   # R
            # Coved ST elevation: downsloping ST
            (ox + seg*0.30, mid + 16),
            (ox + seg*0.40, mid + 8),
            (ox + seg*0.50, mid + 2),
            (ox + seg*0.55, mid - 6),    # T wave INVERTED (negative)
            (ox + seg*0.65, mid),
            (ox + seg*1.00, mid),
        ]
    for i in range(2):
        pts = beat(x + 4 + i * seg)
        trace(c, pts, ECG_GREEN, 1.3)
    draw_annotation(c, x + 8, y + h - 26, "Coved ST↑ ≥2mm in V1-V3", ECG_RED, 5)
    draw_annotation(c, x + 8, y + h - 34, "Downsloping ST + T inversion", ECG_ORANGE, 5)
    # Coved shape annotation
    cx1 = x + 4 + seg*0.26
    cx2 = x + 4 + seg*0.55
    cy = mid + 20
    c.saveState()
    c.setStrokeColor(ECG_YELLOW)
    c.setLineWidth(0.5)
    c.setDash([2, 2])
    c.line(cx1, cy, cx2, mid - 6)
    c.setDash([])
    c.setFillColor(ECG_YELLOW)
    c.setFont("Helvetica-Bold", 4.5)
    c.drawString(cx1 + 4, cy + 2, "Coved")
    c.restoreStyle()
    c.restoreState()


def draw_qt_prolongation(c, x, y, w, h):
    mid = y + h * 0.45
    seg = w / 2.2
    def beat(ox, long_qt=False):
        qt_end = seg * (0.80 if long_qt else 0.58)
        return [
            (ox, mid),
            (ox + seg*0.08, mid),
            (ox + seg*0.11, mid + 3),
            (ox + seg*0.16, mid),
            (ox + seg*0.20, mid),
            (ox + seg*0.22, mid - 4),
            (ox + seg*0.26, mid + 20),
            (ox + seg*0.31, mid - 6),
            (ox + seg*0.37, mid),
            # long QT: T wave comes much later
            (ox + seg*0.50, mid + 1),
            (ox + qt_end - seg*0.08, mid + 2),
            (ox + qt_end - seg*0.04, mid + 14),  # T peak
            (ox + qt_end, mid),
            (ox + seg, mid),
        ]
    # Normal beat
    pts1 = beat(x + 4, False)
    trace(c, pts1, ECG_BLUE, 1.0)
    # Prolonged QT beat
    pts2 = beat(x + 4 + seg, True)
    trace(c, pts2, ECG_RED, 1.3)
    # QT bracket for long beat
    qt_x1 = x + 4 + seg + seg*0.22
    qt_x2 = x + 4 + seg + seg*0.80
    bry = mid - 14
    c.saveState()
    c.setStrokeColor(ECG_RED)
    c.setLineWidth(0.8)
    c.line(qt_x1, bry, qt_x2, bry)
    c.line(qt_x1, bry-2, qt_x1, bry+2)
    c.line(qt_x2, bry-2, qt_x2, bry+2)
    c.setFillColor(ECG_RED)
    c.setFont("Helvetica-Bold", 5.5)
    c.drawCentredString((qt_x1+qt_x2)/2, bry - 10, "Prolonged QTc >440ms")
    c.restoreState()
    draw_annotation(c, x + 5, y + h - 26, "Blue = normal QT", ECG_BLUE, 5)
    draw_annotation(c, x + 5, y + h - 34, "Red = prolonged QT (TdP risk)", ECG_RED, 5)


def draw_lvh(c, x, y, w, h):
    mid = y + h * 0.45
    seg = w / 2.8
    def beat(ox):
        return [
            (ox, mid),
            (ox + seg*0.08, mid),
            (ox + seg*0.11, mid + 3),
            (ox + seg*0.16, mid + 3),    # broad P (P mitrale)
            (ox + seg*0.20, mid),
            (ox + seg*0.23, mid),
            (ox + seg*0.25, mid - 4),    # Q (dagger)
            (ox + seg*0.29, mid + 32),   # Tall R (voltage!)
            (ox + seg*0.33, mid - 8),    # S
            (ox + seg*0.38, mid),
            (ox + seg*0.44, mid - 3),    # ST depression (strain)
            (ox + seg*0.52, mid - 10),   # T inversion (strain pattern)
            (ox + seg*0.65, mid),
            (ox + seg*1.00, mid),
        ]
    for i in range(2):
        pts = beat(x + 4 + i * seg)
        trace(c, pts, ECG_GREEN, 1.3)
    # Voltage measurement arrow
    ox = x + 4 + seg*0.29
    c.saveState()
    c.setStrokeColor(ECG_YELLOW)
    c.setLineWidth(0.7)
    c.line(ox, mid, ox + 5, mid + 32)
    c.setFillColor(ECG_YELLOW)
    c.setFont("Helvetica-Bold", 5)
    c.drawString(ox + 6, mid + 28, "Tall R")
    c.restoreState()
    draw_annotation(c, x + 8, y + h - 26, "Tall R (SV1+RV5 >35mm)", ECG_YELLOW, 5)
    draw_annotation(c, x + 8, y + h - 34, "ST depression + T inversion (strain)", ECG_ORANGE, 5)


# ─────────────────────────────────────────────────────────────────────────────
# PATTERNS REGISTRY
# ─────────────────────────────────────────────────────────────────────────────
PATTERNS = [
    {
        "title": "1. STEMI",
        "subtitle": "ST-Elevation MI",
        "color": ECG_RED,
        "draw_fn": draw_stemi,
        "keys": ["ST elevation ≥1-2mm in ≥2 leads", "Reciprocal ST depression", "Hyperacute T waves", "Q waves = necrosis"],
        "danger": "EMERGENCY - PCI <90min",
    },
    {
        "title": "2. Atrial Fibrillation",
        "subtitle": "AF",
        "color": ECG_BLUE,
        "draw_fn": draw_af,
        "keys": ["No P waves (f-waves)", "Irregularly irregular R-R", "Rate 100-160 bpm (uncontrolled)", "Narrow QRS (usually)"],
        "danger": "Stroke risk - CHA₂DS₂-VASc",
    },
    {
        "title": "3. LBBB",
        "subtitle": "Left Bundle Branch Block",
        "color": HexColor("#8B5CF6"),
        "draw_fn": draw_lbbb,
        "keys": ["QRS ≥120ms", "M-shaped R in I, aVL, V5-V6", "No Q in V5-V6", "Discordant ST/T"],
        "danger": "New LBBB + chest pain = urgent!",
    },
    {
        "title": "4. Hyperkalemia",
        "subtitle": "High K⁺",
        "color": ECG_ORANGE,
        "draw_fn": draw_hyperkalemia,
        "keys": ["Peaked T waves (early)", "PR prolongation", "Wide QRS / absent P", "Sine wave → VF (late)"],
        "danger": "Give Ca²⁺ if wide QRS / sine wave",
    },
    {
        "title": "5. WPW Syndrome",
        "subtitle": "Wolff-Parkinson-White",
        "color": HexColor("#10B981"),
        "draw_fn": draw_wpw,
        "keys": ["Short PR <120ms", "Delta wave (slurred upstroke)", "Broad QRS >110ms", "Secondary ST/T changes"],
        "danger": "Avoid verapamil/adenosine in AF+WPW",
    },
    {
        "title": "6. Pulmonary Embolism",
        "subtitle": "PE - Right Heart Strain",
        "color": HexColor("#06B6D4"),
        "draw_fn": draw_pe,
        "keys": ["Sinus tachycardia (most common)", "S1Q3T3 pattern", "RBBB or incomplete RBBB", "T inversion V1-V4"],
        "danger": "ECG not specific - use Wells + CTPA",
    },
    {
        "title": "7. Complete Heart Block",
        "subtitle": "3rd Degree AV Block",
        "color": HexColor("#F59E0B"),
        "draw_fn": draw_chb,
        "keys": ["P waves dissociated from QRS", "P rate > QRS rate", "Escape rhythm (40-60 bpm)", "Regular P-P, regular R-R"],
        "danger": "May need emergency pacing",
    },
    {
        "title": "8. Brugada Syndrome",
        "subtitle": "Na⁺ Channel Channelopathy",
        "color": HexColor("#EF4444"),
        "draw_fn": draw_brugada,
        "keys": ["Coved ST ≥2mm in V1-V3", "Downsloping ST segment", "T wave inversion V1-V3", "Unmasked by fever/Na⁺ blockers"],
        "danger": "VF/SCD risk - consider ICD",
    },
    {
        "title": "9. Prolonged QT / TdP",
        "subtitle": "Torsades de Pointes Risk",
        "color": HexColor("#F97316"),
        "draw_fn": draw_qt_prolongation,
        "keys": ["QTc >440ms (M), >460ms (F)", "Risk: drugs, hypoK, hypoMg", "TdP: twisting VT morphology", "Bazett formula: QTc=QT/√RR"],
        "danger": "TdP → IV Mg 2g stat",
    },
    {
        "title": "10. LVH",
        "subtitle": "Left Ventricular Hypertrophy",
        "color": HexColor("#8B5CF6"),
        "draw_fn": draw_lvh,
        "keys": ["SV1 + RV5/V6 >35mm (Sokolov)", "Strain: ST↓ + T inversion lateral", "Left axis deviation", "Broad P wave (P mitrale)"],
        "danger": "HTN end-organ damage marker",
    },
]


# ─────────────────────────────────────────────────────────────────────────────
# MAIN DRAWING FUNCTION
# ─────────────────────────────────────────────────────────────────────────────

def create_ecg_reference_card(output_path):
    c = canvas.Canvas(output_path, pagesize=A4)
    c.setTitle("ECG Reference Card - 10 Classic Patterns")
    c.setAuthor("Orris Medical Education")
    c.setSubject("ECG Patterns Quick Reference")

    # ── Background ──────────────────────────────────────────────────────────
    c.setFillColor(BG_DARK)
    c.rect(0, 0, W, H, fill=1, stroke=0)

    # ── Header ──────────────────────────────────────────────────────────────
    c.setFillColor(HEADER_BG)
    c.rect(0, H - HEADER_H, W, HEADER_H, fill=1, stroke=0)

    # ECG trace decoration in header
    c.saveState()
    c.setStrokeColor(HexColor("#003366"))
    c.setLineWidth(0.5)
    for i in range(0, int(W), 6):
        c.line(i, H - HEADER_H, i, H)
    c.restoreState()

    # Decorative ECG line in header
    header_pts = []
    hx = 0
    hmid = H - HEADER_H/2
    beat_w = 80
    while hx < W:
        header_pts += [
            (hx, hmid),
            (hx + beat_w*0.20, hmid),
            (hx + beat_w*0.23, hmid + 4),
            (hx + beat_w*0.27, hmid),
            (hx + beat_w*0.30, hmid),
            (hx + beat_w*0.32, hmid - 4),
            (hx + beat_w*0.35, hmid + 12),
            (hx + beat_w*0.38, hmid - 5),
            (hx + beat_w*0.43, hmid),
            (hx + beat_w*0.53, hmid + 3),
            (hx + beat_w*0.63, hmid),
            (hx + beat_w, hmid),
        ]
        hx += beat_w
    trace(c, header_pts, HexColor("#003d00"), 0.6)

    # Green active ECG strip
    trace(c, header_pts[:80], ACCENT, 0.9)

    # Header text
    c.setFillColor(ACCENT)
    c.setFont("Helvetica-Bold", 18)
    c.drawCentredString(W/2, H - 17, "ECG REFERENCE CARD")
    c.setFillColor(TEXT_LIGHT)
    c.setFont("Helvetica", 9)
    c.drawCentredString(W/2, H - 26, "10 Classic ECG Patterns  •  Emergency & Clinical Reference  •  Educational Use Only")

    # ── Layout cards ────────────────────────────────────────────────────────
    card_w = GRID_W
    card_h = GRID_H

    for idx, pattern in enumerate(PATTERNS):
        col = idx % COLS
        row = idx // COLS

        cx = MARGIN + col * (card_w + 4*mm)
        cy = H - HEADER_H - MARGIN - (row + 1) * card_h - row * 3*mm

        # Card outer border
        c.saveState()
        c.setStrokeColor(pattern["color"])
        c.setLineWidth(1.2)
        c.setFillColor(BG_CARD)
        c.roundRect(cx, cy, card_w, card_h, 3, fill=1, stroke=1)
        c.restoreState()

        # Color bar at top of card
        c.saveState()
        c.setFillColor(pattern["color"])
        c.roundRect(cx, cy + card_h - 13, card_w, 13, 3, fill=1, stroke=0)
        # Title
        c.setFillColor(BG_DARK)
        c.setFont("Helvetica-Bold", 7.5)
        c.drawString(cx + 4, cy + card_h - 9.5, pattern["title"])
        c.setFont("Helvetica", 6)
        c.drawRightString(cx + card_w - 4, cy + card_h - 9.5, pattern["subtitle"])
        c.restoreState()

        # ECG grid area (upper half of card body)
        ecg_area_h = card_h * 0.50
        ecg_y = cy + card_h - 13 - ecg_area_h - 2
        draw_ecg_grid(c, cx + 2, ecg_y, card_w - 4, ecg_area_h)

        # Draw ECG pattern
        pattern["draw_fn"](c, cx + 2, ecg_y, card_w - 4, ecg_area_h)

        # Divider
        c.saveState()
        c.setStrokeColor(HexColor("#1E3A5F"))
        c.setLineWidth(0.5)
        c.line(cx + 4, ecg_y - 2, cx + card_w - 4, ecg_y - 2)
        c.restoreState()

        # Key features (lower half)
        feat_y = ecg_y - 6
        c.setFont("Helvetica-Bold", 5.2)
        c.setFillColor(TEXT_LIGHT)
        c.drawString(cx + 4, feat_y, "KEY FEATURES:")
        feat_y -= 7

        for kf in pattern["keys"]:
            c.saveState()
            # Bullet dot
            c.setFillColor(pattern["color"])
            c.circle(cx + 6, feat_y + 1.5, 1.5, fill=1, stroke=0)
            c.setFillColor(TEXT_LIGHT)
            c.setFont("Helvetica", 5.0)
            c.drawString(cx + 10, feat_y, kf)
            c.restoreState()
            feat_y -= 6.5

        # Danger/clinical pearl box
        danger_y = cy + 3
        c.saveState()
        c.setFillColor(HexColor("#1A1A2E"))
        c.setStrokeColor(pattern["color"])
        c.setLineWidth(0.6)
        c.roundRect(cx + 2, danger_y, card_w - 4, 11, 2, fill=1, stroke=1)
        c.setFillColor(pattern["color"])
        c.setFont("Helvetica-Bold", 5.0)
        c.drawCentredString(cx + card_w/2, danger_y + 3.5, "⚠ " + pattern["danger"])
        c.restoreState()

    # ── Footer ───────────────────────────────────────────────────────────────
    fy = 4
    c.setFillColor(TEXT_DIM)
    c.setFont("Helvetica", 5.5)
    c.drawString(MARGIN, fy, "ECG Reference Card  •  For educational use only  •  Always correlate with clinical context")
    c.drawRightString(W - MARGIN, fy, "Sources: Tintinalli's Emergency Medicine, LITFL ECG Library  •  © 2026 Orris")

    # ── Page border ──────────────────────────────────────────────────────────
    c.saveState()
    c.setStrokeColor(ACCENT)
    c.setLineWidth(1.5)
    c.rect(4, 4, W - 8, H - 8, fill=0, stroke=1)
    c.restoreState()

    c.save()
    print(f"PDF saved to: {output_path}")


if __name__ == "__main__":
    output = "/home/daytona/workspace/ecg-reference-card/ECG_Reference_Card.pdf"
    create_ecg_reference_card(output)
Running Command

cd /home/daytona/workspace/ecg-reference-card && python generate_ecg_card.py 2>&1

Editing File

~/ecg-reference-card/generate_ecg_card.py

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/ecg-reference-card/generate_ecg_card.py'
Running Command

cd /home/daytona/workspace/ecg-reference-card && python generate_ecg_card.py 2>&1

Your credits are exhausted. Your daily credits reset tomorrow, or you can purchase a top-up for more.
This is a shared conversation. Sign in to Orris to start your own chat.