Optimizing GPU Memory with Three.js BufferGeometry Attributes
Learn how to optimize Three.js performance using BufferGeometry attributes, typed arrays, and InterleavedBuffers to reduce GPU memory overhead and improve cache locality.
08 Jul 2026, 16:15 UTC

The Problem: Vertex Data Overhead
When rendering complex 3D models in Three.js, the primary performance bottleneck is often the transfer of vertex data from the CPU to the GPU. Using high-level object abstractions for every vertex creates massive memory overhead and slows down the render loop. To achieve high frame rates, you must use BufferGeometry, which stores data in typed arrays (like Float32Array) that map directly to GPU memory buffers.
Implementing Custom Vertex Attributes
Beyond standard positions and normals, BufferGeometry allows you to define custom attributes. These are per-vertex data points—such as custom colors, weights for skeletal animation, or specialized UV maps—that the GPU can process in parallel via shaders.
To implement a custom attribute, you create a BufferAttribute. This object wraps a typed array and defines the item size (the number of values per vertex). For example, a position attribute has an item size of 3 (X, Y, Z), while a color attribute also uses 3 (R, G, B).
// Assuming Three.js r150+
const vertexCount = 3;
const geometry = new THREE.BufferGeometry();
// 1. Define positions (3 values per vertex: x, y, z)
const positions = new Float32Array([
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
0.0, 1.0, 1.0
]);
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
// 2. Define a custom attribute (e.g., per-vertex color)
const colors = new Float32Array([
1.0, 0.0, 0.0, // Red
0.0, 1.0, 0.0, // Green
0.0, 0.0, 1.0 // Blue
]);
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
const material = new THREE.MeshStandardMaterial({ vertexColors: true });
const mesh = new THREE.Mesh(geometry, material);
Handling Dynamic Updates
GPU buffers are not automatically synced with JavaScript arrays. If you modify the values within a BufferAttribute after the mesh has been rendered once, the GPU will continue using the old data. To force a re-upload of the data to the graphics card, you must set the needsUpdate flag to true.
// Update the first vertex position
geometry.attributes.position.array[0] = -2.0;
// Notify Three.js to re-upload this specific buffer to the GPU
geometry.attributes.position.needsUpdate = true;
Interleaved Buffers for Cache Locality
By default, Three.js creates separate buffers for positions, normals, and colors. This requires the GPU to bind multiple buffers for every draw call. For high-performance applications, use InterleavedBuffer. This stores all attributes for a single vertex contiguously in one array (e.g., PosX, PosY, PosZ, ColR, ColG, ColB, PosX...).
This approach improves cache locality, meaning the GPU can fetch all data for one vertex in a single memory read, reducing the overhead of multiple buffer binds.
Critical Limitations and Common Mistakes
- Frustum Culling Errors: Three.js uses a bounding box and sphere to determine if an object is on-screen. If you manually update vertex positions, the bounding volume becomes outdated. The object may suddenly disappear (be culled) even when it should be visible. Always call
geometry.computeBoundingBox()andgeometry.computeBoundingSphere()after significant attribute changes. - VRAM Over-allocation: Typed arrays cannot be resized. If you allocate a buffer for 10,000 vertices but only use 1,000, the remaining 9,000 slots still occupy VRAM. Size your buffers precisely to your vertex count.
- Precision Mismatch: Using
Float64Arrayis not supported by most WebGL implementations for vertex attributes. Always useFloat32ArrayorUint16Array(for indices) to ensure compatibility.
Verification and Diagnostics
To verify that your attributes are correctly uploaded and recognized by the GPU:
- Inspect the Geometry: Log
geometry.attributesto the console to ensure thearraylength matchesvertexCount * itemSize. - WebGL Inspector: Use a browser extension like the Three.js Inspector to check the
BufferAttributevalues in real-time. - Visual Check: When using custom colors, ensure
material.vertexColorsis set totrue; otherwise, thecolorattribute will be ignored by the shader.
Rollback Procedure
If a custom attribute causes rendering artifacts or crashes the WebGL context, remove the attribute and reset the material to a default state:
geometry.removeAttribute('color');
material.vertexColors = false;
material.needsUpdate = true;
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.