Hacking a Presentation with Mdpress

Last weekend I had the chance to speak at the RuLu conference in Lyon, France. I've spoken at user groups before, but this was my first time speaking at a proper conference.

Before I typically would use OpenOffice to prepare my presentations, like this one about Rails security, but this time I decided to do something different. I'm a minimalist when it comes to computing, and a big fan of plain text formats. At least half of my screen time is spent either at the command line, or in the Emacs text editor.

My friend, the inimitable K.M. Lawson wrote an article recently about using Markdown to create presentations, and I was eager to give it a try. The approach he outlines uses a tool called Mdpress, which converts from Markdown to a HTML presentation powered by Impress.js.

Impress.js provides many of the features of Prezi, so you can create these ultra-dynamic infinte canvas type presentations. It's pretty neat, and it works well even if your needs are more modest than that.

So with these in my toolbox I could start playing around. The great part about using small, dedicated, open source tools like these is that they are super hackable. I ended up making lots of modifications to Mdpress just to suit this presentation. Some of them were ugly hacks on top of ugly hacks, but that's ok, because for once "works for me" was actually good enough.

Setting things up

For starters I set up a local Git repository to be able to track changes as I worked on my presentation. This is the basic layout I started with

.
└── 2013-rulu
    ├── presentation.md
    ├── presentation.org
    ├── Gemfile
    ├── Gemfile.lock
    └── Rakefile

The presentation.md is the source file for my presentation, it's a standard markdown file, but it starts with a YAML preamble to configure some aspects of Mdpress, and individual slides are separated with ---. The presentation.org are my notes in Emacs org-mode format.

Custom Mdpress branch

As soon as I realized I would be tweaking and molding Mdpress to my liking, I forked it on Github and created my own rulu2013 branch. This way I have versioning there as well, and if I want to rebuild my presentation at a later date (or on someone else's laptop after mine has crashed five minutes before going on stage), I can quickly retrieve the exact version I used for this talk.

The Gemfile refers to my custom branch. This is what it looks like

source 'https://rubygems.org'

gem 'mdpress', path: '/home/arne/github/mdpress'
#gem 'mdpress', github: 'plexus/mdpress', branch: 'rulu2013'
gem 'rake'
gem 'rb-inotify'

Basically as long as I'm working on things I want to just use the version of Mdpress that's on my laptop, so I can change things right away. That's what the first gem line does. After I'm done and everything is pushed to github under rulu2013 I will replace the first line with the second.

Rake tasks

To automate things I'm using a Rakefile, it ended up having four tasks.

rake mdpress:build  # build the impress.js presentation
rake mdpress:watch  # watch the presentation and theme for changes and rebuild
rake preview        # push a preview version to a unique URI
rake publish        # Publish the final result after the talk

You can find the final Rakefile in this gist

So now I can open a terminal, type rake mdpress:watch, and it will automatically update the presentation whenever I save the markdown file. When I want to get some feedback on my current version I do rake preview, and I get a unique URL that I can mail around, but that's hard to guess.

Mdpress tweaks

So here comes the fun part, I'm just going to mention the most interesting things I've done, you can find all the changes I made in my rulu2013 branch

Inline graphs

Markdown has a feature called 'fenced code blocks', a way of marking blocks of code so they get syntax highlighting. To do this you specify the language the code is written in.

One of the coolest changes I did was change the way code in the "dot" language is handled. This format used by the Graphviz suite of tools lets you describe complex graphs in a textual way. My presentation contained lots of syntax trees, and being able to edit them like that in place was a real lifesaver.

This went through a few iterations. Basically I just call the command line tool 'dot' in the background to turn the description into SVG (an XML format for vector graphics). Since HTML5 it's allowed to stick that SVG inline in your HTML, so that's exactly what I do. It also receives some post-processing with Nokogiri to change the size, and remove the white background.

See commits e98fcd8 44d48f6
324243d
and 2acb0ab

Speaker notes

In the end I didn't really use these, but it's still a great feature that I might use in the future. Most presentation software has a speaker view where you can display some notes to help you remember what to say. The trick to getting something like this in a HTML presentation is by using the Javascript console. You can decouple this from your browser, display the window on another screen (i.e. your laptop, not the projector), and you can increase the font size with Control-+.

Here again I misused the fenced code blocks but now used the language name "notes". These got turned into a hidden div with class="notes", and a little bit of Javascript tied it all together

document.addEventListener("impress:init", function (event) {
    document.addEventListener("impress:stepenter", function (event) {
        var step = $(event.target);
        if (console.clear) { console.clear(); }
        console.log(step.attr('id'));
        console.log(step.find('.notes').text());
    }, false);
});

Return to start

While I was practicing, I found that I wanted to more easily jump back to first slide. I had a look at how impress.js handled its keyboard input, and came up with this

document.addEventListener("keydown", function ( event ) {
  if ( event.keyCode === 48) { //0
    event.preventDefault();
    impress().goto(0);
  }
}, false);

Now I could press the 0 key and would jump straight back.

Relative offsets

Impress.js uses data attributes to position each 'slide' on an infinite canvas, so you need to specify all of these when using Mdpress as well. By default I wanted my slides to just be layed out from left to right, unless otherwise specified. What I came up with was this :

  • The first slide gets position 0, 0
  • When an attribute like data-x or data-y starts with a + or - it is taken to be relative from the position of the previous slide
  • I added the option to set defaults for these in the preamble, in my case data-x defaults to +1000, and data-y to +0

So now slides are layed out left to right with 1000px between them. I can position them differently, but if I keep using offsets then I can reshuffle slides without having to change the values of all that comes later.

In the end I ended up going for a more traditional slide deck look, so I didn't really use this after all.

Configurable transition time

Mdpress/Impress default to 1000ms transition between slides. When practicing I found that I wanted to be able to set that to something snappier, so I made it configurable in the preamble. In the end I just set it to 0ms to forego animation completely. Maybe in the future I will use this fancier stuff, but since I didn't have the time to focus on that kind of polish, I decided to keep it simple.

Extra classes

To allow easier styling of individual slides, I added the ability to add CSS classes to the div that wraps the slide, using the same notation as is used for the data attributes. See commit 7ce982e8.

Conclusion

Each of these "hacks" took minutes in itself, and in the end I had tools and a workflow that suited me just right! Of course this is due to time invested learning programming, Ruby, web stuff, Javascript, so I know this won't be as easy for everybody.

But don't be afraid to look under the hood. These kind of changes can sometimes be easy to make without having to understand the whole program, and they are great appetizers for playing around more with the code you find. Nowadays after using a tool or library for a little while, I almost always end up with a copy of the sources on my hard drive, just to poke around with and learn. Happy hacking :)

comments powered by Disqus