Typed Arrays and Binary Data

Mental model: An ArrayBuffer is raw bytes; a typed array is a fixed-width window onto those bytes.

Level: advanced · about 10 minutes

const bytes = new Uint8Array(4);   // 4 bytes, all zero

bytes[0] = 255;
bytes[1] = 256;   // wraps to 0
bytes[2] = -1;    // wraps to 255
bytes[3] = 3.7;   // truncates to 3

console.log([...bytes]); // [255, 0, 255, 3]

Values are clamped to the width you chose. No exceptions, no warnings.

A regular array can hold anything and grows on demand. A typed array holds numbers of one fixed width in a contiguous block of memory. You give up flexibility and get a layout that maps directly onto files, sockets, canvas pixels, audio samples and GPU buffers.

TypeBytes eachRangeTypical use
Uint8Array10 to 255bytes, file and network data
Int8Array1-128 to 127signed small integers
Uint8ClampedArray10 to 255, clamped not wrappedcanvas pixel data
Uint16Array / Int16Array20 to 65535 / -32768 to 32767audio samples, code units
Uint32Array / Int32Array4up to about 4.29 billionindices, colours, bitfields
Float16Array2about 3 decimal digitsML weights, compact GPU data (ES2025)
Float32Array4about 7 decimal digitsgeometry, WebGL, audio
Float64Array8full JS number precisionscientific data
BigInt64Array864-bit integerstimestamps, ids beyond 2**53

The buffer is the data; views are how you read it

ArrayBuffer(8)   [ b0 b1 b2 b3 b4 b5 b6 b7 ]
                     │  │  │  │  │  │  │  │
Uint8Array(8)        u0 u1 u2 u3 u4 u5 u6 u7
Uint32Array(2)       └── word 0 ──┘└── word 1 ──┘

writing through one view changes what the other one reads
const buffer = new ArrayBuffer(4);
const asBytes = new Uint8Array(buffer);
const asWord = new Uint32Array(buffer);

asBytes[0] = 1;
asBytes[1] = 1;

console.log([...asBytes]);   // [1, 1, 0, 0]
console.log(asWord[0]);      // 257 on a little-endian machine
console.log(buffer.byteLength, asWord.length); // 4 1
const view = new DataView(new ArrayBuffer(2));

view.setUint16(0, 258, false);          // false = big-endian
console.log([...new Uint8Array(view.buffer)]); // [1, 2]

console.log(view.getUint16(0, false));  // 258, read it back big-endian
console.log(view.getUint16(0, true));   // 513, same bytes, wrong assumption

DataView lets you choose the byte order per read and write.

const samples = new Float32Array([0.5, -0.25, 0.75]);

console.log(samples.map((v) => v * 2));      // Float32Array [1, -0.5, 1.5]
console.log(samples.filter((v) => v > 0));   // Float32Array [0.5, 0.75]
console.log(samples.subarray(1));            // a view, not a copy
console.log(Array.from(samples));            // back to a normal array
// samples.push(1), TypeError: length is fixed

Typed arrays have most array methods, but not the ones that change length.

console.log(new Float64Array([0.1])[0]); // 0.1
console.log(new Float32Array([0.1])[0]); // 0.10000000149011612
console.log(new Float16Array([0.1])[0]); // 0.0999755859375
console.log(new Float16Array([1.5])[0]); // 1.5, exact, it is a power-of-two fraction

When you will actually need this

  • Reading an uploaded file with await file.arrayBuffer() and parsing a header.
  • Canvas pixel manipulation via ctx.getImageData().data, which is a Uint8ClampedArray.
  • WebGL or WebGPU vertex, index and texture buffers.
  • Web Audio, where sample data is Float32Array.
  • Streaming binary protocols over a WebSocket or fetch body.
  • Passing memory to WebAssembly, which only speaks bytes.
const a = new Uint8Array(2);
a[0] = 300;
a[1] = -1;
console.log(a[0], a[1], a.length);

Uint8Array keeps the low 8 bits, so 300 wraps to 44 and -1 becomes 255 in two-s complement. Nothing throws, which is why silent corruption is the classic typed-array bug. Uint8ClampedArray would have given 255 and 0.

Try it yourself

One buffer, several views

const buffer = new ArrayBuffer(8);
const bytes = new Uint8Array(buffer);
const words = new Uint32Array(buffer);
const view = new DataView(buffer);

words[0] = 0x01020304;
console.log('bytes after a little-endian write:', [...bytes.subarray(0, 4)]);

view.setUint32(4, 0x01020304, false); // big-endian
console.log('bytes after a big-endian write:', [...bytes.subarray(4)]);

console.log('as hex:', [...bytes].map((b) => b.toString(16).padStart(2, '0')).join(' '));

Write through the Uint32Array and read the bytes back. Then redo it with a DataView in big-endian and compare the byte order.

Exercises

Bytes to hex

Write bytesToHex(bytes) that takes a Uint8Array and returns a lowercase hex string, two characters per byte, with no separator. An empty input gives an empty string.

Check yourself

What does this log?
4 — Only the low 8 bits are kept, so 260 (which is 256 + 4) becomes 4. Typed arrays never throw on out-of-range writes, which makes this a quiet class of bug. Use Uint8ClampedArray if you want 255 instead.
You are parsing a network packet whose fields are big-endian. Which tool do you use?
DataView with the little-endian flag set to false — Typed array views always use the host byte order, which is little-endian on the machines you ship to. DataView lets you state the byte order per read, so getUint16(0, false) reads big-endian regardless of the hardware.
Which is true of typed arrays?
Their length is fixed at creation, and they have map and filter but no push — A typed array is a fixed-width view over a fixed-size buffer, so nothing can change its length. It does carry the iteration methods, and each one returns a typed array of the same kind rather than a plain array.

Common mistakes

  • Assuming an out-of-range write throws. It wraps (or clamps) silently.
  • Reading multi-byte values with a typed array view and forgetting the host is little-endian.
  • Expecting Float32Array to round-trip a decimal exactly. It stores 32 bits, not 64.

Takeaways

  • An ArrayBuffer is bytes; typed arrays and DataView are views over those bytes.
  • Writes are wrapped or clamped to the element width, never rejected.
  • Typed array views follow host byte order, so use DataView whenever bytes cross a boundary.
  • Most application code never needs these. Recognise them, and reach for them only for real binary work.