The last 5 years have seen a proliferation of browser-based presentation tools, fuelled by widespread HTML5 and CSS3 adoption. A great example is Reveal.js, which lets you make impressive animated slide shows with nothing more than native browser capabilities (see demo).

The field of presentation tool alternatives

Anecdotal evidence and Google Trends suggests that PowerPoint is still king, with Prezi and Keynote making limited inroads.

Google Trend for PowerPoint

Part of the long tail consists of dozens, maybe hundreds of in-browser slide presentation frameworks and services. Many are for the general public, like Google Slides, which is easy to use. Others are for programmers:

  • Impress.js (demo): A Prezi-inspired toolkit for making non-linear presentations.
  • Cleaver (demo): Takes pure Markdown and makes no-nonsense, “for hackers” slides.
  • Deck.js (demo): A complete, well-documented JavaScript API and a system for extensions (e.g. a Markdown extension).
  • Shower (demo): Another very nice HTML5 presentation framework, perhaps not as actively developed or well-known as some of the others.
  • Bespoke.js (demo): A minimal system (only 1KB worth of JavaScript) with an admirable focus on modularity and flexibility.

In the end, I liked Reveal.js because it’s fully-featured and has out-of-the-box support for Markdown. Here’s an introduction to Haskell that uses Reveal.js.

Getting started with Reveal.js

Setup is easy, but I’ll just recap it here (note that this is the “full” rather than “basic” setup).

First, install Node.js. On OSX, that’s as easy as:

brew install node

Next, install Grunt.

npm install -g grunt-cli

Clone the Reveal.js repository.

git clone https://github.com/hakimel/reveal.js.git

Enter the diretory and install dependencies.

cd reveal.js
npm install

Finally, launch the server and navigate to http://localhost:8000:

grunt serve

You should see this:

Default Reveal.js presentation

Authoring slides

A presentation’s contents are captured in a single index.html file. The following is a single slide, composed in HTML:

<section>
  <h2>Touch Optimized</h2>
  <p>Presentations look great on touch devices, like mobile phones and tablets. Simply swipe through your slides.</p>
</section>

Using optional Markdown, a slide looks like this:

<section data-markdown>
  <script type="text/template">
    ## Markdown support
    Write content using inline or external Markdown.
    Instructions and more info available in the [readme](https://github.com/hakimel/reveal.js#markdown).
  </script>
</section>

While it’s running, the grunt server command automatically refreshes your browser when it detects changes in the index.html file, thanks to live reload. This makes it convenient to edit slides and see the effect in real time.

Some limitations

Three things I’m not crazy about:

  1. Changes to external Markdown files are not noticed by live reload.
  2. The JavaScript that enables stretching elements doesn’t detect class="stretch" within Markdown.
  3. The built-in PDF export doesn’t attempt to be a equivalent representation of the presentation.

So, I created a small project to sandbox some ideas and write a bit of scaffolding around the particular things that I wanted Reveal.js to do differently.

Try it out by cloning it:

git clone https://github.com/zharley/present

Install dependencies (among which is Reveal.js itself):

cd present
npm install

I also install Decktape for pixel-perfect export to PDF.

git clone --depth 1 https://github.com/astefanutti/decktape.git
curl -L http://astefanutti.github.io/decktape/downloads/phantomjs-osx-cocoa-x86-64 -o decktape/bin/phantomjs
chmod +x decktape/bin/phantomjs

To begin working on a new presentation, my script new-dir creates a directory containing a Markdown file slides.md and a subdirectory for assets (e.g. images).

./new-dir ~/test

Next, run the server command:

./serve ~/test

You should see this:

Example slides.md

To export to PDF, keep the server running and execute:

./export reveal-test.pdf

You should get a PDF that looks just like the presentation.

How it works

As the name suggests, my Gruntfile.js (based on Reveal.js’s) does most of the hard work. For example, following watch stanza enables live reload based on changes in the Markdown file (config.slides) or any assets (config.assets).

watch: {
  options: {
    livereload: true
  },
  js: {
    files: [ 'Gruntfile.js', 'src/js/**/*.js' ],
    tasks: 'js'
  },
  theme: {
    files: [ 'src/css/theme/*.scss' ],
    tasks: 'sass'
  },
  html: {
    files: [ 'src/*.tpl', config.slides ],
    tasks: [ 'html', 'copy' ]
  },
  assets: {
    files: [ path.join(config.assets, '**') ],
    tasks: 'copy'
  },
},

The index.html file has been refactored as a template file index.html.tpl, in which contents are injected dynamically.

<body>
  <div class="reveal">
    <!-- Any section element inside of this container is displayed as a slide -->
    <div class="slides">
      <%= slides %>
    </div>
  </div>

Finally, I modified the “stretching elements” functionality by writing a custom plugin using Reveal.js’s extension system. Now, stretchable images can be defined within Markdown.

Next steps

There are many more Reveal.js features that I want to explore, including MathJax integration (displaying math equations) and multiplexing (simultaneous presentation to multiple screens controlled from a single device).