Skip to main content
Bun provides full support for working with binary data through ArrayBuffer, TypedArrays, DataView, and Node.js Buffer.

ArrayBuffer

An ArrayBuffer is a fixed-length raw binary data buffer:
// Create a 16-byte buffer
const buffer = new ArrayBuffer(16);

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

// Slice the buffer
const slice = buffer.slice(0, 8);

TypedArrays

TypedArrays provide a view into an ArrayBuffer:

Uint8Array

// Create from size
const bytes = new Uint8Array(4);
bytes[0] = 255;
bytes[1] = 128;

// Create from array
const bytes2 = new Uint8Array([1, 2, 3, 4]);

// Create from ArrayBuffer
const buffer = new ArrayBuffer(8);
const view = new Uint8Array(buffer);

// Create from buffer with offset and length
const view2 = new Uint8Array(buffer, 2, 4);

Other TypedArray Types

// 8-bit
const int8 = new Int8Array(4);
const uint8 = new Uint8Array(4);
const uint8clamped = new Uint8ClampedArray(4);

// 16-bit
const int16 = new Int16Array(4);
const uint16 = new Uint16Array(4);

// 32-bit
const int32 = new Int32Array(4);
const uint32 = new Uint32Array(4);
const float32 = new Float32Array(4);

// 64-bit
const float64 = new Float64Array(4);
const bigint64 = new BigInt64Array(4);
const biguint64 = new BigUint64Array(4);

TypedArray Operations

const bytes = new Uint8Array([1, 2, 3, 4, 5]);

// Properties
bytes.length; // 5
bytes.byteLength; // 5
bytes.byteOffset; // 0
bytes.buffer; // ArrayBuffer

// Slice
const slice = bytes.slice(1, 4); // [2, 3, 4]

// Subarray (view, not copy)
const sub = bytes.subarray(1, 4); // [2, 3, 4]

// Set
bytes.set([10, 20], 0); // [10, 20, 3, 4, 5]

// Copy within
bytes.copyWithin(0, 3); // [4, 5, 3, 4, 5]

// Fill
bytes.fill(0); // [0, 0, 0, 0, 0]

// Find
bytes.indexOf(3); // -1
bytes.includes(0); // true

// Iteration
for (const byte of bytes) {
  console.log(byte);
}

DataView

DataView provides a low-level interface for reading/writing multiple number types in an ArrayBuffer:
const buffer = new ArrayBuffer(16);
const view = new DataView(buffer);

// Write different types
view.setInt8(0, 127);
view.setInt16(1, 32767);
view.setInt32(3, 2147483647);
view.setFloat32(7, 3.14);
view.setFloat64(11, Math.PI);

// Read values
const int8 = view.getInt8(0); // 127
const int16 = view.getInt16(1); // 32767
const float32 = view.getFloat32(7); // 3.14

// Endianness
view.setInt16(0, 256, true); // Little-endian
view.setInt16(0, 256, false); // Big-endian (default)

Buffer (Node.js)

Buffer is a subclass of Uint8Array with additional methods:
// Create from size
const buf = Buffer.alloc(10);
const buf2 = Buffer.allocUnsafe(10); // Faster but uninitialized

// Create from string
const buf3 = Buffer.from("Hello", "utf-8");
const buf4 = Buffer.from("48656c6c6f", "hex");
const buf5 = Buffer.from("SGVsbG8=", "base64");

// Create from array
const buf6 = Buffer.from([72, 101, 108, 108, 111]);

// Create from ArrayBuffer
const ab = new ArrayBuffer(10);
const buf7 = Buffer.from(ab);

Buffer Methods

const buf = Buffer.from("Hello World");

// Convert to string
buf.toString(); // "Hello World"
buf.toString("hex"); // "48656c6c6f20576f726c64"
buf.toString("base64"); // "SGVsbG8gV29ybGQ="
buf.toString("utf-8", 0, 5); // "Hello"

// Write
buf.write("Hi", 0, "utf-8"); // "Hi World"

// Compare
const buf2 = Buffer.from("Hello World");
buf.equals(buf2); // false (was modified)
buf.compare(buf2); // -1, 0, or 1

// Concat
const combined = Buffer.concat([buf, buf2]);

// Copy
const dest = Buffer.alloc(5);
buf.copy(dest, 0, 0, 5);

// Slice
const slice = buf.slice(0, 5);

// Fill
buf.fill(0);
buf.fill("a");

Reading Numbers

const buf = Buffer.alloc(8);

// Write
buf.writeInt8(127, 0);
buf.writeInt16LE(32767, 1);
buf.writeInt32LE(2147483647, 3);
buf.writeFloatLE(3.14, 7);

// Read
buf.readInt8(0); // 127
buf.readInt16LE(1); // 32767
buf.readInt32LE(3); // 2147483647
buf.readFloatLE(7); // 3.14

// Big-endian
buf.writeInt16BE(256, 0);
buf.readInt16BE(0); // 256

Conversions

String ↔ Buffer

// String to Buffer
const buf = Buffer.from("Hello", "utf-8");

// Buffer to String
const str = buf.toString("utf-8");

// Encodings: "utf-8", "utf8", "utf16le", "latin1", "base64", "base64url", "hex", "ascii", "binary"

ArrayBuffer ↔ Buffer

// ArrayBuffer to Buffer
const ab = new ArrayBuffer(10);
const buf = Buffer.from(ab);

// Buffer to ArrayBuffer
const ab2 = buf.buffer;

TypedArray ↔ Buffer

// TypedArray to Buffer
const uint8 = new Uint8Array([1, 2, 3]);
const buf = Buffer.from(uint8);

// Buffer to TypedArray
const uint8_2 = new Uint8Array(buf);

Blob ↔ ArrayBuffer

// Blob to ArrayBuffer
const blob = new Blob(["Hello"]);
const ab = await blob.arrayBuffer();

// ArrayBuffer to Blob
const ab2 = new ArrayBuffer(10);
const blob2 = new Blob([ab2]);

Base64 Encoding/Decoding

Using Buffer

// Encode
const encoded = Buffer.from("Hello").toString("base64");
console.log(encoded); // "SGVsbG8="

// Decode
const decoded = Buffer.from(encoded, "base64").toString();
console.log(decoded); // "Hello"

Using bun:base64

import { encode, decode } from "bun:base64";

// Encode
const encoded = encode("Hello");
// or
const encoded2 = encode(new Uint8Array([72, 101, 108, 108, 111]));

// Decode
const decoded = decode(encoded);

Hex Encoding/Decoding

// Encode
const hex = Buffer.from("Hello").toString("hex");
console.log(hex); // "48656c6c6f"

// Decode
const bytes = Buffer.from(hex, "hex");
console.log(bytes.toString()); // "Hello"

SharedArrayBuffer

SharedArrayBuffer allows sharing memory between threads:
// Create shared buffer
const shared = new SharedArrayBuffer(1024);

// Create view
const view = new Uint8Array(shared);

// Share with worker
const worker = new Worker("./worker.js");
worker.postMessage(shared);

Crypto Random Bytes

// Generate random bytes
const bytes = crypto.getRandomValues(new Uint8Array(16));

// Or with Node.js API
import { randomBytes } from "node:crypto";
const bytes2 = randomBytes(16);

Reading Binary Files

// Read as ArrayBuffer
const file = Bun.file("./data.bin");
const buffer = await file.arrayBuffer();

// Read as Uint8Array
const bytes = new Uint8Array(await file.arrayBuffer());

// Read specific bytes
const header = await file.slice(0, 512).arrayBuffer();

Writing Binary Files

// Write ArrayBuffer
const buffer = new ArrayBuffer(10);
await Bun.write("./data.bin", buffer);

// Write TypedArray
const bytes = new Uint8Array([1, 2, 3, 4]);
await Bun.write("./data.bin", bytes);

// Write Buffer
const buf = Buffer.from([1, 2, 3, 4]);
await Bun.write("./data.bin", buf);

Performance Tips

  1. Use Buffer.allocUnsafe() for temporary buffers - Faster but contains uninitialized data
  2. Use subarray() instead of slice() when possible - Creates a view instead of copying
  3. Reuse buffers - Avoid frequent allocations
  4. Use TypedArrays for specific data types - Better performance than DataView
  5. Use Buffer.concat() for combining buffers - More efficient than manual copying
// Good: Reuse buffer
const buf = Buffer.allocUnsafe(1024);
for (let i = 0; i < 100; i++) {
  const bytesRead = readInto(buf);
  process(buf.subarray(0, bytesRead));
}

// Bad: Allocate each time
for (let i = 0; i < 100; i++) {
  const buf = Buffer.alloc(1024);
  const bytesRead = readInto(buf);
  process(buf.slice(0, bytesRead));
}

Build docs developers (and LLMs) love