diff --git a/.gitignore b/.gitignore
index 4bb4556..106a8c4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,3 +31,36 @@ dist-ssr
*.njsproj
*.sln
*.sw?
+
+# Benchmarks live in their own repo: irinanazarova/anycable-socketio-benchmarks
+benchmark/
+
+# Tooling / Claude Code session artifacts
+.claude/
+.playwright-mcp/
+
+# Scratch / audit notes that aren't part of the site
+TODO-*.md
+llm-*.md
+*-audit.md
+
+# Scratch directory for ephemeral notes, drafts, research dumps.
+# Anything inside is ignored. Use this instead of leaving stray files
+# at the repo root.
+tmp/
+
+# Editor settings
+.zed/
+
+# Test screenshots / page captures at repo root (real images live under src/)
+/*.png
+/*.jpeg
+/*.jpg
+
+# Stray benchmark / report HTML dumps at repo root
+/*-report.html
+/*-benchmark.html
+
+# package-lock.json is generated locally; project bundles via Vite
+package-lock.json
+.gstack/
diff --git a/netlify.toml b/netlify.toml
index 4a3cf70..52b95c8 100644
--- a/netlify.toml
+++ b/netlify.toml
@@ -3,6 +3,21 @@
publish="/dist"
command="yarn build"
+# Preserve SEO from the original /compare/socket-io URL after we
+# renamed the slug to /compare/nodejs-websocket for organic
+# discoverability ("Node.js WebSocket server" / "Socket.io alternative"
+# queries). The new page is the canonical home; old inbound links
+# transfer their rank via the 301.
+[[redirects]]
+ from = "/compare/socket-io"
+ to = "/compare/nodejs-websocket"
+ status = 301
+
+[[redirects]]
+ from = "/compare/socket-io/"
+ to = "/compare/nodejs-websocket/"
+ status = 301
+
[[redirects]]
from = "/anycasts/anycable-v1-4-reliable-real-time-for-all/"
to = "https://blog.anycable.io/p/anycable-v14-reliable-real-time-for-all"
diff --git a/package.json b/package.json
index 300cdcb..c4bab26 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,7 @@
"license": "MIT",
"type": "module",
"scripts": {
- "dev": "vite --host",
+ "dev": "vite --host --strictPort",
"build": "vite build",
"preview": "vite preview"
},
diff --git a/src/blog/anycable-vs-socket-io/index.md b/src/blog/anycable-vs-socket-io/index.md
deleted file mode 100644
index 5e5ae86..0000000
--- a/src/blog/anycable-vs-socket-io/index.md
+++ /dev/null
@@ -1,108 +0,0 @@
-# AnyCable vs Socket.io: built-in reliability vs roll-your-own
-
-April 1, 2026
-{date}
-
-
-
-Socket.io is the most popular WebSocket library in the JavaScript ecosystem. It's battle-tested, well-documented, and free. So why would you use AnyCable instead? Because Socket.io doesn't guarantee delivery — and in production, that's the problem that breaks everything else.
-{intro}
-
-
-
-## Socket.io doesn't guarantee delivery
-
-This is the core difference between the two tools, and everything else flows from it.
-
-Socket.io provides **at-most-once delivery**. Their own [documentation][socketio-delivery] says it plainly: "if the connection is broken while an event is being sent, then there is no guarantee that the other side has received it." There is no retry, no buffering for disconnected clients, and no catch-up on reconnection.
-
-AnyCable provides **at-least-once delivery**. Publications are stored in logs. The client tracks its stream position. On reconnection, it automatically recovers every missed message. Your application code doesn't change — you broadcast messages, and every client receives every one of them, even through disconnections.
-
-This matters more than you think. WebSocket connections are not as reliable as they appear in development. In production, micro-disconnections happen on stable connections, longer interruptions occur during commutes and subway rides, and every server deploy severs active connections. Without delivery guarantees, every one of these events silently corrupts your client state.
-
-### What this looks like in practice
-
-**Live chat:** A user enters a tunnel for 3 seconds. Two messages arrive in the group chat during that window. With Socket.io, those messages are gone — the user sees an incomplete conversation. With AnyCable, the client catches up automatically on reconnection.
-
-**LLM/AI streaming:** You're streaming an AI response word by word. The client briefly loses connection mid-sentence. With Socket.io, the response is truncated or garbled — chunks are lost with no recovery. With AnyCable, every chunk is recovered in order. As we described in our article on [the pitfalls of LLM streaming][llm-streaming], this is a real production problem that breaks AI-powered features.
-
-**Real-time dashboards:** A monitoring dashboard shows live metrics. A 200ms network blip causes a gap in the data. With Socket.io, that gap is permanent. With AnyCable, the missed data points are recovered and the chart stays complete.
-
-**Deploys:** You ship code. With Socket.io, every WebSocket connection is severed — every client must reconnect, and any in-flight messages are lost. With AnyCable, the Go-based WebSocket server is a separate process from your application. When you deploy your app, the WebSocket server stays up. Connections are never interrupted. Users never notice a deployment.
-
-As Doximity engineers [described on the On Rails podcast][doximity-podcast]:
-
-> "AnyCable allows them to keep that connection open. That Go service stays up, and you can continue shipping your Rails application as normal."
-
-## A standalone server, not an embedded library
-
-Socket.io is a library you embed in your Node.js application. It runs in the same process as your business logic, competing for the same CPU and memory.
-
-AnyCable is a standalone server written in Go. It handles WebSocket connections independently — your application communicates with it via HTTP API. This separation has three consequences:
-
-1. **Scale independently.** Need more WebSocket capacity? Scale AnyCable. Need more app server capacity? Scale your app. They don't compete for resources.
-
-2. **Deploy independently.** Ship code to your app without touching WebSocket connections. This is why connections survive deploys.
-
-3. **Use any backend language.** Socket.io locks you into Node.js. AnyCable works with Rails, Laravel, Node, Python/FastAPI, or any backend that can make HTTP requests. Broadcast a message from any language:
-
-```
-POST /api/v1/broadcasts
-{ "stream": "chat/42", "data": "{\"message\": \"hello\"}" }
-```
-
-### Why Go for WebSockets
-
-WebSocket connections are long-lived — a user can hold one open for hours. In Node.js, each connection consumes meaningful memory in the V8 runtime. At 10,000 concurrent connections, this adds up fast.
-
-Go's goroutine-based concurrency model handles long-lived connections with minimal overhead. AnyCable serves 10,000+ concurrent connections per server using a fraction of the memory that Node.js or Ruby would need. And Go compiles to a single binary — no `node_modules`, no runtime version conflicts.
-
-## What you don't have to build
-
-With Socket.io, you get a WebSocket transport and rooms. Everything else is your responsibility:
-
-| What you need | Socket.io | AnyCable |
-|---------------|-----------|----------|
-| Reliable delivery | No (at-most-once) | Built-in (at-least-once) |
-| Missed message recovery | No catch-up mechanism | Automatic on reconnection |
-| Presence (who's online) | Build it yourself | Built-in |
-| Authentication | Build your own middleware | JWT, signed streams |
-| Pub/sub clustering | Redis adapter (separate setup) | Embedded NATS (zero extra infra) |
-| Monitoring | Add your own | Prometheus & StatsD built-in |
-| Binary compression | No | Yes (Pro) |
-| Deploy resilience | Not possible (same process) | Built-in (separate server) |
-
-Each of these is weeks of engineering work to build properly, and months to battle-test in production. AnyCable ships them as built-in primitives because we've been doing this since 2017.
-
-## Proven at scale
-
-AnyCable has been powering real-time features in production since 2017, for companies like:
-
-- **Doximity** — telehealth for 80% of US physicians
-- **CoinGecko** — cryptocurrency market data at massive scale
-- **Jobber** — field service management ($167M revenue)
-- **Headway** — mental health therapy ($2.3B valuation)
-- **Circle** — community platform ($30M+ raised)
-- **ClickFunnels** — sales funnels (~$265M revenue)
-
-The project is actively developed with regular releases, a dedicated team, commercial Pro support, and a growing ecosystem including [Laravel support][laravel-post], [Pusher protocol compatibility](https://docs.anycable.io/guides/pusher), and the emerging [Durable Streams](https://docs.anycable.io/guides/durable_streams) standard.
-
-## When Socket.io is the right choice
-
-To be fair: if you're building a small Node.js app, prototyping, or need full control over a custom protocol, Socket.io is a fine choice. It's well-documented, has a massive community, and is free.
-
-But if you need delivery guarantees — and in production, you almost certainly do — you'll end up building them yourself on top of Socket.io. AnyCable gives you delivery guarantees, presence, authentication, and scaling out of the box, with any backend language, tested in production by companies serving millions of users.
-
-## Get started
-
-- [Documentation](https://docs.anycable.io)
-- [Rails getting started guide](https://docs.anycable.io/rails/getting_started)
-- [Laravel guide](https://docs.anycable.io/guides/laravel)
-- [JavaScript/TypeScript client](https://github.com/anycable/anycable-client)
-- [GitHub](https://github.com/anycable/anycable)
-- [AnyCable Pro](https://plus.anycable.io/pro) — free 2-month trial
-
-[socketio-delivery]: https://socket.io/docs/v4/delivery-guarantees
-[llm-streaming]: https://evilmartians.com/chronicles/anycable-rails-and-the-pitfalls-of-llm-streaming
-[doximity-podcast]: https://podcast.rubyonrails.org/2462975/episodes/17653501
-[laravel-post]: https://evilmartians.com/chronicles/anycable-for-laravel
diff --git a/src/compare/nodejs-websocket/index.html b/src/compare/nodejs-websocket/index.html
new file mode 100644
index 0000000..0027d4e
--- /dev/null
+++ b/src/compare/nodejs-websocket/index.html
@@ -0,0 +1,981 @@
+
+
+ {{> dochead pageTitle="Node.js WebSocket Server Comparison: Socket.io vs uWebSockets.js vs AnyCable (2026 Benchmark)" pageDescription="Benchmark of five Node.js WebSocket setups on identical hardware: default Socket.io, Socket.io with Connection State Recovery, uWebSockets.js, AnyCable OSS, AnyCable Pro. Compared on memory per connection, broadcast throughput, message delivery under WiFi-drop jitter, and deploy resilience. Includes a self-hosted Pusher and Ably alternative for teams who want flat pricing. All numbers reproducible from the open-source benchmark repo." pageUrl="https://anycable.io/compare/nodejs-websocket"}}
+
+
+ {{> header}}
+
+
+ {{!-- Hero — three at-a-glance cards, one per load-bearing rubric
+ finding. Each compares the headline metric across the four
+ tested setups; the page below lays out the methodology and
+ the next two rubrics (operations + optionality). --}}
+
+
+
+
Looking for a Socket.io alternative for production Node.js?
+
+ Socket.io vs uWS vs AnyCable
+
+
+ A 2026 benchmark of five WebSocket setups for Node.js, TypeScript, Bun, and Deno apps. Self-hosted alternative to Pusher Channels and Ably included.
+
+ Your TS/JS application doesn't want to sit still: it needs WebSockets for all sorts of real-time messaging, streaming from LLMs, and collaborative features. What to choose? We benchmarked Socket.io, uWebSockets, and AnyCable across three rubrics: latency + throughput, reliability, and scalability. If we want to dance, let's dance smoothly.
+
+ Five production-shaped options, same hardware:
+
+
+
Default Socket.io — baseline popular option.
+
Socket.io + CSR — Socket.io with Connection state recovery, the opt-in for delivery and order guarantees.
+
uWebSockets.js + topics — the “just use uWS” alternative, using its built-in subscribe/publish API.
+
AnyCable OSS — a separate Go binary your app broadcasts to over HTTP, broker built in.
+
AnyCable Pro — same protocol as OSS; denser per-connection memory, shared replay state, commercial license.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
// Server
+const io = new Server(httpServer);
+
+io.on('connection', (socket) => {
+ socket.on('subscribe', (t) => socket.join(t));
+});
+
+// Broadcast from anywhere:
+io.to('chat:42').emit('message', payload);
+
+// Trade-off: at-most-once delivery — messages
+// during a disconnect are gone.
+
+
// Server — same code, plus the CSR option
+const io = new Server(httpServer, {
+ connectionStateRecovery: {
+ maxDisconnectionDuration: 2 * 60 * 1000,
+ },
+});
+
+// Same broadcast call:
+io.to('chat:42').emit('message', payload);
+
+// Trade-off: replay buffers held in memory.
+// Adapter must support CSR (in-memory or pg).
// Your Node app stays put. anycable-go runs as
+// a separate Go process; you broadcast over HTTP:
+await fetch('http://anycable:8080/_broadcast', {
+ method: 'POST',
+ body: JSON.stringify({
+ stream: 'chat:42',
+ data: JSON.stringify(payload),
+ }),
+});
+
+// Trade-off: extra process to run; broadcast hop
+// over HTTP caps single-publisher throughput.
+
+
// Same broadcast code as OSS — just a different
+// binary (anycable-go-pro) for the WS server:
+await fetch('http://anycable:8080/_broadcast', {
+ method: 'POST',
+ body: JSON.stringify({
+ stream: 'chat:42',
+ data: JSON.stringify(payload),
+ }),
+});
+
+// Trade-off: commercial license; in return you get
+// the embedded broker, lower per-conn memory, and
+// horizontal scale with shared replay state.
+
+
+
+
+
+
+
+ {{!-- Note on architecture — sits between "What we test" and Section 1.
+ Left: user-written prose explaining why we test in microservice mode.
+ Right: embedded-vs-microservice architecture diagram + the 10k
+ embedded test numbers that motivate the architectural choice. --}}
+
+
+ Production realtime needs the WS server as a standalone service, not embedded in your Node app. Embedded Socket.io/uWS freeze every user for >2 seconds and lose messages on every app deploy. That's what the rest of the page compares: the WS layer as a separate process.
+
+
+
What we tested: 3-node cluster, embedded Socket.io + Redis, rolling deploy (redeploy each node in turn, wait for it back, move on).
+
Result: every user sees a >2 s freeze on rolling deployment when their node restarts. 99.6% delivered. 2-3 lost per user during the gap.
+
+
+
+ {{!-- Architecture diagram: embedded mode (app + WS in one
+ Node process) vs microservice mode (separate WS service).
+ Adapted from the older Socket.io-vs-AnyCable diagram but
+ reframed around topology rather than vendor. --}}
+
+ {{!-- Two stacked panels. Top = embedded mode (what we tested);
+ bottom = microservice mode (target setup). Typography is the
+ page's: Stem for box titles + descriptions, Martian Mono for
+ small labels + annotations. Color: accent red for the fragile
+ (embedded) path, dark for the stable (microservice) path. --}}
+
+ Embedded mode vs standalone service. Embedded leads to WS freeze every time main app deploys.
+
+
+
+
+
+
+ {{!-- Evidence for the architecture argument: avalanche test.
+ Moved here from the standalone "Does the WS layer survive
+ deploys?" section since it's making the architecture point. --}}
+
+
+
+ What happens when an in-process WS layer restarts. Same Socket.io / +CSR / uWS server on a 1 vCPU / 0.5 GB box, scaled from 5K to 25K idle clients, then a real railway redeploy on the app. “Recovery” is wall-clock until 95% reconnected. AnyCable runs the WS layer as a separate Go binary, so the app deploy never touches it. Methodology · thundering-herd reading.
+
+
+
+
+
Past 25K, in-process WS can't recover from its own restart. Standalone WS doesn't restart, so there's nothing to recover from.
+
+
+
+
Clients
+
Socket.io recovery (also +CSR, uWS)†
+
Reconnected
+
AnyCable OSS & Pro
+
+
+
+
+
5,000
+
4.5 s
+
100%
+
0 s
+
+
+
10,000
+
3.9 s
+
100%
+
0 s
+
+
+
15,000
+
5.8 s
+
98.5% (224 lost)
+
0 s
+
+
+
20,000
+
8.0 s
+
96.2% (753 lost)
+
0 s
+
+
+
25,000
+
never
+
0% (all 25K lost)
+
0 s
+
+
+
+
†Socket.io, +CSR, and uWS run the WS layer inside the Node app, so railway redeploy restarts it. Past ~20K connections the reconnect storm OOMs the new container. A bigger box just moves the cliff: uWS pushes it to ~90K. AnyCable runs WS as a separate Go binary; app deploys never touch it.
+
+
+
+
+
+
+ {{!-- Section 1 — How fast is it? Latency at 1k/10k/100k,
+ broadcast throughput, whispers. 100k tier, uWS topics whispers,
+ AnyCable Pro whispers, and standalone hop overhead still TBD. --}}
+
+
+ Latency is the floor of what realtime can feel like — the tempo your app dances to. Two things to measure: roundtrip latency (one publisher to all subscribers) and broadcast throughput (how many messages per second the server can fan out before that latency starts inflating). All in the standalone shape: WS server as a separate process, publisher posting over HTTP.
+
+
+
+
+
+ {{!-- Latency at 1k and 10k subscribers --}}
+
+
+
Roundtrip latency
+
+ Same broadcast workload at two connection counts. The shape of the curve from 1k to 10k tells you whether the server has headroom or is approaching a wall.
+
+
+
+
+
All five servers hold the tail below 1 s at 10k subscribers. uWS leads on the p50 floor; AnyCable Pro holds the tightest p99.
+
+
+
+
Setup p50 / p99
+
1k subs
+
10k subs
+
+
+
+
Socket.io default
11 / 20 ms
88 / 176 ms
+
Socket.io + CSR
12 / 24 ms
88 / 349 ms
+
uWS topics
8 / 17 ms
61 / 292 ms†
+
AnyCable OSS
10 / 26 ms
236 / 880 ms
+
AnyCable Pro
11 / 23 ms
234 / 694 ms
+
+
+
1K and 10K subscribers, 100 broadcasts over 90 s (1K) / 130 s (10K), 500 ms intervals. †uWS's single-writer app.publish can fall into a backpressure path on contended infrastructure that pushes p99 to several seconds; the value here is the well-behaved case, the saturated case has been observed at 4-11 s.
+
+
+
+
+ {{!-- Throughput sub-section: max sustainable msg/sec at the
+ 1M-delivery target. All options as standalone services
+ (external HTTP publisher, pool=16), 10K subscribers,
+ 100% delivery required. --}}
+
+
+
Broadcast throughput
+
+ Target 1M deliveries/sec to 10K subscribers, 100% delivery required. Publisher is a separate process posting to the WS server's HTTP /_broadcast at concurrency 16. The result is what each server sustains end-to-end.
+
+
+
+
+
All five servers sustain 1M deliveries with 100% delivery. uWS is fastest at the floor; CSR and AnyCable hold the tightest tails. Default Socket.io's tail is the loosest.
+
+
+
+
Setup 10K subs, 1M deliveries, HTTP pool=16
+
Delivered
+
p50
+
p99
+
+
+
+
Socket.io default
100%
0.74 s
3.93 s
+
Socket.io + CSR
100%
0.50 s
2.26 s
+
uWS
100%
0.22 s
3.17 s
+
AnyCable OSS
100%
0.36 s
3.13 s
+
AnyCable Pro
100%
0.37 s
3.93 s
+
+
+
External publisher posts to /_broadcast at concurrency 16, 100 messages every 10 ms (target 1M deliveries/sec). uWS's app.publish is the fastest C++ hot path. AnyCable OSS and Pro track each other on raw msg/sec; Pro's wins are memory and the embedded broker. Default Socket.io's tail is roughly 2× Pro's at the same target.
+
+
+
+
+ {{!-- Whispers sub-section: client-to-client without server roundtrip.
+ This is where AnyCable plays in the Liveblocks/Yjs/PartyKit category,
+ not just the WS-server category. --}}
+
+
+
Whispers: client-to-client updates that bypass the backend
+
+ Cursors, typing indicators, presence pings. These travel client → WS server → peers without invoking your app. AnyCable ships it as a native primitive; Socket.io emulates with rooms; uWS with topics. The differences show up under load.
+
+
+ This is where AnyCable competes with Liveblocks, Yjs providers, and PartyKit, not just with WS libraries. If your product has live cursors or shared selections, this row matters more than raw broadcast throughput.
+
+
+
+
+
Socket.io rooms loses a third of whispers because every whisper goes back through the WS process. AnyCable and uWS skip that hop and deliver everything under 200 ms.
+
+
+
+
Setup 1k clients, 10 rooms, 100 peers/room, 2 Hz
+
Native?
+
Delivered
+
p50
+
p99
+
+
+
+
Socket.io rooms
Emulated
62.3%
1.78 s
12.07 s
+
uWS topics
Emulated
100%
10 ms
21 ms
+
AnyCable OSS
Native
100%
18 ms
78 ms
+
AnyCable Pro
Native
100%
18 ms
64 ms
+
+
+
1K clients in 10 rooms (100 peers/room), 2 Hz whispers for 30 s. Each whisper fans out to 99 peers, so each client receives ~200 msgs/sec. Socket.io's rooms emulation re-emits through the server, Express can't keep up, and a third of the deliveries drop. AnyCable and uWS bypass the hop and stay under 200 ms p99. At 10K clients the bench-runner saturates client-side, which inflates latencies and confuses the delivery story; 1K × 100 peers/room is the cleanest comparison of the fanout paths.
+ Your users aren't always on a high-speed fiber network. Micro disruptions are mostly invisible to HTTP, but they're the deal-breaker for realtime: a missed beat in the music. Two things decide how it feels: how often messages are lost and how long the recovery window drags. Both come down to whether the protocol carries replay state.
+
+
+ Default Socket.io is at-most-once; so is uWS. CSR adds replay (opt-in, adapter constraints). AnyCable ships reliable streams by default: per-stream history, epoch + offset, restart-survivable with NATS or Redis. Under simulated WiFi jitter (1 s TCP drops every ~15 s) at 10K subscribers, AnyCable delivers 100%, CSR resumes about 20% of reconnects cleanly to land at 76%, and the no-replay options drop most of the test once the server can't keep up with the reconnect storm.
+
+
+
+
+
Replay protocols (AnyCable, CSR) deliver under heavy jitter. Default Socket.io and uWS drop most messages once reconnects start failing.
+
+
+
+
Setup
+
Delivery
+
Lost of 1.2M
+
p95
+
p99 replay tail
+
+
+
+
+
Socket.io default
+
27.5%
+
870,000
+
0.29 s
+
no replay
+
+
+
Socket.io + CSR
+
75.5%
+
294,000†
+
80 s
+
107 s
+
+
+
uWS topics
+
34.6%
+
785,000
+
—
+
no replay
+
+
+
AnyCable OSS
+
100%
+
0
+
4.1 s
+
6.2 s
+
+
+
AnyCable Pro
+
100%
+
0
+
4.1 s
+
6.2 s
+
+
+
+
10K clients, 120 broadcasts, each client TCP-drops 1 s every ~15 s. 1.2M expected deliveries. Socket.io and uWS deliver well when reconnects succeed, but each WiFi blip is also a chance to lose the connection entirely; the page numbers reflect what users actually see when reconnect storms overlap with broadcast load. CSR resumes ~20% of disconnects cleanly; the rest fall back to live-from-now. †CSR's lost count counts what fell outside the retention window; in-gap losses on the resumed connections are zero. AnyCable replays across the full disconnect, 100% delivery, sub-7 s p99. Methodology.
+
+
+
+
+
+
+ {{!-- Use cases / impact --}}
+
+
+
+
+
What this breaks
+
+
Lost messages cluster around network events — exactly when the user is watching. CSR resumes about 20% of reconnects cleanly, and its p99 replay tail stretches to nearly two minutes, which reads as “the app stopped working.” AnyCable replays the same messages in about 6 seconds, with full delivery. For sequential workloads, both loss and delay break the flow.
+
+
+
+
+
+ Live chat & notifications
+ Messages disappear during network blips. Users see incomplete conversations, no indication anything is missing.
+
+
+ LLM / AI streaming
+ Responses stream word by word. A mid-sentence disconnect leaves output truncated or garbled — chunks lost, no recovery.
+
+
+ Real-time collaboration & presence
+ Lost operations make documents diverge silently. Cursors jump, presence flickers; users lose trust in what they see.
+
+
+ Dashboards & monitoring
+ Permanent gaps in time-series data. The 200 ms blip during a traffic spike is the data point you needed.
+
+ Your user base will grow and your realtime layer needs to grow with it. The question isn't how cheap is this today, it's when we 10× users, does the box cliff before we have time to react? The numbers below come from the single-node idle-connections load test on a 32 vCPU / 32 GB Railway box, 1M-connection target.
+
+
+
+
+
uWS holds the most connections per byte. AnyCable Pro is the lightest setup that includes built-in replay. Socket.io tops out around 120K.
+
+
+
+
Setup
+
Connections held
+
RAM/conn
+
CPU peak % of 1 vCPU
+
Replay?
+
+
+
+
Socket.io (1M attempted)
119,826
~52 KB
n/a
no
+
AnyCable OSS
821,877
34 KB
9.8%
yes
+
AnyCable Pro
822,037
18 KB
7.9%
yes
+
uWebSockets.js
1,018,366
5.4 KB
n/a
no
+
+
+
Ramp to 1M connections at 200/sec, hold 2 min. CPU peak measured on the WS server during the hold window via Railway metrics (CPU traces for uWS and Socket.io weren't captured during these runs). uWS is bare wire, no broker. AnyCable Pro is the lightest with replay built in; the extra KB/conn covers per-stream history and reconnect-resume.
+
+
+
+
+
+
+
+ {{!-- Feature comparison --}}
+
+
+
+
+
What you don't have to build
+
+ Socket.io gives you WebSocket transport and rooms. uWebSockets.js gives you a faster transport. Both stop there. Everything else — reliability, presence, auth, clustering, monitoring — is weeks of engineering plus months of hardening, on a single Node event loop you also have to keep alive across deploys.
+
+
+ AnyCable ships these as primitives, hardened in production since 2017, in a process you don't have to restart when your app deploys.
+
+
+
+
+
Socket.io and uWS give you transport. AnyCable gives you the whole realtime framework: reliability, presence, auth, deploy resilience.
uWS fixes Socket.io's wire overhead. Everything beyond raw transport is still DIY. AnyCable OSS and Pro share every feature here; Pro differentiates on memory, embedded broker, and commercial support.
+
+
+
+
+
+
+ {{!-- Run AnyCable in your stack — demos + JS libs + serverless. --}}
+
+
+
+ Does AnyCable replace Socket.io, or work alongside it?
+
It replaces Socket.io. Your Node.js app broadcasts to AnyCable via HTTP instead of calling io.emit(). Clients connect to AnyCable over its protocol (actioncable-v1-ext-json, via @anycable/core) instead of socket.io-client.
+
+
+
+ What's the operational cost of running AnyCable?
+
One extra process — a single Go binary (or Docker container). It's stateless for broadcasts and scales horizontally. No database, no custom build. Defaults work for most apps; Redis or NATS can be added for multi-node pub/sub.
+
+
+
+ How does AnyCable compare on performance?
+
Three workloads on the same 32 vCPU / 32 GB Railway box, every option as a standalone WS service.
10K reconnecting clients under WiFi jitter: AnyCable delivers 100% (p99 ~6 s replay tail). Socket.io+CSR resumes about 20% of reconnects cleanly, lands at 76% delivery with a much longer replay tail. Default Socket.io and uWS drop most of the test (27% and 35%) once the server can't absorb the reconnect storm.
Broadcast throughput at 1M deliveries: all five sustain 100% delivery; uWS holds the floor at 0.22 s p50, AnyCable Pro and CSR hold the tightest p99 around 3 s. Reproduce.
+
+
+
+ What about uWebSockets.js? It's faster than Socket.io.
+
uWS is genuinely faster on the wire, we measured it. At 1M idle connections it uses 5.45 GB (5.4 KB per conn) vs AnyCable Pro's 14.8 GB at 822K (18 KB per conn). The “10× faster than Socket.io” claim is honest. But uWS is a WebSocket library, not a realtime framework: no replay buffer, no broker, no separate-process deploy resilience. Under jitter it drops down to 35% delivery, similar to default Socket.io, because both are at-most-once and embedded in the same Node process as your app. uWS solves “Socket.io's wire is too heavy.” It doesn't solve “we lose messages during disruption” or “every deploy hits our users.”
+
+
+
+ Can I use AnyCable for streaming LLM responses?
+
Yes — message ordering and durable streams are exactly what token streaming needs. If a user briefly disconnects mid-response, AnyCable replays the missed chunks in order from the last offset they saw. Out-of-order arrivals (which corrupt LLM output) don't happen because each stream is monotonically ordered. The Next.js + Twilio + OpenAI demo linked above shows this end-to-end.
+
+
+
+ What backends can use AnyCable?
+
Any. Anycable-go is a standalone server with an HTTP broadcast API; your app pushes messages over plain HTTP. Node.js, Laravel, FastAPI, Django, Go, Elixir — anything that can issue an HTTP POST works. There is no language SDK requirement.
+
+
+
+ Can I self-host AnyCable for HIPAA / SOC2 compliance?
+
Yes. anycable-go runs entirely on your infrastructure — no data flows through third-party servers, no shared cloud. Multiple healthcare and fintech teams self-host AnyCable for this reason: real-time features (patient-doctor chat, live data feeds) without data ever leaving controlled environments. For HIPAA specifically, deploy AnyCable inside your existing PHI boundary; it's just another service on your network.
+
+
+
+ Is AnyCable open source?
+
Yes, MIT-licensed since 2017. anycable/anycable on GitHub. A commercial Pro tier adds broker features (long-term history, embedded NATS) and priority support — free for small deployments.
+
+
+
+ Is there a managed (hosted) AnyCable?
+
Yes. AnyCable+ is the managed tier — zero ops, free for early users, paid plans for scale. Same protocol and feature surface as self-hosted, so you can switch in either direction without changing app code.
+
+
+
+ How does the cost compare to Pusher or Ably?
+
Pusher and Ably charge per concurrent connection — costs scale linearly with your user base. AnyCable Pro (self-hosted, unlimited connections and instances) is a flat-rate annual license — $1,490/yr with a 2-month free trial. AnyCable+ Managed is free for early users. At 10K+ concurrent connections, the flat-rate or self-hosted options typically save thousands per month versus per-connection pricing.
+
+
+
+ How do you handle authentication?
+
Signed JWT tokens or signed stream names, issued by your application. AnyCable verifies the signature on connect and channel subscribe — your app stays the source of truth for identity without being on the hot path.
+
+
+
+ What happens if AnyCable itself restarts?
+
Less often than app deploys (you don't ship anycable-go on every code change), but it does happen — version upgrades, config changes, host reboots. The behavior depends on the broker: with an external broker (NATS JetStream or Redis Streams), replay state survives the restart, so clients reconnect and resume from the last offset they saw. With the default in-memory broker (or AnyCable Pro's embedded broker), replay state is lost on restart and clients fall back to “live from now” — same delivery profile as a cold reconnect to default Socket.io. For production, run multiple anycable-go instances behind a load balancer and a shared broker; restart one at a time during upgrades and clients seamlessly reconnect to the others.
+
+
+
+ How do I run anycable-go in production?
+
One Go binary. Docker images at hub.docker.com/r/anycable/anycable-go (and anycable-go-pro for Pro). Minimum to run: docker run -p 8080:8080 anycable/anycable-go --broadcast_key=YOUR_SECRET. Configurable via flags or ANYCABLE_* env vars. Health endpoint at /health, Prometheus metrics at /metrics. Graceful drain on SIGTERM (configurable via --shutdown_timeout) so rolling deploys don't drop connections. Behind a load balancer with sticky sessions for multi-instance setups. Helm chart and Fly/Railway templates linked from the docs.
+
+
+
+
+
+
+
+ {{!-- CTA — two paths: run locally with docs targeted at Node teams,
+ or try the managed free tier. --}}
+
+
+
Run AnyCable in your Node app
+
+ Self-host the Go binary alongside your Node service, or skip the deploy and start on the free managed tier.
+
+
+ {{!-- FAQPage structured data — LLM and search-engine readable Q-A pairs.
+ Mirrors the visible accordion above. --}}
+
+ {{!-- TechArticle structured data — gives the page itself an
+ author/publisher/date signal so Gemini's engineering-blog
+ retrieval path has something to attach to. --}}
+
+
+
+
+
diff --git a/src/compare/nodejs-websocket/style/index.html b/src/compare/nodejs-websocket/style/index.html
new file mode 100644
index 0000000..a37f958
--- /dev/null
+++ b/src/compare/nodejs-websocket/style/index.html
@@ -0,0 +1,319 @@
+
+
+ {{> dochead pageTitle="Compare-page style index — system reference" pageDescription="Internal style index for the AnyCable compare-page design system. Every block authors should reach for, rendered once with the HTML structure visible. Copy from here, paste into any compare/* page."}}
+
+
+ {{> header}}
+
+
+ {{!-- ============================================================
+ SYSTEM REFERENCE
+ Every section below shows: (1) what it looks like rendered,
+ (2) the HTML structure to copy, (3) when to reach for it.
+ Source of truth — if a pattern isn't here, it isn't part of
+ the system yet. Add it here first, then to the page.
+ ============================================================ --}}
+
+
+
+ Compare-page system reference
+
+
+ What authors writing or extending a comparison page should reach for. Every block here renders as it will on the live page. Copy the HTML next to each one. If a pattern is missing, add it to the spine (src/modules/blocks/compare-spine.scss) first, then add a demo entry here.
+
+
+ All spine rules are scoped under .c-spine. The comparison page activates them by adding c-spine to its <main class='content compare-page'>.
+
+ Body prose — capped at 580px line length. Inline code sits as a calm pill scaled to 0.8em so it optically aligns with Stem. Links carry the accent color.Strong for emphasis. Use t-mute for quieter text, t-tiny for 11px small print, t-accent for brand red, and t-num on numbers like 123,456 for tabular spacing.
+
+ Every section on the page is a compare-rubric. Two-column by default (prose left, media right); use --full for single-column sections like FAQ or appendix.
+
+ Media modifiers:--align-top aligns the media column to the top of the prose; --sticky pins it during long scroll. Combine both for the default benchmark rubric.
+
+
+ Full-width: for sections without a media partner (FAQ, appendix, CTA, intro).
+
+ Caveat or framing. Use the callout for a methodology note, caveat, or "how to read this" framing. Accent-red left rule signals "important context, not main body."
+
+
+
+ Lists inside callouts get smaller spacing automatically. No need to inline-style anything.
+
+
100% of users affected by every deploy
+
~2-second freeze per user
+
2–3 messages lost per user, per deploy
+
+
+
+
<div class='compare-callout'>
+ <strong>Caveat.</strong> Body text.
+</div>