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

Video - Livebook, trying it out

Last friday I did my second live stream. A lot of nice people stopped by and I spent the time showing and getting more familiar with the newly released Livebook....

Read More

Lumen - Statically compiled Erlang for x86

The Lumen Project is an ambitious compiler development effort to create a complimentary set of compilers and tools that allow developers to get the power of the Erlang VM, The BEAM, in places it does not traditionally fit. Such as the browser. Currently the project is at an early released stage as covered in Luke's ElixirConf talk. It does not yet implement all of Erlang OTP and as such won't handle most Erlang/Elixir you could throw at it. I want to show something that it does do. And while the project is a complicated compiler project you do not need to know that stuff to try it out. This should be achievable for most developers and to ensure that I wasn't talking out of my rear I had my assistant developer follow the instructions without my input and it worked out well....

Read More

A Telegram Bot in Elixir feat. LiveView

I asked my network about noting ideas quickly and got a lot of good responses. One mentioned saving them in Telegram. I don't think I want to do specifically that but I do want a minimum friction way of noting ideas for later review and refinement. And sending them to a Telegram chat would be quite nice. So I started on the path of something like a note-taking system using Telegram for ingesting quick notes. And I want to share the satisfaction I felt with seeing the near real-time way that it works....

Read More
Read All Posts →