Breast stroke technique
breaststroke swimming technique tips 2025
https://www.usms.org/fitness-and-training/guides/breaststrok…
| Phase | Arms | Legs |
|---|---|---|
| Glide | Extended forward | Together, extended back |
| Pull | In-sweep inward | Straight, passive |
| Breathe | Shooting forward | Beginning to draw up |
| Kick | Fully extended forward | Full kick drive |
| Glide | Streamline | Squeezed together |
| Mistake | Fix |
|---|---|
| Head lifting too high to breathe | Let the body rise carry your chin up naturally |
| Wide knee flare on kick | Keep knees hip-width, not wider |
| Kicking while pulling | Separate the actions - pull, then kick |
| Short/rushed glide | Hold streamline for a beat after kick finishes |
| Arms pulling past the hips | Keep the pull compact - hands stop under the chest |
| Feet not flexing outward | Work on ankle flexibility for the catch position |
Generate a breaststroke training drill program for beginners
ls -la /tmp/workspace/ 2>/dev/null && mkdir -p /tmp/workspace/breaststroke-program
~/breaststroke-program/generate_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import mm, cm
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, PageBreak, KeepTogether
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
from reportlab.platypus import BaseDocTemplate, Frame, PageTemplate
OUTPUT_PATH = "/tmp/workspace/breaststroke-program/Breaststroke_Beginner_Program.pdf"
# ── Colour palette ──────────────────────────────────────────────────────────
NAVY = colors.HexColor("#0D2B55")
TEAL = colors.HexColor("#0093B2")
LTBLUE = colors.HexColor("#D6EEF5")
ACCENT = colors.HexColor("#F0A500")
WHITE = colors.white
LGRAY = colors.HexColor("#F4F4F4")
MGRAY = colors.HexColor("#CCCCCC")
DKGRAY = colors.HexColor("#333333")
# ── Styles ───────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
def s(name, **kw):
return ParagraphStyle(name, **kw)
COVER_TITLE = s("CoverTitle", fontSize=34, leading=42, textColor=WHITE,
fontName="Helvetica-Bold", alignment=TA_CENTER)
COVER_SUB = s("CoverSub", fontSize=16, leading=22, textColor=LTBLUE,
fontName="Helvetica", alignment=TA_CENTER)
COVER_NOTE = s("CoverNote", fontSize=10, leading=14, textColor=MGRAY,
fontName="Helvetica", alignment=TA_CENTER)
H1 = s("H1", fontSize=16, leading=20, textColor=WHITE,
fontName="Helvetica-Bold", alignment=TA_LEFT,
backColor=NAVY, spaceAfter=4,
leftIndent=-12, rightIndent=-12,
borderPadding=(6, 12, 6, 12))
H2 = s("H2", fontSize=12, leading=16, textColor=NAVY,
fontName="Helvetica-Bold", spaceAfter=4, spaceBefore=8)
BODY = s("Body", fontSize=9, leading=13, textColor=DKGRAY,
fontName="Helvetica", spaceAfter=3)
BULLET = s("Bullet", fontSize=9, leading=13, textColor=DKGRAY,
fontName="Helvetica", leftIndent=14, bulletIndent=4,
spaceAfter=2)
SMALL = s("Small", fontSize=8, leading=11, textColor=colors.HexColor("#666666"),
fontName="Helvetica-Oblique")
NOTE_STYLE = s("Note", fontSize=8.5, leading=12, textColor=NAVY,
fontName="Helvetica-Oblique",
backColor=LTBLUE, leftIndent=8, rightIndent=8,
borderPadding=(5,8,5,8))
TH = s("TH", fontSize=8.5, leading=11, textColor=WHITE,
fontName="Helvetica-Bold", alignment=TA_CENTER)
TC = s("TC", fontSize=8.5, leading=11, textColor=DKGRAY,
fontName="Helvetica", alignment=TA_LEFT)
TC_BOLD = s("TCBold", fontSize=8.5, leading=11, textColor=NAVY,
fontName="Helvetica-Bold", alignment=TA_LEFT)
# ── Helper: header/footer ────────────────────────────────────────────────────
def add_header_footer(canvas, doc):
canvas.saveState()
w, h = A4
# Header bar
canvas.setFillColor(NAVY)
canvas.rect(0, h - 20*mm, w, 20*mm, fill=1, stroke=0)
canvas.setFillColor(WHITE)
canvas.setFont("Helvetica-Bold", 9)
canvas.drawString(15*mm, h - 13*mm, "BREASTSTROKE BEGINNER TRAINING PROGRAM")
canvas.setFont("Helvetica", 8)
canvas.drawRightString(w - 15*mm, h - 13*mm, "25m Pool | 4-5 Sessions/Week")
# Footer
canvas.setFillColor(MGRAY)
canvas.rect(0, 0, w, 10*mm, fill=1, stroke=0)
canvas.setFillColor(DKGRAY)
canvas.setFont("Helvetica", 7.5)
canvas.drawString(15*mm, 3.5*mm, "For personal use only. Consult a qualified swimming coach before starting.")
canvas.drawRightString(w - 15*mm, 3.5*mm, f"Page {doc.page}")
canvas.restoreState()
# ── Document setup ────────────────────────────────────────────────────────────
doc = BaseDocTemplate(
OUTPUT_PATH, pagesize=A4,
leftMargin=15*mm, rightMargin=15*mm,
topMargin=25*mm, bottomMargin=16*mm,
title="Breaststroke Beginner Training Program",
author="Orris AI",
)
frame = Frame(doc.leftMargin, doc.bottomMargin,
doc.width, doc.height, id="main")
doc.addPageTemplates([
PageTemplate(id="cover", frames=[Frame(0, 0, A4[0], A4[1], id="cf")]),
PageTemplate(id="normal", frames=[frame], onPage=add_header_footer),
])
story = []
# ════════════════════════════════════════════════════════════
# COVER PAGE
# ════════════════════════════════════════════════════════════
from reportlab.platypus import NextPageTemplate
# Full-page navy background
story.append(NextPageTemplate("cover"))
def cover_bg(canvas, doc):
w, h = A4
canvas.setFillColor(NAVY)
canvas.rect(0, 0, w, h, fill=1, stroke=0)
# Teal accent bar
canvas.setFillColor(TEAL)
canvas.rect(0, h*0.38, w, 5, fill=1, stroke=0)
canvas.setFillColor(ACCENT)
canvas.rect(0, h*0.38 - 3, w*0.35, 3, fill=1, stroke=0)
doc.pageTemplates[0].onPage = cover_bg
cover_items = [
Spacer(1, 60*mm),
Paragraph("BREASTSTROKE", COVER_TITLE),
Paragraph("BEGINNER TRAINING PROGRAM", COVER_TITLE),
Spacer(1, 8*mm),
HRFlowable(width="60%", thickness=2, color=ACCENT, spaceAfter=6),
Paragraph("4-Week Progressive Plan | 4–5 Sessions Per Week | 25m Pool", COVER_SUB),
Spacer(1, 60*mm),
Paragraph("Drill-based • Technique-first • Beginner-friendly", COVER_NOTE),
Spacer(1, 4*mm),
Paragraph("Generated July 2026", COVER_NOTE),
]
story.extend(cover_items)
story.append(PageBreak())
# ════════════════════════════════════════════════════════════
# MAIN CONTENT
# ════════════════════════════════════════════════════════════
story.append(NextPageTemplate("normal"))
# ── PROGRAM OVERVIEW ─────────────────────────────────────────────────────────
story.append(Paragraph("PROGRAM OVERVIEW", H1))
story.append(Spacer(1, 3*mm))
overview_text = (
"This 4-week beginner program builds breaststroke technique from the ground up. "
"Each week has a specific focus, and sessions are designed for a 25m pool at "
"400–1,000m total volume. Rest generously - quality over quantity."
)
story.append(Paragraph(overview_text, BODY))
story.append(Spacer(1, 2*mm))
overview_table = [
[Paragraph("Week", TH), Paragraph("Focus", TH), Paragraph("Volume/Session", TH),
Paragraph("Sessions", TH)],
[Paragraph("1", TC), Paragraph("Body position & kick mechanics", TC),
Paragraph("400–500m", TC), Paragraph("4", TC)],
[Paragraph("2", TC), Paragraph("Arm pull & breathing timing", TC),
Paragraph("500–650m", TC), Paragraph("4–5", TC)],
[Paragraph("3", TC), Paragraph("Full stroke coordination & timing", TC),
Paragraph("650–800m", TC), Paragraph("5", TC)],
[Paragraph("4", TC), Paragraph("Endurance, turns & consolidation", TC),
Paragraph("800–1000m", TC), Paragraph("5", TC)],
]
t = Table(overview_table, colWidths=[22*mm, 80*mm, 38*mm, 28*mm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), TEAL),
("BACKGROUND", (0,1), (-1,1), LGRAY),
("BACKGROUND", (0,3), (-1,3), LGRAY),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LGRAY]),
("GRID", (0,0), (-1,-1), 0.4, MGRAY),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING",(0,0),(-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(t)
story.append(Spacer(1, 4*mm))
story.append(Paragraph(
"Key abbreviations: L = length (25m), REST = rest between sets, "
"WU = warm-up, CD = cool-down, KB = kickboard, Pull = arms only (buoy optional), "
"FS = freestyle, BS = breaststroke.",
SMALL))
story.append(Spacer(1, 3*mm))
# ── DRILL LIBRARY ─────────────────────────────────────────────────────────────
story.append(Paragraph("DRILL LIBRARY", H1))
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
"Refer to these drills throughout the program. Master each before moving to the next.",
BODY))
story.append(Spacer(1, 2*mm))
drills = [
("D1 - Kick on Back",
"Lie on your back, arms at sides. Perform breaststroke kick. Lets you watch your feet "
"and feel if toes flex outward correctly. Feet should skim the surface on recovery, not break it."),
("D2 - Kick with Board",
"Hold a kickboard at arm's length, face in water. Focus on drawing heels to glutes, "
"flexing feet outward, then driving backward in a circular arc. Squeeze legs together at finish."),
("D3 - 2-Beat Kick Pause",
"After each kick, hold streamline for 3 seconds. Teaches the glide and builds body-line awareness."),
("D4 - Arms-Only (Pull Buoy)",
"Place pull buoy between thighs. Focus entirely on the arm pull: out-sweep, catch, "
"in-sweep (heart-shape path), then shoot hands forward. Breathe on each pull."),
("D5 - Catch-Up Drill",
"One arm stays extended while the other completes a full pull cycle before switching. "
"Slows the stroke down and makes timing errors obvious."),
("D6 - 1 Pull + 2 Kicks",
"Perform one full arm pull then take two kicks before the next pull. "
"Builds rhythm and separates propulsion phases clearly."),
("D7 - Head-Lead Body Dolphin",
"Gentle undulation from head to toe, no arms. Teaches body wave and hip timing "
"useful for breaststroke body movement."),
("D8 - Fist Drill",
"Swim full breaststroke with closed fists. Forces you to use the forearm for the catch "
"and improves feel for the water on the in-sweep."),
("D9 - Wall Touch Turns",
"Practice two-hand touch to the wall, then a legal breaststroke turn (tuck, push, "
"one underwater dolphin + one breaststroke kick, surface). Repeat slowly."),
]
for title, desc in drills:
drill_data = [[Paragraph(title, TC_BOLD), Paragraph(desc, TC)]]
dt = Table(drill_data, colWidths=[42*mm, 130*mm])
dt.setStyle(TableStyle([
("BACKGROUND", (0,0), (0,0), LTBLUE),
("BACKGROUND", (1,0), (1,0), WHITE),
("BOX", (0,0), (-1,-1), 0.5, TEAL),
("LINEBEFORE", (1,0), (1,0), 0.5, TEAL),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING",(0,0),(-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 7),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
story.append(KeepTogether(dt))
story.append(Spacer(1, 1.5*mm))
story.append(Spacer(1, 2*mm))
# ── SESSION STRUCTURE BOX ─────────────────────────────────────────────────────
story.append(Paragraph("SESSION STRUCTURE (every session)", H1))
story.append(Spacer(1, 2*mm))
structure_data = [
[Paragraph("Phase", TH), Paragraph("Duration", TH), Paragraph("Content", TH)],
[Paragraph("Warm-Up", TC_BOLD), Paragraph("~10 min", TC),
Paragraph("4 x 25m easy FS (REST 20s) + 2 x 25m backstroke easy", TC)],
[Paragraph("Drill Block", TC_BOLD), Paragraph("~15–20 min", TC),
Paragraph("2–3 targeted drills from the Drill Library (session-specific)", TC)],
[Paragraph("Main Set", TC_BOLD), Paragraph("~15–20 min", TC),
Paragraph("Breaststroke sets with defined rest intervals (session-specific)", TC)],
[Paragraph("Cool-Down", TC_BOLD), Paragraph("~5 min", TC),
Paragraph("2–4 x 25m easy FS or backstroke, focus on relaxed breathing", TC)],
]
st = Table(structure_data, colWidths=[35*mm, 25*mm, 112*mm])
st.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), TEAL),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LGRAY]),
("GRID", (0,0), (-1,-1), 0.4, MGRAY),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING",(0,0),(-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
story.append(st)
story.append(Spacer(1, 4*mm))
# ════════════════════════════════════════════════════════════
# WEEKS 1-4
# ════════════════════════════════════════════════════════════
def week_header(n, focus, vol, sessions):
data = [[
Paragraph(f"WEEK {n}", s("WH", fontSize=14, leading=18, textColor=WHITE,
fontName="Helvetica-Bold")),
Paragraph(focus, s("WF", fontSize=11, leading=15, textColor=LTBLUE,
fontName="Helvetica-Bold")),
Paragraph(f"{vol} | {sessions} sessions", s("WV", fontSize=9, leading=12,
textColor=MGRAY, fontName="Helvetica",
alignment=TA_RIGHT)),
]]
t = Table(data, colWidths=[28*mm, 100*mm, 44*mm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), NAVY),
("TOPPADDING", (0,0), (-1,-1), 7),
("BOTTOMPADDING",(0,0),(-1,-1), 7),
("LEFTPADDING", (0,0), (-1,-1), 8),
("RIGHTPADDING", (0,0), (-1,-1), 8),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("LINEBELOW", (0,0), (-1,-1), 2, ACCENT),
]))
return t
def session_table(day_label, total_m, rows):
header_row = [
Paragraph(f"SESSION {day_label} — ~{total_m}m total", TC_BOLD),
Paragraph("Set", TH),
Paragraph("Distance", TH),
Paragraph("Details", TH),
Paragraph("REST", TH),
]
data = [header_row] + rows
col_w = [28*mm, 12*mm, 22*mm, 90*mm, 20*mm]
t = Table(data, colWidths=col_w)
row_styles = [
("BACKGROUND", (0,0), (-1,0), TEAL),
("SPAN", (0,0), (0,0)),
("GRID", (0,0), (-1,-1), 0.4, MGRAY),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING",(0,0),(-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 5),
("VALIGN", (0,0), (-1,-1), "TOP"),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LGRAY]),
]
# Shade warm-up and cool-down
for i, row in enumerate(data[1:], start=1):
if isinstance(row[0], Paragraph) and "WU" in row[0].text:
row_styles.append(("BACKGROUND", (0,i), (-1,i), colors.HexColor("#EAF6FB")))
if isinstance(row[0], Paragraph) and "CD" in row[0].text:
row_styles.append(("BACKGROUND", (0,i), (-1,i), colors.HexColor("#EAF6FB")))
t.setStyle(TableStyle(row_styles))
return t
def R(phase, set_no, dist, detail, rest):
return [Paragraph(phase, TC_BOLD), Paragraph(str(set_no), TC),
Paragraph(dist, TC), Paragraph(detail, TC), Paragraph(rest, TC)]
# ─────────────── WEEK 1 ────────────────────────────────────────────────────
story.append(PageBreak())
story.append(week_header(1, "Body Position & Kick Mechanics", "400–500m", "4"))
story.append(Spacer(1, 3*mm))
story.append(Paragraph(
"Week 1 is all about getting comfortable in the water with the correct body line "
"and developing an efficient frog kick. Do not rush into full stroke - "
"drill-perfect kick is the single biggest speed lever for beginners.",
NOTE_STYLE))
story.append(Spacer(1, 3*mm))
# W1 S1
rows = [
R("WU", "4x25m", "100m", "Easy freestyle, focus on relaxed breathing", "20s"),
R("Drill D1", "4x25m", "100m", "Kick on back - watch feet, toes flare out, squeeze on finish", "25s"),
R("Drill D2", "4x25m", "100m", "Kick with board - slow and deliberate, full extension at finish", "30s"),
R("Main Set", "4x25m", "100m", "Breaststroke kick only (board) - try to maintain rhythm", "30s"),
R("CD", "2x25m", "50m", "Easy backstroke, arms at sides", "20s"),
]
story.append(session_table("1A", 450, rows))
story.append(Spacer(1, 3*mm))
# W1 S2
rows = [
R("WU", "4x25m", "100m", "Easy freestyle", "20s"),
R("Drill D1", "4x25m", "100m", "Kick on back - faster tempo, still watch feet", "20s"),
R("Drill D3", "4x25m", "100m", "Kick with 3-sec pause in streamline after each kick", "30s"),
R("Main Set", "4x25m", "100m", "Kick with board - count strokes per length, aim for consistent rhythm", "30s"),
R("CD", "2x25m", "50m", "Easy freestyle, slow", "20s"),
]
story.append(session_table("1B", 450, rows))
story.append(Spacer(1, 3*mm))
# W1 S3
rows = [
R("WU", "4x25m", "100m", "Easy freestyle + 2x25 backstroke", "20s"),
R("Drill D2", "4x25m", "100m", "Kick with board - narrow kick focus, knees hip-width only", "25s"),
R("Drill D3", "4x25m", "100m", "Kick with pause - aim to feel glide momentum", "30s"),
R("Main Set", "6x25m", "150m", "Kick only (no board), arms extended forward, face in water", "35s"),
R("CD", "2x25m", "50m", "Easy backstroke", "—"),
]
story.append(session_table("1C", 500, rows))
story.append(Spacer(1, 3*mm))
# W1 S4
rows = [
R("WU", "4x25m", "100m", "Easy freestyle", "20s"),
R("Drill D1", "2x25m", "50m", "Kick on back - final check of foot flex", "20s"),
R("Drill D2", "4x25m", "100m", "Kick with board - aim for 16–20 kicks per 25m", "25s"),
R("Main Set", "4x25m", "100m", "Full breaststroke - arms only doing minimal pull, legs do all work", "35s"),
R("CD", "2x25m", "50m", "Easy freestyle", "—"),
]
story.append(session_table("1D", 400, rows))
story.append(Spacer(1, 3*mm))
story.append(Paragraph(
"Week 1 check: Can you kick a full 25m without stopping? Do your heels come up "
"to your glutes (not your knees)? Do your feet flex outward at the set position? "
"If yes, you are ready for Week 2.",
NOTE_STYLE))
# ─────────────── WEEK 2 ────────────────────────────────────────────────────
story.append(PageBreak())
story.append(week_header(2, "Arm Pull & Breathing Timing", "500–650m", "4–5"))
story.append(Spacer(1, 3*mm))
story.append(Paragraph(
"Now that the kick is established, Week 2 introduces the arm pull and the "
"breath. The key cue: breathe ON the in-sweep, not after it. "
"Hands shoot forward as the face returns to the water.",
NOTE_STYLE))
story.append(Spacer(1, 3*mm))
# W2 S1
rows = [
R("WU", "4x25m", "100m", "Easy freestyle", "20s"),
R("Review", "4x25m", "100m", "Kick with board - refresh Week 1 kick quality", "25s"),
R("Drill D4", "4x25m", "100m", "Arms only (pull buoy) - trace heart shape under chest, head rises naturally", "30s"),
R("Main Set", "4x25m", "100m", "Drill D4 + focus on breathing on in-sweep", "35s"),
R("CD", "2x25m", "50m", "Easy backstroke", "—"),
]
story.append(session_table("2A", 500, rows))
story.append(Spacer(1, 3*mm))
# W2 S2
rows = [
R("WU", "4x25m", "100m", "Easy freestyle", "20s"),
R("Drill D5", "4x25m", "100m", "Catch-up drill - one arm extended while other completes full pull", "30s"),
R("Drill D4", "4x25m", "100m", "Arms only - fast hands forward after in-sweep", "30s"),
R("Main Set", "6x25m", "150m", "Arms only (pull buoy) - count arm cycles, aim for 12–16 per 25m", "35s"),
R("CD", "2x25m", "50m", "Easy freestyle", "—"),
]
story.append(session_table("2B", 550, rows))
story.append(Spacer(1, 3*mm))
# W2 S3
rows = [
R("WU", "4x25m", "100m", "Easy freestyle + 2x25 backstroke", "20s"),
R("Drill D4", "2x25m", "50m", "Arms only - final quality check before combining", "25s"),
R("Drill D2", "2x25m", "50m", "Kick with board - refresh kick", "25s"),
R("Drill D6", "4x25m", "100m", "1 Pull + 2 Kicks - feels awkward at first, persist!", "35s"),
R("Main Set", "4x25m", "100m", "Full breaststroke - slow, think Pull-Breathe-Kick-Glide", "40s"),
R("CD", "2x25m", "50m", "Easy backstroke", "—"),
]
story.append(session_table("2C", 550, rows))
story.append(Spacer(1, 3*mm))
# W2 S4
rows = [
R("WU", "4x25m", "100m", "Easy freestyle", "20s"),
R("Drill D6", "4x25m", "100m", "1 Pull + 2 Kicks - smoother now?", "35s"),
R("Main Set", "6x25m", "150m", "Full breaststroke - focus on glide after every kick", "40s"),
R("Main Set", "2x50m", "100m", "Full breaststroke at easy pace, REST 45s between", "45s"),
R("CD", "2x25m", "50m", "Easy freestyle, slow", "—"),
]
story.append(session_table("2D", 600, rows))
story.append(Spacer(1, 3*mm))
# W2 S5 (optional)
rows = [
R("WU", "4x25m", "100m", "Easy freestyle", "20s"),
R("Drill D8", "4x25m", "100m", "Fist drill - feel the forearm catch, focus on in-sweep", "30s"),
R("Main Set", "4x50m", "200m", "Full breaststroke - focus on breathing timing, REST 45s", "45s"),
R("CD", "4x25m", "100m", "2x easy freestyle + 2x backstroke", "20s"),
]
story.append(session_table("2E (optional)", 500, rows))
story.append(Spacer(1, 3*mm))
story.append(Paragraph(
"Week 2 check: Does your chin clear the water on the in-sweep without you tilting your head? "
"Do your hands shoot forward quickly (not slowly)? Does the pull feel short and compact? Good - move on.",
NOTE_STYLE))
# ─────────────── WEEK 3 ────────────────────────────────────────────────────
story.append(PageBreak())
story.append(week_header(3, "Full Stroke Coordination & Timing", "650–800m", "5"))
story.append(Spacer(1, 3*mm))
story.append(Paragraph(
"Week 3 brings everything together. The mantra: Pull - Breathe - Kick - Glide. "
"Never pull and kick at the same time. Use the glide. Add a short rest pause at "
"the wall each length to reset your streamline before pushing off.",
NOTE_STYLE))
story.append(Spacer(1, 3*mm))
# W3 S1
rows = [
R("WU", "4x25m", "100m", "Easy freestyle", "20s"),
R("Drill D6", "4x25m", "100m", "1 Pull + 2 Kicks - reinforce timing separation", "30s"),
R("Main Set", "6x25m", "150m", "Full BS - count strokes, aim to reduce by 1 each set", "35s"),
R("Main Set", "2x50m", "100m", "Full BS at steady pace, focus on glide length", "45s"),
R("CD", "2x25m", "50m", "Easy backstroke", "—"),
]
story.append(session_table("3A", 650, rows))
story.append(Spacer(1, 3*mm))
# W3 S2
rows = [
R("WU", "4x25m", "100m", "Easy FS + 2x25 backstroke", "20s"),
R("Drill D8", "4x25m", "100m", "Fist drill - feel forearm on catch", "30s"),
R("Main Set", "4x50m", "200m", "Full BS: Lengths 1&2 drill focus (catch), 3&4 full speed", "45s"),
R("Main Set", "4x25m", "100m", "Full BS - sprint 25m, focus on explosive kick finish", "35s"),
R("CD", "2x25m", "50m", "Easy freestyle", "—"),
]
story.append(session_table("3B", 700, rows))
story.append(Spacer(1, 3*mm))
# W3 S3
rows = [
R("WU", "4x25m", "100m", "Easy freestyle", "20s"),
R("Drill D3", "4x25m", "100m", "Kick pause - reset body line awareness", "30s"),
R("Main Set", "1x200m", "200m", "Full BS - even pace, focus on maintaining form as fatigue sets in", "2 min"),
R("Main Set", "4x25m", "100m", "Full BS fast - max effort per 25m", "40s"),
R("CD", "4x25m", "100m", "Easy FS + backstroke alternating", "20s"),
]
story.append(session_table("3C", 700, rows))
story.append(Spacer(1, 3*mm))
# W3 S4
rows = [
R("WU", "4x25m", "100m", "Easy freestyle", "20s"),
R("Drill D6", "2x25m", "50m", "1 Pull + 2 Kicks - timing refresher", "30s"),
R("Drill D4", "2x25m", "50m", "Arms only - fast hands forward", "25s"),
R("Main Set", "3x100m", "300m", "Full BS: 100m sets, REST 90s. Focus on stroke consistency.", "90s"),
R("CD", "2x25m", "50m", "Easy backstroke", "—"),
]
story.append(session_table("3D", 650, rows))
story.append(Spacer(1, 3*mm))
# W3 S5
rows = [
R("WU", "4x25m", "100m", "Easy freestyle", "20s"),
R("Drill D9", "5 reps", "—", "Wall touch turns - practice legal 2-hand touch and pullout", "30s"),
R("Main Set", "2x200m", "400m", "Full BS continuous - even effort, legal turns at wall", "2 min"),
R("CD", "4x25m", "100m", "Easy FS + backstroke", "20s"),
]
story.append(session_table("3E", 700, rows))
story.append(Spacer(1, 3*mm))
story.append(Paragraph(
"Week 3 check: Can you swim 200m of breaststroke without stopping? "
"Is your stroke rhythm consistent (not speeding up when tired)? "
"Are your turns legal (two-hand simultaneous touch)? Ready for Week 4.",
NOTE_STYLE))
# ─────────────── WEEK 4 ────────────────────────────────────────────────────
story.append(PageBreak())
story.append(week_header(4, "Endurance, Turns & Consolidation", "800–1000m", "5"))
story.append(Spacer(1, 3*mm))
story.append(Paragraph(
"Week 4 extends distance, sharpens turns, and consolidates everything learned. "
"By the end of this week you should be able to swim 400m of breaststroke "
"continuously with good technique.",
NOTE_STYLE))
story.append(Spacer(1, 3*mm))
# W4 S1
rows = [
R("WU", "4x25m", "100m", "Easy freestyle", "20s"),
R("Main Set", "4x100m", "400m", "Full BS - REST 90s. Focus on glide length not just speed.", "90s"),
R("Drill Mix", "4x25m", "100m", "Alternate D2 (kick) and D4 (pull) lengths", "25s"),
R("CD", "4x25m", "100m", "Easy FS + backstroke", "20s"),
]
story.append(session_table("4A", 800, rows))
story.append(Spacer(1, 3*mm))
# W4 S2
rows = [
R("WU", "4x25m", "100m", "Easy freestyle", "20s"),
R("Drill D9", "6 reps", "—", "Turns: legal touch, tuck, explosive push, one dolphin + one BS kick off wall", "30s"),
R("Main Set", "5x100m", "500m", "Full BS with legal turns - REST 90s per 100m", "90s"),
R("CD", "4x25m", "100m", "Easy backstroke", "20s"),
]
story.append(session_table("4B", 850, rows))
story.append(Spacer(1, 3*mm))
# W4 S3
rows = [
R("WU", "4x25m", "100m", "Easy freestyle", "20s"),
R("Drill D6", "4x25m", "100m", "1 Pull + 2 Kicks - technique sharpener mid-week", "30s"),
R("Main Set", "1x400m", "400m", "Full BS continuous - even effort, count stroke cycles per 25m", "Done"),
R("CD", "4x25m", "100m", "Easy FS + backstroke", "20s"),
]
story.append(session_table("4C", 800, rows))
story.append(Spacer(1, 3*mm))
# W4 S4
rows = [
R("WU", "4x25m", "100m", "Easy freestyle", "20s"),
R("Main Set", "8x50m", "400m", "Full BS - REST 40s per 50m. Every 2nd 50m aim slightly faster.", "40s"),
R("Drill D8", "4x25m", "100m", "Fist drill - check catch is still good under fatigue", "30s"),
R("CD", "4x25m", "100m", "Easy FS + backstroke", "20s"),
]
story.append(session_table("4D", 800, rows))
story.append(Spacer(1, 3*mm))
# W4 S5
rows = [
R("WU", "4x25m", "100m", "Easy freestyle", "20s"),
R("Main Set", "1x400m", "400m", "GRADUATION SWIM - Full BS, continuous, best effort. Time yourself!", "—"),
R("Cool Set", "4x50m", "200m", "Easy alternating FS and BS - celebrate the milestone", "30s"),
R("CD", "4x25m", "100m", "Easy backstroke", "—"),
]
story.append(session_table("4E - Graduation", 800, rows))
story.append(Spacer(1, 3*mm))
story.append(Paragraph(
"Graduation goal: 400m breaststroke continuous with legal technique. "
"Compare your stroke count on Session 4E vs Session 1A. "
"Fewer strokes per length = better efficiency. Well done!",
NOTE_STYLE))
# ── TIPS PAGE ─────────────────────────────────────────────────────────────────
story.append(PageBreak())
story.append(Paragraph("GENERAL TRAINING TIPS", H1))
story.append(Spacer(1, 3*mm))
tips = [
("Rest is progress", "You adapt BETWEEN sessions, not during them. Take your rest intervals fully, especially in Weeks 1-2."),
("Film your kick", "Use a waterproof phone or ask someone to film you underwater from the side and behind. Seeing your own technique gap is worth 10 coaching tips."),
("Dry-land ankle stretch", "Sit on your heels and lean back gently for 60 seconds before each session. Better ankle flexion = a more powerful kick catch position."),
("Count strokes", "Track stroke cycles per 25m every session. Aim to trend downward over 4 weeks. Lower count = more distance per stroke = better efficiency."),
("Nose clips are fine", "If water up the nose is distracting your technique, use a nose clip in Weeks 1-2. Remove it in Week 3."),
("Do not skip the glide", "The urge to rush the stroke is the most common beginner error. A 1-second glide in streamline can add 0.5m per stroke cycle."),
("Hydrate", "You sweat in a pool. Drink water before and after every session."),
("Pain vs soreness", "Knee discomfort is common in early breaststroke training due to the rotational kick load. If it is sharp or persists, rest and see a physio. Mild muscle soreness in hips and inner thighs is normal."),
]
for title, body in tips:
tip_data = [[Paragraph(f" {title}", TC_BOLD), Paragraph(body, TC)]]
tt = Table(tip_data, colWidths=[45*mm, 127*mm])
tt.setStyle(TableStyle([
("BACKGROUND", (0,0), (0,0), colors.HexColor("#FFF4D6")),
("LINEAFTER", (0,0), (0,0), 1, ACCENT),
("BOX", (0,0), (-1,-1), 0.4, MGRAY),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING",(0,0),(-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
story.append(KeepTogether(tt))
story.append(Spacer(1, 1.5*mm))
story.append(Spacer(1, 5*mm))
# ── PROGRESS TRACKER ─────────────────────────────────────────────────────────
story.append(Paragraph("PROGRESS TRACKER", H1))
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
"Fill in after each session to track progress over the 4 weeks.", BODY))
story.append(Spacer(1, 2*mm))
tracker_header = [
Paragraph("Session", TH), Paragraph("Date", TH),
Paragraph("Total Distance", TH), Paragraph("Stroke Count/25m", TH),
Paragraph("Key Focus", TH), Paragraph("Notes / How it Felt", TH),
]
tracker_rows = [tracker_header]
sessions_list = [
"1A", "1B", "1C", "1D",
"2A", "2B", "2C", "2D", "2E",
"3A", "3B", "3C", "3D", "3E",
"4A", "4B", "4C", "4D", "4E",
]
for sn in sessions_list:
tracker_rows.append([
Paragraph(sn, TC_BOLD),
Paragraph("", TC), Paragraph("", TC),
Paragraph("", TC), Paragraph("", TC), Paragraph("", TC),
])
tr = Table(tracker_rows, colWidths=[16*mm, 20*mm, 24*mm, 26*mm, 30*mm, 56*mm])
tr.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), TEAL),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LGRAY]),
("GRID", (0,0), (-1,-1), 0.4, MGRAY),
("TOPPADDING", (0,0), (-1,-1), 7),
("BOTTOMPADDING",(0,0),(-1,-1), 7),
("LEFTPADDING", (0,0), (-1,-1), 5),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(tr)
# ── BUILD ─────────────────────────────────────────────────────────────────────
doc.build(story)
print(f"PDF saved to: {OUTPUT_PATH}")
cd /tmp/workspace/breaststroke-program && python generate_pdf.py 2>&1
ls -lh /tmp/workspace/breaststroke-program/
Breaststroke Beginner Program
PDF Document · PDF
| Week | Focus | Volume |
|---|---|---|
| 1 | Body position & kick mechanics | 400-500m |
| 2 | Arm pull & breathing timing | 500-650m |
| 3 | Full stroke coordination | 650-800m |
| 4 | Endurance, turns & consolidation | 800-1000m |
Treatment of irritable bowel syndrome associated with intermittent colic pain
"irritable bowel syndrome" AND "abdominal pain" AND treatment
Harrison's Principles of Internal Medicine 22E, p. 2618
Goldman-Cecil Medicine, p. 1418; Harrison's 22E, p. 2618
| Drug | Dose | Notes |
|---|---|---|
| Hyoscyamine | 0.125 mg 3-4x/day or as needed | Anticholinergic; short-term relief well established |
| Dicyclomine (Dicycloverine) | 10-20 mg 3-4x/day | Synthetic anticholinergic; fewer side effects than natural alkaloids |
| Peppermint oil (enteric-coated) | 1-2 capsules 3x/day | Smooth muscle relaxant; in a meta-analysis of 10 RCTs (n=1030), superior to placebo for global IBS symptoms and abdominal pain reduction |
| Otilonium bromide | 40 mg 3x/day | Strong evidence in RCTs; commonly used in Europe |
| Hyoscine (scopolamine) | 10 mg 3x/day | 3 studies (n=426) support use; ACG grade 2D recommendation |
Harrison's 22E, p. 2618; Goldman-Cecil Medicine, p. 1418-1419; Katzung 16th Ed, p. 1712
| Drug | Dose | Side Effects |
|---|---|---|
| Amitriptyline | 10-25 mg at bedtime (up to 100 mg) | Sedation, dry mouth, constipation |
| Desipramine | 10-25 mg at bedtime (up to 100 mg) | Dry mouth, dizziness |
| Nortriptyline | 10-25 mg at bedtime (up to 100 mg) | Better tolerated than amitriptyline |
Katzung 16th Ed, p. 1712; Goldman-Cecil Medicine, p. 1419 (Table 123-3)
Harrison's 22E, p. 2619; Goldman-Cecil Medicine Table 123-3; Katzung 16th Ed, p. 1713-1715
Harrison's 22E, p. 2619

| Treatment Target | Drug/Intervention | Evidence |
|---|---|---|
| Colic/spasm (immediate) | Peppermint oil, dicyclomine, hyoscyamine (30 min before meals) | Strong for short-term |
| Chronic/severe pain | Amitriptyline or desipramine 10-50 mg nocte | Well-established |
| IBS-D with colic | Loperamide, rifaximin, alosetron (refractory women) | FDA-approved |
| IBS-C with colic | Linaclotide, lubiprostone, PEG laxatives | FDA-approved |
| Dietary | Low-FODMAP diet (dietitian-supervised) | Grade A evidence |
| Gut flora | Rifaximin, probiotics | Moderate evidence |
| Refractory pain | CBT, gut-directed hypnotherapy | Strong evidence |
550 mg 3x/day means thrice daily???
How would it be taken after meals
| Dose | Timing |
|---|---|
| 1st dose | After breakfast |
| 2nd dose | After lunch |
| 3rd dose | After dinner |