C.W.K.
Stream
Lesson 03 of 08 · published

Sending & Receiving Messages

~12 min · browser, send, json, binary

Level 0Poller
0 XP0/60 lessons0/10 achievements
0/120 XP to next level120 XP to go0% complete

send() takes more than strings

ws.send() accepts a string (sent as a text frame), or any of ArrayBuffer, Blob, or a typed array like Uint8Array (sent as a binary frame). The browser handles the framing for you. There is no chunking at the API level — one send() is one logical message.

JSON is the default protocol

For 95% of applications, your wire format is JSON: stringify on the way out, parse on the way in. Pair this with a type field (Track 5) and you have a perfectly serviceable application protocol.

Binary types and DataView

For compact wire formats — game state, market data, custom protocols — set ws.binaryType = 'arraybuffer' so messages arrive as ArrayBuffer instead of Blob. Then use DataView to read and write fields at known byte offsets. The savings vs. JSON are dramatic: a position update is 8-16 bytes binary vs. 50+ bytes JSON.

bufferedAmount and backpressure

If you call send() faster than the network can drain it, bytes pile up in ws.bufferedAmount. There is no callback for "drained" — you poll. For most apps this never matters; for apps that fan out many messages quickly (cursors, position broadcasts), throttle when bufferedAmount climbs above a few KB.

Code

All the ways to send·javascript
// Plain string -> text frame
ws.send('hello');

// JSON envelope (the dominant pattern)
ws.send(JSON.stringify({
  type: 'chat.message',
  data: { room: 'general', text: 'Hi everyone!' }
}));

// Raw bytes from typed array
const bytes = new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f]);
ws.send(bytes);

// ArrayBuffer with structured layout
const buf = new ArrayBuffer(8);
const view = new DataView(buf);
view.setUint32(0, 42, /* littleEndian */ true);
view.setFloat32(4, 3.14, true);
ws.send(buf);

// Blob (e.g. a small image you assembled in JS)
const blob = new Blob([bytes], { type: 'application/octet-stream' });
ws.send(blob);
Backpressure-aware sender·javascript
const HIGH_WATERMARK = 16 * 1024; // 16 KB

function sendWithBackpressure(ws, data) {
  if (ws.bufferedAmount < HIGH_WATERMARK) {
    ws.send(data);
    return true;
  }
  // Skip this update or queue for later.
  return false;
}

External links

Exercise

Modify your echo client from the previous lesson: send 10,000 100-byte JSON messages in a tight loop. Plot ws.bufferedAmount against time using a setInterval sampling. Now switch to Uint8Array of the same payload size and repeat. Compare how quickly each drains.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.