The tech behind my Rust docs presentation

Yesterday I gave a presentation to the San Fransisco Bay Area Rust meetup, A broad vision for the Rust docs stack.

I gave this presentation from my home in Melbourne, Australia to the audience in the San Fransisco Bay Area; yet the slides that I used were running on a machine there and I controlled them from my own laptop.

I wanted to use the full WebRTC fanciness for the presentation (data for sync, plus video and audio), but the associated risk was a little too high, so I didnā€™t try it. (Iā€™m behind NAT, so Iā€™m not sure if it would have worked given the current lack of TURN support in WebRTC implementationsā€”ā€‹it would depend on the network configuration at the other end.) We ended up going with the boring lowā€tech approach of using Skype for the audio and video.

(I have a laptop without a camera; I borrowed a laptop with a camera for the presentation, but logged in as a guest on that machine I was unable to install the proper video conferencing software that Mozilla use; hence Skype.)

The format: SVG

First of all, the basic technology: I tried various HTML5 slide deck things (most notably, deck.js, reveal.js, impress.js and the Google I/O 2012 slides template) but wasnā€™t really happy with any of them; I could have worked with what was there, extending them with CSS and so forth, but I didnā€™t have a great deal of time, so I decided to go with an unconventional tool that Iā€™m already familiar with, Inkscape paired with JessyInk (an extension bundled with Inkscape).

The end result is an SVG presentation in which I get as complete control of the drawing area as one would with PowerPoint and which runs nice and smoothly in decent browsers. By choice, I didnā€™t use any advanced features of JessyInk (no zooming Ć  la Prezi, no transitions, no effects).

Fonts

I normally use Ubuntu and Ubuntu Mono and I also wanted a nice serifā€”ā€‹I settled on Cardo (regularly a favourite of mine).

Of course, as Iā€™m deploying the presentation on other peopleā€™s machines, the fonts need to be embedded in some way. (Or else convert all the text to paths, which isnā€™t good for accessibility or file size.)

Fortunately, thereā€™s no need to mess about with SVG fonts; you can use the same technology as you would in HTML, web fonts; but you just canā€™t use a <link> element (itā€™s not valid SVG); youā€™ll need to use a different technique. I went for the quickā€andā€easy approach of using @import inside a <style> element. Just be careful to escape any ampersands as &amp;.

You can do this from inside Inkscape with the XML editor, or you can just pull out your favourite text editor and add it into a <defs> element.

<style
   type="text/css"
   id="style11865">
  @import url(https://fonts.googleapis.com/css?family=Cardo:400,400italic,700|Ubuntu:400,500,400italic,500italic|Ubuntu+Mono:400,700,400italic,700italic&amp;subset=latin,latin-ext);
</style>

I believe that writing <?xml-stylesheet href="https://..." type="text/css"?> would also work, but it wasnā€™t the first thing that occurred to me.

Remote control

At first I wanted (as a loyal Mozilla fanā€”ā€‹and also because itā€™s pretty cool) to use TogetherJS, but itā€™s been designed for working in HTML, and I didnā€™t have time to adapt it to SVG (at the least, itā€™d need changing away from using document.head as the place to inject scripts and also for any HTML stuff to be done as foreign objects), so I decided to just write the stuff myself. I didnā€™t want the bother of setting up a WebSocket server myself (it takes time that I really didnā€™t have spare, and rust-http certainly isnā€™t in any state to do WebSocket), so I went with a hosted solution, PubNub.

The simplest place to hook communications in was the keyboard event handlers, so I assessed JessyInkā€™s and replaced them with things that just publish a message to all subscribers and have the reception method perform the actual local change. This worked a treat. (I reckoned itā€™d take 20 minutes, so I allocated myself an hour, 3Ɨ generally being a decent estimation inflation coefficient. It took 20 minutes to be all working, though I probably spent another ten minutes playing with it.)

No, this is not at all perfect; if someone comes in after the start, or if you click (I could have disabled thisā€”ā€‹itā€™s a firstā€class feature to do soā€”ā€‹but didnā€™t bother) or use slide inventory mode and move the mouse to a different slide, theyā€™ll be out of sync; the Home or End keys can fix this up. Controlling the actual location would be a much more solid approach.

I also observed that the slide inventory mode, supposedly activated by the i key, now required pressing that key twice to activate or deactivate; I didnā€™t investigate why this is at all.

I didnā€™t hook up the mouse, so even if Iā€™d wanted to I couldnā€™t have used JessyInkā€™s fancy live drawing tools.

I had cause to try it briefly on a Mac in Safari; it didnā€™t work well. Iā€™m not sure whose fault that is. But it worked fine on Firefox, which is all I needed.

This is the actual code contained in the SVG:


<script
   xlink:href="https://cdn.pubnub.com/pubnub.min.js"
   id="script697" />
<script
   id="script699">
      if (document.location.search == &quot;?sync&quot;) {
          var pubnub = PUBNUB.init({
              publish_key: 'pub-c-ae886adc-785e-4677-9669-38dcf8b777f9',
              subscribe_key: 'sub-c-c7e137e0-676a-11e3-8ebb-02ee2ddab7fe'
          });

          var _jessyInkKeyDown = keydown;
          function keydown(e) {
              if (!e) e = window.event;
              var code = e.keyCode || e.charCode;
              pubnub.publish({'channel': 'keydown', 'message': code});
              //return _jessyInkKeyDown(e);
          }
          document.onkeydown = keydown;
          var _jessyInkKeyPress = keypress;
          function keypress(e) {
              if (!e) e = window.event;
              var code = e.keyCode || e.charCode;
              pubnub.publish({'channel': 'keypress', 'message': code});
              //return _jessyInkKeyPress(e);
          }
          // Deliberately not doing mouse events: they're added complexity I don't need today

          pubnub.subscribe({
              channel: 'keydown',
              message: function(m) { _jessyInkKeyDown({'keyCode': m}); }
          });

          pubnub.subscribe({
              channel: 'keypress',
              message: function(m) { _jessyInkKeyPress({'keyCode': m}); }
          });
      }
  </script>

Yes, thatā€™s all that was needed to make my slides sync a quarter of the way round the world. (Approximately 92.47Ā° of longitude.) Note that I made it so that you had to add ?sync onto the end of the URL to activate syncing, so that I could be lazy and publish exactly what I had used, without needing to change it.