Where to use ArrayBuffer vs typed array in JavaScript?

Karel Bílek picture Karel Bílek · Feb 23, 2017 · Viewed 13.3k times · Source

I am moving from Node.js to browser environment, and I am still confused over ArrayBuffer vs. typed arrays (such as Uint8Array).

I am confused over where to use the typed arrays, and where to use ArrayBuffer directly. It's not hard to convert one to the other and vice versa, but which to use when?

For example, when I am creating an object that will represent a chunk of data in my code, should it be ArrayBuffer or Uint8Array? What does it depend on?

Or: should I rather return ArrayBuffer from my functions (for example, for external API), or the typed arrays?

Note that I can google how exactly to add elements etc to those typed arrays; what I am missing is some short general guide what to use where. Especially when moving from node's Buffer.

Answer

user1693593 picture user1693593 · Feb 23, 2017

Concepts

ArrayBuffers represents a byte-array in physical memory. An ArrayBuffer is the actual storage for the bytes but is rarely used directly - in fact, you don't have access to read content of ArrayBuffer directly and can only pass a reference for it. They are on the other hand used for binary data transfers between server and client, or from the user's file system via Blobs.

ArrayBuffer byte array in memory
ArrayBuffer byte array in memory - each index equals one byte. ArrayBuffer is aligned in memory.

To read the content of an ArrayBuffer you need to use a view. This sits on top and offers an "api" to access the bytes by different width types, or arbitrarily.

Width-dependent Views

The different views are used depending on what you need. If you only need to read byte values, ie. signed values between -128 and 127 -or- unsigned values between 0-255, you would use Int8Array or Uint8Array. Notice that their names are a bit "misleading" as they are views and not arrays, and only references the underlying ArrayBuffer.

Likewise, you have views for Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint3Array, Float32Array and Float64Array.

With the exception of *int8Arrays the others come with some requirement to ArrayBuffer size. For example, a Uint32Array view must sit on top of an ArrayBuffer that is divisible by four, otherwise it throws an error. *int 16 views would require a two-byte boundary.

This is usually not a problem because you can specify number of indexes using the view's constructor directly and a matching ArrayBuffer will be created automatically for it fulfilling these requirements.

And since the ArrayBuffer is a byte-array a *int16 view reads two bytes from it - or, one index = two bytes, *int32 four, or one index = four bytes, and so on.

The main difference between Uint8Array and Uint8ClampedArray is that values outside the range are subject to modulo (for example 256 becomes 0) with the ordinary arrays. In the clamped array the values are as suggested clamped instead (256 becomes 255).

*int16 view
Int16/Uint16 views - each index represents two bytes and is memory aligned.

*int32 view
Int32/Uint32 and Float32 views - each index represents four bytes and is memory aligned.

Float64 view
Float64 view - each index represents eight bytes and is memory aligned.

DataView for flexibility

Then there is the DataView. This is intended for scenarios where you need a flexible ArrayBuffer and need to read variable widths and from positions in the buffer that is not necessarily width or memory aligned.

For example, a *int32 index will always point to a memory location that is dividable by four. A DataView on the other hand can read a Uint32 from say, position 5 and will take care of all the needed steps internally (bit shifting, masking etc.), but at the cost of a tiny overhead.

One other difference is that a DataView doesn't use indexes but absolute byte-positions for the data it represents, and it comes with its own methods to read or write various widths from/to any position.

DataView
DataView - can read from any position and any width.

In other cases you can use several different views referencing the same underlying ArrayBuffer.

There is currently not 64-bits views for integer numbers, but seem to be proposed for ES8.

SharedArrayBuffers

It's also useful to mention the new SharedArrayBuffers that can be used across web workers.

You could (and still can) use transferable objects in the past in some browsers, but SharedArrayBuffers is more efficient in the sense the memory stays the same, only information about it is transferred. SharedArrayBuffers cannot become detached as ArrayBuffers can.

Purpose and Usage areas

Typed arrays are good to store specific numeric values and are fast. Bitmaps is a typical candidate for typed arrays (e.g. canvas 2D/WebGL).

Heavy data processing of data inside web workers is another use and so on. I already mentioned binary transfer between client and server or the file-system.

DataViews are perfect to parse or build binary files and file formats.

Typed arrays are an excellent way to pack binary data for sending over the net, to server or via web sockets and things like data-channels for WebRTC.

If you deal with audio, video, canvas, or media recording, there is often no way around using typed arrays.

The keys for using typed arrays are performance and memory. They are most often used in special scenarios, but there is nothing wrong using them in ordinary cases when you only need to store numeric values (or utf-8 strings, encryption vectors etc.). They are fast and have a low memory footprint.

Precautions

There are a couple of precautions to be aware of:

Byte-order

Some precautions must be made in regards to byte-order. Typed arrays always reflects the CPU-architecture they run under, ie. little-endian or big-endian. Most consumer systems are little-endian but when using *int16 and *int32 arrays you must pay special attention to byte-order. DataView can help with this part too, but is not always a good choice if performance is important.

Byte-order is also important when receiving data from server. They are usually always in big-endian format (AKA "network order"). For parsing file formats the same will apply.

Floating Point Number Encoding

Float32/Float64 will read and write numbers encoded in the IEEE-754 format. This is also something to be aware of if several views are used for the same buffer.

Cross-browser Support

Most browsers supports typed arrays nowadays. If you have to deal with older browsers you have to go back to IE9 or older mobile browsers to not be able to use them.

Safari is not particular optimized in regards to their performance, but the other benefits are there. Version 5.1 does not support Float64.

Mobile devices has their own hardware limitations, but in general: typed arrays are safe to use. For special cases there exist a polyfill.