Narrated-video editor for PDF slide decks with a narration sidecar
Project description
Write, preview, and render narration for a finished slide PDF — keeping the spoken words in a plain-text sidecar file next to the deck.
slideSonnet inverts the usual flow. Your PDF is the frozen artifact; the narration lives in a human-readable, git-diffable file keyed to slides by stable ids. Edit a line, hit re-render, and only that line is re-synthesized — no recompiling slides, no re-recording a microphone. A local GUI gives you subtitle-style editing synced to each slide; an LLM can write the first draft; the CLI scripts the whole pipeline.
v1 is a rewrite. Earlier 0.x releases compiled MARP/Beamer source into video with narration embedded inline (
<!-- say: -->,\say{}). v1 works from a PDF + narration sidecar instead. SeeCHANGELOG.md.
How it works
deck.pdf ──(invisible \ssid markers)──┐
├──► slidesonnet ──► deck.mp4 + deck.srt
deck.narration ──(@slide-id blocks)────┘ (preview in the GUI)
- Mark every slide in your Beamer source with a stable id (
\ssid{...}), compile to PDF. - Scaffold a sidecar from the PDF's ids (
slidesonnet init). - Write narration — by hand, by an LLM, or in the GUI (
slidesonnet edit). - Render a narrated (or silent) MP4 with subtitles (
slidesonnet export).
Installation
External dependencies
| Tool | Required? | What it does | Install |
|---|---|---|---|
| ffmpeg / ffprobe | Yes | Audio + video composition | sudo apt install ffmpeg |
| pdftoppm | Yes | Rasterize PDF pages to images | sudo apt install poppler-utils |
| latexmk + pdflatex | To compile your deck | Build the Beamer PDF (your job, not the tool's) | sudo apt install latexmk texlive-latex-base |
PyMuPDF (PDF id extraction) and NiceGUI (the editor) install as Python
dependencies. After installing, run slidesonnet doctor.
Install for use
Install slideSonnet as an isolated, global CLI — its own virtualenv, separate from your system Python and any source checkout:
uv tool install "slidesonnet[kokoro]" # or: pipx install "slidesonnet[kokoro]"
The [kokoro] extra adds Kokoro (82M,
Apache-2.0) for free, natural-sounding local speech (~2x real-time on CPU;
the model downloads on first use). Then run slidesonnet doctor to confirm the
external tools above are visible.
Upgrade with uv tool upgrade slidesonnet; remove with uv tool uninstall slidesonnet. To hack on slideSonnet itself instead, see
Development for the editable install.
Quick start
# 1. Drop the LaTeX macro next to your Beamer source and \usepackage it
slidesonnet sty # writes slidesonnet.sty
# 2. In your .tex: \usepackage{slidesonnet} and \ssid{...} on every frame,
# then compile however you like:
latexmk -pdf deck.tex
# 3. Scaffold the narration sidecar from the PDF's slide-ids
slidesonnet init deck.pdf # writes deck.narration
# 4. Write narration (edit deck.narration, or open the editor)
slidesonnet edit deck.pdf
# 5. Render — narrated MP4 + subtitles
slidesonnet export deck.pdf -o deck.mp4 --engine kokoro
Marking slides — the \ssid macro
slidesonnet sty writes slidesonnet.sty. In your Beamer preamble add
\usepackage{slidesonnet}, then give each emitted page an id:
\begin{frame}
\ssid<1>{euler-setup} % id for overlay step 1
\ssid<2>{euler-trick} % id for overlay step 2 (ranges work: \ssid<2-3>{...})
\only<1->{...}\onslide<2->{...}
\end{frame}
\begin{frame}
\ssid{intro-title} % non-overlay frame: one id for its single page
...
\end{frame}
The id is stamped as invisible text (PDF rendering mode 3 — like an OCR
layer): never shown, on any background, but reliably recovered from the text
layer. Any page you forget to name gets a positional auto-… default and a
warning, so it gets a real name.
The narration sidecar
An indented, line-oriented, git-diffable file (deck.narration). Each slide is
an @id block of one or more utterance: blocks and pause: lines:
# a comment
@intro-title
utterance:
text: Welcome to the course on the Basel problem.
pause: 1.5
utterance:
text: Today we'll see how Euler summed the reciprocals of the squares.
@intro-overview
pause: 3 # silent slide — held 3s while they read
@euler-trick
utterance:
voice: bernoulli # optional per-utterance voice
pace: slow # slow | normal | fast
text: Watch the denominators carefully. This is the trick.
@<slide-id>starts a block; eachutterance:carries the spokentext:plus optionalvoice:/pace:/direct:(a director's note engines ignore). A slide can mix voices — each utterance is its own synthesis call.pause: Nis an explicit silence in seconds: between utterances, as an end-of-slide hold, or alone as a silent slide.- A slide can bracket itself with
transition-in:/transition-out:lines (cut, the default, orcrossfade N).
The full authoring guide — marking overlay steps, the complete sidecar
grammar, and the optional slidesonnet.toml config — is in
docs/authoring.md.
The editor
slidesonnet edit deck.pdf opens a local NiceGUI app:
page through the deck, edit narration beside each slide, set voice/pace, generate
per-slide TTS, and preview the whole deck. The preview plays one pre-rendered
track with the pauses baked in and flips the slide image on cue — so the preview
is sample-accurate to the exported video. A diagnostics panel flags duplicate,
missing, orphan, or auto-… ids.
WSL note: to open the editor in your Windows browser instead of a Linux one:
slidesonnet edit deck.pdf --app— a chromeless app window via Edge/Chrome (auto-detected on the Windows side; Firefox has no app-window mode).- install
wslview(sudo apt install wslu) — used automatically — for a normal tab in your default browser. --browser CMDfor full control (e.g."cmd.exe /c start", or a browser path; a{url}token is substituted). Also settable viaSLIDESONNET_BROWSER.
CLI
slidesonnet sty [-o PATH] write the LaTeX macro
slidesonnet init deck.pdf [--merge|--force] scaffold a blank sidecar
slidesonnet check deck.pdf reconcile ids (exit≠0 on errors)
slidesonnet tts deck.pdf [--engine ...] [--id ID ...] synthesize into the cache
slidesonnet export deck.pdf -o OUT.mp4
[--engine kokoro] [--silent]
[--timing tts|estimate|fixed:N] [--wpm N]
[--subtitles srt|vtt|both|none] [--sub-granularity segment|slide]
slidesonnet subs deck.pdf -o OUT.srt [--format srt|vtt] [--timing ...]
slidesonnet edit deck.pdf launch the editor
slidesonnet clean deck.pdf [--keep nothing|api|current|exact]
slidesonnet doctor
Every operation is also a typed Python function in slidesonnet.api
(init_sidecar, synthesize_deck, export, write_subs, …) so an LLM/CI loop
can drive the pipeline without the GUI.
Timing & silent renders
--timing tts (default) uses real synthesized audio. --timing estimate
approximates from word count at --wpm for a fast rough cut with no TTS;
--timing fixed:N holds every page N seconds. --silent renders with no
narration (timing falls back to estimate) — pair it with subtitles for a
captioned silent cut.
Examples
examples/basel-problem/— a 22-page Euler proof with overlay steps and a second voice for the Bernoulli quote.examples/showcase/— a self-narrated tour that teaches the workflow as a two-voice dialog (written in this very format).
Build them from source with make basel / make showcase (Kokoro). Videos are
hosted as GitHub Release assets, not committed.
Development
make install # editable install with Kokoro + dev tools
make test-unit # fast unit tests (no external tools)
make test # full suite (needs ffmpeg, pdftoppm, kokoro)
make lint # ruff
make typecheck # mypy --strict
All source is fully typed (mypy --strict); paid cloud TTS is never exercised
in tests (it costs money) — Kokoro and mocks only.
License
slideSonnet's own code is MIT (see LICENSE).
It depends on PyMuPDF (for reading slide-ids), which is AGPL-3.0 (or a commercial license from Artifex). AGPL is copyleft: if you redistribute slideSonnet or run it as a network service, the AGPL terms apply to the combined work. For local use or a normal open-source install this is a non-issue — it only matters if you want to build a closed-source product on top of it.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file slidesonnet-1.0.0a1.tar.gz.
File metadata
- Download URL: slidesonnet-1.0.0a1.tar.gz
- Upload date:
- Size: 148.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
23f7b7e0c60e7eda7669024460516c5e55a6c964ca4be21e82d485d4ec428617
|
|
| MD5 |
f683866df7d75b09699265e068e771f2
|
|
| BLAKE2b-256 |
de87750fd049bb4e6d9570217e82bdc6c9922f7397629cfd9605fcf9692aae52
|
File details
Details for the file slidesonnet-1.0.0a1-py3-none-any.whl.
File metadata
- Download URL: slidesonnet-1.0.0a1-py3-none-any.whl
- Upload date:
- Size: 96.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a919b5dd73dde9de8d5ad1ab7c8af48785ed978ac92ab1091ac7e3443a03ad34
|
|
| MD5 |
6713aa3385808b8d23d8ba70716c5584
|
|
| BLAKE2b-256 |
59dba59213fca0f43536e2da2d9b92e991b96ca756e1c20758ddf4dd2c8e7467
|