Docs

6.4-TypedArrays-Binary-Data

6.4 TypedArrays and Binary Data

Overview

TypedArrays provide a way to work with raw binary data in JavaScript. They're essential for WebGL, audio processing, file handling, network protocols, and performance-critical applications.


Table of Contents

  1. β€’ArrayBuffer
  2. β€’TypedArray Types
  3. β€’DataView
  4. β€’Working with Binary Data
  5. β€’Use Cases

ArrayBuffer

ArrayBuffer is a fixed-length container for raw binary data. You can't directly manipulate itβ€”you need views.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    ArrayBuffer (16 bytes)                   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F           β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                    Raw Binary Bytes                         β”‚
β”‚                                                             β”‚
β”‚  Views (interpret the same data):                           β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚ Uint8Array:  [0, 1, 2, 3, 4, 5, 6, 7, ...]  16 elementsβ”‚   β”‚
β”‚  β”‚ Uint16Array: [256, 770, 1284, ...]         8 elements β”‚   β”‚
β”‚  β”‚ Uint32Array: [50462976, ...]               4 elements β”‚   β”‚
β”‚  β”‚ Float64Array: [...]                        2 elements β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Creating ArrayBuffer

// Create a 16-byte buffer
const buffer = new ArrayBuffer(16);

console.log(buffer.byteLength); // 16

// Check if something is an ArrayBuffer
console.log(buffer instanceof ArrayBuffer); // true

// Slice a portion (creates new buffer)
const slice = buffer.slice(0, 8); // First 8 bytes

TypedArray Types

TypeBytesRangeDescription
Int8Array1-128 to 127Signed 8-bit integer
Uint8Array10 to 255Unsigned 8-bit integer
Uint8ClampedArray10 to 255Clamped unsigned 8-bit
Int16Array2-32768 to 32767Signed 16-bit
Uint16Array20 to 65535Unsigned 16-bit
Int32Array4-2Β³ΒΉ to 2Β³ΒΉ-1Signed 32-bit
Uint32Array40 to 2Β³Β²-1Unsigned 32-bit
Float32Array4Β±3.4E3832-bit float
Float64Array8Β±1.8E30864-bit float (double)
BigInt64Array8-2⁢³ to 2⁢³-1Signed 64-bit BigInt
BigUint64Array80 to 2⁢⁴-1Unsigned 64-bit BigInt

Creating TypedArrays

// From length
const uint8 = new Uint8Array(4); // [0, 0, 0, 0]

// From array
const int16 = new Int16Array([1, 2, 3, 4]);

// From another TypedArray
const float32 = new Float32Array(int16);

// From ArrayBuffer
const buffer = new ArrayBuffer(8);
const view1 = new Uint8Array(buffer); // 8 elements
const view2 = new Uint16Array(buffer); // 4 elements
const view3 = new Uint32Array(buffer); // 2 elements

// From ArrayBuffer with offset and length
const partial = new Uint8Array(buffer, 2, 4); // 4 bytes starting at offset 2

TypedArray Properties and Methods

const arr = new Uint8Array([10, 20, 30, 40, 50]);

// Properties
console.log(arr.length); // 5
console.log(arr.byteLength); // 5 (bytes)
console.log(arr.byteOffset); // 0
console.log(arr.buffer); // Underlying ArrayBuffer
console.log(arr.BYTES_PER_ELEMENT); // 1

// Standard array methods work
arr.forEach((x) => console.log(x));
const doubled = arr.map((x) => x * 2);
const filtered = arr.filter((x) => x > 25);
const sum = arr.reduce((a, b) => a + b, 0);

// TypedArray-specific methods
const copy = arr.slice(1, 4); // New TypedArray [20, 30, 40]
arr.set([100, 200], 2); // Set values at index 2
const sub = arr.subarray(1, 4); // View into same buffer

Uint8ClampedArray (for Canvas)

// Regular Uint8Array overflows
const uint8 = new Uint8Array([255]);
uint8[0] = 256;
console.log(uint8[0]); // 0 (overflow!)

// Uint8ClampedArray clamps values
const clamped = new Uint8ClampedArray([255]);
clamped[0] = 256;
console.log(clamped[0]); // 255 (clamped!)

clamped[0] = -10;
console.log(clamped[0]); // 0 (clamped!)

// Used by Canvas ImageData
const imageData = ctx.getImageData(0, 0, 100, 100);
console.log(imageData.data instanceof Uint8ClampedArray); // true

DataView

DataView provides more control over byte order (endianness) and mixed types.

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

// Write values with explicit endianness
view.setInt32(0, 42, true); // Little-endian at offset 0
view.setInt32(4, 42, false); // Big-endian at offset 4
view.setFloat64(8, 3.14159); // Float at offset 8

// Read values
console.log(view.getInt32(0, true)); // 42 (little-endian)
console.log(view.getInt32(4, false)); // 42 (big-endian)
console.log(view.getFloat64(8)); // 3.14159

DataView Methods

// Getters
view.getInt8(offset);
view.getUint8(offset);
view.getInt16(offset, littleEndian);
view.getUint16(offset, littleEndian);
view.getInt32(offset, littleEndian);
view.getUint32(offset, littleEndian);
view.getFloat32(offset, littleEndian);
view.getFloat64(offset, littleEndian);
view.getBigInt64(offset, littleEndian);
view.getBigUint64(offset, littleEndian);

// Setters (same pattern)
view.setInt32(offset, value, littleEndian);
// ... etc

Working with Binary Data

Converting Between Types

// String to bytes
const encoder = new TextEncoder();
const bytes = encoder.encode('Hello'); // Uint8Array

// Bytes to string
const decoder = new TextDecoder();
const str = decoder.decode(bytes); // 'Hello'

// Hex string to bytes
function hexToBytes(hex) {
  const bytes = new Uint8Array(hex.length / 2);
  for (let i = 0; i < hex.length; i += 2) {
    bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
  }
  return bytes;
}

// Bytes to hex string
function bytesToHex(bytes) {
  return Array.from(bytes)
    .map((b) => b.toString(16).padStart(2, '0'))
    .join('');
}

Base64 Encoding

// ArrayBuffer to Base64
function bufferToBase64(buffer) {
  const bytes = new Uint8Array(buffer);
  let binary = '';
  bytes.forEach((b) => (binary += String.fromCharCode(b)));
  return btoa(binary);
}

// Base64 to ArrayBuffer
function base64ToBuffer(base64) {
  const binary = atob(base64);
  const bytes = new Uint8Array(binary.length);
  for (let i = 0; i < binary.length; i++) {
    bytes[i] = binary.charCodeAt(i);
  }
  return bytes.buffer;
}

Use Cases

1. Canvas Image Manipulation

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data; // Uint8ClampedArray [R,G,B,A, R,G,B,A, ...]

// Invert colors
for (let i = 0; i < data.length; i += 4) {
  data[i] = 255 - data[i]; // Red
  data[i + 1] = 255 - data[i + 1]; // Green
  data[i + 2] = 255 - data[i + 2]; // Blue
  // Alpha (i + 3) unchanged
}

ctx.putImageData(imageData, 0, 0);

2. WebSocket Binary Data

const socket = new WebSocket('ws://example.com');
socket.binaryType = 'arraybuffer';

socket.onmessage = (event) => {
  const buffer = event.data;
  const view = new DataView(buffer);

  const messageType = view.getUint8(0);
  const payload = new Uint8Array(buffer, 1);

  console.log('Type:', messageType, 'Data:', payload);
};

// Send binary data
const buffer = new ArrayBuffer(5);
const view = new DataView(buffer);
view.setUint8(0, 1); // Message type
view.setUint32(1, 12345, true); // Payload
socket.send(buffer);

3. File Reading

const input = document.querySelector('input[type="file"]');

input.addEventListener('change', async (e) => {
  const file = e.target.files[0];
  const buffer = await file.arrayBuffer();
  const bytes = new Uint8Array(buffer);

  // Check file signature (magic bytes)
  const isPNG =
    bytes[0] === 0x89 &&
    bytes[1] === 0x50 &&
    bytes[2] === 0x4e &&
    bytes[3] === 0x47;

  console.log('Is PNG:', isPNG);
});

4. Audio Processing (Web Audio API)

const audioCtx = new AudioContext();

// Create buffer for 2 seconds of stereo audio
const sampleRate = audioCtx.sampleRate;
const buffer = audioCtx.createBuffer(2, sampleRate * 2, sampleRate);

// Generate sine wave
const channelData = buffer.getChannelData(0); // Float32Array
for (let i = 0; i < channelData.length; i++) {
  channelData[i] = Math.sin((2 * Math.PI * 440 * i) / sampleRate);
}

Summary

ConceptUse Case
ArrayBufferRaw binary data container
Uint8ArrayByte-level access, files, network
Uint8ClampedArrayCanvas image data
Float32ArrayAudio, WebGL
DataViewMixed types, control endianness
TextEncoder/DecoderString ↔ bytes

Next Steps

  • β€’Learn about Blob and File APIs for file handling
  • β€’Explore Web Audio API for audio processing
  • β€’Study WebGL for 3D graphics
.4 TypedArrays Binary Data - JavaScript Tutorial | DeepML