Using the command line
The lattice command turns a Markdown deck into a PDF, a PowerPoint, images,
or a web page. This page is a set of recipes: find the task, copy the command,
change the file names. Every flag is listed in the
CLI reference.
The one rule to learn first: the output file’s extension picks the format.
npx lattice deck.md deck.pdfChange deck.pdf to deck.pptx and you get a PowerPoint instead. Everything
else is an option you add after the two file names.
Before you start
Section titled “Before you start”You need Node 22.13 or newer. Install Lattice into your project:
npm install @laticent/latticeOr clone the repository, which also gives you the helper scripts in Check a deck before you render it:
git clone https://github.com/Laticent/lattice.gitcd latticenpm installEither way, npm install downloads the headless Chromium that Lattice renders
with. The render itself needs no network and no account.
Every command on this page runs the copy installed in your project, which is
what npx lattice finds. Outside a project, use npx @laticent/lattice (or
install it globally with npm install -g @laticent/lattice and type lattice):
the unscoped lattice package on npm belongs to someone else, so a bare
npx lattice in an empty folder downloads that package instead.
To check it works, ask for the version:
npx lattice --versionnpx lattice --help prints one screen: the output formats and the options most
decks need. npx lattice --help all prints every option, the same list as the
CLI reference.
Turn on Tab completion
Section titled “Turn on Tab completion”With lattice on your PATH (a global install, or npx lattice replaced by a
shell alias), one line makes Tab complete subcommands, every option, the values
an option takes (palettes, --size canvases, --paper sheets, plugin names)
and the packages you installed:
| Shell | Add this line |
|---|---|
| bash (3.2 or newer) | eval "$(lattice completion bash)" in ~/.bashrc |
| zsh | source <(lattice completion zsh) in ~/.zshrc, after compinit |
| fish | run mkdir -p ~/.config/fish/completions && lattice completion fish > ~/.config/fish/completions/lattice.fish once |
| PowerShell | lattice completion powershell | Out-String | Invoke-Expression in your $PROFILE |
lattice completion <shell> only prints a script; it never edits a file. Paths
still complete as before, narrowed to the files that fit: .md for the deck,
the output formats for the output. Open a new shell after you reinstall
Lattice somewhere else, so the script finds the new copy.
Everyday tasks
Section titled “Everyday tasks”Render a PDF
Section titled “Render a PDF”npx lattice deck.md deck.pdfThe PDF keeps real text: you can select it, search it, and copy from it. The first render of a session takes longer while Chromium starts.
Every format except .html also writes an HTML copy beside the output
(deck.html here). You can open it in a browser or delete it.
Render on another canvas
Section titled “Render on another canvas”A deck’s size: sets its canvas. To render one deck on a different canvas
without editing it, pass --size. Try square, portrait, story,
mobile-landscape or 4K:
npx lattice deck.md square.pdf --size squarenpx lattice deck.md story.png --size storyPick a palette
Section titled “Pick a palette”The deck’s front matter usually names its palette (theme: cuoio). To render
the same deck in a different palette without editing it, name one on the
command line:
npx lattice deck.md deck.pdf cuoioThe same thing with a named flag, which reads better in scripts:
npx lattice deck.md -o deck.pdf -p cuoioTo see every palette you can name:
npx lattice packages list --type themeA palette on the command line beats the LATTICE_PALETTE environment variable,
which beats the deck’s theme:. The default is indaco.
Make a PowerPoint
Section titled “Make a PowerPoint”npx lattice deck.md deck.pptxEach slide becomes one full-bleed image, so the PowerPoint looks exactly like the PDF. The trade-off is that the text is not editable in PowerPoint.
To edit the text, add --editable. Every paragraph becomes a real text box in
the deck’s font, over a picture of the slide with the text removed:
npx lattice deck.md deck.pptx --editableCharts, diagrams and equations stay part of the picture. The .pptx carries the
deck’s fonts, but an app that ignores embedded fonts (LibreOffice, Keynote, Google
Slides) shows the deck’s fonts only where they are installed. Without them every
word is measured in a substitute font, so lines run wider or narrower than the
slide’s picture, and a small picture set inside a sentence (an icon, a spark or an
avatar) can sit off its words. Install the deck’s fonts, or use the picture
.pptx or the PDF when the layout must hold exactly.
Make a LibreOffice deck
Section titled “Make a LibreOffice deck”npx lattice deck.md deck.odp # one picture per slidenpx lattice deck.md deck.odp --editable # real text boxes, fonts embeddedLattice writes the .odp itself, so you do not need LibreOffice to export. The
editable .odp carries the deck’s fonts, so it looks the same on any machine
with LibreOffice Impress.
Export slides as images
Section titled “Export slides as images”One PNG per slide, numbered deck.001.png, deck.002.png, and so on:
npx lattice deck.md deck.pngFor JPEG or WebP, or for thumbnails and chart files in one download, ask for a
.zip image set:
npx lattice deck.md slides.zipnpx lattice deck.md slides.zip --image-format webp --image-size 1xnpx lattice deck.md slides.zip --image-format jpeg --image-quality 80 --no-svgBy default the zip holds full-size PNGs, a thumbnails/ folder, and an
assets/ folder with each chart and diagram as its own SVG. There is no loose
.jpg or .webp output. Asking for deck.webp stops with an error that
names the .zip command above.
Print a handout
Section titled “Print a handout”Print mode draws the deck in black ink on white, and gives chart series hatching and dot patterns so they still read in grayscale:
npx lattice deck.md handout.pdf --printTo fit each slide onto office paper with a safe margin, add --paper. Use
auto to let Lattice pick the sheet, or name letter, legal or a4:
npx lattice deck.md handout.pdf --print --paper a4npx lattice deck.md handout.pdf --paper letter --orientation portrait--paper pages are images, so the text is no longer selectable.
Present from a PDF
Section titled “Present from a PDF”--present marks the PDF to open full screen in Acrobat and most desktop
viewers, with a soft fade between slides. You still advance the slides
yourself:
npx lattice deck.md talk.pdf --presentSharing a deck
Section titled “Sharing a deck”Send one file that plays anywhere
Section titled “Send one file that plays anywhere”The player is a single .html file with everything inside it. It works
offline, opens with a double-click, and offers three views: Present, Read
Slides and Read Article.
npx lattice deck.md deck.html --playerSend the player, not a plain .html. A plain .html (and the one written
beside every PDF) keeps any <script> or onerror= in your deck live, so it
runs in whoever opens it, and the CLI warns when a deck has any. The player
drops both.
Your speaker notes travel with the player so that you can present from it. If you are sending it to someone else, strip them first:
npx lattice deck.md deck.html --player --strip-notesThe player shows a deck’s motion when the deck turns it on. To ship still
slides in a file you forward, add --no-player-motion.
To open the player in a set mode, whatever the deck says, add
--player-mode light, dark or system. To give it a spoken voice-over,
add --narrate. It uses the same optional voice install as
a narrated video:
npx lattice deck.md deck.html --player --player-mode darknpx lattice deck.md deck.html --player --narrate --strip-notesA player loads nothing from the web, so a picture from a web address ships as
a placeholder. To let the reader choose, name the sites with
--player-consent. The player then shows a banner that names them, and loads
their pictures only when the reader presses Load pictures. It asks again
each time the file opens. With JavaScript off nothing loads, and the banner
lists each picture as a link instead:
npx lattice deck.md deck.html --player --player-consent https://images.example.comOnly pictures placed with  are offered. A web background ()
stays a placeholder, and the run says so. The site must allow pages to read its
pictures from another origin (it sends an Access-Control-Allow-Origin
header). If it does not, the banner says the pictures could not be loaded and
lists their links. To put the pictures in the file itself instead, so the reader
is never asked, export with --allow-remote: the player then embeds every web
picture it can fetch.
Publish the deck as an article
Section titled “Publish the deck as an article”--read writes the deck as a web article instead of a slide stack: real
headings, paragraphs and lists, with charts and tables as figures. Reader
modes and “summarize this page” tools can read it.
npx lattice deck.md article.html --readMake the deck readable on a phone
Section titled “Make the deck readable on a phone”--fluid writes a viewer where each slide fills the screen and reflows to
portrait on a phone. Readers swipe between slides.
npx lattice deck.md deck.html --fluidIf you pass both --fluid and --player, --player wins.
Keep the source inside the PDF
Section titled “Keep the source inside the PDF”--embed-source attaches the deck’s Markdown to the PDF. Anyone with the PDF
can re-render the deck. The source includes your speaker notes, so combine it
with --strip-notes if the notes are private:
npx lattice deck.md deck.pdf --embed-sourcenpx lattice deck.md deck.pdf --embed-source --strip-notesSend a PDF or PowerPoint someone can edit
Section titled “Send a PDF or PowerPoint someone can edit”--reopenable puts the whole deck inside a .pdf or .pptx as a .lattice
project. Whoever you send it to opens the Studio, picks Import deck… in the
deck switcher, and gets the deck back to edit, exactly as you wrote it. It is
the same file the Studio’s “Re-openable in Lattice” switch writes, so the two
tools’ exports open the same way.
Review comments never go in. Hidden slides do, and so do speaker notes unless
you add --strip-notes. A theme or component you installed with
lattice packages add travels with the deck.
The deck travels as written, so its theme is the one in its front matter.
If you theme the export with -p instead, the PDF shows that theme but the
deck re-opens in its own, and the CLI warns you. Put theme: <name> in the
deck to make the theme travel.
npx lattice deck.md deck.pdf --reopenable --strip-notesnpx lattice deck.md deck.pptx --reopenableSpeaker notes and captions
Section titled “Speaker notes and captions”A plain HTML comment on a slide is that slide’s speaker note:
<!-- Open with the one number that matters. -->Notes ride inside every PDF, hidden. Three switches change that:
# Write the notes to deck.notes.txt as wellnpx lattice deck.md deck.pdf --notes
# Show a clickable note icon on every slide that has onenpx lattice deck.md deck.pdf --notes-icon
# Remove the notes from every copy: the PDF, the HTML and any embedded sourcenpx lattice deck.md deck.pdf --strip-notes--captions writes WebVTT read-along captions: one deck.vtt for the whole
deck, plus one deck.01.vtt per slide. Each slide reads its own content
unless you wrote a <!-- say: … --> for it.
npx lattice deck.md deck.pdf --captions--strip-say removes the spoken lines you wrote yourself (say:) and keeps
the generated narration. It also removes the retired caption: comments and
captions: map, including a comment that starts with Caption:, and says so
when it does. It does not touch any other speaker note, and --strip-notes
does not touch say: lines.
Check a deck before you render it
Section titled “Check a deck before you render it”These commands run from a clone of the repository.
Lint the deck
Section titled “Lint the deck”The linter finds authoring mistakes in about a second, with no browser. Each finding names the slide, says what will go wrong, and shows the fix.
npm run lint:deck -- deck.mdThe options, one per line:
npm run lint:deck -- 'decks/*.md' # every deck matching a glob (keep the quotes)npm run lint:deck -- --strict deck.md # warnings fail toonpm run lint:deck -- --fix deck.md # apply every automatic fix, in placenpm run lint:deck -- --json deck.md # machine-readable, for scripts and agentsThe linter exits with 1 when it finds an error (or any finding, with
--strict), so it can guard a CI job or a commit hook.
Start a slide from a skeleton
Section titled “Start a slide from a skeleton”new:slide prints a working example of any layout, ready to paste into a
deck:
node tools/new-slide.js --list # every layout, grouped by what it is fornode tools/new-slide.js kpi # a kpi slide, printed to the terminalThe skeleton starts at the layout comment, so add a --- line before it when
you append it to a deck:
{ printf '\n---\n\n'; node tools/new-slide.js kpi; } >> deck.mdFind overflow while you write
Section titled “Find overflow while you write”A slide with more content than fits is clipped, and the render prints an
OVERFLOW warning that names the page. To see the problem marked on the page
itself, render a proof with the author marker:
npx lattice deck.md proof.pdf --overflow-marker authorThe marker draws a red ring and an “Overflows” flag on each clipped slide.
The default, reader, draws a calm “Content clipped” tag instead, and off
draws nothing. The terminal warning prints at every level.
To make author your standing default, set LATTICE_OVERFLOW_MARKER=author
in your shell.
Advanced tasks
Section titled “Advanced tasks”Render every deck in a folder
Section titled “Render every deck in a folder”The command renders one deck at a time, so loop in the shell. -q keeps the
output to errors only, and Lattice creates the out/ folder if it is missing:
for f in decks/*.md; do npx lattice "$f" "out/$(basename "${f%.md}").pdf" -q || echo "FAILED: $f"doneRender one deck in several palettes
Section titled “Render one deck in several palettes”for p in indaco cuoio ardesia; do npx lattice deck.md "out/deck-$p.pdf" -p "$p" -qdoneUse a palette for a whole session
Section titled “Use a palette for a whole session”Set LATTICE_PALETTE once and every render in that shell uses it:
export LATTICE_PALETTE=cuoionpx lattice deck.md deck.pdfInstall a theme or component made in the Studio
Section titled “Install a theme or component made in the Studio”Themes, components, finishes and motion made in the Studio are packages. Install one once, and any deck can name it:
npx lattice packages check brand.zip # test the package without installing itnpx lattice packages add brand.zip # install itnpx lattice packages list # everything shipped and installedA theme named after a shipped one installs under a new name, and add tells
you which. A package that carries code asks you to approve it during add;
--trust approves it without asking. Replace an installed package with --replace, and remove one by
type and name:
npx lattice packages add brand.zip --replacenpx lattice packages remove theme/brandTo share a package, export it to a zip. This works for shipped packages too:
npx lattice packages export theme/brand -o brand.zipLattice ships a small motion library: npx lattice packages list --type motion
names the scenes. A deck never names a motion package. The Studio’s Library
lists the shipped scenes on its Motion tab, and Insert copies one into the deck,
so the deck still renders on a machine without it.
Packages live in ~/.lattice/packages. Set LATTICE_HOME to move the store,
or pass --packages <folder> to use a different store for one run, renders
included:
npx lattice deck.md deck.pdf --packages ./team-packagesApprove a component that runs code
Section titled “Approve a component that runs code”Some components carry code. Lattice runs that code only after you approve it, in a browser with no network. A deck that uses an unapproved component stops with the component’s name and the command to approve it:
npx lattice packages trust component/org-chart # show the code, then asknpx lattice packages trust component/org-chart --yesnpx lattice packages untrust component/org-chartIf the code changes, Lattice asks again. Run without a terminal, packages add
cannot ask, so it installs the package unapproved; approve it afterward with
trust.
When Chromium’s OS sandbox is off for that code, the approval prompt says so,
with the reason and the fix, and every render repeats that it is off. If
Lattice runs as root, as it often does in a container, the fix is to set
CHROME_PATH to a Chromium that an unprivileged user can run. On Ubuntu 23.10
and later, AppArmor stops the sandbox of a browser it has no profile for, such
as the Chrome that puppeteer downloads, even for an ordinary user. There the
fix is an AppArmor profile that lets the browser create user namespaces, or
setting the kernel.apparmor_restrict_unprivileged_userns sysctl to 0. Inside a
Docker container, Docker’s default seccomp profile is what blocks the sandbox,
whatever the host’s AppArmor says: start the container with a seccomp profile
that allows user namespaces.
To write a component that carries code, see Writing a code package.
Start a plugin
Section titled “Start a plugin”In a clone of the repository, packages new plugin scaffolds a plugin in
lib/plugins/<name>/: a manifest, a render script, styles, docs and test
fixtures. Then rebuild and run the plugin tests, as the command tells you:
npx lattice packages new plugin demo-plotnpm run buildnpm run test:pluginsTo write it somewhere else for review, add --dir. The plugin lands in
<dir>/<name>/:
npx lattice packages new plugin demo-plot --dir ./reviewRender a narrated video
Section titled “Render a narrated video”lattice video turns a deck into an MP4 with a spoken voice-over and a caption
track, plus a .vtt caption file beside it. It voices the deck with Kokoro, the
same on-device voice the Studio uses, so you need no account and no key.
The voice is an optional install. The first video also downloads the voice model once (about 80 MB):
npm i --no-save kokoro-js@1.2.1 @breezystack/lamejs@1.2.7Then render:
npx lattice video deck.md talk.mp4npx lattice video deck.md talk.mp4 --mode dark --fps 60npx lattice video deck.md phone.mp4 --size mobile-landscape # fills a phone held sidewaysnpx lattice video deck.md talk.mp4 --no-guide --no-captionsBy default the video follows the narration with the Guide, the focus and
gestures you see in Present mode. --no-guide leaves them out.
Already have a narrated export from the Studio (Share → Webpage, with
Include narration audio turned on)? Pass the .html instead, and nothing
is voiced again:
npx lattice video talk.html talk.mp4 --lead-in 2000 --outro 3000Video needs a Chrome that can encode H.264, such as Chrome for Testing. Point
CHROME_PATH at it if Lattice picks the wrong browser.
Format the code in a deck
Section titled “Format the code in a deck”lattice fmt pretty-prints a deck’s fenced code blocks in place, with
Prettier, ruff (Python) or sql-formatter (SQL), whichever reads the block’s
language:
npx lattice fmt deck.mdnpx lattice fmt deck.md --check # exit 1 if a block would change, for CIA block whose @code{…} record says format=off keeps its text, and
width=48 in a record sets that block’s line width. The formatters are
optional dependencies; if one is missing, lattice fmt names the package to
install.
Allow web images
Section titled “Allow web images”By default a render stays off the network. An image or font from a web address shows as a placeholder, and the run tells you how many it left out. To fetch them:
npx lattice deck.md deck.pdf --allow-remoteFetching an image tells its server that someone opened the deck. Local files and the bundled fonts, diagrams and math always work offline.
When a PDF viewer shows something wrong
Section titled “When a PDF viewer shows something wrong”Lattice writes the PDF itself: one photo of each slide’s background, with the words, charts and links drawn on top as real text and shapes. If a viewer still gets something wrong, try these one at a time:
# One full-page image per slide. The most compatible, but the text is not selectable.npx lattice deck.md deck.pdf --raster
# Print with Chrome's own PDF printer instead of Lattice's writernpx lattice deck.md deck.pdf --chrome-pdf
# Keep SVG images as vectors (they are converted to 2x PNG by default)npx lattice deck.md deck.pdf --keep-vector-imagesA very large deck times out
Section titled “A very large deck times out”Each render step has a 90-second watchdog. On slow hardware with a very large deck, raise it:
LATTICE_RENDER_WATCHDOG_MS=300000 npx lattice deck.md deck.pdfHand a deck to someone who uses Marp
Section titled “Hand a deck to someone who uses Marp”From a clone, export:marp writes a self-contained bundle that renders with
the recipient’s own marp-cli. It bakes the page splits into the Markdown and
includes the themes, fonts and assets:
npm run export:marp -- deck.md deck-marp.zipnpm run export:marp -- deck.md deck-marp.zip cuoioThe export does not render, so it cannot find overflow. Render a PDF first to check.
When something goes wrong
Section titled “When something goes wrong”| What you see | What to do |
|---|---|
error: palette not found: brand | Check the spelling against npx lattice packages list --type theme, or install the theme with packages add |
error: unsupported output extension | Use .pdf, .pptx, .odp, .png, .zip or .html. For JPEG or WebP, use a .zip with --image-format |
error: unknown option | Check the flag in the CLI reference. Flags that take a value accept --flag value and --flag=value |
| The render says it cannot find a browser | Set CHROME_PATH to a Chrome or Chromium on your machine |
| A web image shows as a placeholder | Add --allow-remote |
OVERFLOW — 1 slide exceed the frame | The slide holds more than fits. Render with --overflow-marker author to see it, then cut or split the content |
What to read next
Section titled “What to read next”- CLI reference — every flag, variable and exit code.
- Deck settings — the front-matter keys, including
player:,read:,fluid:andpresent:, which do the same job as the flags on this page. - Themes & palettes — the palettes you can name with
-p.