HSHSKY Lab
workflows6 min read

I Asked Claude Code to Build a Custom PowerPoint Generator in Python — Then Cut It in Half

Instead of opening PowerPoint, I had Claude Code write a Python script that builds a fully designed 16:9 deck from code. Then I asked for a 10-minute version, and it restructured the content instead of just deleting slides.

In-Article Ad — Replace with ad code after approval

Same day as the sitemap bug fix, completely different task: I needed a slide deck for a presentation, and instead of opening PowerPoint, I asked Claude Code to write a Python script that generates the whole thing.

The Brief: A Deck, Not a Document

I had an outline — roughly 16 sections of talking points for a concept presentation — and needed it turned into a polished, on-brand 16:9 slide deck: consistent colors, a logo, cards, diagrams, the works.

The obvious option is python-pptx with a template .pptx file. I didn't have a template, and didn't want to build one by hand in PowerPoint first just so a script could fill it in. So the ask was simpler than that: "write a script that generates the deck from scratch, styled the way I'd want it."

Step One: A Design System, Not a Template

Rather than starting with slide content, Claude Code started with a small design system — a color palette as named constants, then a handful of drawing primitives built on top of python-pptx's shape and text APIs:

PRIMARY  = RGBColor(0x1E, 0x6B, 0xFF)
ACCENT   = RGBColor(0x06, 0xB6, 0xD4)
DARK     = RGBColor(0x0F, 0x17, 0x2A)
GRAY     = RGBColor(0x64, 0x74, 0x8B)
BORDER   = RGBColor(0xE2, 0xE8, 0xF0)
BG_LIGHT = RGBColor(0xF5, 0xF7, 0xFA)

Then primitives for the shapes that show up on every slide:

def add_card(slide, x, y, w, h, fill=WHITE, line=BORDER, radius=0.06):
    return add_rect(slide, x, y, w, h, fill=fill, line=line, line_w=Pt(1),
                     shape_type=MSO_SHAPE.ROUNDED_RECTANGLE, radius=radius)

def add_pill(slide, x, y, w, h, text, fill, color, size=11):
    add_rect(slide, x, y, w, h, fill=fill,
             shape_type=MSO_SHAPE.ROUNDED_RECTANGLE, radius=0.5)
    add_text(slide, x, y, w, h, text, size=size, color=color, bold=True,
             align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE)

def add_header(slide, idx, title, subtitle=None):
    set_bg(slide, WHITE)
    add_gradient_rect(slide, 0, 0, SW, Inches(1.15), PRIMARY, ACCENT)
    add_text(slide, Inches(0.7), Inches(0.16), Inches(11.0), Inches(0.65),
             title, size=27, color=WHITE, bold=True, anchor=MSO_ANCHOR.MIDDLE)
    if subtitle:
        add_text(slide, Inches(0.72), Inches(0.74), Inches(11.0), Inches(0.35),
                 subtitle, size=11.5, color=RGBColor(0xDB, 0xEA, 0xFE))

add_card is a rounded rectangle. add_pill is a rounded badge with centered text. add_header draws the gradient title bar that appears at the top of every content slide, with a slide number in the corner. None of this is content yet — it's the visual vocabulary every slide gets built from.

Step Two: Slides Are Just Function Calls

Once the primitives existed, each slide became layout math plus a loop. Here's the shape of the pattern (content replaced with generic placeholders — the real deck's content isn't):

steps = [
    ("1", "Plan",   "Define scope",      PRIMARY, BLUE_BG),
    ("2", "Build",  "Implement",         PRIMARY, BLUE_BG),
    ("3", "Test",   "Verify behavior",   GREEN,   GREEN_BG),
    ("4", "Review", "Confirm & adjust",  GREEN,   GREEN_BG),
    ("5", "Ship",   "Deploy & monitor",  ORANGE,  ORANGE_BG),
]

n = len(steps)
margin, arrow_w = Inches(0.55), Inches(0.32)
box_w = (SW - margin * 2 - arrow_w * (n - 1)) / n
y, box_h = Inches(2.6), Inches(2.0)

for i, (num, title, sub, color, bg) in enumerate(steps):
    x = margin + i * (box_w + arrow_w)
    add_card(s, x, y, box_w, box_h, fill=bg, line=color, radius=0.1)
    add_pill(s, x + (box_w - Inches(0.42)) // 2, y - Inches(0.2),
             Inches(0.42), Inches(0.42), num, color, WHITE, size=14)
    add_text(s, x, y + Inches(0.4), box_w, Inches(0.85), title,
             size=12.5, color=DARK, bold=True, align=PP_ALIGN.CENTER)
    add_text(s, x, y + Inches(1.3), box_w, Inches(0.65), sub,
             size=10, color=GRAY, align=PP_ALIGN.CENTER)
    if i < n - 1:
        add_text(s, x + box_w, y, arrow_w, box_h, "→", size=16, color=LGRAY,
                 align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE)

That's a horizontal pipeline diagram — N boxes, evenly spaced, connected by arrows, each with a numbered badge, a title, and a caption. Swap the steps list for a different array and the same ~15 lines draws a completely different diagram. The actual deck has about a dozen slides built from variations of this: a row of cards, a 2x2 grid, a comparison table, a cover slide with the logo and a gradient panel.

The Real Test: "Now Make It Fit in 10 Minutes"

The first version was 16 slides — comfortable for a longer walkthrough. Then the actual time slot turned out to be 10 minutes, which meant roughly 11 slides.

The lazy fix is deleting 5 slides. That's not what happened. Claude Code went back to the original outline and restructured it:

  • Two slides that covered related sub-topics separately got merged into one, with the layout redesigned to fit both at a smaller scale
  • A new slide that didn't exist in the 16-page version got added — a single framework/overview slide, because in a 10-minute talk the audience needs the big picture stated explicitly even if the supporting detail gets cut
  • The closing and cover slides were rewritten to match the shorter format rather than just having their timing notes changed

The output was a second, independent script — same design system, same primitives, different slide composition — producing an 11-slide deck that's a restructured summary, not a trimmed-down copy.

Tip

If you need both a "full" and a "short" version of the same deck, ask for the short version as a content-restructuring task, not a slide-deletion task. "What's the minimum set of slides that still tells the whole story" produces a better result than "cut this to N slides."

Warning

Two things worth checking before you trust a generated deck on another machine: the script hardcodes an absolute local path to the logo image, and it sets the font to a specific CJK typeface (微软雅黑 / Microsoft YaHei). Both are fine on the machine that generated the deck and silently wrong anywhere else.

Same Tool, Different World

What stood out to me wasn't the deck itself — it's that the same session that, earlier today, traced a one-character config bug across 19 call sites in a Next.js/TypeScript codebase also wrote a from-scratch layout engine in Python with python-pptx, then used it to make a design decision (what to cut, what to add) when the constraints changed. Different language, different domain, same back-and-forth.

FAQ

Can Claude Code really generate a full PowerPoint deck from scratch?

Yes — using python-pptx, it can write a script that builds shapes, gradients, text boxes, and images programmatically, with no .pptx template required. The output is a real .pptx file you open in PowerPoint or Keynote like any other.

Do I need a PowerPoint template for this to work?

No. The approach here was the opposite — Claude Code built the visual style (colors, cards, headers, badges) as reusable code first, then composed slides from those pieces. That's more setup than filling in a template, but the result is a generator you can re-run with new content.

What's the advantage over just asking for the slide content and pasting it into PowerPoint?

You get something re-runnable and diffable. When the brief changed from 16 slides to 11, the fix was "edit the script and run it again," not "manually rebuild half the deck." For a one-off five-slide deck this is overkill — for anything you'll need to revise, it pays off fast.

Bottom Line

The interesting part wasn't "AI made slides." It was watching the same tool switch from debugging a sitemap to designing a layout system to making an editorial call about what a 10-minute audience actually needs — all in the same afternoon, all without me touching either codebase by hand.

In-Article Ad — Replace with ad code after approval

Tags

claude codepythonpython-pptxautomationworkflow
H

Written by HSKY

Developer writing about AI coding tools — Claude Code, Cursor, agents, and the workflows that make them work.