Stupid solutions: Live server push without JS

So in my post Is this evil? I covered a way of tracking users with CSS. While thinking about those weird ways of using the web I also started thinking about pushing live data to clients without JS. Or at least maintaining a connection. So WebSockets requires JS. WebRTC requires JS. Even HLS (video streaming), which would otherwise be super cool, with captions for accessibility. But no. Or rather, maybe on Apple platforms. Eh. Not good enough.

And then it hit me. From some old Nerves projects I'd seen, that there is a standard for just sending a stream of JPEG frames as a video. MJPEG. Did you know about MJPEG? Lots of people don't. It is used by lots of webcams and security cameras. Common option for Raspberry Pi hacks as well. MJPEG is super simple which is its big advantage.

But video is what we expect. I was going for something else. So this could be used for a CI status light, showing any amount of visual status information. I use it for this:

live visual readership indicator

That's live. Or dead if my server falls over.

So how does MJPEG work. Well, you take an <img/> tag and you shove an MJPEG URL into it. Done.

Okay, that's how you use it. Not how it works. I implemented it in Elixir, Elixir is quite good at keeping state and serving updates. Links are below. But basically the browser opens the connection, receives some headers and some chunks of data and then realizes it is dealing with MJPEG. It wil then just expect the chunks to keep coming. Indefinitely. Because this is live video. Frame by frame of JPEG.

The basic code for the MJPEG headers and chunking was lifted from a pi camera repo made by the Nerves team. It had a lot of Frank Hunleth and Connor Rigby in it so kudos to those guys as always. This is what I did with it: lawik/mjpeg

My server implementation is here and uses the above code: lawik/mjpeg_example

So I receive the connection and then that calls my MjpegExample GenServer to persist the connection and keep track of how to notify that connection about new data. It also triggers an update to notify everyone already connected.

This is not polished, it is hammered together and I'm curious to see if it falls over the next time I get a decent amount of traffic.

I really like this approach because it is a fun hack that simply happens to work across browsers and quite well at that. I like how it is just an img element and no frills. I added lazy loading because that works more nicely with things like Google Lighthouse scores and the loading experience (your browser doesn't spend a few seconds thinking about loading the image).

Unfortunately it is absolutely a poor choice to actually use aside from fun and hacky stuff. There is no good way of doing accessibility with it. You can update the pixels and that is it. Unless you can chunk-stream a txt-file in an iframe. Haven't tried that yet...

So, don't use it. But isn't it pretty neat?

Of course, much like the CSS tracking, this can be used for evilish things. You can absolutely keep track of how long someone keeps receiving your frames and use that for your analytics. I also find it neat that it lets me know concurrent users. I just log the number along with sending it out because I want to know at what number it breaks and out of curiosity but you can probably do some mildly untoward things with this. Oh.. Wait. You could serve rotating ads with this. You know what the last frame you sent was so if you get a click on it you can direct that particular user to the right thing. New title: Ad placement entirely without JS, ugh, no thanks. Moving on.

If you know how to make this more dirty, hacky and fun or even more useful or accessible feel free to get in touch at lars@underjord.io or on Twitter where I'm @lawik.

Latest Posts

Asking a tech recruiter

While working I mostly found the attention of recruiters slightly reassuring but often annoying. I think that annoyance is fairly common, usually built up from countless LinkedIn drive-by attempts from unreading keyword-hunting recruiters. I thought that now, out on my own, maybe this legion of recruiters can be my sales department....

Read More

The Mac is losing me

I've been mostly happy using a Mac since I got myself my first computer earned with programmer money. I believe it was a mid 2009 15" MacBook Pro. That was a computer I used at least until 2016 which I consider very decent usable life. At that point I had replaced the hard-drive with an SSD, upgraded the RAM and switched a battery that was worn out. I stopped using it when it just straight died some time in 2016....

Read More

The BEAM marches forward

The BEAM is the virtual machine that Erlang and Elixir runs on. It is widely cited as a battle-tested piece of software though I don't know in which wars it has seen action. It has definitely paid its dues in the telecom space as well as globally scaled projects such as Whatsapp and Discord. It is well suited to tackle soft-realtime distributed systems with heavy concurrency. It has been a good platform chugging along. And with a small team at Ericsson responsible for much of its continuing development it has been managed in a deeply pragmatic way. Erlang has always been a bit of a secret and silent success. Almost no-one uses it if you look at market shares. But among the ones that use it there seems to be a very positive consensus. And then Elixir came and caused a bit of a boom. I think the BEAM has benefited from Elixir and Elixir wouldn't exist without the BEAM. With that bit of background I'd like to shine a light on some cool developments that I think makes the BEAM more interesting or even uniquely interesting in the future....

Read More
Read All Posts →