WebSocket development keeps a single connection open so the server can push a chat message, a stock tick or a cursor move the moment it happens. The browser no longer asks “anything new?” every few seconds, and a screen shared by several people stays in agreement without a refresh.
Contents
At CodeWiz we design and build that live layer for web applications, Laravel and Node.js backends, and internal tools. The durable record still lives in PostgreSQL, MySQL or Redis. The socket carries the events that cannot wait for the next request: a new message, a presence change, a move in a game, a character typed into a shared document.
This service is for products adding chat or a live activity feed, for operations teams that want alerts inside the app rather than only in email, and for tools where two people edit the same thing at once. If the interface feels late because it is waiting on a timer, a persistent connection is usually the missing piece.

WebSocket Development for Real-Time Chat
Chat looks small until a message is sent twice on a flaky network, a private room leaks, or a restart wipes the history. We treat the socket as the live tail of a conversation whose source of truth is the database.
What the protocol has to settle
- Every message has an id generated by the client, so a retry after a dropped connection does not insert a duplicate.
- Rooms are an authorization boundary. Joining a room is a checked operation, not a string the client may invent.
- History is loaded over ordinary HTTP, paginated. The socket delivers only what happened after the client connected, plus a short overlap so nothing falls in the gap.
- Presence, typing and read receipts are ephemeral. They are not rows you want to keep forever, and they expire when the heartbeat stops.
What we hand over
- A short event catalogue: message, edit, delete, typing, presence, read. Each event has a payload and a rule for who receives it.
- A server on Node.js (
ws, uWebSockets.js or Socket.IO) or Laravel (Reverb with Echo on the client). - Client helpers that reconnect with backoff, resubscribe to rooms and deduplicate by message id.
- Persistence, moderation hooks (block, delete, slow mode) and a way to export a room when someone asks for the record.
The browser side of the connection is specified in the WebSockets API on MDN. We follow that transport and put the product rules in the events on top of it.
Live Notifications That Arrive While the App Is Open
A notification fans out differently from chat. One event, an order paid or a deploy finished, must reach many users who are not sitting in a conversation. A per-user channel delivers it once and updates the badge, which polling every open tab never quite manages.
- Fan-out by user, not by a global broadcast. An event for one account goes to that account’s connections. An event for a team goes to the team channel. Nothing is sent to every socket on the server.
- The socket is the channel for people who are online. Web push, email or a mobile push provider still covers the locked phone. The same event record feeds all of them, so the wording does not diverge.
- Acknowledgements. The client confirms the ids it has shown. After a reconnect it asks for anything newer than the last id, which closes the hole left by a dead connection.
- Quiet hours and muting live in the preference store. The socket server reads them before it pushes, so a muted room stays quiet on every device.
Gaming Applications and Authoritative State
Multiplayer games use the same transport with a stricter clock. A card game, a turn-based tactic, a live quiz or a casual shared world can run on WebSockets. A twitch shooter that needs unreliable, unordered packets is usually a better fit for WebRTC data channels or a UDP protocol, and we will say so before building the wrong loop.

- The server owns the rules. Clients send inputs (“play this card”, “move to this cell”). They do not send the resulting score, inventory or position as fact.
- A tick or a turn advances state in one place. Spectators and players receive the same snapshot or the same list of inputs, so two screens cannot disagree about whose turn it is.
- Late and repeated inputs are ignored by sequence number. A reconnect gets a snapshot plus the inputs it missed, not a replay of the whole match from zero unless the match is short enough for that.
- Lobbies, matchmaking and results live in the database. The socket room exists only while the match does.
A quiz or a live board needs a modest tick in one region. A global game network is the wrong size for players who sit in one country and take turns.
Collaborative Tools on Shared State
Collaborative editing is the case where “last write wins” damages someone’s work. Two people in the same invoice, whiteboard or lesson plan will type at the same time. The socket carries the edits; a merge strategy decides what the document becomes.

- CRDTs such as Yjs fit text, lists and maps that must merge without a central lock. Each client applies remote updates as they arrive, including updates that were in flight during a disconnect.
- Operational transform still fits some editor stacks that already speak it. We do not introduce it on a greenfield document if a CRDT library already covers the data types.
- Awareness (cursors, selections, “Ana is viewing this page”) rides along as ephemeral state, separate from the document, and disappears when the connection drops.
- Snapshots are written to the database on a timer and on leave, so a new joiner loads a snapshot plus the updates after it, instead of replaying years of keystrokes.
Comments, assignments and permissions stay ordinary records. The live layer does not become a second permissions system.
Scaling WebSocket Development Past One Process
One Node.js process often holds tens of thousands of idle connections, limited by memory per socket rather than by request rate. The ceiling appears when you run more than one process, because a message published on process A must reach a socket that happened to land on process B.
| Approach | How a message finds the right socket | When it fits |
|---|---|---|
| One process | In memory, on that process | A single server and a connection count you have measured |
| Sticky sessions | The load balancer pins a client to one process | A few nodes, and broadcasts that can still cross processes |
| Pub/sub backplane | Redis or NATS carries events; every node delivers to its own sockets | Horizontal growth, deploys that restart nodes, several app instances |
| Dedicated realtime server | Soketi, Centrifugo or Laravel Reverb owns connections; the app publishes events | You want the HTTP API and the socket fleet scaled separately |
Socket.IO’s Redis adapter is the usual backplane when we already use Socket.IO rooms; the Redis adapter documentation describes the model. On Laravel, Reverb plays the same role and Echo is the client. Multi-region hosting is a separate decision, covered in our notes on deployment across DigitalOcean, AWS, Hetzner and bare metal.
Proxies and idle connections
WebSockets start as HTTP and then upgrade. Nginx, Traefik and Cloudflare can proxy that upgrade. A normal Varnish cache should not: the socket path bypasses it and lands on the app. Every connection has a heartbeat, because idle TCP gets dropped, and reconnects use backoff so a deploy does not stampede the server.
WebSockets, Server-Sent Events or Polling
A persistent socket is the right tool when the client also sends frequent small messages, as in chat, games and collaborative editing. A one-way live feed is often simpler as server-sent events. Polling remains a reasonable choice for data that can be a few seconds old and for clients that cannot hold a connection.
| WebSocket | Server-sent events | Short polling | |
|---|---|---|---|
| Direction | Client and server | Server to client | Client asks, server answers |
| Browser support | Broad, including a small client library where useful | Broad, with a built-in reconnect on EventSource | Anything that can send HTTP |
| Infrastructure | Upgrade, heartbeats, a backplane once you scale out | Ordinary HTTP, easy to put behind a proxy | Ordinary HTTP, more requests as the audience grows |
| Fits | Chat, cursors, games, two-way tools | Feeds, logs, notifications while the tab is open | Dashboards that tolerate delay, and a fallback |
Security on a Long-Lived Connection
A WebSocket is a long request that never quite ends. Authentication happens at the handshake, and authorization happens again on every room join and every publish. A valid login is not permission to listen to every channel the process knows about.
- Transport is
wss://only. The session cookie or a short-lived ticket proves identity during the upgrade. Long-lived secrets do not sit in the query string, where access logs keep them. - The
Originheader is checked against the sites you actually serve. - Each message has a maximum size, each connection has a maximum rate, and a noisy client is disconnected. The same idea as HTTP rate limiting on Node.js and Express, applied to frames instead of routes.
- Rooms check membership on the server. A client cannot subscribe to
user:42ororder:1842by guessing the name. - Backpressure: a slow client does not get an unbounded queue in memory. Past a limit, the connection closes and that client resynchronizes from the database.
Common WebSocket Mistakes We Fix
- Keeping the only copy of messages in the memory of one process, then losing the room on restart.
- No heartbeat, so dead phones occupy a slot until a proxy times the TCP connection out.
- Broadcasting every event to every connection, which turns a single busy room into a problem for the whole server.
- Trusting a score, a price or a permission flag that arrived from the browser.
- Reconnecting in a tight loop after a deploy, which creates a larger outage than the deploy itself.
- Putting the socket behind a cache tier that does not speak the upgrade, then debugging a handshake that never completes.
What Drives the Cost of a Real-Time Feature
We estimate from the events, the number of people connected at once, and how the feature behaves when a connection dies. Those three questions decide the size of the work more than the choice of library.
- Peak concurrent connections, and whether they are idle listeners or people sending many messages a second.
- How much history, search and moderation the product needs beyond the live tail.
- Presence, typing and read receipts, which are cheap to describe and easy to get wrong at scale.
- One region or several, and whether the HTTP app and the socket server scale separately.
- Offline paths: web push, email, a mobile notification, and the shared event record behind them.
- A one-off launch, or ongoing tuning once real rooms and match times show up in metrics.
WebSocket development is quoted against that list, after a look at the current app, and before implementation. A feed that can be ten seconds old may not need a socket at all.
How We Work
- Discovery: we list the events, who may send them, who must receive them, and what has to be true after a refresh.
- Plan: we choose the server, the room model, the persistence and the backplane, and we write the event catalogue with you.
- Build: we implement the handshake, authorization, delivery, reconnect and the client helpers in the app you already have.
- Test: we drop connections, retry the same message id, join rooms the user must not see, and load the hot path.
- Launch: we ship behind the existing login, watch connection count and message failures, and keep a polling or SSE fallback where a client needs it.
- Support: we tune heartbeats, slow mode and capacity from real traffic. A self-hosted uptime check covers the HTTP side; socket health sits beside it as its own metric.
Why CodeWiz
- Full-stack: we build the Laravel, Node.js and WordPress applications the socket has to join, so the live events match the domain model instead of bypassing it.
- Open-source first:
ws, Socket.IO, Laravel Reverb, Soketi, Redis and NATS, without a per-connection invoice from a hosted realtime vendor. - Sized to the product: one process and a clean protocol when that is enough; a backplane when you are about to run a second node.
- Fair pricing: premium development without premium prices, from Banja Luka, working remotely with clients locally and internationally since 2023.
- Performance and security in the same delivery: heartbeats, Cloudflare in front of the upgrade, and authorization on every room. More about the studio is on the about CodeWiz page.
Frequently Asked Questions
How much does WebSocket development cost?
A single live channel on an existing app, with history in the database and a reconnecting client, is a small project. Chat with moderation, presence and a backplane, or a collaborative document, is larger. You receive a written estimate after we see the events and the expected number of concurrent connections.
Should we use raw WebSockets or Socket.IO?
Raw WebSockets are a small protocol and a good fit when we control every client. Socket.IO adds rooms, acknowledgements, automatic reconnect and a long-polling fallback, with a heavier protocol. We pick Socket.IO when browsers are the audience and the fallback matters, and raw ws or uWebSockets.js when the message path has to stay minimal.
How many users can one server hold?
Idle connections are inexpensive: one Node.js process commonly holds tens of thousands, with memory as the limit. Active chat or a game tick costs CPU and bandwidth per message. We measure fan-out, then choose one process, sticky sessions or a Redis backplane.
Do notifications require WebSockets?
Only the ones that must appear while someone is looking at the app. A digest, a receipt or an alert that can wait a minute is a fine job for email, web push or a poll. We use the socket for the open tab and a push channel for everything else, driven by the same event.
Will this work behind Cloudflare and a load balancer?
Yes. Cloudflare proxies WebSocket upgrades, and Nginx or Traefik do the same on your own server. The path must not go through a cache that expects short HTTP responses. Once you run more than one app process, sticky sessions or a pub/sub backplane keep a message on the node that holds the socket.
Can you add live chat to an existing Laravel or Node.js app?
Yes. Laravel Reverb and Echo, or a Node.js socket server beside the current API, can share the same login session and the same database. We do not rebuild the application to add a room and a message table.
Ready to Add a Live Layer?
Tell us which events should arrive instantly and how many people are connected when the product is busy. We will propose a protocol, a server and a rollout that fits the app you already run. Contact us to talk it through, or see our WebSocket application services.