Socket.IO Tester
Decode any Socket.IO wire packet into type, namespace, ack id and args — or build one from an event form — right on this device.
Decodes and builds Socket.IO wire packets on this device — it does not and cannot connect to a Socket.IO server.
Paste what the WebSocket pane shows
Every Socket.IO frame is two layers of plain text: an Engine.IO digit, then a Socket.IO packet. Paste one line per packet — or load the example — and each layer is broken out below.
—The exact string a client puts on the wire — compare it against the devtools WebSocket pane or a server log.
Step a scripted exchange
Replays a connect → emit → ack script locally, with a fixed simulated sid — the timeline shows the wire lines both sides would send.
No exchange stepped yet.
Press ? for shortcuts · Packets are never stored — only the mode, packet type and namespace persist.
Do it offline:$ node -e 'console.log("42"+JSON.stringify(["hello",{ok:true}]))'
How to use it
- Stay on Decode and paste wire lines from the devtools WebSocket pane — one packet per line — or press Load example.
- Press Decode packets (⌘/Ctrl + Enter). Each line is split into its Engine.IO layer and, for message packets, the Socket.IO type, namespace, ack id and payload.
- Read the re-encoded wire line under each packet — the canonical spelling of what you pasted, ready to compare against a server log.
- Switch to Compose to go the other way: pick a packet type, fill in the event name, namespace, ack id and arguments, then press Build packet.
- Use Step a scripted exchange to replay a connect, emit and acknowledgement sequence locally, and copy any line from its timeline.
Every line in a Socket.IO conversation is plain text with two prefixes: an Engine.IO digit, then a Socket.IO packet type. Paste any of those lines — 42["hello",1], a bare heartbeat 2, a 0{"sid":…} handshake — and this page splits them into type, namespace, ack id and arguments, or builds the line from an event form. Everything runs on this device: no connection is opened, ever.
Examples
42["hello",{"ok":true}]EVENT "hello" on / — args [{"ok":true}]The everyday emit: Engine.IO digit 4 (message) plus Socket.IO digit 2 (EVENT), then one JSON array holding the event name first and the arguments after it.
event "reply" on /admin — ack id 13, args [{}]42/admin,13["reply",{}]Compose builds the exact wire line from the form: the namespace ends with a comma, the ack id sits between it and the payload, and the server's answer would come back as 43/admin,13[…].
451-["bin",{"_placeholder":true,"num":0}]BINARY_EVENT "bin" on / — 1 attachment as a JSON placeholderBinary arguments travel out of band: the JSON keeps a numbered placeholder per attachment, and the digits before the dash declare how many to expect. The tool checks that the two counts agree.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
Two protocols share every line
The leading digit belongs to Engine.IO, the transport layer — 4 means message. The digit after it belongs to Socket.IO — 2 means EVENT. An ordinary emit starts with 42 because it is two types glued together, and the tool labels each layer separately so neither digit has to be memorized.
The lone 2 and 3 are heartbeats
A WebSocket pane full of bare 2 and 3 frames is normal: the server pings with 2 and the client answers 3, on the interval the handshake announced as pingInterval. During a transport upgrade a single 2probe/3probe pair appears too — that one is expected exactly once.
Protocol versions do not match package versions
The EIO=4 query parameter in the connection URL marks Engine.IO protocol v4, which carries Socket.IO protocol v5 — the dialect this page reads. Older captures whose polling frames wear a length prefix such as 96: are protocol v3; they are recognized and declined with the version named.
Two different sids, one session
The Engine.IO open packet 0{"sid":…} and the Socket.IO CONNECT reply 40{"sid":…} both grant a sid, and the two values differ on purpose: the first names the transport session, the second the namespace connection. Paste each one and the tool shows which layer it belongs to.
What this page deliberately will not do
It never opens a connection — there is no server address to type anywhere — so it cannot probe an endpoint or emit into a running app. Raw binary attachment bytes are out of scope in this version: placeholders decode, while a b-prefixed attachment frame is refused by name. So is the old v3 dialect and its polling framing.
Common errors
The JSON payload at position 2 does not parse — a Socket.IO packet ends with one complete JSON value (an EVENT carries its name and arguments as a single array), so everything after the header digits must be valid JSON; fix the payload text.- The usual cause is a payload trimmed mid-copy — or a trailing comma that JSON forbids.
The namespace at position 2 never ends — the Socket.IO protocol closes a namespace with a comma before the payload, as in 42/admin,["reply"]; add the comma after the namespace.- Hand-typed packets often drop the comma; the serializer always writes it, even when the payload follows immediately.
Characters after the JSON payload at position 11 — the Socket.IO protocol reads exactly one JSON value here, and an ack id belongs before the payload (42/admin,13[…]), not after it; remove the trailing text or move the ack id in front of the array.- An ack id pasted after the array instead of before it — the grammar puts it between the namespace and the payload.
FAQ
Can this connect to my Socket.IO server?
No — and it cannot be made to. The page carries no connection code and no field for a server address; it only reads and writes the wire format, on this device. To look at real traffic, open your browser devtools, select the WebSocket connection on the Network tab and paste frames from its Messages view here. The scripted exchange is a simulation and says so on its own card.
Why does my payload start with 42?
Because two protocols each contribute one digit: 4 is Engine.IO's message type and 2 is Socket.IO's EVENT type. By the same rule 40 joins a namespace, 43 acknowledges a numbered event, and a longer head such as 42/admin,13 adds a namespace and an ack id before the JSON array.
Why is my binary attachment shown as a placeholder?
Socket.IO ships binary arguments out of band: the JSON packet keeps a {"_placeholder":true,"num":0} marker per attachment and the bytes follow as separate frames. This page decodes the markers and checks their count against the declared attachment count; the raw bytes themselves are out of scope in this version.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Decode the paste, or build the packet from the form.
- ⌘/Ctrl + C
- Copy the wire line when nothing is selected and focus is in the tool.
- ⌘/Ctrl + Shift + C
- Copy that line even while a selection is active.
- ? or ⌘/Ctrl + /
- Open the shortcut help overlay.