My Emacs Presentation Stack
This is my (last minute) submission for Emacs Carnival 2025-11 — An Ode to Org Babel.
When I need to do presentations at work, I naturally reach for Emacs. My presentations include diagrams, code snippets and running small shell blocks. I found that Org Mode with Babel is the perfect fit. In this post, I'll describe my presentation stack based on Org mode.
Base — Org mode
I like to plan my presentations using outlines. It is easy to structure the presentation into sections and sub-sections. Outlines also let me easily reorganize parts of the presentation. I don't think outlines are standard features in common presentation software. But Emacs comes with a great outline package, Org Mode. This is a subset of my configuration to make the presentations pretty.
(use-package org
:hook
;; Lets me use serif for text and monospace for code snippets.
(org-mode . variable-pitch-mode)
:custom
(org-hide-emphasis-markers t)
(org-pretty-entities t)
(org-ellipsis "…")
(org-src-fontify-natively t)
(org-src-tab-acts-natively t)
(org-src-preserve-indentation t))
I start with an empty Org mode file and write the outline for my presentation using Org mode headings. Each heading (and sub-heading) becomes a slide title. I use the Logos package to present the Org file as slides. Logos uses Outline headings as page endings, and uses narrowing to only show the content under that heading. Logos in turn integrates with the Olivetti package to center the content on the screen. This is the glue code to make it all work together.
(use-package logos
:ensure t
:init
;; Always expand the current Heading.
(defun arg-reveal-entry ()
"Reveal Org or Outline entry."
(cond
((and (eq major-mode 'org-mode)
(org-at-heading-p))
(org-show-entry))
((or (eq major-mode 'outline-mode)
(bound-and-true-p outline-minor-mode))
(outline-show-entry))))
:custom
(logos-outlines-are-pages t)
(logos-olivetti t)
:hook
(logos-page-motion . arg-reveal-entry)
:bind
;; Binds Logos functions for Page motions.
(:map org-mode-map
([remap forward-page] . logos-forward-page-dwim)
([remap backward-page] . logos-backward-page-dwim))
:config
;; Disable distracting elements during Focus mode.
(setq-default logos-hide-mode-line t
logos-hide-header-line t))
(use-package olivetti
:ensure t
:config
;; Fancy mode uses margins and fringes to create vertical
;; blocks on either sides of the content.
(setq olivetti-style 'fancy)
(setq-default olivetti-body-width 0.4)
(add-hook 'olivetti-mode-on-hook
(lambda ()
;; Disable Line numbers.
(display-line-numbers-mode -1)
;; Disable Buffer boundaries.
(setq-local indicate-buffer-boundaries nil)))
(add-hook 'olivetti-mode-off-hook
(lambda ()
;; Enable Line numbers.
(display-line-numbers-mode 1)
;; Restores Buffer boundaries.
(setq-local indicate-buffer-boundaries 'left))))
I have the arg-start-presentation and arg-stop-presentation commands to toggle the presentation mode. In presentation mode, logos-focus-mode is toggled with the current heading expanded. It also minimizes other elements of the Emacs frame like Tab-bar, and Menu-bar. To move between the slides, I use the Emacs page motion keybindings: Forward C-x ], and Backward C-x [.
(defun arg-start-presentation ()
"Start Presentation - Configures Frame, Style, etc."
(interactive)
(when (eq major-mode 'org-mode)
(setq-local arg-presentation t)
(logos-focus-mode 1)
(logos-narrow-dwim)
(org-fold-show-entry)
(toggle-frame-tab-bar (selected-frame))
(menu-bar-mode -1)))
(defun arg-stop-presentation ()
"Stop Presentation - Reverts Frame, Style, etc."
(interactive)
(cond ((not (eq major-mode 'org-mode))
(error "This command only works in Org buffers."))
((not arg-presentation)
(error "Not presenting right now."))
(t (progn
(logos-focus-mode -1)
(widen)
(toggle-frame-tab-bar (selected-frame))
(setq-local arg-presentation nil)
(menu-bar-mode +1)))))
This setup is inspired by System Crafter's Presentation style.
Magic — Org Babel
Org mode comes with built-in support for literate programming, Org Babel. If you're familiar with Jupyter Notebooks, Org Babel is a bit like that but in my opinion, Babel is more powerful and extensible. It supports most common languages out of the box such as Python, shell, and SQL, and it can be extended to work with almost any language using simple Emacs lisp functions. It also supports using multiple languages in the same file.
Diagrams
To add diagrams to my presentations, I like to use Pikchr. It is a special purpose markup language for diagrams. Org mode allows me to define diagrams in-line right next to the slide content using code blocks.
Using Org Babel, I can compile the diagram using C-c C-c. The generated SVG is placed directly in the buffer using the RESULTS block. Org mode can optionally render the SVG in-line as well using C-c C-x C-v or M-x org-toggle-inline-images. Updating the diagrams is also straightforward: I can edit the code block and recompile the diagram to update the SVG. There is no hassle of exporting and importing the diagrams repeatedly.
#+begin_src pikchr :file hello.svg
box "Hello!"
#+end_src
#+RESULTS:
[[file:hello.svg]]
Shell Execution
If I have to present some shell commands, I add the Shell code blocks. Org Babel supports tons of options for shell. Some options that I use are:
:results– How the result is parsed. If the output is tabular, Org mode can display it as a table, otherwise verbatim.:async– If the command takes time, run it asynchronously without blocking Emacs.:dir– Sets the default directory for the Shell block.
For live demos, I add the shell blocks directly in my slide content. During the presentation, I can simply execute the block using C-c C-c. This helps me focus on my content, saves time and I can avoid embarrassing typos.
In case of pre-recorded commands, I execute the commands beforehand. Similar to diagrams, Org mode places the output of the command in the RESULTS block. This is convenient when the commands require external setup, or take longer to run. Here is an example from my last presentation.
#+begin_src shell :results output
docker images --format "{{.Size}}\t{{.Repository}}"
#+end_src
#+RESULTS:
: 8.31MB alpine
: 120MB debian
: 2.33GB ros
Version Control
Because Org mode uses plain text format, I can version control my presentations. It is one of those features that you'll appreciate when you use it. I put all my presentations along with the SVG artifacts in a single Git repository. A nice little bonus is that Github and Forgejo can also render Org markup. So I have a webpage complete with table of contents for my slides for free!