Working with Typed Arrays in Modern JavaScript

Updated: May 16, 2023 By: Goodman Post a comment

What are Typed Arrays?

Typed Arrays are a way to handle binary data in JavaScript. They are array-like objects that provide a mechanism for reading and writing raw binary data in memory buffers. Unlike normal arrays, Typed Arrays have a fixed length and only contain numeric element types, such as Int8, Uint32, Float64, etc. This means they are far more efficient and faster than normal arrays for some specific operations, such as image/video processing and machine learning.

How to Create a Typed Array

When you start working with Typed Arrays, it’s natural to think about a class named TypedArray. However, the TypedArray class is nowhere to be found because such a thing doesn’t exist in JavaScript. Instead, the programming language provides 11 classes for kinds of Typed Arrays, each with a different element type and size:

  • Int8Array: 8-bit two’s complement signed integer
  • Uint8Array: 8-bit unsigned integer
  • Uint8ClampedArray: 8-bit unsigned integer (clamped)
  • Int16Array: 16-bit two’s complement signed integer
  • Uint16Array: 16-bit unsigned integer
  • Int32Array: 32-bit two’s complement signed integer
  • Uint32Array: 32-bit unsigned integer
  • Float32Array: 32-bit IEEE floating point number
  • Float64Array: 64-bit IEEE floating point number
  • BigInt64Array: 64-bit two’s complement signed big integer
  • BigUint64Array: 64-bit unsigned big integer

Each type of Typed Array has a constructor that takes an array-like or iterable object as an argument and creates a new Typed Array with the same elements converted to the corresponding type.

Example:

const int8 = new Int8Array([0, 1, 2]); 
console.log(int8);

const uint8 = new Uint8Array([-1, 256, 3.5]); 
console.log(uint8); 

const uint8c = new Uint8ClampedArray([-1, 256, 3.5]);
console.log(uint8c); 

The output (in my Chrome DevTools console):

Typed Arrays are not true arrays. This sounds confusing. The code below will help you clear the cloud:

const int8 = new Int8Array([0, 1, 2, 3]);
console.log(Array.isArray(int8)); // false

Another example:

// 1024 bytes or 1 kilobyte
let bytes = new Uint8Array(1024);

// A 3x3 matrix
let matrix = new Float64Array(9);

// A point in 3D space
let point = new Int16Array(3);

// A 4-byte RGBA pixel value (red, green, blue, alpha)
let rgba = new Uint8ClampedArray(4);

// a chess board
let board = new Int8Array(64);

Accessing, Updating, and Deleting Elements in a Typed Array

You can access elements in a Typed Array using the same syntax as normal arrays, that is, using square brackets and an index.

Example:

const int8 = new Int8Array([0, 1, 2]); 
console.log(int8[0]); // 0
console.log(int8[1]); // 1
console.log(int8[2]); // 2

You can update elements in a Typed Array by using the same syntax as regular arrays.

Example:

const int8 = new Int8Array([0, 1, 2]);
int8[0] = 5; // update the first element to 5
console.log(int8); // Int8Array(3) [ 5, 1, 2 ]

You cannot remove elements from a Typed Array, because Typed Arrays have a fixed length that cannot be changed. However, you can create a new Typed Array with a smaller length and copy the elements you want to keep from the original Typed Array.

Example:

const int8 = new Int8Array([0, 1, 2, 3]);

// create a new Typed Array with length 2
const int8small = new Int8Array(2);

// copy the first two elements from int8
int8small.set(int8.subarray(0, 2));

Iterating over a Typed Array

You can iterate over a Typed Array using the same methods as normal arrays, such as the for…of loop, the forEach() method, or the for loop.

Example:

const int8 = new Int8Array([0, 1, 2]); // [0, 1, 2]

// using for...of loop
for (const element of int8) {
  console.log(element); // 0, 1, 2
}

// using forEach method
int8.forEach((element) => {
  console.log(element); // 0, 1, 2
});

// using for loop
for (let i = 0; i < int8.length; i++) {
  console.log(int8[i]); // 0, 1, 2
}

Type Array Methods and Properties

Typed Arrays are not true arrays, but most of the standard array methods and properties are available for them, such as length, indexOf(element, fromIndex), includes(element, fromIndex), slice(begin, end), reverse(), map(callback, thisArg), filter(callback, thisArg), reduce(callback, initialValue), etc. You can see more on this page: The Modern JavaScript Arrays Cheat Sheet.

Let’s have a look at methods and properties that are only available for Typed Arrays, but not regular arrays:

  • buffer: a property that returns the underlying ArrayBuffer of the Typed Array.
  • byteLength: a property that returns the length in bytes of the Typed Array.
  • byteOffset: a property that returns the offset in bytes of the Typed Array within the buffer.
  • BYTES_PER_ELEMENT: a static property that returns the size in bytes of each element of the Typed Array.
  • set(array, offset): a method that copies the elements of an array or a Typed Array into the Typed Array, starting at a given offset.
  • subarray(begin, end): a method that returns a new Typed Array that shares the same buffer as the original Typed Array but with a specified begin and end index.

Example:

// create a Typed Array with 8 elements
const int8 = new Int8Array([0, 1, 2, 3, 4, 5, 6, 7]); 
// Int8Array(8) [0, 1, 2, 3, 4, 5, 6, 7]

const array = [8, 9];

// use the set() method to copy another array into int8 at a given offset
// copy array into int8 starting from index 4
int8.set(array, 4); 
console.log(int8); 
// Int8Array(8) [0, 1, 2, 3, 8, 9, 6, 7]

// use the subarray() method to create a new Typed Array that shares the same buffer as int8
// create a subarray from index 2 to index 6 (not inclusive)
const int8sub = int8.subarray(2, 6); 
console.log(int8sub); 
// Int8Array(4) [2, 3, 8, 9]
console.log(int8sub.buffer === int8.buffer); 
// true

// modify the subarray
int8sub[0] = -1;
int8sub[1] = -2;

// see the changes in the original array
console.log(int8); 
// Int8Array(8) [0, 1, -1, -2, 8, 9, 6, 7]

ArrayBuffer, DataView, and Endianness

DataView and ArrayBuffer are two JavaScript objects that are used to handle binary data. They were added to the language along with the Typed Array data structure.

An ArrayBuffer is an object that represents a generic, fixed-length binary data buffer. You cannot directly manipulate the contents of an ArrayBuffer; instead, you create a Typed Array view or a DataView which represents the buffer in a specific format and use that to read and write the contents of the buffer.

A DataView is a low-level interface that provides a getter/setter API to read and write multiple number types in a binary ArrayBuffer, without having to care about the platform’s endianness. Endianness is the order of bytes in a multi-byte number, which can vary depending on the machine architecture1. With a DataView, you can access data as elements of different types and sizes, such as Uint8, Int16, Float32, etc., at any byte offset inside an ArrayBuffer.

An example of using a DataView to access an ArrayBuffer is:

// create an ArrayBuffer with 11 bytes
const buffer = new ArrayBuffer(11);

// create a DataView for the buffer
const dataView = new DataView(buffer);

// write 65 as an unsigned 8-bit integer at byte 0
dataView.setUint8(0, 65); 
// read the unsigned 8-bit integer at byte 0
console.log(dataView.getUint8(0)); 
// 65

// write -100 as a signed 16-bit integer at byte 1
dataView.setInt16(1, -100); 
// read the signed 16-bit integer at byte 1
console.log(dataView.getInt16(1)); 
// -100

// write 3.14 as a 64-bit floating point number at byte 3
dataView.setFloat64(3, 3.14);
// read the 64-bit floating point number at byte 3
console.log(dataView.getFloat64(3));
// 3.14

Before finishing this article, let’s see one more example. What we will do is to create Typed Arrays from an ArrayBuffer:

// create an ArrayBuffer with 8 bytes
const buffer = new ArrayBuffer(8);

// create a Uint8Array view of the buffer
const uint8 = new Uint8Array(buffer);

// assign some values to the Uint8Array
uint8[0] = 0;
uint8[1] = 1;
uint8[2] = 2;
uint8[3] = 3;
uint8[4] = 4;
uint8[5] = 5;
uint8[6] = 6;
uint8[7] = 7;

// create a Float64Array view of the same buffer
const float64 = new Float64Array(buffer);

// print the values of the Float64Array
console.log(float64[0]); 
// 7.949928895127363e-275

// change the value of the first element of the Float64Array
float64[0] = 3.14;

// print the values of the Uint8Array
console.log(uint8);
// Uint8Array(8) [31, 133, 235, 81, 184,  30, 9, 64]

As you can see, the Typed Arrays share the same underlying buffer and can access and modify the same binary data in different formats.