5 ms·
> The quick rule: if you need bidirectional, low-latency communication (chat, collaboration, games), WebSocket; if you only push from the server, SSE is simpler
by hackingonempty 2mo ago
> The quick rule: if you need bidirectional, low-latency communication (chat, collaboration, games), WebSocket; if you only push from the server, SSE is simpler and cheaper to operate.
For most apps just use SSE and the built-in code for making HTTP requests (Fetch) instead of hacking up your own client side JS to make requests over a WebSocket. The latency is the same because modern browsers multiplex HTTP requests over a single TCP connection that is left open.
Maybe if you are making many client requests per second there is an advantage to not sending full headers/cookies/etc... on each request but not if you're sending requests in response to user clicks/touches.
Any sufficiently complicated SPA contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Fetch.
- clappski 2mo ago> The latency is the same because modern browsers multiplex HTTP requests over a single TCP connection that is left open. In my experience this isn’t true; firstly you’re relying on an implementation detail of the platform that you’re executing on, of which you have no control over on the client side. Secondly, even if you aren’t opening a new connection per request, you’re still travelling through an entire HTTP stack implementation rather than the incredibly simple WebSocket protocol - effectively a length and a mask to get the contents, rather than some (in http1 land) fuzzy parser. If you can guarantee you’re hitting http2 or http3 then you might be closer in latency, but due to the complexity of both I would imagine plain http1 negotiated persistent WebSockets provide the best latency.
- jallmann 2mo agoSure, but SSE connections in the browser - the typical use case - are persistent, so there shouldn't be frequent re-connecting. Unlike WebSockets, browser SSE also has a built-in recovery mechanism so you don't miss events across reconnects (`Last-Event-ID` header), although this does require explicit support from the server-side app. Given that the WebSocket handshake has an additional round-trip, SSE would typically be faster in terms of time-to-first-byte. > you’re still travelling through an entire HTTP stack implementation rather than the incredibly simple WebSocket protocol Both protocols have their own minimal framing, but "an entire HTTP stack" is a stretch. It's hard to argue that `data:value\n\n` is more complex than the WebSocket binary protocol. Simplistic, sure (no binary payloads), but also simple enough to, say, pipe through a regex. Good luck doing that with WebSocket.
- crabmusket 2mo ago> faster in terms of time-to-first-byte Yes, but all these approaches are optimised for long sessions, not TTFB. If that were very important (say, ecommerce) you might prefer a framework that can hydrate and send the full page on first request, rather than setting up a socket to get data. > data:value\n\n I believe the poster was referring to requests from the client to the server having to traverse the HTTP stack, not data coming down from the server via the SSE stream.
- jallmann 2mo ago> I believe the poster was referring to requests from the client to the server having to traverse the HTTP stack, not data coming down from the server via the SSE stream. Ah that's probably correct, I mis-read.
- galaxyLogic 2mo agoBut, if you can do things on the client (with JavaScript) you don't need to send (so many) requests to the server, making latency less of an issue. Doing things on the client means user's CPU is doing some work which else would need to be done on the server, for maybe thousands of clients at the same time. So I understand some people don't like JavaScript, but then I think the solution would be WebAssembly. I mean the point of distributed computting is that the computational load can be distributed. Perhaps counter-intuitively that often also means less need fo communications and latencies.
- clappski 2mo agoIn my experience (building things like realtime trade monitoring front ends), client side processing sucks. You have 10 or 20 data sources that need joining up in a client defined set of ways (think viewing X price feed with Y trade feed and some Z meta data feed), all natively sending many updates a second - I’ve tried and failed to build a client side heavy implementation (although that might just be my lack of front end skills!), much better to have some DAG of backend processing units that the front end can consume feeds from and remain really light, just managing subscriptions and rendering the data.
- galaxyLogic 2mo agoDeveloping on the back-end has much more mature tools in my experience, IDEs and QA tools. Client-development is often not much fun dealing with the browser dev-tools. So I share your feeling thath serious development is better done on the back-end. But in principle there should be a client-dev-tool as good as those for the back-end. There is some progress. I've used WebStorm to do client-side work which has a niocve feature that as I debug my client-side program I can use WebStorm instead of browseer-tools and if I see a typo in my code while debugging it I can edit it away and it gets saved into the source-file where it came from.
- paulddraper 2mo agoWebSockets have head-of-line blocking. HTTP/2 does not.
- zero_shift 2mo agoBut surely it does? It is TCP, even with multiplexing I thought that was the whole motivation for building HTTP/3 on QUIC (UDP)?
- paulddraper 2mo agoWell, yes. There is always some form of it on TCP. But your HTTP/2 reverse proxy won't block fast responses on slow responses. As long as your WebSocket reverse proxy does the same, you're fine. (And same stipulation for your client.)
- treve 2mo agoYes you are right. With HTTP/2 responses can be sent out of order, but once they are sent down the pipe they have head of line blocking and have to arrive at that order, unlike QUIC. Websockets don't really have an advantage here because typically websockets are implemented server-side as event based systems, and if a RPC paradigm is put on top of it has the same properties as HTTP/2 request/responses ordering. So if HTTP/2 doesn't have head of line blocking, then you can't argue that websockets do.
- 7bit 2mo agoh2 does have head-of-line blocking on the transport level.
- est 2mo ago> incredibly simple WebSocket protocol Srsly bro? Websocket is simple? Talking about handling state persistency and connection partitions, such a headache.
- clappski 2mo agoYes, WebSockets are very simple to implement a server or a client for. It’s a small variable size header, 5 or so frame types and a typically constant mask over the contents.
- Borg3 2mo agoHehe, yeah.. I recently touched WebSockets and yes, its complicated stuff. But hey, for vibe coders everything is easy, right? ;)
- clappski 2mo agoYou can genuinely implement the protocol with good performance in 150/200 lines, from memory; 1. Read the opcode 2. If PING, send PONG. If binary or text, continue 3. Read the header size 4. Read the payload size (which is a variable-width integer, hence getting the header size first) 5. Unmask the payload 6. If fin in the header is false, add the payload to a buffer. Else, either immediately dispatch (continuation buffer is empty) or buffer and dispatch (continuation buffer isn’t empty) There are extensions like setting RSV1 to indicate you need to decompress the payload, but that’s the only one I’ve encountered building clients for >20 WebSocket based APIs.
- Borg3 2mo agoDamn, im must be very bad then: 210 lines @ src/webirc.rb Skeletal test, just to move data from/into websocket. Nothing more... Anyway, yes, doable. But compared to HTTP1/1 request or just TCP socket, way more complicated. That was unexpected.
- clappski 2mo agoI’ve had more difficulty building http1.1 implementations that broadly work, still a lot of weird stuff like incorrect chucked message parsing you have to handle on the server side. Yeah plain TCP is obviously simpler but you can’t consume a raw socket in a browser, that’s why web sockets win for me - easy to consume over api and browser.
- rcarmo 2mo agoThis seems misguided since the whole point of the browser stack is not reinventing the wheel. The heinous crimes to code maintainability that have been perpetrated on top of websockets litter the halls of technical debt, haunted by the liminal career ghosts of too clever coders…
- clappski 2mo agoThe claim I was rebutting was specifically about latency. Of course you can make a mess of it, no different than you can with request/response based protocols. On the other hand, you are going to struggle to implement bidirectional event based protocols without WebSockets that can be consumed by browser based clients or an application - WebSockets are very useful for that exact case, where you have a mixed client type consuming. It means you don’t need to offer web hooks and rest and some streaming protocol, you just have a WebSocket with some defined JSON message types without the rigmarole of distributing .proto or whatever.
- sublinear 2mo ago> Any sufficiently complicated SPA contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Fetch. Sound advice otherwise, but you could have left this part out of the comment :D You underestimate how many "sufficiently complicated SPAs" are slapped together low-code projects. Their maintainers have no idea what you're even talking about.
- hackingonempty 2mo agoYou're right and said maintainers probably have never heard of Greenspun's Tenth Rule either. https://en.wikipedia.org/wiki/Greenspun's_tenth_rule https://en.wikipedia.org/wiki/Greenspun's_tenth_rule
- sam_lowry_ 2mo agoArgh... you did not get the joke. Here is the original: https://en.wikipedia.org/wiki/Greenspun%27s_tenth_rule https://en.wikipedia.org/wiki/Greenspun%27s_tenth_rule
- sublinear 2mo agoOf course I did. All of these are on HN multiple times a day. Here, I'll do you one better: https://en.wikipedia.org/wiki/List_of_eponymous_laws https://en.wikipedia.org/wiki/List_of_eponymous_laws You and your sibling reply are the same comment and I don't like talking to bots. You sound like a redditor from 15 years ago, but your account isn't new, so I'm not sure.
- hackingonempty 2mo ago> You and your sibling reply are the same comment and I don't like talking to bots. You sound like a redditor from 15 years ago, but your account isn't new, so I'm not sure. Sound advice otherwise, but you could have left this part out of the comment :D
- simlevesque 2mo agoNo mention of WebTransport makes me very doubtful of this article. It's 2026, all browsers support it and it's bidirectional and lower-latency than WebSocket.
- opendomain 2mo agoI just looked on the w3c WebTransport is in proposal stage - it is not official yet
- 7bit 2mo agoIt's supported in all major browsers. Doesn't have to be published for it to be used. Look at SPDY which was used well before it even reached RFC status and all major browsers supported h3 before it was completed. It's not a blocker.
- dzonga 2mo agoyep 100%. the SSE version is less headache and can easily scale.
- inigyou 2mo agoThere's no advantage of SSE compared to Websocket unless you're using a fan-out proxy.
- CodesInChaos 2mo ago> just use SSE And then the user opens your website in a handful of tabs, and everything breaks because having enough open SSE connections blocks ordinary http requests to that origin. You can avoid that by using a shared worker for all your tabs, but then you lose the simplicity advantage.
- formerly_proven 2mo agoThis is only true if the browser is connecting via HTTP/1.1, then you get the six connections per origin limit. Relevant for localhost:8000 though.
- rowanG077 2mo agoI love SSE. But it has real limitations. One not mentioned yet is that you cannot send binary data.
- josephg 2mo ago> For most apps just use SSE and the built-in code for making HTTP requests (Fetch) instead of hacking up your own client side JS to make requests over a WebSocket. HTTP doesn't guarantee in-order delivery. Websocket messages do. In-order delivery is important for stateful protocols. For example, you can start a connection by authenticating, then associate the user with the TCP (or websocket) session. Video games often push this way futher. For example, if you log in to a minecraft server, your in-game character is associated with the TCP session. If you use HTTP fetch requests, you can't guarantee that - for example - the authentication request will reach the server before authenticated messages.
- alt227 2mo agoHad a problem with this recently. Switched from Websockets to SSE and then couldnt understand why messages were coming out of order. Had to implement microtime timestamps on each sent message and then perform a check on the client to disregard any messages with older timestamps than the max currently seen for that entity. Nothing I read anywhere on the setup and testing told me that SSE events were not guaranteed in order. I may have been blind, but I spent a week researching testing and setting it up in a live environment. You would have thought I would see something in that time!
- josephg 2mo agoEvents sent over an sse stream should be in order. It’s a single tcp stream with messages separated by newlines. If you’re seeing messages arrive out of order, there may be a bug in whatever sse stream library you’re using. Http messages of course have no ordering guarantee. If you want ordering (and connection state), websockets might be a better choice.