Claude Makes Slides: Markdown In, Deck Out

When you want a deck, the tempting move is to ask the model for the thing you actually want to end up with — a .pptx. Don’t. Asking an LLM for a presentation file turns it into a layout engine: it invents a grid, picks font sizes, decides how much whitespace a bullet deserves, and quietly negotiates with itself about where the box goes. Every token spent on that is a token not spent on your argument, and the output drifts between decks because nothing holds it to a shape.

Give it markdown instead and let a real tool do the layout. That tool is Marp.

The whole toolchain

There’s no apt package, and the marp-cli in the snap store is a third-party build sitting at 1.1.1 while upstream is at 4.5.0 — so this one is a node install:

npx @marp-team/marp-cli@latest deck.md -o deck.html

npx while you’re curious; npm i -g @marp-team/marp-cli once you’re sold. There are also standalone binaries on the releases page if you want no node on the box at all. HTML export needs nothing else — no browser, no LaTeX. PDF, PPTX and PNG shell out to Chrome/Chromium.

The deck is one file

That file, in full, is what Claude writes:

---
marp: true
theme: default
paginate: true
---

<!-- _paginate: false -->

# Composition Over Inheritance
## and the one place to break it

---

## The rule

- inherit to *be* a thing
- compose to *share* a thing

```kotlin
class Foo : JComponent() { }
```

<!-- This is a speaker note. Nobody sees it on the slide. -->

---

<!-- _class: invert -->

## The carve-out

UI components. Every time.

Everything you need is in there. --- separates slides. Front matter is written once. A comment starting with _ is a scoped directive — it applies to that one slide only, so _paginate: false de-numbers the title page and _class: invert gives you a dark slide for the moment you want the room to shut up. A comment without the underscore is a speaker note; --notes dumps them all to a text file for your presenter monitor.

Then --pdf when you need something to email.

The loop that actually earns the setup

This is the part worth the install:

marp -w deck.md -o deck.html

Watch mode injects a WebSocket into the generated HTML — I checked, it’s __marpCliWatchWS in there — so the browser reloads itself the instant the markdown changes. Put the browser on one half of the screen and Claude on the other, and you watch the deck assemble while you argue with it about slide four. No export step, no refresh, no clicking.

Three things that will bite you

Always pass --no-stdin. Run marp-cli from a script, a Makefile or an agent and it blocks forever waiting on stdin. My first automated run hung for three minutes in total silence before finally admitting finished reading. (Pass --no-stdin option if it was not intended). Nothing about the symptom points at the cause.

Point CHROME_PATH at your browser. If the only Chromium you have is the snap, PDF export needs CHROME_PATH=/snap/bin/chromium to find it. HTML export doesn’t care.

Don’t keep local images under /tmp. Snap-confined Chromium can’t see the host’s /tmp, so ![bg right:40%](logo.png) from a scratch directory silently degrades to The local file is missing and will be ignored. The identical deck in ~/ renders the image fine. Keep decks in a real project directory.

Tell Claude the rules once

Drop this in CLAUDE.md next to the deck and stop repeating yourself:

Decks are `deck.md`, rendered by marp-cli. When editing one:
- One idea per slide. Max 5 bullets, max ~10 words each.
- Slides split by `---`. Never write raw HTML to fake a layout.
- Stick to front matter + `---` + headings/bullets/code fences.
- Prose belongs in speaker notes, not on the slide.
- Always invoke marp with `--no-stdin`.

The bullet-count line is doing more work than it looks. Left alone, a model writes documents — full sentences, eight bullets deep — and markdown will happily render a wall of text at 12pt. The format stops the layout disasters; you still have to stop the essays.

Why this works

It isn’t the themes, and it isn’t the PDF export. It’s that markdown makes the bad deck unwriteable. Claude can’t hand me a fourteen-bullet slide with a hand-rolled two-column div and a font size chosen by vibes, because there is no syntax in which to say it. The vocabulary is headings, bullets, code and a slide break, so the only variable left is whether the content is any good — which is the only thing I wanted to review in the first place.

The format is the prompt.

Written on August 20, 2026