How to Use Three.js InterleavedBuffer

This article explains how to define and use custom interleaved data buffers in Three.js using InterleavedBuffer and InterleavedBufferAttribute. You will learn how to combine multiple vertex attributes—such as positions, colors, and UV coordinates—into a single, contiguous array buffer to optimize GPU memory access and improve rendering performance.

Understanding Interleaved Buffers

By default, Three.js geometries often use separate arrays (buffers) for different vertex attributes, such as one array for positions, one for normals, and another for UVs. This is known as a non-interleaved or “array of structures” approach.

Interleaving merges these attributes into a single array. Instead of storing all positions followed by all UVs, you store them sequentially for each vertex: [position_x, position_y, position_z, uv_u, uv_v, ...].

This structure improves GPU cache locality because all data required to process a single vertex is located next to each other in memory.

Step 1: Create the Raw Typed Array

To set up an interleaved buffer, first define a single typed array (usually a Float32Array) containing all your vertex data. You must determine a consistent “stride,” which is the total number of elements allocated for each vertex.

In this example, each vertex has a 3-element position (x, y, z) and a 2-element UV coordinate (u, v). The stride is 5.

// A single triangle with interleaved Position (3 floats) and UV (2 floats)
const vertexData = new Float32Array([
    // x,  y,  z,    u,   v
     0.0,  1.0, 0.0,  0.5, 1.0, // Vertex 1
    -1.0, -1.0, 0.0,  0.0, 0.0, // Vertex 2
     1.0, -1.0, 0.0,  1.0, 0.0  // Vertex 3
]);

Step 2: Instantiate the InterleavedBuffer

Next, pass your typed array and the calculated stride to the THREE.InterleavedBuffer constructor. The stride parameter tells Three.js how many elements make up a single complete vertex block.

import * as THREE from 'three';

const stride = 5; // 3 positions + 2 UVs
const interleavedBuffer = new THREE.InterleavedBuffer(vertexData, stride);

Step 3: Define InterleavedBufferAttributes

To let your shader know where each attribute begins and how many elements it uses, create instances of THREE.InterleavedBufferAttribute.

The constructor accepts three parameters: 1. InterleavedBuffer: The shared buffer created in Step 2. 2. itemSize: How many elements belong to this attribute (e.g., 3 for positions, 2 for UVs). 3. offset: The starting index of this attribute within the stride.

// Positions start at index 0 and have 3 elements (x, y, z)
const positionAttribute = new THREE.InterleavedBufferAttribute(interleavedBuffer, 3, 0);

// UVs start at index 3 (after x, y, z) and have 2 elements (u, v)
const uvAttribute = new THREE.InterleavedBufferAttribute(interleavedBuffer, 2, 3);

Step 4: Assign Attributes to BufferGeometry

Finally, instantiate a standard THREE.BufferGeometry and assign your custom interleaved attributes using .setAttribute().

const geometry = new THREE.BufferGeometry();

// Assign the custom attributes
geometry.setAttribute('position', positionAttribute);
geometry.setAttribute('uv', uvAttribute);

// Create a mesh using the geometry
const material = new THREE.MeshBasicMaterial({ color: 0xffffff, side: THREE.DoubleSide });
const mesh = new THREE.Mesh(geometry, material);

Once assigned, Three.js automatically manages uploading the single interleaved buffer to the GPU, streamlining memory bandwidth during rendering.