Creating and Publishing Presentations with Org-reveal

For several years, I’ve been using Org-mode to compose slides for my lectures. This method is great, because I get to work in plain-text and focus on the content of my lectures rather than animations; but it’s meant that when I want to share my presentations with others, there’s a certain amount of work involved as I move from a local copy on my computer to a web-based version. (This has largely been an issue because I sometimes compose my lectures sitting in a café with lousy Internet, and I sometimes give my lectures in a horrible classroom at U of T with terrible Internet reception.) I’ve now largely solved this problem, though there is hopefully an improvement coming down the pipe which will make it even easier.

Org-mode has the capacity to export to a number of slide-like formats, including the LaTeX-based Beamer format, which also makes good PDF presentations, a couple of Emacs-based presentation tools, and a number of HTML5 formats. Since I teach about the web all the time, the HTML5 formats have always been the most appealing to me.

Org-Reveal Setup

I have used and still very much like deck.js (exporter here), but have recently switched to org-reveal, which I really like a lot. It’s not part of the official org distribution, so installation and setup are a little more involved, but not difficult. I just cloned the org-reveal and reveal.js repositories:

cd ~/src
git clone https://github.com/hakimel/reveal.js.git
git clone https://github.com/yjwen/org-reveal.git

and put this in my emacs-init.el:

;; org-reveal
(add-to-list 'load-path "~/src/org-reveal")
(require 'ox-reveal)
;; set local root
(setq org-reveal-root "file:///home/matt/src/reveal.js")

That’s all that’s needed to get export working! I find it’s really fast to prepare lectures.

Publishing

That’s great for giving lectures, and is all I really need at 9:55 when I’m trying to type my lecture and walk to class at the same time. But after lecture I want to put my slides somewhere my students can see them. Even if I wanted to, it would be impossible for me to post to Blackboard, which turns these files into garbage. What I want to do is publish them to the web; but I need to make sure that all the JS and CSS links are pointing to the web-based libraries and not my local copies, which of course no one but me can see. To do this I had to make one small change to org-reveal.el, which I have submitted as a pull request. This creates a new variable, org-reveal-extra-css, which I can refer to in my own functions.

Then I use org-mode’s fantastic built-in Publishing functions to push my slides to a public website. Publishing allows you to perform an export on many files, and customize the output in powerful ways that are mostly beyond me, actually. Still, I have a setup that I like a lot.

First, my org-publish-project-alist, which defines the publishing targets. Note especially the top part, which defines “meta-projects”: for instance, I can publish all the slides and source files for all my classes with one command, M-x org-publish-projects [RET] courses.

(setq org-publish-project-alist
      '(
        ("courses"
         :components ("dh" "rlg231"))
        ("rlg231"
         :components ("rlg231-lecture-slides" "rlg231-lecture-source"))
        ("dh"
         :components ("digital-history-lecture-slides" "digital-history-lecture-source"))

        ("rlg231-lecture-slides"
         :base-directory "~/RLG231/Lectures/"
         :base-extension "org"
         :publishing-directory "/ssh:matt@shimano:/var/www/sandbox/RLG231/Lectures/Slides"
         :recursive t
         :publishing-function mwp-org-reveal-publish-to-html
         :preparation-function nil 
         :completion-function nil
         :headline-levels 4             ; Just the default for this project.
         :exclude "LectureOutlines.org"
         :exclude-tags note noexport
         :auto-preamble t)

        ("rlg231-lecture-source"
         :base-directory "~/RLG231/Lectures/"
         :base-extension "org"
         :publishing-directory "/ssh:matt@shimano:/var/www/sandbox/RLG231/Lectures/Source"
         :recursive t
         :publishing-function org-org-publish-to-org
         :preparation-function nil
         :completion-function nil
         :headline-levels 4             ; Just the default for this project.
         :exclude "LecturePlans.org"
         ;; :exclude "LectureOutlines.org"
         :exclude-tags note noexport
         :auto-preamble t)

        ("digital-history-lecture-source"
         :base-directory "~/DH/Lectures"
         :base-extension "org"
         :publishing-directory "/ssh:matt@shimano:/var/www/sandbox/DigitalHistory/Lectures/Source"
         :recursive t
         :publishing-function org-org-publish-to-org
         :preparation-function 
         :completion-function 
         :headline-levels 4             ; Just the default for this project.
         ;; :exclude "LecturePlans.org"
         :exclude "LectureOutlines.org"
         :exclude-tags note noexport
         :auto-preamble t)

        ("digital-history-lecture-slides"
         :base-directory "~/DH/Lectures"
         :base-extension "org"
         :publishing-directory "/ssh:matt@shimano:/var/www/sandbox/DigitalHistory/Lectures/Slides"
         :recursive t
         :publishing-function mwp-org-reveal-publish-to-html
         :preparation-function 
         :completion-function 
         :headline-levels 4             ; Just the default for this project.
         ;; :exclude "LecturePlans.org"
         :exclude "LectureOutlines.org"
         :exclude-tags note noexport
         :auto-preamble t)

        ;; ("newone-lecture-slides"
        ;;  :base-directory "~/NewOne/Lectures/"
        ;;  :base-extension "org"
        ;;  :publishing-directory "/ssh:matt@shimano:/var/www/sandbox/NewOne/Lectures"
        ;;  :recursive t
        ;;  :publishing-function org-deck-publish-to-html
        ;;  :headline-levels 4             ; Just the default for this project.
        ;;  :exclude-tags note noexport
        ;;  :auto-preamble t)

        ;; ("newone-lecture-notes"
        ;;  :base-directory "~/NewOne/Lectures/"
        ;;  :base-extension "org"
        ;;  :publishing-directory "/ssh:matt@shimano:/var/www/sandbox/NewOne/Lectures-with-notes"
        ;;  :recursive t
        ;;  :publishing-function org-html-publish-to-html
        ;;  :headline-levels 4             ; Just the default for this project.
        ;;  :exclude-tags noexport
        ;;  :auto-preamble t)

        ;;  ("newone-images"
        ;;        :base-directory "~/NewOne/Images/"
        ;;        :base-extension "jpg\\|gif\\|png"
        ;;        :publishing-directory "/ssh:matt@shimano:/var/www/sandbox/NewOne/Images"
        ;;        :publishing-function org-publish-attachment)

        ;;  ("newone" :components ("newone-lecture-slides" "newone-lecture-notes" "newone-images") )

        ;;  ("presentations"
        ;;   :base-directory "~/Dropbox/Work/Talks/"
        ;;   :base-extension "org"
        ;;   :publishing-directory "/ssh:matt@shimano:/var/www/sandbox/Presentations"
        ;;   :headline-levels 4 ; just the default for this project
        ;;   :exclude-tags noexport
        ;;   :auto-preamble t
        ;;   :publishing-function mwp-org-deck-publish-to-html
        ;;   ;; :completion-function mwp-update-published-paths
        ;;   )

      ))

Notice the publishing function, which is set to mwp-org-deck-publish-to-html. This is a simple function that resets the base url and extra-css values to web-based ones before publication, so that the presentations work when online. Notice I’ve also reset the deck.js base url, in case I ever decide to change back to deck.

(defun mwp-org-reveal-publish-to-html (plist filename pub-dir)
  "Publish an org file to reveal.js HTML Presentation.
FILENAME is the filename of the Org file to be published.  PLIST
is the property list for the given project.  PUB-DIR is the
publishing directory. Returns output file name."
  (let ((org-deck-base-url "http://sandbox.hackinghistory.ca/Tools/deck.js/")
        (org-reveal-root "http://sandbox.hackinghistory.ca/Tools/reveal.js/")
        (org-reveal-extra-css "http://sandbox.hackinghistory.ca/Tools/reveal.js/css/local.css"))

    (org-publish-org-to 'reveal filename ".html" plist pub-dir))
  )

And that’s it, magic!

Still to do

I like this a lot, but there are a couple of pieces I’d still like to implement.

Fix all local file URL’s
I’d like to write a function to take a final pass through all the links and change file:/// links to HTML relative links. That will take some work though.
Export as standalone
There is work underway to allow presentations to be generated as stand-alone files that can be, e.g, sent by email. I like this idea a lot. See this Github issue.
Standardize notes, fragments
Every time I switch from one presentation framework to another, I have to learn a whole different syntax for things like fragments (bits of content that don’t appear on the slide immediately, but are instead stepped through) and speaker notes (that don’t appear on the slide that your viewers see, but are only visible to you in some kind of preview mode). It would be great if the various slide modes could work towards a common syntax for these things. If I have time, energy, and skills, I would like to help develop this a little.

See my slides

If you want to see some examples of the end product, here is a link to my Digital History lecture archive (still being built!). Many of my course materials are also online at Github.