Marouane Souda
Working with Binary in JavaScript: What You Need to Know About ArrayBuffer

Working with Binary in JavaScript: What You Need to Know About ArrayBuffer

Most of the time in JavaScript, we're juggling strings, numbers, and objects. We usually don't need to deal with lower level details like direct memory management garbage collection, but sometimes—especially when working with files, network data, or lower-level APIs—we need to dive into binary data. That’s where ArrayBuffer steps in.

In this post, we’ll explore what ArrayBuffer is, when to use it, and how to actually do something useful with it. Whether you're parsing a file or handling a binary protocol, this is a tool you’ll want in your toolkit.

📦 What Exactly Is an ArrayBuffer?

Think of an ArrayBuffer as a chunk of raw memory—a fixed-length continuous memory area, a sequence of bytes. It doesn’t know or care what those bytes represent. On its own, it’s pretty useless.

But pair it with a typed array or a DataView, and now you can read and write values (like 32-bit floats or 16-bit unsigned integers) directly into that memory.

Here’s how to create one:

const buffer = new ArrayBuffer(8); // 8 bytes = 64 bits
console.log(buffer.byteLength);    // 8

Boom. You’ve just reserved a tiny slice of memory.

⚠️ ArrayBuffer is Not an Array of Something

Despite the name, ArrayBuffer has nothing to do with the arrays we are all familiar with:

  • Fixed size: ArrayBuffer can’t be resized once created.
  • No methods: Unlike arrays, it doesn’t have .map(), .forEach(), or similar helpers.
  • Requires a view: You always need a typed array or DataView to access the contents.

🔎 Viewing the Data: Typed Arrays

Like I mentioned earlier, you can't do anything with ArrayBuffer on their own. They just hold your data for you in a specific, predefined area of memory.

To actually do something with this buffer, you need a view.

🤔 What Is a View?

A view is an object that provides a way to read and write data in an ArrayBuffer, interpreting the bytes in a meaningful way (like integers, floats, etc).

The way I understood it best is like a pair of glasses or sunglasses you put on to interpret what you're looking at.

Different glasses (e.g., red-tinted, magnifying, polarized) help you see the same thing in different ways—just like different views (Uint8Array, DataView, etc.) help you read the same buffer in different ways.

There are two types of views in JavaScript:

🔹 1. Typed Arrays (e.g., Uint8Array, Float32Array, etc.)

These let you read/write data of a specific type. For example:

const buffer = new ArrayBuffer(4);
const uint8View = new Uint8Array(buffer);

uint8View[0] = 255;
uint8View[1] = 128;

console.log(uint8View); // Uint8Array(4) [255, 128, 0, 0]

This Uint8Array view treats the buffer as a series of 8-bit unsigned integers.

255 and 128 are the decimal representations of the binaries stored in their respective memory slots, 11111111 and 01111111.

Other views include:

  • Int8Array, Uint8Array
  • Int16Array, Uint16Array
  • Int32Array, Uint32Array
  • Float32Array, Float64Array

Each one interprets the binary data differently, depending on how you want to read or write it.

Those views store the buffer they're representing inside their buffer property.

let arr8 = new Uint8Array([0, 1, 2, 3]);

// another view on the same data
let arr16 = new Uint16Array(arr8.buffer);

Notice that we can also pass an array to the view, and that can read the same data in two different ways, using two different typed arrays: Uint8Array and Uint16Array.

Keep in mind that TypedArray refers to all of the views listed above. There is no actual class in JavaScript with the name TypedArray. It's just a generic term that represents all those views that shere the same properties and methods.

🔹 2. DataView For More Control

When your data format is more complex—like a mix of bytes, shorts, and floats—DataView gives you precise control over what to read/write and where.

const buffer = new ArrayBuffer(4);
const view = new DataView(buffer);

view.setUint16(0, 5000);  // Write 5000 at byte offset 0
view.setUint16(2, 1000);  // Write 1000 at byte offset 2

console.log(view.getUint16(0)); // 5000
console.log(view.getUint16(2)); // 1000

💡 Real-World Use Case: Reading a Binary File

Let’s say you’re reading a file from an <input> element:

async function handleFile(file) {
  const buffer = await file.arrayBuffer();
  const view = new Uint8Array(buffer);

  console.log('First byte:', view[0]);
  // Do something cool with the binary data
}

This pattern is super common when working with file uploads, WebSockets, Bluetooth devices, or streaming APIs.

Be careful when you're loading the entire file into memory at once, because this can cause performance issues, or crash the browser on very large files (hundreds of MBs or more). Instead, you should consider streaming the file, or reading in chunks with Blob.slice() and FileReader.

🎉 BONUS: Read Binary As Text

If the data stored in the memory is utf-8 text, we can use TextDecoder to decode that data and read it as text.

const view = new Uint8Array([72, 69, 76, 76, 79]);
const decoder = new TextDecoder();
const decodedText = decoder.decode(view);
console.log(decodedText) // HELLO

The numbers in the array supplied to the view correspond to specific characters according to the utf-8 character set table. You can check it here.

TextDecoder constructor accepts an optional label argument, specifying the character encoding. By default, it's utf-8.

decode method of TextDecoder takes either an ArrayBuffer or the view of an ArrayBuffer as its argument, and returns the utf-8 decoded string.

✨ Wrap-up

JavaScript isn’t just for the web—it’s got real chops when it comes to low-level data, too. If you're working with files, protocols, or APIs that require binary formats, ArrayBuffer is your gateway.

It might seem intimidating at first, but once you get the hang of it, it opens the door to a whole new category of problems you can solve.