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

Binary Data

~11 min · browser, binary, dataview

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

Why binary at all

JSON is human-readable, debuggable, and slow. For high-frequency or compact messages — multiplayer game positions, market ticks, sensor streams — binary cuts payload size by 60-80% and parse time even more. The browser supports binary natively; the cost is that you write your own encoder/decoder.

DataView is your friend

DataView reads and writes typed values at byte offsets in an ArrayBuffer. Pick endianness explicitly (almost always pass true for little-endian) so the same wire format works between your JS client and your Python server. Combine multiple fields into one buffer with known offsets and you have a hand-rolled binary protocol.

When to choose MessagePack or Protobuf instead

Hand-rolled binary is fine for tightly-defined messages with few fields. Once your protocol grows — schema evolution, optional fields, nested structures — reach for MessagePack (drop-in JSON replacement, 30% smaller, no schema) or Protobuf (best compression, requires .proto definitions). We cover those in Track 5.

Code

Pack-and-unpack a player position·javascript
ws.binaryType = 'arraybuffer';

// Encode: id(uint16) + x(int16) + y(int16) + flags(uint8)
function encodePos(id, x, y, flags) {
  const buf = new ArrayBuffer(7);
  const v = new DataView(buf);
  v.setUint16(0, id, true);
  v.setInt16(2, x, true);
  v.setInt16(4, y, true);
  v.setUint8(6, flags);
  return buf;
}

ws.send(encodePos(42, 150, -300, 0b0001));

ws.addEventListener('message', (e) => {
  if (!(e.data instanceof ArrayBuffer)) return;
  const v = new DataView(e.data);
  const id = v.getUint16(0, true);
  const x  = v.getInt16(2, true);
  const y  = v.getInt16(4, true);
  const flags = v.getUint8(6);
  applyPosition(id, x, y, flags);
});
Streaming many positions in one frame·javascript
// 7 bytes per record, N records in one frame
function encodeAll(positions) {
  const buf = new ArrayBuffer(positions.length * 7);
  const v = new DataView(buf);
  positions.forEach((p, i) => {
    const o = i * 7;
    v.setUint16(o,     p.id, true);
    v.setInt16( o + 2, p.x,  true);
    v.setInt16( o + 4, p.y,  true);
    v.setUint8( o + 6, p.flags);
  });
  return buf;
}

External links

Exercise

Profile a JSON vs. binary version of the same player-position protocol. Send 1,000 updates of {id, x, y, flags} as JSON, then again as the 7-byte binary above. Measure: (a) total bytes on the wire (use the network tab), (b) time to parse 1,000 of each (performance.now() around the JSON.parse vs. the DataView reads). Write down the ratios.

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.