Skip to content
JavaScript js arrays 5 min read

Typed Arrays & ArrayBuffer

Most of the time you reach for a regular Array to hold values, but the web platform constantly moves raw bytes around: image pixels, audio samples, network packets, files, and GPU buffers. Typed arrays give JavaScript a way to read and write that binary data efficiently, with fixed numeric types and contiguous memory. They are the backbone of the Canvas, Fetch, WebSocket, Web Audio, and WebGL APIs.

ArrayBuffer: the raw memory

An ArrayBuffer is a fixed-length block of raw bytes. You cannot read or write it directly — it is just memory. To interact with it you create a view that interprets those bytes as numbers of a particular type.

const buffer = new ArrayBuffer(16); // 16 bytes of zeroed memory
console.log(buffer.byteLength);

Output:

16

Typed array views

A typed array is a view layered on top of an ArrayBuffer. The view decides how many bytes each element occupies and how to interpret them. Writing through one view changes the underlying buffer, which means other views see the same data.

const buffer = new ArrayBuffer(8);
const ints = new Int32Array(buffer); // two 32-bit integers
ints[0] = 256;

const bytes = new Uint8Array(buffer); // same buffer, byte view
console.log(bytes[0], bytes[1]); // little-endian layout

Output:

0 1

You can also construct a typed array directly from a length or an array of numbers, and it allocates its own buffer behind the scenes.

const samples = new Float32Array([0.5, -0.25, 1.0]);
console.log(samples.length, samples.buffer.byteLength);

Output:

3 12

The available view types

Each view enforces a numeric type, clamping or wrapping values that fall outside its range.

ViewBytes / elementValue range
Int8Array1-128 to 127
Uint8Array10 to 255
Uint8ClampedArray10 to 255 (clamps, no wrap)
Int16Array2-32768 to 32767
Uint16Array20 to 65535
Int32Array4±2.1 billion
Uint32Array40 to ~4.3 billion
Float32Array432-bit float
Float64Array864-bit float
BigInt64Array864-bit signed BigInt
BigUint64Array864-bit unsigned BigInt

Uint8ClampedArray is special: assigning 300 stores 255 and -5 stores 0, instead of wrapping around. This is exactly what canvas pixel data needs, which is why ImageData uses it.

How they differ from normal arrays

Typed arrays look array-like — they support map, filter, forEach, indexing, and length — but they are not regular arrays. The differences matter in practice.

FeatureRegular ArrayTyped array
Element typesany valueone fixed numeric type
Lengthdynamic (grows/shrinks)fixed at creation
push / popyesno
Holes / undefinedpossiblenever (always numeric)
Memoryscattered objectscontiguous bytes
const view = new Uint8Array(3);
view[0] = 42;
view[5] = 99; // out-of-bounds write is silently ignored
console.log(view.length, view[5]);

Output:

3 undefined

Because map on a typed array returns another typed array of the same kind, use Array.from when you need a plain array or a different element type.

const data = new Uint8Array([10, 20, 30]);
const doubled = data.map((n) => n * 2); // still a Uint8Array
const asArray = Array.from(data, (n) => n / 10);
console.log(doubled, asArray);

Output:

Uint8Array(3) [ 20, 40, 60 ] [ 1, 2, 3 ]

DataView for mixed, explicit layouts

When a buffer packs different types together — for example a binary file header — DataView lets you read each field at a specific byte offset and control endianness explicitly.

const buffer = new ArrayBuffer(8);
const view = new DataView(buffer);
view.setUint16(0, 513, false); // big-endian
view.setFloat32(2, 3.14, true); // little-endian
console.log(view.getUint16(0, false));

Output:

513

Canvas pixels: a real use case

The most common place front-end developers meet typed arrays is image manipulation. getImageData returns an ImageData object whose .data is a Uint8ClampedArray of RGBA bytes — four entries per pixel.

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
ctx.fillStyle = "tomato";
ctx.fillRect(0, 0, canvas.width, canvas.height);

const image = ctx.getImageData(0, 0, canvas.width, canvas.height);
const pixels = image.data; // Uint8ClampedArray, length = w * h * 4

// Invert every pixel: R, G, B (leave alpha untouched)
for (let i = 0; i < pixels.length; i += 4) {
  pixels[i] = 255 - pixels[i];
  pixels[i + 1] = 255 - pixels[i + 1];
  pixels[i + 2] = 255 - pixels[i + 2];
}

ctx.putImageData(image, 0, 0);

The same pattern powers WebGL (vertex and color buffers are Float32Array) and the Web Audio API (sample data is Float32Array).

Reading binary from the network

Typed arrays pair naturally with fetch. Calling .arrayBuffer() on a response gives you the raw bytes, ready to wrap in a view.

const res = await fetch("/logo.png");
const buffer = await res.arrayBuffer();
const bytes = new Uint8Array(buffer);
// PNG files start with the magic bytes 137 80 78 71
console.log(bytes.slice(0, 4).join(" "));

Output:

137 80 78 71

Best Practices

  • Reach for typed arrays only when you genuinely handle binary data or need contiguous numeric memory; a plain Array is simpler for everyday lists.
  • Pick the smallest view that fits your value range — Uint8Array for bytes, Float32Array for GPU/audio — to save memory.
  • Use Uint8ClampedArray for canvas pixels so values clamp to 0–255 instead of wrapping.
  • Remember the length is fixed: build a new buffer instead of trying to push, and use .set() to copy chunks between views.
  • Use DataView when a buffer mixes types or when endianness matters; raw typed arrays follow the platform’s native byte order.
  • Convert with Array.from(typedArray) when you need plain-array semantics or a non-numeric result.
Last updated June 1, 2026
Was this helpful?