A virtual party
This weekend, I threw a Virtual Free Fringe party for some friends. The party was under-attended, but it’s fine because I got to experiment with some tech that I’d been meaning to try.
If you ever want to run something like this yourself1, here’s how I did it.
My goals were:
- A web page at which any attendee could “watch together” a streaming video2,
- A “chat” overlay, powered by a WhatsApp group3 (the friend group I was inviting were all using WhatsApp anyway, so this was an obvious choice), and
- To do all the above cheaply or for free.
There were two parts to this project:
- Setting up a streaming server that everybody can connect to, and
- Decorating the stream with a WhatsApp channel
Setting up a streaming server
Linode offers a free trial of $100 of hosting credit over 60 days and has a ready-to-go recipe for installing Owncast, an open-source streaming server I’ve used before, so I used their recipe, opting for a 4GB dedicated server in their London datacentre: at $36/mo, there’d be no risk of running out of my free trial credit even if I failed to shut down and delete the virtual machine in good time. If you prefer the command-line, here’s the API call for that:
curl -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -X POST -d '{ "authorized_users": [ "[YOUR LINODE USERNAME]" ], "backups_enabled": false, "booted": true, "image": "linode/debian10", "label": "owncast-eu-west", "private_ip": false, "region": "eu-west", "root_pass": "[YOUR ROOT PASSWORD]", "stackscript_data": { "server_hostname": "[YOUR DOMAIN NAME]", "email_address": "[YOUR EMAIL ADDRESS]" }, "stackscript_id": 804172, "tags": [], "type": "g6-dedicated-2" }' https://api.linode.com/v4/linode/instances
The IP address got assigned before the machine finished booting, so I had time to copy that into my DNS configuration so the domain was already pointing to the machine before it was fully running. This enabled it to get its SSL certificate set up rightaway (if not, I’d have had to finish waiting for the DNS change to propogate and then reboot it).
Out of the box, Owncast is insecure-by-default, so I wanted to jump in and change some passwords. For some reason you’re initially only able to correct this over unencrypted
HTTP! I opted to take the risk on this server (which would only be alive for a few hours) and just configure it with this
limitation, logging in at http://mydomain:8080/admin
with the default username and password (admin
/ abc123
), changing the credentials to
something more-secure. I also tweaked the configuration in general: setting the service name, URL, disabling chat features,
and so on, and generating a new stream key to replace the default one.
Now I was ready to configure OBS Studio to stream video to my new Owncast server, which would distribute it to anybody who tuned-in.
Decorating the stream
I configured OBS Studio with a “Custom…” stream service with server rtmp://mydomain:1935/live
and the stream key I chose when configuring Owncast and kicked off a test
stream to ensure that I could access it via https://mydomain
. I added a VLC source4
to OBS and fed it a playlist of videos, and added some branding.
With that all working, I now needed a way to display the WhatsApp chat superimposed over the video.For this, I added a Window Capture source and pointed it at a Firefox window that was showing a WhatsApp Web view of the relevant channel. I added a Crop/Pad filter to trim off the unnecessary chrome.
Next, I used the Firefox debugger “Style Editor” to inject some extra CSS into WhatsApp Web. The class names vary frequently, so there’s no point we re-documenting all of them here, but the essence of the changes were:
- Changing the chat background to a solid bright color (I used red) that can then be removed/made transparent using OBS’s Chroma Key filter. Because you have a good solid color you can turn the Similarity and Smoothness way down.
-
Making all messages appear the same (rather than making my messages appear different from everybody else’s). To do this, I added:
-
.message-in, .message-out { align-items: flex-start !important; }
to align them all to the left -
[aria-label="You:"]::after { content: "Dan Q"; height: 15px !important; display: block; color: #00f !important; padding: 8px 0 0 8px; }
to force my name to appear even on my own messages -
[aria-label^="Open chat details for "] { display: none; }
to remove people’s avatars -
[data-testid="msg-meta"] { display: none !important; }
to remove message metadata - A hacky bit of CSS to make the backgrounds all white and to remove the speech bubble “tails”
-
-
Removing all the sending/received/read etc. icons with
[data-icon] { display: none; }
I aimed where possible to exploit selectors that probably won’t change frequently, like [aria-label]
s; this improves the chance that I can use the same code next time. I
also manually removed “old” messages from the channel that didn’t need to be displayed on the big screen. I wasn’t able to consistently remove “X new messages” notifications, but I’ll
probably try again another time, perhaps with the help of an injected userscript.
A little bit of a shame that more people didn’t get to see the results of this experiment, but I’m sure I’ll use the techniques I’ve learned on another ocassion.
Footnotes
1 Or, let’s be honest, if you’re Future Dan and you’re trying to remember how you did it in last time.
2 We were to watch a show by one of my favourite comedians Peter Buckley Hill, the man behind the Free Fringe. I’ve written about him previously… here, there, also several times in 2012 when I also helped make an official digital map of Free Fringe venues. I was especially delighted to have my photo taken with him in 2006. I might be a bit of a fanboy.
3 This could probably be adapted for any other chat system that has a web interface, so if you prefer Telegram or Slack or whatever ever, that’s fine.
4 OBS’s VLC source is just amazing: not only can you give it files, but you can give it URLs, meaning that you can set up a playlist of YouTube videos, or RTSP security camera feeds, or pretty much anything else you feel like (and have the codecs for).