JavaScript Typed Arrays: Binary Data, Buffers, and Views
Typed arrays let you work with raw binary data in JavaScript using compact, fixed-size numeric arrays. They are essential for files, graphics, audio, network protocols, and any code that needs predictable memory layout.
Quick answer: A typed array is a numeric view over an ArrayBuffer. It stores one specific number type, such as bytes or 32-bit integers, and is much better than a normal array when you need binary data or performance-sensitive memory access.
Difficulty: Intermediate
You'll understand this better if you know: basic arrays, JavaScript numbers, and how objects can reference shared data.
1. What Is Typed Arrays?
Typed arrays are array-like objects designed for binary data. Unlike normal JavaScript arrays, they store values in a fixed numeric format such as 8-bit unsigned integers or 32-bit floating-point numbers.
- They always use a specific numeric type.
- They are backed by an ArrayBuffer, which holds the raw bytes.
- They are ideal for low-level data handling and performance-sensitive code.
- They are not general-purpose arrays for mixed values like strings, objects, and numbers.
In practice, you usually create a typed array when you need to read or write binary bytes directly instead of managing ordinary JavaScript values.
2. Why Typed Arrays Matter
JavaScript arrays are flexible, but that flexibility is not always what you want. If you are handling image pixels, audio samples, compressed data, or network packets, you need a structure that matches the binary format exactly.
Typed arrays matter because they provide predictable storage, efficient numeric access, and a clean way to share the same memory across different views. That makes them useful in browser APIs, Node.js, and WebAssembly integrations.
They are not meant to replace regular arrays in everyday application code. Use them when the data is truly binary or when you need tight control over bytes and numeric representation.
3. Basic Syntax or Core Idea
Creating a buffer and a typed array
The simplest typed array uses an ArrayBuffer plus a typed view such as Uint8Array. The buffer owns the bytes, and the typed array decides how to interpret them.
const buffer = new ArrayBuffer(8);const bytes = new Uint8Array(buffer);bytes[0] = 255;bytes[1] = 42;console.log(bytes);This code creates 8 raw bytes and exposes them as an unsigned 8-bit array. Each element must stay between 0 and 255.
Common typed array constructors
JavaScript provides several typed array classes for different numeric ranges and use cases.
- Uint8Array for bytes.
- Int16Array for signed 16-bit integers.
- Uint32Array for unsigned 32-bit integers.
- Float32Array for 32-bit floating-point numbers.
- Float64Array for 64-bit floating-point numbers.
Each constructor determines how values are stored and read from the same underlying bytes.
4. Step-by-Step Examples
Example 1: Filling a byte buffer
This example shows the most common starting point: create bytes, write values, and read them back.
const bytes = new Uint8Array(4);bytes[0] = 10;bytes[1] = 20;bytes[2] = 30;bytes[3] = 40;console.log(bytes[2]);Here, the typed array behaves like a numeric list, but each entry is stored as a single byte.
Example 2: Shared memory with two views
The same bytes can be viewed in different ways. This is one of the biggest strengths of typed arrays.
const buffer = new ArrayBuffer(8);const bytes = new Uint8Array(buffer);const words = new Uint16Array(buffer);bytes[0] = 1;bytes[1] = 0;console.log(words[0]);Both views point at the same memory, so changing one affects the other. This is useful for parsing binary formats efficiently.
Example 3: Converting from a regular array
You can build a typed array from a normal array when you already have numeric values.
const source = [3, 7, 11];const values = new Int16Array(source);console.log(values);This copies the numbers into a fixed numeric format. If a number cannot fit the target type, it is converted according to that type’s rules.
Example 4: Using subarray for a slice-like view
Typed arrays can create lightweight views over part of the same buffer.
const data = new Uint8Array([10, 20, 30, 40, 50]);const middle = data.subarray(1, 4);middle[0] = 99;console.log(data);subarray does not copy the data. It returns a new view into the same memory, so edits affect the original buffer.
5. Practical Use Cases
- Reading binary files such as images, fonts, or audio samples.
- Parsing network protocols and binary message formats.
- Working with canvas pixel data in browser graphics code.
- Passing numeric data to Web APIs and WebAssembly.
- Handling performance-sensitive numeric computations.
- Building compact data structures when memory usage matters.
When you see raw bytes or a spec that defines field sizes in bits and bytes, typed arrays are usually the right tool.
6. Common Mistakes
Mistake 1: Expecting typed arrays to behave like normal arrays
Typed arrays look like arrays, but they follow stricter rules. You cannot store strings, objects, or out-of-range values the way you can in a regular array.
Problem: This code assumes a typed array can hold any kind of data, but typed arrays only store numbers in their defined numeric range.
const items = new Uint8Array(3);items[0] = "hello";items[1] = { value: 1 };Fix: Use a regular array if you need mixed data, or convert only numeric values into the typed array.
const items = ["hello", { value: 1 }];const bytes = new Uint8Array([1, 2, 3]);The corrected version separates general-purpose arrays from numeric binary storage.
Mistake 2: Confusing shared views with copies
Methods like subarray create a view, not a copy. Many bugs happen when a developer expects one buffer to stay unchanged.
Problem: Changing the subarray also changes the original data because both objects reference the same underlying bytes.
const source = new Uint8Array([1, 2, 3, 4]);const view = source.subarray(1, 3);view[0] = 99;console.log(source);Fix: Use slice when you need a copied typed array.
const source = new Uint8Array([1, 2, 3, 4]);const copy = source.slice(1, 3);copy[0] = 99;console.log(source);The fixed version protects the original data because slice copies the selected bytes.
Mistake 3: Assuming all numeric types store the same range
Each typed array has its own numeric limits. Writing a value outside the range can wrap, clamp through conversion rules, or lose precision.
Problem: A Uint8Array cannot preserve values larger than 255, so direct assignment produces unexpected wrapping behavior.
const bytes = new Uint8Array(1);bytes[0] = 300;console.log(bytes[0]);Fix: Choose a type that matches the data range, or validate values before storing them.
const values = new Uint16Array(1);values[0] = 300;console.log(values[0]);The corrected version works because the selected type can represent the full value without unwanted conversion.
7. Best Practices
Practice 1: Pick the narrowest type that fits your data
Smaller types use less memory and often match binary formats more closely. If your data is a stream of bytes, Uint8Array is usually the best starting point.
const pixelBytes = new Uint8Array(1024);Using the narrowest useful type keeps your code efficient and easier to match against external data formats.
Practice 2: Use DataView for mixed binary layouts
If a binary protocol mixes integers of different sizes or byte orders, DataView is often a better choice than one typed array type.
const buffer = new ArrayBuffer(4);const view = new DataView(buffer);view.setUint16(0, 500, true);view.setUint16(2, 900, true);This approach is more flexible when the byte structure is not uniform.
Practice 3: Prefer slice when you need isolation
Use subarray for speed and shared-memory workflows. Use slice when a new copy is safer and easier to reason about.
const original = new Uint8Array([5, 6, 7]);const safeCopy = original.slice();This reduces accidental mutation bugs when different parts of your program should not share memory.
8. Limitations and Edge Cases
- Typed arrays can only store numbers, not arbitrary JavaScript values.
- They have fixed length after creation; you cannot push or pop items like a normal array.
- Assignments may convert values to fit the target type, which can cause wrapping or precision loss.
- ArrayBuffer holds bytes, but endianness matters when reading multi-byte values through DataView.
- Some APIs return views that share memory, so edits can affect the original source unexpectedly.
- The same code can behave differently if a binary format expects big-endian data but you read it as little-endian, or vice versa.
A common source of confusion is that typed arrays feel list-like, but they are really fixed numeric views over raw memory.
9. Practical Mini Project
In this small example, we will build a simple grayscale pixel buffer and invert its values. This is the kind of task typed arrays are designed for.
const pixels = new Uint8Array([0, 64, 128, 192, 255]);for (let i = 0; i < pixels.length; i++) { pixels[i] = 255 - pixels[i];}console.log(pixels);This project shows how typed arrays are often used in practice: iterate through bytes, transform them in place, and keep memory usage predictable.
10. Key Points
- Typed arrays store numeric data in a fixed binary format.
- An ArrayBuffer holds the raw bytes underneath.
- Different typed array constructors represent different number sizes and ranges.
- subarray shares memory, while slice copies.
- DataView is best for mixed-size binary data and explicit endianness.
- They are most useful for file formats, graphics, audio, protocols, and performance-sensitive work.
11. Practice Exercise
- Create a Uint8Array with the values [2, 4, 6, 8, 10].
- Double each value in place.
- Log the final result.
Expected output: a typed array containing [4, 8, 12, 16, 20].
Hint: Use a for loop and assign back into the same array.
const numbers = new Uint8Array([2, 4, 6, 8, 10]);for (let index = 0; index < numbers.length; index++) { numbers[index] = numbers[index] * 2;}console.log(numbers);12. Final Summary
Typed arrays give JavaScript a practical way to work with binary data using fixed numeric storage. They are not a replacement for regular arrays, but they are the right choice whenever your data is really bytes, samples, pixels, or protocol fields.
Once you understand the relationship between ArrayBuffer, typed array views, subarray, slice, and DataView, you can read and write binary formats with much more confidence. If you want to go further, the next step is learning ArrayBuffer and DataView in more depth, especially for endianness and multi-byte parsing.