Skip to content
JavaScript Basics: Read Binary Fields with ArrayBuffer and DataView

JavaScript Basics: Read Binary Fields with ArrayBuffer and DataView

October 19, 2026

Binary records often mix one-byte flags, integers, and floating-point measurements. ArrayBuffer supplies storage; DataView interprets fields at byte offsets with an explicit byte order. These examples focus on binary layouts rather than text encodings.

These examples were tested on Linux with Node.js v24.19.0. They use built-in APIs only, require no network access, and include assertions that check the results. See MDN’s DataView reference for field access and byte order.

Prepare a scratch directory

You need Bash, a text editor, Node.js 24 on your PATH, and the mktemp, rm, and rmdir utilities. Keep using the same terminal. Save each named source file as UTF-8; .mjs selects ES modules. Each example runs independently.

node --version
start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 1

The tested runtime prints:

v24.19.0

1. Write a mixed-width header

Save this as encode-binary-header.mjs:

import assert from 'node:assert/strict';

const storage = new ArrayBuffer(7);
const header = new DataView(storage);
header.setUint8(0, 2);
header.setUint16(1, 300, false);
header.setUint32(3, 65536, false);
assert.equal(header.getUint8(0), 2);
assert.equal(header.getUint16(1, false), 300);
assert.equal(header.getUint32(3, false), 65536);
const hex = Array.from(new Uint8Array(storage),
  byte => byte.toString(16).padStart(2, '0')).join(' ');
assert.equal(hex, '02 01 2c 00 01 00 00');
console.log(hex);
console.log(`Payload length: ${header.getUint32(3, false)}`);

Run it:

node encode-binary-header.mjs

Output:

02 01 2c 00 01 00 00
Payload length: 65536

Offsets count bytes, starting at zero. The fields occupy one, two, and four bytes. Passing false selects big-endian order; true selects little-endian. Both readers and writers must follow the format’s agreed order. A DataView can access a multibyte field at an unaligned offset such as 1.

2. Decode a signed value and a float

Save this as decode-sensor-reading.mjs:

import assert from 'node:assert/strict';

const bytes = Uint8Array.from([0xec, 0xff, 0x00, 0x00, 0x48, 0x41]);
const reading = new DataView(bytes.buffer);
const offset = reading.getInt16(0, true);
const temperature = reading.getFloat32(2, true);
assert.equal(offset, -20);
assert.equal(temperature, 12.5);
assert.equal(reading.getUint16(0, true), 65516);
console.log(`Signed offset: ${offset}`);
console.log(`Temperature: ${temperature}`);

Run it:

node decode-sensor-reading.mjs

Output:

Signed offset: -20
Temperature: 12.5

The same bits mean different things under signed, unsigned, and floating-point interpretations. This record uses little-endian fields. A 32-bit float cannot represent every decimal exactly; 12.5 is deliberately exact. DataView does not discover a file format or validate application-level units.

3. Respect a sliced record and reject truncation

Save this as limit-binary-view.mjs:

import assert from 'node:assert/strict';

function readCount(bytes) {
  if (bytes.byteLength !== 2) throw new RangeError('Expected a two-byte count');
  const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
  return view.getUint16(0, false);
}
const packet = Uint8Array.from([0xff, 0x01, 0x2c, 0xee]);
const record = packet.subarray(1, 3);
assert.equal(readCount(record), 300);
assert.throws(() => readCount(record.subarray(0, 1)), RangeError);
const view = new DataView(record.buffer, record.byteOffset, record.byteLength);
assert.throws(() => view.getUint32(0, false), RangeError);
console.log(`Record count: ${readCount(record)}`);
console.log('Truncated and out-of-view reads rejected');

Run it:

node limit-binary-view.mjs

Output:

Record count: 300
Truncated and out-of-view reads rejected

A subarray shares its backing buffer. Passing only record.buffer would expose the surrounding bytes and start at the wrong position. Preserve its offset and length when building the view. Explicit length checks make format failures clearer; DataView also throws when a field extends beyond its view. Neither view copies the bytes.

Clean up

In the same terminal, remove only the named example files, return to your original directory, and remove the empty scratch directory:

rm -- encode-binary-header.mjs decode-sensor-reading.mjs limit-binary-view.mjs
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dir

Acknowledgements

This post was created with assistance from AI.

Last updated on