ArrayBuffer

Master raw binary data handling in JavaScript for cross-platform mobile development. Learn to work with typed arrays, convert to Blob, and build efficient binary data pipelines in React Native, iOS, and Android.

What is ArrayBuffer?

ArrayBuffer is a built-in JavaScript object that represents a fixed-length sequence of bytes in memory. Unlike regular JavaScript arrays that hold values of any type, an ArrayBuffer is specifically designed to hold raw binary data without any inherent interpretation of what those bytes represent.

Key Characteristics

  • Fixed length: Once created, the byte length cannot be changed (unless using resizable ArrayBuffer)
  • Raw bytes: Contains uninterpreted binary data - just a sequence of 8-bit bytes
  • Not directly manipulable: You cannot read or write bytes directly on an ArrayBuffer
  • Views required: To work with the data, you create typed array views or DataView

ArrayBuffer is essential for mobile developers handling images, files, network responses, and any scenario where you need to work with non-text data at the byte level.

MDN Web Docs - ArrayBuffer Reference

Core ArrayBuffer Concepts

Everything you need to understand ArrayBuffer fundamentals

Fixed-Length Binary Storage

Create buffers of specific byte lengths for raw data storage

Typed Array Views

Interpret bytes as Int8Array, Uint32Array, Float64, and more

DataView Flexibility

Read/write mixed data types with explicit byte order control

Transfer Operations

Zero-copy data movement between contexts for performance

Blob Conversion

Convert to Blob for file operations and uploads/downloads

Mobile Integration

React Native, iOS, and Android specific considerations

Creating and Working with ArrayBuffer

Creating an ArrayBuffer

The ArrayBuffer constructor creates a new buffer of the specified size:

// Create a buffer for 1024 bytes
const buffer = new ArrayBuffer(1024);

// Create with maxByteLength for resizable buffers
const resizableBuffer = new ArrayBuffer(1024, { maxByteLength: 4096 });

console.log(buffer.byteLength); // 1024
console.log(resizableBuffer.maxByteLength); // 4096

Checking Buffer State

ArrayBuffer provides properties to inspect its current state:

const buffer = new ArrayBuffer(256);

// Check if resizable
console.log(buffer.resizable); // true or false

// Check maximum size for resizable buffers
console.log(buffer.maxByteLength); // Maximum allowed size

// Check if detached (after transfer)
console.log(buffer.detached); // true if buffer has been transferred

Resizing an ArrayBuffer

For resizable ArrayBuffers, you can adjust the size within the maxByteLength limit:

const buffer = new ArrayBuffer(256, { maxByteLength: 1024 });

// Resize to a larger size
buffer.resize(512);

// Resize to a smaller size
buffer.resize(128);

New bytes are automatically initialized to 0.

For efficient binary data handling in web and mobile applications, working with raw byte streams is a critical skill that complements our web development services focused on performance optimization.

Typed Array Views: Interpreting Binary Data

Understanding the View Architecture

ArrayBuffer alone is just a container of bytes. To read or write data, you create "views" that interpret the bytes in different ways. This architecture provides flexibility while maintaining efficiency.

Typed Array Types Reference

TypeBytesSignedRange
Int8Array1Yes-128 to 127
Uint8Array1No0 to 255
Uint8ClampedArray1No0 to 255 (clamped)
Int16Array2Yes-32,768 to 32,767
Uint16Array2No0 to 65,535
Int32Array4Yes-2,147,483,648 to 2,147,483,647
Uint32Array4No0 to 4,294,967,295
Float32Array4Yes±3.4e38
Float64Array8Yes±1.8e308
BigInt64Array8Yes-2^63 to 2^63-1
BigUint64Array8No0 to 2^64-1

Creating and Using Typed Arrays

const buffer = new ArrayBuffer(16); // 16 bytes

// Create views on the same buffer
const int8View = new Int8Array(buffer);
const uint16View = new Uint16Array(buffer);
const float64View = new Float64Array(buffer);

// Write using one view
int8View[0] = 42;

// Read using another view
console.log(uint16View[0]); // 10758 (little-endian interpretation)

// Shared underlying buffer
console.log(int8View.buffer === uint16View.buffer); // true

MDN Web Docs - Typed Arrays Guide

DataView: Flexible Binary Data Access

When to Use DataView

DataView provides more control over how bytes are interpreted, making it ideal for:

  • Reading/writing mixed data types from a single buffer
  • Working with specific byte orders (big-endian vs little-endian)
  • Parsing binary file formats or network protocols
const buffer = new ArrayBuffer(12);
const view = new DataView(buffer);

// Write different types at different offsets
view.setInt8(0, 42); // Signed byte at offset 0
view.setUint16(2, 1000); // Unsigned 16-bit at offset 2
view.setFloat32(4, 3.14159); // 32-bit float at offset 4
view.setBigInt64(8, 9007199254740991n); // BigInt at offset 8

// Read with explicit endianness
const int8 = view.getInt8(0);
const uint16 = view.getUint16(2, true); // little-endian
const float = view.getFloat32(4, false); // big-endian
const bigInt = view.getBigInt64(8);

Byte Order Considerations

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

// Little-endian (most common on x86, mobile ARM)
view.setUint32(0, 0x12345678, true);
console.log(view.getUint8(0).toString(16)); // 78
console.log(view.getUint8(1).toString(16)); // 56

// Big-endian (network byte order)
view.setUint32(0, 0x12345678, false);
console.log(view.getUint8(0).toString(16)); // 12
console.log(view.getUint8(1).toString(16)); // 34

ArrayBuffer Operations

Slicing: Creating Sub-Buffers

const buffer = new ArrayBuffer(100);

// Create a new buffer with bytes 20-50
const sliced = buffer.slice(20, 50);

console.log(sliced.byteLength); // 30
console.log(sliced === buffer); // false (new object)

Transferring: Zero-Copy Data Movement

ArrayBuffer can be transferred between execution contexts without copying:

// Create a worker (web or React Native NativeModule context)
const worker = new Worker('worker.js');

// Transfer ownership (original becomes detached)
worker.postMessage({ buffer: myBuffer }, [myBuffer]);

// Now myBuffer is detached and unusable
console.log(myBuffer.byteLength); // 0

Transfer vs TransferToFixedLength

const buffer = new ArrayBuffer(100, { maxByteLength: 200 });

// Transfer to fixed-length buffer
const transferred = buffer.transferToFixedLength(80);
// Can only transfer to fixed length if resizable

// Transfer maintaining resizable capability
const transferred2 = buffer.transfer(80);
// If buffer is resizable, result is also resizable

Zero-copy transfer operations are particularly valuable when building AI-powered automation solutions that process large datasets efficiently.

ArrayBuffer to Blob Conversion

Why Convert to Blob?

Blob objects are better suited for:

  • File operations
  • Upload/download scenarios
  • Object URL creation
  • MIME-type awareness

Conversion Methods

// Create Blob from ArrayBuffer
const buffer = new ArrayBuffer(1024);
const blob = new Blob([buffer], { type: 'application/octet-stream' });

// Convert Blob back to ArrayBuffer
const arrayBuffer = await blob.arrayBuffer();

Practical File Upload Example

async function uploadBinaryData(data, filename, mimeType) {
 // Ensure we have an ArrayBuffer
 const buffer = data instanceof ArrayBuffer
 ? data
 : data.buffer;

 // Create blob with proper MIME type
 const blob = new Blob([buffer], { type: mimeType });

 // Create FormData for upload
 const formData = new FormData();
 formData.append('file', blob, filename);

 // Upload
 const response = await fetch('/api/upload', {
 method: 'POST',
 body: formData,
 });

 return response.json();
}

React Native and Cross-Platform Mobile Considerations

The React Native Challenge

React Native's JavaScript bridge creates unique challenges for binary data:

// Traditional web approach - works but has bridge overhead
const buffer = new ArrayBuffer(1024);
const view = new Uint8Array(buffer);

// Bridge serialization means data is copied
// For large binary data, this is inefficient

// Solution: Keep binary operations on native side
// Use react-native-blob-util or react-native-fs

Using react-native-blob-util

import RNFetchBlob from 'react-native-blob-util';

class BinaryDataHandler {
 // Read file as ArrayBuffer
 async readFileAsArrayBuffer(filePath) {
 const content = await RNFetchBlob.fs.readFile(filePath, 'base64');

 // Decode base64 to ArrayBuffer
 const binaryString = atob(content);
 const bytes = new Uint8Array(binaryString.length);
 for (let i = 0; i < binaryString.length; i++) {
 bytes[i] = binaryString.charCodeAt(i);
 }

 return bytes.buffer;
 }

 // Write ArrayBuffer to file
 async writeArrayBuffer(filePath, buffer) {
 const bytes = new Uint8Array(buffer);
 let binaryString = '';
 for (let i = 0; i < bytes.length; i++) {
 binaryString += String.fromCharCode(bytes[i]);
 }

 await RNFetchBlob.fs.writeFile(filePath, btoa(binaryString), 'base64');
 }

 // Fetch as ArrayBuffer
 async fetchAsArrayBuffer(url) {
 const response = await RNFetchBlob.config({
 path: RNFetchBlob.fs.dirs.CacheDir + '/temp.bin',
 }).fetch(url);

 return this.readFileAsArrayBuffer(response.path());
 }
}

LogRocket Blog - React Native Blob Handling

Practical Mobile App Use Cases

Use Case 1: Image Processing Pipeline

async function processAndResizeImage(sourcePath, targetWidth, targetHeight) {
 // Read source image
 const sourceBuffer = await readFileAsArrayBuffer(sourcePath);

 // Create bitmap from buffer
 const sourceBitmap = await createBitmap(sourceBuffer);

 // Resize using typed array manipulation
 const resizedBuffer = resizeImageBuffer(sourceBuffer, targetWidth, targetHeight);

 // Save to file
 await writeArrayBuffer(targetPath, resizedBuffer);
}

function resizeImageBuffer(buffer, newWidth, newHeight) {
 const view = new Uint32Array(buffer);
 // Perform pixel interpolation
 // Return new ArrayBuffer with resized data
}

Use Case 2: Cached Network Data

class NetworkCache {
 constructor(maxSize = 10 * 1024 * 1024) { // 10MB default
 this.maxSize = maxSize;
 this.cache = new Map();
 }

 async cacheResponse(url, responseBuffer) {
 const entry = {
 buffer: responseBuffer,
 timestamp: Date.now(),
 size: responseBuffer.byteLength
 };

 // Evict old entries if needed
 await this.evictIfNeeded(entry.size);

 this.cache.set(url, entry);
 }

 get(url) {
 const entry = this.cache.get(url);
 if (!entry) return null;

 // Move to front (LRU)
 this.cache.delete(url);
 this.cache.set(url, entry);

 return entry.buffer;
 }
}

These binary data handling patterns are essential for building performant cross-platform mobile applications with efficient data processing pipelines.

Best Practices and Performance Tips

Memory Management

// ❌ Avoid: Holding large buffers unnecessarily
function badExample(data) {
 const buffer = new ArrayBuffer(data.length);
 const view = new Uint8Array(buffer);
 view.set(new Uint8Array(data));
 // Buffer remains in memory even after function returns
 return processData(buffer);
}

// ✅ Good: Explicit cleanup or transfer
function goodExample(data) {
 const buffer = new ArrayBuffer(data.length);
 const view = new Uint8Array(buffer);
 view.set(new Uint8Array(data));

 try {
 return processData(buffer);
 } finally {
 // Detach buffer to free memory immediately
 buffer.transfer(0);
 }
}

Transfer When Possible

// ❌ Copying large data
worker.postMessage({ buffer: buffer }); // Copies entire buffer

// ✅ Transfer ownership (zero-copy)
worker.postMessage({ buffer: buffer }, [buffer]);
// buffer is now detached in main thread

Typed Array Selection

Choose the right view for your data type:

// For text/ASCII data
const bytes = new Uint8Array(buffer);

// For audio samples
const samples = new Float32Array(buffer);

// For color values
const colors = new Uint32Array(buffer); // ABGR or RGBA packed

// For network protocol parsing
const view = new DataView(buffer);
view.getUint16(offset, true); // Little-endian

Summary

ArrayBuffer provides the foundation for efficient binary data handling in JavaScript and cross-platform mobile development. Key takeaways:

  1. ArrayBuffer is the container - fixed-length raw byte storage
  2. Views provide interpretation - typed arrays and DataView enable access
  3. Transfer for performance - zero-copy between contexts
  4. Mobile requires special handling - React Native bridge considerations
  5. Blob for file operations - ArrayBuffer to Blob conversion for uploads/downloads

Sources

  1. MDN Web Docs - ArrayBuffer Reference
  2. MDN Web Docs - Typed Arrays Guide
  3. LogRocket Blog - Fetch and Handle Blob Data in React Native

Ready to Build High-Performance Mobile Apps?

Our team specializes in cross-platform mobile development with React Native, delivering efficient binary data handling, smooth file operations, and optimized performance.