completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

ArrayBuffer & typed arrays

Learn JavaScript typed arrays from bytes to ArrayBuffer, DataView, endian order, files, pixels, transfer, and resizable memory.

By the end, you can
  • 01
    Picture bytes and buffersExplain what a byte is, write it in hex, and show how several views read the same bytes in an ArrayBuffer.
  • 02
    Pick the right viewChoose a typed array type or a DataView by which numbers fit, how they round, and which byte order a file uses.
  • 03
    Share, copy and move bytes on purposeKnow when subarray, slice, set, resize, transfer and postMessage share bytes, copy them, or move them.

Start with bytes

The previous lesson, regex performance, ended the Regular expressions module. This lesson starts a new module, Binary data & memory. "Binary data" means raw bytes instead of text. You meet raw bytes in many places:

  • The first bytes of a file, which say what kind of file it is.
  • The pixels of an image.
  • Sound, stored as many small numbers.
  • Data for 3D graphics, such as WebGL.
  • Compressed files and messages sent over a network.

A bit is one 0 or 1. A byte is 8 bits in a row. 8 bits can make 256 different patterns. Read as a number with no minus sign, a byte is a whole number from 0 to 255.

People often write bytes in hexadecimal, or "hex". Hex counts in sixteens. It uses the digits 0 to 9 and then the letters a to f. Every byte fits in exactly two hex digits. In JavaScript, a hex number starts with 0x: 0x00 is 0, and 0xff is 255.

One byte written three waysPop out in the code editor (opens in a new tab)JavaScript
const byte = 0b10101010;console.log(byte);console.log(byte.toString(16).padStart(2, "0"));console.log(byte.toString(2).padStart(8, "0"));

Line 1 writes a byte as bits. 0b means "the digits after me are bits". Line 2 prints it as a normal number, 170. Line 3 prints it in hex, aa. Line 4 prints its 8 bits, 10101010.

  • toString(16) turns a number into hex text.
  • toString(2) turns a number into bits.
  • padStart adds zeros at the front, so a byte always shows 2 hex digits or 8 bits.

This first example uses only a normal number, on purpose. A byte is just a value. Decimal, hex and bits are three ways to write the same value.

Plain definition

An ArrayBuffer is a block of raw bytes in memory. It holds the bytes, but it doesn't say what they mean. A view says what the bytes mean. There are two kinds of view:

  • A typed array, such as Uint8Array, reads the bytes as a list of numbers that all have one type.
  • A DataView reads any type of number at any byte position you name.

Normal arrays from Array basics are different. They can grow, and they can hold any mix of values: numbers, strings, objects. A typed array holds only numbers, all of one type, packed side by side with no gaps. Browser features that work with bytes, such as files, images and sound, use typed arrays.

The number rules come from Number & Math in depth and BigInt. This lesson shows how those numbers are stored as bytes.

One buffer, many views

STEP THROUGH

You can't read a byte with buffer[0]. An ArrayBuffer has no number type, so it can't say what its bytes mean. First you make a view over it. Then you read and write through the view.

A buffer needs a viewPop out in the code editor (opens in a new tab)JavaScript
const buffer = new ArrayBuffer(2);console.log(buffer.byteLength);console.log(buffer[0]);const bytes = new Uint8Array(buffer);bytes[0] = 65;console.log(bytes[0]);
  • Line 1 makes a buffer of 2 bytes. Every byte starts as 0.
  • Line 2 prints the buffer's size in bytes, 2.
  • Line 3 prints undefined. The buffer itself has no numbered slots to read.
  • Line 4 makes a Uint8Array view over the same 2 bytes. Uint8 means "unsigned 8-bit", and unsigned means "no minus sign". Each slot is one byte, read as a whole number from 0 to 255.
  • Line 5 stores 65 in the first byte. Line 6 reads it back and prints 65.

One buffer can have many views at the same time. A view doesn't copy the bytes. So when you write through one view, the other views see the change right away.

Real-life analogyA buffer is an empty warehouse shelf

A new shelf in a warehouse has eight empty slots. Nobody has decided yet what goes on it. One worker reads it with a label that says "each slot is one small box". Another worker uses a label that groups the slots in pairs. A third worker carries a clipboard. She points at any slot and writes down what she sees there. All three look at the same shelf.

In real life: A shelf with eight empty slots
In JavaScript: new ArrayBuffer(8)
In real life: A label that says each slot is one small box
In JavaScript: new Uint8Array(buffer)
In real life: A label that groups the slots in pairs
In JavaScript: new Uint16Array(buffer)
In real life: A clipboard for reading any slot you point at
In JavaScript: new DataView(buffer)
Watch three views share one buffer
Step 0 of 11Ready
Your turn: follow the blue line

Step through one ArrayBuffer with three views. A write through one view shows up in the others right away.

Running in
  1. script
Next: line 1
const bytes = new Uint8Array(buffer);bytes.set([0x34, 0x12, 0, 0]);const words = new Uint16Array(buffer);console.log(words[0].toString(16));words[1] = 0x7fff;console.log(Array.from(bytes.slice(2, 4)).join(" "));const view = new DataView(buffer);view.setInt8(4, -1);console.log(bytes[4]);console.log(ArrayBuffer.isView(view));
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
One buffer of 8 bytes: 0x34, 0x12, then six 0x00 bytes. bytes[0] reads the first byte, 0x34. words[0] reads the first two bytes together, 0x1234.0x34bytes[0]one buffer, 8 bytes0x340x120x000x000x000x000x000x000x1234words[0]
Both views read the same 8 bytes. bytes[0] reads one byte, and words[0] reads the first two bytes together as 0x1234.

The step-through uses a Uint16Array. Uint16 means each slot is 2 bytes (16 bits), read as a whole number from 0 to 65535. When a number takes more than one byte, the computer must choose which byte comes first. This choice is called byte order.

A typed array uses your computer's own byte order. On almost every computer today, the smallest part of the number comes first. That is why the bytes 34 12 read as 0x1234. A file or a network message may use the other order. For those, use a DataView, where you choose the order yourself. You'll see how two sections below.

In the playground below, change any of the 8 bytes. Every reading on the right uses the same bytes, so they all change together.

Change a byte, watch every view
One buffer, read in several waysPop out in the code editor (opens in a new tab)JavaScript
const buffer = new ArrayBuffer(8);const bytes = new Uint8Array(buffer);bytes.set([0x78, 0x56, 0x34, 0x12, 0xff, 0x00, 0x80, 0x3f]);const view = new DataView(buffer);console.log(view.getUint32(0, true).toString(16));console.log(view.getUint32(0, false).toString(16));console.log(new Float32Array(buffer, 4, 1)[0]);
Live bytes8 bytes
Uint8Array bytes78 56 34 12 ff 00 80 3f
getUint32(0, true)0x12345678
getUint32(0, false)0x78563412
getInt16(0, true)22136
getInt16(0, false)30806
Float32Array at byte 41.0000303983688354
Try it yourself

Bytes 0 to 3 are 78 56 34 12. A DataView reads them as 305419896 little-endian and 2018915346 big-endian.

Change one byte and every reading changes, because every view reads the same 8 bytes. Each box only takes whole numbers from 0 to 255.

Typed array types: sizes, limits and rounding

There are several typed array types. Each one decides three things:

  • How many bytes each number takes: 1, 2, 4 or 8.
  • What kind of number it holds: a whole number with or without a minus sign, a decimal number, or a BigInt.
  • What happens to a number that doesn't fit.

A number that doesn't fit is changed in one of three ways, depending on the type:

  • Whole-number types wrap around, like a car's distance counter going from 999999 back to 0. In a Uint8Array, 256 becomes 0.
  • Decimal types, such as Float32Array, round the number to the closest value they can store.
  • Uint8ClampedArray is made for image pixels. It clamps: anything below 0 becomes 0, and anything above 255 becomes 255.
Copy a normal array into bytes and backPop out in the code editor (opens in a new tab)JavaScript
const scores = [10, 20, 30];const bytes = new Uint8Array(scores);const copiedScores = Array.from(bytes);bytes[0] = 99;console.log(copiedScores.join(","));console.log(Array.from(bytes).join(","));

Line 1 makes a normal array. Line 2 copies its numbers into a new Uint8Array. Line 3 uses Array.from to copy the bytes back into a new normal array. Line 4 changes the first byte to 99. The copy from line 3 doesn't change. So line 5 prints 10,20,30, and line 6 prints 99,20,30.

The table below lists every typed array type. Read each row from left to right: how many bytes one number takes, what kind of number it is, which numbers fit, and where you'll meet it.

Typed array types at a glance
TypeBytes per numberKind of numberNumbers that fitWhere you meet it
Int8Array1Whole number, can be negative-128 to 127Small changes in value, sound samples, fields in network messages
Uint8Array1Whole number, never negative0 to 255Bytes from files, network messages, crypto and WebAssembly
Uint8ClampedArray1Whole number, never negative, clamped0 to 255. Other numbers are rounded, then moved to 0 or 255.Canvas ImageData pixels (red, green, blue, alpha)
Int16Array / Uint16Array2Whole number, can be negative / never negative-32768 to 32767 / 0 to 65535Sound samples and file formats with 2-byte fields
Int32Array / Uint32Array4Whole number, can be negative / never negativeAbout -2.1 billion to 2.1 billion / 0 to about 4.3 billionIndexes, on/off flags packed into bits, WebGL and WebGPU data
Float16Array2Decimal number, low precisionAbout 3 correct digits. The largest is 65504.Graphics, machine learning and image data, where supported
Float32Array4Decimal numberAbout 7 correct digitsWebGL positions, sound data, science data files
Float64Array8Decimal number, high precisionThe same as a normal JavaScript numberLists of numbers that need full precision
BigInt64Array / BigUint64Array8BigInt, can be negative / never negative64-bit whole numbers, every digit exactLarge IDs, counters, positions inside big files, very precise times
Pick the right tool for each job
  • Read a PNG's width at byte offset 16.
  • Change the red, green, blue and alpha of canvas pixels.
  • Send x, y and z positions to WebGL.
  • Hand a 10 MB buffer to a worker without copying it.
  • Look at every byte of an incoming network message.
  • Store exact 64-bit IDs that are never negative.
Try it yourself
0 of 6 correct

Put each job under the tool you would reach for first.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Now see what each type does with numbers that don't fit. Each of lines 1 to 5 makes one typed array, and lines 6 to 10 print them.

What happens to numbers that don't fitPop out in the code editor (opens in a new tab)JavaScript
const signed = new Int8Array([127, 128, 129, -129]);const unsigned = new Uint8Array([255, 256, 257, -1]);const pixels = new Uint8ClampedArray([-20, 12.5, 13.5, 255.4, 300]);const floats = new Float32Array([Math.PI, 1 / 3, 1e40]);const bigs = new BigInt64Array([1n, -2n]);console.log(Array.from(signed).join(","));console.log(Array.from(unsigned).join(","));console.log(Array.from(pixels).join(","));console.log(Array.from(floats).join(","));console.log(Array.from(bigs).join(","));
Left, Uint8Array: 256 becomes 0 and -1 becomes 255, so it wraps around. Right, Uint8ClampedArray: 300 becomes 255 and -20 becomes 0, so it stops at the edge.Uint8ArrayUint8ClampedArray2560300255-1255-200wraps aroundstops at the edge
A Uint8Array wraps a number that doesn't fit around to the other end. A Uint8ClampedArray stops it at the nearest edge, 0 or 255.

Each row of the next table is one line of the code above. Read across for what you write, what gets printed, and why.

What each line of the conversion example prints
What you writeWhat it printsWhy
new Int8Array([127, 128, 129, -129])127,-128,-127,127An Int8Array holds -128 to 127. Numbers outside that wrap around by 256: 128 becomes -128.
new Uint8Array([255, 256, 257, -1])255,0,1,255A Uint8Array holds 0 to 255. It wraps around too: 256 becomes 0, and -1 becomes 255.
new Uint8ClampedArray([-20, 12.5, 13.5, 255.4, 300])0,12,14,255,255Numbers below 0 become 0, and numbers above 255 become 255. Others round to a whole number; a .5 goes to the even neighbour.
new Float32Array([Math.PI, 1 / 3, 1e40])3.1415927410125732,0.3333333432674408,InfinityA Float32Array keeps only about 7 correct digits, so the digits after that change. 1e40 is too big, so it becomes Infinity.
new BigInt64Array([1n, -2n])1,-2A BigInt array only accepts BigInt values such as 1n. A normal number like 1 throws a TypeError.
BigInt arrays need BigInt values

BigInt64Array and BigUint64Array hold 64-bit whole numbers, 8 bytes each. They only accept BigInt values, such as 1n. A normal number like 1 throws a TypeError. Use them when a file or message really stores 64-bit whole numbers, and every digit must stay exact.

Float16Array is new

Float16Array holds decimal numbers in just 2 bytes each. It was added in ES2025, the 2025 version of JavaScript. Older browsers and Node 22 don't have it, so check that it exists before you use it.

It works in Chrome and Edge 135, Firefox 129, Safari 18.2, and Node.js 24 or later. Node 22 does not have Float16Array.

Use Float16Array only if it existsPop out in the code editor (opens in a new tab)JavaScript
const Float16 = globalThis.Float16Array;if (typeof Float16 === "function") {  const values = new Float16([1, 1 / 3, 65504, 1e-8]);  console.log(Array.from(values).join(","));} else {  console.log("Float16Array not supported here");}
  • Line 1 reads Float16Array from globalThis, the object that holds every global name. If it doesn't exist, you get undefined instead of an error.
  • Line 2 checks that it is a function. Only then does line 3 use it.
  • If it doesn't exist, line 6 prints a message instead.

DataView and byte order

BYTE ORDER

Some numbers need more than one byte. For example, 0x12345678 needs 4 bytes: 12, 34, 56 and 78. The byte 12 is the biggest part of the number, like the 1 in 1234, which counts thousands. The byte 78 is the smallest part.

Byte order, also called endianness, says which part is stored first:

  • Big-endian stores the biggest part first. The bytes are 12 34 56 78, the same order you write them.
  • Little-endian stores the smallest part first. The bytes are 78 56 34 12.

Most computers use little-endian in their own memory. But every file format and network format picks its own order, and many pick big-endian.

A DataView is the safest way to read bytes laid out by a file or network format. Every read and write names two things:

  • The byte position to start at, called the offset. Offset 0 is the first byte.
  • For numbers bigger than one byte, the byte order. true means little-endian. false, or leaving it out, means big-endian.
Read 0x12345678 in both byte orders
Step 0 of 6Ready
Your turn: follow the blue line

Write one 4-byte number with a DataView, then read the same four bytes in both byte orders.

Running in
  1. script
Next: line 1
const view = new DataView(buffer);view.setUint32(0, 0x12345678, false);console.log(Array.from(new Uint8Array(buffer)).map((byte) => byte.toString(16).padStart(2, "0")).join(" "));console.log(view.getUint32(0, false).toString(16));console.log(view.getUint32(0, true).toString(16));
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
The same byte-order example as plain codePop out in the code editor (opens in a new tab)JavaScript
const buffer = new ArrayBuffer(4);const view = new DataView(buffer);view.setUint32(0, 0x12345678, false);console.log(Array.from(new Uint8Array(buffer)).map((byte) => byte.toString(16).padStart(2, "0")).join(" "));console.log(view.getUint32(0, false).toString(16));console.log(view.getUint32(0, true).toString(16));

Line 3 writes 0x12345678 big-endian, because its last argument is false. Line 4 prints the bytes: 12 34 56 78. Line 5 reads them back big-endian and prints 12345678. Line 6 reads the same bytes little-endian and prints 78563412.

The bytes 12 34 56 78 read two ways. getUint32(0, false) reads them big-endian, where the first byte is the biggest part, and gives 12345678. getUint32(0, true) reads them little-endian, where the first byte is the smallest part, and gives 78563412.getUint32(0, false)12345678biggest part12345678big-endiangetUint32(0, true)12345678smallest part78563412little-endian
The bytes never change. With false the first byte, 12, is the biggest part of the number; with true it is the smallest part.
Which one to use

If the bytes are your own list of numbers, made and used on the same computer, a typed array is easy. If the bytes come from a file, a network message, or any format with written rules, read them with DataView. Then the byte order is written right in your code, where others can check it.

Share or copy: subarray, slice, set and isView

Typed arrays look like normal arrays. But you need to know when bytes are shared and when they are copied. Four methods matter most:

  • subarray(start, end) makes a new view over part of the same bytes. Nothing is copied.
  • slice(start, end) copies those bytes into a new typed array that has its own bytes.
  • set(source, offset) copies the values in source into a typed array you already have, starting at index offset.
  • ArrayBuffer.isView(value) returns true if value is a typed array or a DataView.

For subarray and slice, the end index is not included. So (1, 3) means indexes 1 and 2.

A shared view versus a copyPop out in the code editor (opens in a new tab)JavaScript
const bytes = new Uint8Array([1, 2, 3, 4]);const shared = bytes.subarray(1, 3);const copied = bytes.slice(1, 3);shared[0] = 99;console.log(bytes.join(","));console.log(copied.join(","));bytes.set([7, 8], 2);console.log(bytes.join(","));console.log(ArrayBuffer.isView(shared));console.log(ArrayBuffer.isView(bytes.buffer));
  • Line 2 makes shared, a view over indexes 1 and 2 of bytes.
  • Line 3 makes copied, a copy of the same two bytes.
  • Line 4 writes 99 through shared. That changes bytes too, so line 5 prints 1,99,3,4.
  • Line 6 prints 2,3. The copy kept its own bytes.
  • Line 7 copies 7 and 8 into bytes, starting at index 2. So line 8 prints 1,99,7,8.
  • Line 9 prints true, because shared is a view. Line 10 prints false, because a buffer is not a view.
After line 4: bytes holds 1, 99, 3, 4. shared points at the middle two bytes of bytes, the same bytes. copied is a separate pair of cards holding 2 and 3, its own copy.bytes19934sharedsame bytes23copiedits own copy
shared uses the middle two bytes of bytes, so writing 99 through it changes bytes. copied has its own bytes, so it still holds 2 and 3.

Each row of the next table is one method. Read across to see whether it copies bytes, and what happens next.

Share or copy: four methods
When you writeDoes it copy bytes?What happens next
view.subarray(1, 3)No. It shares the same bytes.Later writes to those bytes show up in both.
view.slice(1, 3)Yes. It gets its own bytes.Later writes to view don't change the copy.
view.set([7, 8], 2)Yes, into view's own bytes.view now holds 7 and 8 at indexes 2 and 3.
ArrayBuffer.isView(value)No. It only checks.It returns true for typed arrays and DataView, and false for an ArrayBuffer.

Where typed arrays show up: files, pixels, graphics and sound

Browser features that work with bytes usually hand you an ArrayBuffer. Some examples:

  • Files and blobs: blob.arrayBuffer() gives you a file's bytes.
  • fetch(): response.arrayBuffer() gives you the bytes of a download.
  • Canvas: the pixels of an image, called ImageData, are a Uint8ClampedArray.
  • 3D graphics (WebGL and WebGPU), sound (Web Audio), compression tools and WebAssembly all take typed arrays. Typed arrays are packed with no gaps, so the browser can hand them on quickly.
Keep pixel colors in rangePop out in the code editor (opens in a new tab)JavaScript
const pixel = new Uint8ClampedArray([20, 120, 240, 255]);pixel[0] = -10;pixel[1] = 300;console.log(Array.from(pixel).join(","));

Each pixel has 4 numbers, called channels: red, green, blue and alpha. Alpha says how see-through the pixel is. Each channel must be between 0 and 255.

  • Line 2 writes -10 to red. It becomes 0.
  • Line 3 writes 300 to green. It becomes 255.
  • So line 4 prints 0,255,240,255.

A normal Uint8Array would wrap these numbers around instead: -10 would become 246, and 300 would become 44. The colors would come out wrong.

Each row of the next table is one place where bytes show up in front-end work. Read across to see how the bytes reach you, and what you usually do next.

Where typed arrays show up in front-end work
WhereHow the bytes reach youWhat you usually do
File headersCall blob.arrayBuffer(), or file.slice(0, 24).arrayBuffer() for just the first bytes.Read fields at the offsets the format names, with DataView. A PNG stores its width and height as big-endian 4-byte numbers.
Canvas pixelsImageData.data is a Uint8ClampedArray with 4 bytes per pixel: red, green, blue and alpha.Change the bytes directly. The canvas lesson shows how to draw them back.
Downloads and storagefetch(url).then((r) => r.arrayBuffer()), the Cache API, IndexedDB and streams all give you bytes.Make a Uint8Array view and read it one byte at a time.
Graphics and soundWebGL, WebGPU and Web Audio expect packed lists of numbers, such as Float32Array and Uint16Array.Fill a typed array and pass it on. It has no gaps, so the browser can hand it on quickly.

A PNG image file starts with 8 fixed bytes called the signature. They mark the file as a PNG. Next comes a part called the IHDR chunk, which describes the image. It stores the width at offset 16 and the height at offset 20. Each one is a 4-byte whole number, stored big-endian. That makes PNG a good DataView example.

Read a PNG's size from its first bytesPop out in the code editor (opens in a new tab)JavaScript
const png = new Uint8Array([  0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,  0x00, 0x00, 0x00, 0x0d,  0x49, 0x48, 0x44, 0x52,  0x00, 0x00, 0x03, 0x20,  0x00, 0x00, 0x02, 0x58,]);const view = new DataView(png.buffer, png.byteOffset, png.byteLength);const signatureOk = png.slice(0, 8).join(" ") === "137 80 78 71 13 10 26 10";const width = view.getUint32(16, false);const height = view.getUint32(20, false);console.log(signatureOk);console.log(width + "x" + height);
  • Lines 1 to 7 are the first 24 bytes of a PNG file.
  • Line 8 makes a DataView over exactly those bytes. png.byteOffset and png.byteLength say where they start in the buffer and how many there are.
  • Line 9 checks the signature.
  • Lines 10 and 11 read the width and height with getUint32: 4 bytes, a whole number with no minus sign, big-endian.
  • Line 12 prints true, and line 13 prints 800x600.

A packet is one small message sent over a network. The next packet has three fields:

  • Byte 0 is the message type.
  • Bytes 1 and 2 hold a length, as one 2-byte big-endian number.
  • The bytes after that are text, one byte per letter.
Read a small network messagePop out in the code editor (opens in a new tab)JavaScript
const packet = new Uint8Array([2, 0, 5, 104, 105]);const view = new DataView(packet.buffer);const type = packet[0];const length = view.getUint16(1, false);const body = String.fromCharCode(...packet.subarray(3));console.log(type, length, body);

Line 3 reads the type with the byte view. Line 4 reads the length with getUint16(1, false). Line 5 takes the bytes from index 3 to the end with subarray. String.fromCharCode turns them into letters. Line 6 prints 2 5 hi.

With a real file, you only need its first 24 bytes. Line 2 below takes them with file.slice(0, 24) and loads them with arrayBuffer(). await waits until the bytes arrive. Lines 7 and 8 read the width and height the same way as before.

Read the size of a real PNG fileJavaScript
async function readPngSize(file) {  const header = new Uint8Array(await file.slice(0, 24).arrayBuffer());  const view = new DataView(header.buffer, header.byteOffset, header.byteLength);  const signature = header.slice(0, 8).join(" ");  if (signature !== "137 80 78 71 13 10 26 10") throw new Error("Not a PNG");  return {    width: view.getUint32(16, false),    height: view.getUint32(20, false),  };}
Sharing bytes with workers

A worker is JavaScript that runs in the background, at the same time as your page. A SharedArrayBuffer is a buffer that the page and its workers can all read and write at once. Atomics is a set of functions that stops them from changing the same bytes at the same moment. This is a bigger topic. See Messaging & cloning and Web Workers when you need work to run side by side.

Buffers that grow, shrink or move

A normal typed array has a fixed length. Writing past the end does nothing. It doesn't grow the way a normal array does.

Typed arrays have a fixed lengthPop out in the code editor (opens in a new tab)JavaScript
const bytes = new Uint8Array(2);bytes[0] = 7;bytes[1] = 8;bytes[2] = 9;console.log(bytes.length, bytes.join(","));

Line 1 makes a typed array with 2 slots. Lines 2 and 3 fill them. Line 4 tries index 2, which doesn't exist. Nothing happens, and there is no error. So line 5 prints 2 7,8.

A newer kind of ArrayBuffer can change size. To make one, pass maxByteLength, the largest size it may ever have. Then resize(newSize) changes its size, up to that limit. Views over it come in two kinds:

  • A view made with a length, like new Uint8Array(buffer, 0, 4), keeps that length.
  • A view made without a length, like new Uint8Array(buffer), follows the buffer's size as it grows and shrinks.

Each row of the next table is one word you'll see with these buffers. Read across for the code and what it means.

Words for buffers that grow, shrink or move
WordCodeWhat it means
Resizable buffernew ArrayBuffer(4, { maxByteLength: 8 })A buffer that can grow or shrink with resize(), up to 8 bytes here.
Fixed-length viewnew Uint8Array(buffer, 0, 4)A view that always has 4 slots. If the buffer shrinks below 4 bytes, the view's length becomes 0.
Length-tracking viewnew Uint8Array(buffer)A view made with no length. Its length follows the buffer as it grows and shrinks.
Transferbuffer.transfer()Moves the bytes into a new buffer. The old buffer becomes empty (detached), and its byteLength is 0.
Transfer to a fixed sizebuffer.transferToFixedLength()Moves the bytes into a new buffer that can never change size.
Transfer listworker.postMessage(buffer, [buffer])Moves the bytes to a worker without copying them. Your page's buffer becomes empty.
A buffer that grows and shrinksJavaScript
const buffer = new ArrayBuffer(4, { maxByteLength: 8 });const fixed = new Uint8Array(buffer, 0, 4);const tracking = new Uint8Array(buffer);buffer.resize(6);console.log(fixed.length);console.log(tracking.length);buffer.resize(2);console.log(fixed.length);console.log(tracking.length);
  • Line 1 makes a 4-byte buffer that can grow to 8 bytes.
  • Line 2 makes a view with a fixed length of 4. Line 3 makes a view with no length, so it follows the buffer.
  • Line 4 grows the buffer to 6 bytes. Line 5 prints 4, and line 6 prints 6.
  • Line 7 shrinks the buffer to 2 bytes. Now the fixed view's 4 bytes don't fit, so line 8 prints 0. Line 9 prints 2.

Transfer means moving the bytes, not sharing them. buffer.transfer() moves the bytes into a new buffer. The old buffer becomes detached: it is empty, its byteLength is 0, and its old views can't read anything. transferToFixedLength() does the same, but the new buffer can never change size.

Moving the bytes empties the old bufferJavaScript
const buffer = new ArrayBuffer(1024);const moved = buffer.transfer();console.log(buffer.byteLength);console.log(moved.byteLength);const fixed = moved.transferToFixedLength();console.log(fixed.resizable);

Line 2 moves the bytes. Line 3 prints 0 for the old buffer, and line 4 prints 1024 for the new one. Line 5 moves them again, into a buffer that can't change size. So line 6 prints false.

Sending a buffer to a worker

worker.postMessage(buffer, [buffer]) sends a buffer to a worker. The second argument is the transfer list: the buffers to move instead of copy. Moving is fast even for a large buffer, because no bytes are copied. After the call, your page's buffer is detached. Without a transfer list, postMessage sends a copy. Messaging & cloning and Web Workers explain both ways.

Common misconceptions

Each row is a common wrong idea about typed arrays. Read across for what is really true, and a habit that keeps you safe.

Typed array mistakes to avoid
Wrong ideaWhat is trueHabit that helps
An ArrayBuffer is an array.It is a block of raw bytes with a byteLength. Only a view can read numbers from it.Name variables buffer for the bytes and bytes, view or words for the views.
All typed arrays use the same byte order as the file.A typed array uses your computer's byte order. A DataView lets you choose.Read files and network messages with DataView.
subarray makes a copy.subarray shares the same bytes. slice makes a copy.Use slice when other code must not see your changes.
Writing a number that doesn't fit throws an error.Whole-number types wrap around, Uint8ClampedArray clamps, and decimal types round. None of them throw.Run small tests with real numbers before you read bytes from outside.
A transferred buffer is shared.Transfer moves the bytes, and the sender's buffer becomes empty. Sharing needs SharedArrayBuffer and Atomics.Transfer to hand bytes over. Share only when both sides must change the same bytes.

Practice exercises

Exercise 1 · Warm-upPredict numbers that don't fit

Predict both printed lines before you run the code.

Starter codePop out in the code editor (opens in a new tab)JavaScript
const signed = new Int8Array([127, 128, 129, -129]);
const clamped = new Uint8ClampedArray([-1, 12.5, 13.5, 300]);
console.log(Array.from(signed).join(","));
console.log(Array.from(clamped).join(","));

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeWrite a PNG size reader

    Write the two getUint32 reads that get a PNG's width and height. Then compare with the finished version.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const png = new Uint8Array([
      0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
      0x00, 0x00, 0x00, 0x0d,
      0x49, 0x48, 0x44, 0x52,
      0x00, 0x00, 0x03, 0x20,
      0x00, 0x00, 0x02, 0x58,
    ]);
    const view = new DataView(png.buffer, png.byteOffset, png.byteLength);
    const signatureOk = png.slice(0, 8).join(" ") === "137 80 78 71 13 10 26 10";
    const width = view.getUint32(16, false);
    const height = view.getUint32(20, false);
    console.log(signatureOk);
    console.log(width + "x" + height);

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeFind the shared-view bug

      A teammate thought shared and copy were both separate copies. Predict what bytes and copy print after line 4 changes shared.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      const bytes = new Uint8Array([1, 2, 3, 4]);
      const shared = bytes.subarray(1, 3);
      const copy = bytes.slice(1, 3);
      shared[0] = 99;
      console.log(bytes.join(","));
      console.log(copy.join(","));

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeRead the same bytes in both orders

        Predict the two hex strings. Both reads use the same four bytes.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        const buffer = new ArrayBuffer(4);
        const view = new DataView(buffer);
        view.setUint32(0, 0x12345678, false);
        console.log(view.getUint32(0, false).toString(16));
        console.log(view.getUint32(0, true).toString(16));

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · PracticeStore a big 64-bit number exactly

          Check that a BigUint64Array stores a big BigInt value with every digit exact.

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          const ids = new BigUint64Array([2n ** 63n]);
          console.log(ids[0] === 9223372036854775808n);

          Answer, then press Check. Spacing and letter case don’t matter.

            Check your understanding

            Lesson quiz · 8 questionsScore: first tries count
            1. Question 1 of 8What is an ArrayBuffer on its own?

              Choose an answer to see the explanation.

            2. Question 2 of 8What does this Int8Array print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log(new Int8Array([127, 128, 129, -129]).join(','));

              Choose an answer to see the explanation.

            3. Question 3 of 8What does this Uint8ClampedArray print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log(new Uint8ClampedArray([-1, 12.5, 13.5, 300]).join(','));

              Choose an answer to see the explanation.

            4. Question 4 of 8Which value can you put in a BigInt64Array?

              Choose an answer to see the explanation.

            5. Question 5 of 8What does reading the same bytes little-endian print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const b = new ArrayBuffer(4);
              const v = new DataView(b);
              v.setUint32(0, 0x12345678, false);
              console.log(v.getUint32(0, true).toString(16));

              Choose an answer to see the explanation.

            6. Question 6 of 8Which one shares bytes with the original typed array?

              Choose an answer to see the explanation.

            7. Question 7 of 8What happens to the sender's buffer after a transfer?

              Choose an answer to see the explanation.

            8. Question 8 of 8Where can you use Float16Array today?

              Choose an answer to see the explanation.

            Key takeaways

            • A byte is 8 bits. Read with no minus sign, it is a whole number from 0 to 255, often written as two hex digits.
            • An ArrayBuffer holds raw bytes. Typed arrays and DataView are views that say what the bytes mean.
            • Numbers that don't fit change: whole-number types wrap around, Uint8ClampedArray clamps to 0–255, and decimal types round. BigInt arrays only accept BigInt values.
            • Use DataView to read bytes at a named offset, in the byte order a file or network format uses.
            • subarray shares bytes, slice copies them, set copies values in, and ArrayBuffer.isView checks for a view.
            • Some buffers can grow and shrink. Transfer moves the bytes and empties the old buffer. A SharedArrayBuffer really shares them.

            Up next: Text encoding & Base64, where these same bytes turn into text and back.

            CompleteFrontend Clear concepts. Working examples.