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.
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 array | Regular array | |
|---|---|---|
| Element type | Fixed, single numeric type | Any mix of types |
| Length | Fixed at creation | Grows and shrinks freely |
| Memory layout | Contiguous, matches the type’s byte width | Sparse-capable, boxed elements |
| Performance on numeric workloads | Fast, predictable | Slower — general-purpose overhead |
| Typical use | Binary data, buffers, GPU input, audio/image samples | General 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.
Keep reading
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.
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.
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.