Create terminal-based presentation slides using presenterm's markdown format with themes, diagrams, code highlighting, and more
92
90%
Does it follow best practices?
Impact
97%
2.15xAverage score across 5 eval scenarios
Passed
No known issues
Create presentations in presenterm's markdown format — a tool that renders markdown files as rich slides directly in the terminal.
Output: A single .md file that the user runs with presenterm slides.md.
For detailed references, see:
---
title: "My Presentation Title"
sub_title: Optional subtitle
author: Author Name
theme:
name: dark
---Generates an introduction slide automatically. All fields optional.
For multiple authors use authors: array.
<!-- end_slide -->Not ---. Use <!-- end_slide --> consistently.
My Slide Title
===Setext headers render centered with special styling. ATX headers (# H1) render
as normal headings within a slide — NOT as slide titles.
| Command | Purpose |
|---|---|
<!-- end_slide --> | End current slide, start next |
<!-- pause --> | Content below appears on next keypress |
<!-- jump_to_middle --> | Vertically center subsequent content |
<!-- new_line --> / <!-- new_lines: N --> | Insert blank lines |
<!-- column_layout: [3, 2] --> | Define column layout with proportional widths |
<!-- column: 0 --> | Switch to column (zero-indexed) |
<!-- reset_layout --> | End column layout |
<!-- incremental_lists: true --> | Each bullet on separate keypress |
<!-- alignment: center --> | Align text: left, center, right |
<!-- speaker_note: Your note --> | Speaker note (visible in presenter mode) — no colons or em dashes inside the note text |
<!-- font_size: 2 --> | Font size 1-7 (kitty terminal only) |
<!-- no_footer --> | Hide footer on this slide |
<!-- include: other.md --> | Include content from another file |
User comments (ignored): <!-- // note --> or <!-- comment: note -->
Speaker note text is parsed as a YAML scalar, so any : (colon) or — (em dash) inside the note breaks the entire deck with mapping values are not allowed here. This is the most common way to ship a broken presenterm file. Use - (hyphen) for both.
WRONG — fails to parse, kills the whole deck:
<!-- speaker_note: Walk through producer config: bootstrap.servers, key.serializer -->
<!-- speaker_note: Pause here — let the audience absorb the diagram -->
<!-- speaker_note: Demo URL: localhost:9092 -->RIGHT:
<!-- speaker_note: Walk through producer config - bootstrap.servers, key.serializer -->
<!-- speaker_note: Pause here - let the audience absorb the diagram -->
<!-- speaker_note: Demo URL is localhost on port 9092 -->Before writing any speaker note, mentally scan it for : and —. If either appears (including inside URLs, ratios, time codes, parentheticals, or quoted phrases), rewrite with hyphens or rephrase to remove them entirely.
Mark optional content with the literal token CUTTABLE at the start of the note so you can skip slides under time pressure:
<!-- speaker_note: CUTTABLE - the migration timeline slide if running over --><!-- column_layout: [2, 1] -->
<!-- column: 0 -->
Left content (2/3 width)
<!-- column: 1 -->
Right content (1/3 width)
<!-- reset_layout -->Sizing: [2, 1] = total 3 units. Use [1, 1] for halves, [1, 3, 1] to center.
Standard fenced code blocks with language identifiers. Key features:
Selective highlighting: ```rust {1,3,5-7} highlights specific lines.
Dynamic highlighting: ```rust {1,3|5-7|all} — each group on successive keypress.
Attributes: +exec (executable), +line_numbers, +no_background, +render (diagrams/LaTeX), +exec_replace, +acquire_terminal.
Hidden lines: Prefix with # to hide from display but include in execution.
External snippets:
```file +exec +line_numbers
path: snippet.rs
language: rust
start_line: 5
end_line: 10
```Static images: 
Image sizing:  or 
IMPORTANT: Image filenames with spaces will fail to render. Always use hyphens:
my-image.png not my image.png.
presenterm has no background image feature. To use an image as a full title slide:
title, sub_title, and author from frontmatter (prevents auto-generated title slide)<!-- no_footer -->:---
theme:
name: catppuccin-mocha
override:
footer:
style: template
left: "@gamussa"
right: "{current_slide} / {total_slides}"
height: 2
---
<!-- no_footer -->

<!-- end_slide -->
First Content Slide
===
...The image renders at full width. Terminal padding means it won't be truly edge-to-edge, but it's the closest presenterm supports.
Diagrams: Use +render attribute on fenced code blocks.
mermaid +render) — flowcharts, sequences, ER, state, class, Gantt, pie, mindmap, xychart-beta (bars/lines). See references/mermaid.md.d2 +render) — architecture, styled diagrams, SQL tables, containers. See references/d2.md.latex +render / typst +render) — inline formulas.Use D2 for polished architecture diagrams. Use Mermaid for variety (Gantt, mindmaps, pie charts).
Columns are for side-by-side prose (description vs description, code vs explanation). They are not a way to display quantitative data.
If you have numeric or comparative data — latency benchmarks, throughput numbers, percentages, before/after counts, prices, scores — use a diagram, not columns:
| Data shape | Reach for |
|---|---|
| Comparing magnitudes (e.g. p99 latency across 3 databases) | mermaid +render with xychart-beta (bars) or pie |
| Showing share of total | mermaid +render with pie |
| Showing change over time | mermaid +render with xychart-beta (line) |
| Comparing systems by attributes (prose tradeoffs) | column layout |
| Architecture / data flow | d2 +render |
A 3-bar chart beats a 3-column wall of numbers every time. The column layout is for words placed next to other words, not for numbers placed next to other numbers.
For architecture and data-flow diagrams, default to D2. The Mermaid options above (xychart-beta, pie, etc.) are for charts and quantitative shapes — they are not the right tool for system topology, request flow, or component relationships. If you're drawing boxes connected by arrows to represent services, databases, or queues, use d2 +render, not Mermaid.
Diagram sizing: Keep 5-10 nodes max. Use LR for wide, TD for tall. Match dark themes.
Set in frontmatter: theme: { name: catppuccin-mocha }.
Safe defaults: catppuccin-mocha, dark. Use terminal-dark to inherit terminal colors.
Available: catppuccin-{latte,frappe,macchiato,mocha}, dark, light, gruvbox-dark,
terminal-{dark,light}, tokyonight-{storm,moon,day,night}.
For overrides, footers, palettes, and colored text see references/themes.md.
Manual: <!-- pause --> between content blocks.
Lists: <!-- incremental_lists: true --> before bullet list.
presenterm slides.md # Development mode (hot reload)
presenterm -p slides.md # Presentation mode
presenterm -x slides.md # Enable code execution
presenterm -e slides.md # Export to PDF
presenterm -E slides.md # Export to HTML
presenterm -P slides.md # Publish speaker notesThe export.dimensions config controls font size in PDFs — fewer columns/rows = bigger fonts.
| Grid | Font Size | Use Case |
|---|---|---|
60x18 | Very large | Overflows on text-heavy slides — avoid |
80x24 | Large, readable | Recommended — clean PDFs with readable fonts |
100x28 | Default, small | presenterm default — too small for sharing |
Create a dedicated export config (e.g., .presenterm-export.yaml):
export:
dimensions:
columns: 80
rows: 24
snippet:
render:
threads: 4
mermaid:
scale: 2
d2:
scale: 2Export with:
presenterm -c .presenterm-export.yaml -e slides.md -o slides.pdfAdd a reusable make pdf target with an overridable SLIDE variable:
SLIDE ?= slides.md
EXPORT_CONFIG := .presenterm-export.yaml
pdf:
> presenterm -c $(EXPORT_CONFIG) -e $(SLIDE) -o $(SLIDE:.md=.pdf)Usage:
make pdf # Default slide
make pdf SLIDE=drafts/my-talk.md # Any slidepresenterm has no native PPTX export. Convert via PDF as image-based slides:
# Requires: pip install python-pptx pdf2image (and poppler installed)
python3 << 'PYEOF'
from pdf2image import convert_from_path
from pptx import Presentation
from pptx.util import Inches
import tempfile, os
images = convert_from_path("slides.pdf", dpi=200)
prs = Presentation()
prs.slide_width = Inches(13.33)
prs.slide_height = Inches(7.5)
for img in images:
slide = prs.slides.add_slide(prs.slide_layouts[6])
tmp = tempfile.mktemp(suffix=".png")
img.save(tmp)
slide.shapes.add_picture(tmp, Inches(0), Inches(0),
prs.slide_width, prs.slide_height)
os.unlink(tmp)
prs.save("slides.pptx")
PYEOFThe result is image-based slides (not editable text). Keynote opens PPTX natively.
presenterm reads config.yaml from a platform-specific directory — not ~/.config on macOS:
| OS | Config directory |
|---|---|
| macOS | ~/Library/Application Support/presenterm/ |
| Linux | ~/.config/presenterm/ (or $XDG_CONFIG_HOME/presenterm/) |
If mermaid +render or d2 +render blocks show as plain code, the renderers are likely installed but the config is missing or in the wrong location. Create config.yaml in the correct directory:
snippet:
render:
threads: 4
mermaid:
scale: 2
d2:
scale: 2Requires mmdc (Mermaid CLI) and d2 installed and on $PATH. Install with:
npm install -g @mermaid-js/mermaid-cli # mmdc
brew install d2 # d2options:
end_slide_shorthand: true # Allow --- as slide separator
implicit_slide_ends: true # Auto-end slides on headings
command_prefix: "cmd:" # Require prefix for commands---
title: "**Streaming** Data with _Kafka_"
sub_title: A quick introduction
author: Viktor Gamov
theme:
name: catppuccin-mocha
override:
footer:
style: template
right: "{current_slide} / {total_slides}"
height: 2
---
Why Streaming?
===
Every business is becoming a real-time business.
<!-- pause -->
Batch processing means **hours of delay**.
Event streaming means **milliseconds**.
<!-- end_slide -->
What is Kafka?
===
<!-- incremental_lists: true -->
* **Producers** send events to topics
* **Consumers** read events from topics
* **Brokers** store and replicate the data
<!-- end_slide -->
<!-- jump_to_middle -->
Demo Time
===
<!-- end_slide -->
Producer Code
===
```java {1-3|5-8|10-13|all}
import org.apache.kafka.clients.producer.*;
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
Producer<String, String> producer =
new KafkaProducer<>(props);
producer.send(new ProducerRecord<>(
"my-topic", "key", "value"
));
producer.close();Thank you! Questions?
---
## Writing Guidelines
1. **Start with frontmatter** — Always include title, author, and theme.
2. **Use `<!-- end_slide -->`** consistently — Don't mix separators.
3. **Setext headers for titles** — `Title\n===` not `# Title`.
4. **Narrative Arc** — Setup, confrontation, resolution. See [design patterns](references/design-patterns.md).
5. **Progressive reveal** — Use pauses, incremental lists, dynamic highlighting.
6. **Column layouts** for side-by-side content.
7. **`<!-- jump_to_middle -->`** for section breaks.
8. **Speaker notes** — Script demos and mark cuttable sections with `CUTTABLE`. **Scan every note for `:` and `—` before saving** — both break YAML parsing and kill the whole deck. See the Speaker Notes section above.
9. **One idea per slide** — Avoid bullet walls.
10. **Dynamic code highlighting** — `{1-3|5-8|all}` to walk through code.
11. **Diagrams over text — and diagrams over columns for numbers.** Quantitative data (benchmarks, percentages, magnitudes) goes in a `pie` or `xychart-beta`, never a column wall. Mermaid for flowcharts, D2 for architecture.
12. **Match theme colors** in diagrams.
13. **Save as `.md`** — User runs with `presenterm filename.md`.