Guillotine: beheadings for your next online presentation4 minutes read | 813 words by Ruben Berenguel
Sometimes I get an idea and I need to implement it. Guillotine (and GuillotineJS) are one example of this.
Online presentations in this new normality are hard to handle. Recording yourself (which is the way I prefer as an organiser) means that either you are not visible on screen, have to resort to some hack or customise a setup in OBS. I like having a circular floating cut-off, and that is doable with OBS and nothing else. I have done it. My CPU is not that happy when using OBS, though. I wanted a simpler, and standalone solution.
I let this idea rest for a while. Like 6 months. Or more, lockdowns mess with your time perception.
After our outstanding PyDay 2020 (Christmas & lockdown edition), I decided to implement it. All talks were recorded, and most people found a way or another to appear on their recorded talk, but I wanted a better way. Or at least, my way.
Once I decided to implement it, I had to figure out how. My first hazy plan was that it shouldn’t be that hard to create a Mac app that does this. After half an afternoon checking Swift stuff:
Next, I had to implement the cut-off and framing. The hard part of this was basically making sure everything was aligned:
- There are two windows: the full camera with framing controls and then the cut-off floating window.
- Frame controls require enlarging or reducing the video and frame: these operations need to be replicated in the cut-off.
- To accomplish that I’d pass transformation steps from the control window to the floating window. You can do that with Electron.
After I got it more or less working I found a strange problem. If the transformation that I had to apply to the floating, circular window had any pixel values that were not integers, the background wouldn’t be transparent (so I’d get a black square with a circular camera in it). The fix was just rounding those floating numbers. Weird bug.
Once this was working nicely, I wanted to add controls for how rounded the corners of the circle should be, from full square to full circle. And here I found another (similar) weird bug: 50% rounded (circle) had transparent background as expected. Anything other than 50% (but larger than 0%) didn’t, and had again a black square with a circular-ish camera inside. I couldn’t find any way to fix it except… setting the background to 0.01 opacity. This works, go figure. The almost-invisible square is invisible as far as I can see, unless you drive your screen contrast to nuts levels.
There wasn’t much more aside from making it look good by adding a help window, keyboard controls and information to the README.
There is a small problem with Guillotine, though. All presentations I do, online or otherwise are typeset and presented using Deckset. When you are in present mode, you can either be in full screen or in window mode. Full screen is ideal for real presentations, where you share the screen in a projector. Window is ideal when you just want to share a window to a Meet/Zoom presentation. And my Guillotine wouldn’t work in either:
- In full-screen mode, a floating window does not show, since any window is below a full-screen window.
- In window mode, when presenting online you are sharing just a window, so the floating window won’t show (because you are sharing the presentation).
Once I realised, I wrote GuillotineJS. It’s 90% (guesstimate) of the same code, but lives in a floating
div. Two years ago, I wrote awkrdeck, a converter from presentations written in Markdown for Deckset to reveal.js, and this year I rewrote it as haskset (from AWK to Haskell). This works as an HTML presentation, can be full-screened or even window-moded. And I could add the floating head of
GuillotineJS to it.
Actually, you can add it to any webpage, like the one you are in right now. Press
Ctrl-Shift-V and allow camera access to see it working. Otherwise, enjoy the usage video:
Since I finished these I have given no presentations (and have no plans at the moment), but of course, I will use it on the next.