Articles

JavaScript Typed Arrays and ArrayBuffer, Explained

ArrayBuffer allocates raw binary memory; typed arrays like Uint8Array give it a numeric type and view. How JavaScript handles binary data directly.

Takina Takina · · 4 min read
A laptop screen showing JavaScript code in blue light

An ArrayBuffer is a fixed-length block of raw binary memory in JavaScript, and a typed array — Uint8Array, Float64Array, and similar constructors — is a view onto that memory that interprets its bytes as a specific numeric type. Together they give JavaScript a way to work with contiguous, fixed-type binary data directly, instead of routing everything through regular, dynamically-typed arrays.

Why JavaScript needed this

A regular JavaScript array is flexible by design: it can hold mixed types, grow or shrink freely, and have gaps. That flexibility comes from every element being a boxed value with type information attached, which is convenient for general-purpose code but wasteful for binary data — decoding an image, processing audio samples, parsing a network packet, or feeding vertex data to a GPU. Those tasks want a flat block of memory where every element is the same fixed-width type, laid out exactly as it would be in a lower-level language. ArrayBuffer and typed arrays exist to serve that need without leaving JavaScript.

ArrayBuffer is just memory

An ArrayBuffer on its own is opaque — you allocate a fixed number of bytes, but you can’t read or write them directly:

const buffer = new ArrayBuffer(16); // 16 bytes, uninitialized to zero

To actually work with that memory, you create a view over it — a typed array or a DataView — which interprets the raw bytes as numbers.

Typed array views

Each typed array constructor interprets the underlying buffer as a specific numeric type: Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array, and the 64-bit integer variants BigInt64Array and BigUint64Array. Creating one over the same buffer with a different constructor reads the identical bytes as a different type:

const buffer = new ArrayBuffer(4);
const asInt32 = new Int32Array(buffer);
const asUint8 = new Uint8Array(buffer);

asInt32[0] = 1;
console.log(asUint8); // the same 4 bytes, read as four 8-bit values

Multiple views can share one buffer at once, which is exactly how you’d parse a binary format with mixed field types — allocate one buffer, then layer different typed-array views over different byte ranges of it.

DataView for mixed-type, endian-aware access

Typed arrays assume a single type and the platform’s native byte order for the whole view. When a format mixes types at specific offsets, or needs explicit control over byte order — parsing a binary file format or a network protocol, for example — DataView is the right tool. It lets you read or write a specific type at a specific byte offset, choosing big-endian or little-endian per call. See big-endian vs little-endian for why that choice matters when a format’s byte order doesn’t match the platform’s native order.

const view = new DataView(buffer);
view.setUint16(0, 258, false); // big-endian
console.log(view.getUint16(0, false)); // 258

Typed arrays vs regular arrays

Typed arrayRegular array
Element typeFixed, single numeric typeAny mix of types
LengthFixed at creationGrows and shrinks freely
Memory layoutContiguous, matches the type’s byte widthSparse-capable, boxed elements
Performance on numeric workloadsFast, predictableSlower — general-purpose overhead
Typical useBinary data, buffers, GPU input, audio/image samplesGeneral application logic

Where this shows up in practice

Binary APIs across the web platform are built around ArrayBuffer and typed arrays rather than regular arrays. A fetch() response can be read with .arrayBuffer() to get raw bytes instead of parsed JSON or text — see the Fetch API for the rest of that surface. Rendering pipelines built on WebGPU hand vertex and texture data to the GPU as typed arrays, since the GPU expects fixed-width numeric buffers, not boxed JavaScript values. The Web Audio API exposes raw sample data the same way.

Typed arrays also matter for moving data efficiently between contexts. structuredClone and postMessage can transfer an ArrayBuffer’s ownership to another thread or worker instead of copying it, which avoids duplicating potentially large binary payloads. And SharedArrayBuffer with Atomics builds directly on the same foundation, letting multiple threads read and write the same underlying memory with race-free operations — something that’s only possible because the memory has a fixed, predictable byte layout in the first place.

The takeaway

ArrayBuffer is raw, fixed-length binary memory that can’t be read or written directly; typed arrays and DataView are the views that give that memory meaning, interpreting its bytes as specific numeric types. Reach for them whenever JavaScript needs to work with binary data on its own terms — decoding files, feeding a GPU, handling audio, or transferring large payloads between threads — rather than paying the overhead of boxed, dynamically-typed regular arrays for data that was never variably typed to begin with.

Takina Takina · · 6 min read

How to Break Up Long Tasks in JavaScript

Long tasks block the main thread for 50ms or more and make pages feel frozen. How to split them with yielding, scheduler.yield(), postTask, and workers.

#JavaScript #Performance #Web Development
Takina Takina · · 5 min read

Microtasks vs Macrotasks in JavaScript, Explained

Microtasks (promise callbacks) run before the next macrotask (timers, events). How the two queues are ordered, and why it matters for your code.

#JavaScript #Web Development #Performance
Takina Takina · · 5 min read

OffscreenCanvas API Explained

OffscreenCanvas lets you render canvas graphics off the main thread, in a web worker, so heavy drawing work stops blocking scrolling, input, and animation.

#JavaScript #Web Development #Performance