Readit News logoReadit News
donatj · 5 years ago
I made a little nojs shared drawing board a few years back that worked with MJPEG as the background of an image button ‘<input type="image" ...>’.

Clicking the image button submits the form with x and y click coordinates, and the server returns an HTTP 204 telling it to stay where it is, followed by pushing out the updated jpeg to all connected clients.

It’s pretty fun, and I wanted to have it online all the time but it has a problem I haven’t sorted out where sometimes when a client disconnects the server won’t realize and trying to push them a jpeg locks the whole rendering system.

https://github.com/donatj/imgboard

lawik · 5 years ago
204 you say. Please don't make this rabbit hole deeper for me :)
donatj · 5 years ago
204 is a wonderful rabbit hole! You can use it to send messages to the server without refreshing the page without JS. It was a pretty common trick in the early 2000s
forgotmypw17 · 5 years ago
Thank you very much for sharing this.

I'm attempting to support every client in every configuration on my web-based forum, and this would be a nice little toy.

Sephr · 5 years ago
I envisioned this same concept a decade ago[1] but never implemented it. It's nice to see this actually work in practice!

[1]: https://stackoverflow.com/questions/2576715/pushing-data-onc...

IgorPartola · 5 years ago
That problem should definitely be solvable. I used MJEPG to create my own streaming server that wasn’t mjpegstreamer and even with HTTPS and reverse proxying there are ways to detect when to stop sending the data, though it isn’t fun to find all the edge cases.

I need to revisit and update that project sometime but it was a very neat event loop C implementation that reads frames from a cheap consumer webcam and without doing any reencoding feeds it to connected sockets as an MJPEG stream (it does have support for re-encoding if the camera can’t produce JPEGs). Was a really fun exercise in minimalism and efficiency as my goal was to stream multiple cameras from a single Raspberry Pi back when they were super slow.

donatj · 5 years ago
I have no doubt, I just haven't found the time. The codes kind of a nasty mess / proof of concept.

Deleted Comment

arraypad · 5 years ago
The core of this is of course the response header "Content-Type: multipart/x-mixed-replace" which in theory lets the server push updated chunks of any content type (although apparently only images in Chrome since 2013 [1]).

While it's a crude and handy way to update some content after the initial load without JS, it was also widely used with JS before Web Sockets - to push messages to clients with less latency than XHR polling.

[1] https://en.wikipedia.org/wiki/Multipart/x-mixed-replace#Mixe...

bullen · 5 years ago
The big problem with this spec (which is shared today by all the HTTP uploads in the world) is that you have to check for the delimiter every byte!

"Content-Type: chunked" is much better because it gives you the size of each chunk upfront! But that requires .js and also was buggy in IE until version 7.

I made a multiplayer online system that relies on chunked: https://github.com/tinspin/fuse

pronoiac · 5 years ago
omg! I thought it would slowly create and send along an mjpeg video file. Server push animation is a 90's thing. I wrote something up about it in 2009, and posted a still-working demo, because of a thread here. https://pronoiac.org/misc/2009/10/server-push-animation/
jswrenn · 5 years ago
> although apparently only images in Chrome since 2013

I wonder if this includes SVG images...

EDIT: It does!

notRobot · 5 years ago
So now we know that a #1 post on HN has about ~450 concurrent visitors

Edit° (~1 hour later): So I've been checking every few minutes, and it's consistently been at ~450, this is a cool metric to have.

°This link was posted to HN ~4 hours ago. I first checked ~1.5 hours after it'd been posted, and it was #1 on the front page. It's still #1 as of this edit. The counter has consistently been between 445 and 455 the whole time.

lawik · 5 years ago
I think it started falling over around 450. I've been getting 500 on my Nginx for workers, so I bumped that but the resulting Nginx restart killed the count and it is currently at 217 as it has recovered but I probably lost tons of idle tabs.

The application has retained a 3 day uptime. But I don't think the 450 number is entirely true with Nginx causing an artifical cap on it. Would probably be higher if I didn't hit that limit. Drats, upped the limit, hope it recovers.

richrichardsson · 5 years ago
When the Europeans are coming up for midday at least. I wonder how that number will change depending on who's up.
browserface · 5 years ago
And... It crashed. Image is currently down.
Raphmedia · 5 years ago
This is pretty good considering most people will only stay on the links for a few seconds.
trevor-e · 5 years ago
I had a top post a few years ago and I think in total I had around ~60k visitors over a couple days. Have a bunch of screenshots lying around somewhere for a blog post that I never ended up writing.
golergka · 5 years ago
2 hours after your comment, at ~190 concurrent visitors. 4th in the HN top atm.
notRobot · 5 years ago
Update: now it's on #2, and it's hovering around 430
notRobot · 5 years ago
Update: #3 and ~130. Whoa, dramatic fall.

Edit: #4, post is six hours old, counter up to ~190.

Edit: #5, ~6 hours, 173 points, ~220 current site readers.

Edit: Back to #4, ~6 hours, 186 points, ~300 site readers.

smichel17 · 5 years ago
You have to adjust this for people who read the comments but haven't clicked the link :P
bdcravens · 5 years ago
They mean visitors to the link
notRobot · 5 years ago
How so?
allanrbo · 5 years ago
He mentioned chunk streaming text in an iframe I think as a joke, but I remember actually implementing a decent chat web app exactly like that years ago in the IE5 days or so.
ks · 5 years ago
A company I worked for in 2001 used chucked streaming to provide search results. The search results were collected by performing live searches in parallel (server side) with multiple partners and then aggregated into a single stream of chunked content. Basically the HTTP connection was kept open until all searches were complete, which would take a few minutes. The results were pushed incrementally to the browser as soon as they were available

This worked surprisingly well. They started with almost pure HTML with the results split into multiple tables to produce what looked like a single table of results. The only drawback was that the columns needed fixed width so that they would align properly.

After a while they switched to a Javascript based solution with dynamic filters. They used the same backend streaming engine to output chunks of Javascript code inside <script>-tags, which called a global "addResult(...)" method to update the state. If the search was cached, it would first render HTML code server side, which was then "hydrated" by the javascript code if needed.

Later it was replaced with a standard XHR polling mechanism.

It is interesting to look back at what we had to do that is trivial with today's technology.

robertlagrant · 5 years ago
Yeah you basically never close the response, right? Just keep appending! Chat is a good use case for it.

Maybe with CSS these days you could make only the last child of a div visible, and have constant updates with no need for JS.

Anyway. I quite like this idea of no JS. I know he said it would be evil to serve ads using MJPEG, but I'd probably prefer it if my page had less JS on it.

lukeramsden · 5 years ago
> Anyway. I quite like this idea of no JS. I know he said it would be evil to serve ads using MJPEG, but I'd probably prefer it if my page had less JS on it.

Honestly I don't see how it's more evil, just because it doesn't use JS surely doesn't mean uBlock and such can't still block it? To be evil all ads full stop would have to be evil. As you say, I would much prefer this to ads that use JavaScript. If you can still show me ads on your news article when I haven't got JS enabled I'm not going to be too bothered, because hopefully everything should load plenty fast. It also means the rest of the site can load while the ads are loading.

alex_duf · 5 years ago
Yep, I've done that to send the status of a large upload, that must have been around 2008 or so.

We had a progress bar that would show the upload percentage, then the unzipping, then the virus scan on the server, then it would be marked as done.

finiteloop · 5 years ago
At FriendFeed, when we first introduced real-time comments and likes (pretty sure we were the first social network to do this), we did it with long-polling via an iFrame because web sockets did not exist. It actually worked pretty well!

https://www.ft.com/content/87b8107e-9c6d-3d3f-a60d-3e4f6315d...

reimertz · 5 years ago
This is so cool and very similar to a hack I did some time ago: Noscript-compatible twitch-plays-Zelda

http://nes-o-png.herokuapp.com

So crazy what you can do with server-side-rendering if you really want to. :)

mercora · 5 years ago
It is quite cool but for me it wont work without javascript on firefox
Shared404 · 5 years ago
Works for me without JS.
lawik · 5 years ago
When I posted this in another context someone linked this beauty: https://github.com/kkuchta/css-only-chat
fishtoaster · 5 years ago
Hey, that's mine! But yeah, it includes an alternate approach to the same problem: use http chunked encoding and continually append html to the web page (which never quite finishes loading).

Funnily enough, it also uses another evil trick with css background images, which TFA mentions as well: https://underjord.io/is-this-evil.html

koliber · 5 years ago
There are a few other ways:

- dynamically generated animated GIFs - Content-Type: multipart/x-mixed-replace : the way to animate images on the web since 1993 - before animated GIFs

reaperducer · 5 years ago
since 1993 - before animated GIFs*

Animated GIFs have been around since 1987.

browserface · 5 years ago
I use MJPEG to livestream headless chrome[1]. You can set it up to use the HTTP endpoint but I just do it over WebSocket.

[1]: https://github.com/dosyago/OuterShell