Share BufferGeometry Across Multiple Meshes in Three.js

This article explains how to optimize memory usage in Three.js by sharing a single BufferGeometry instance across thousands of individual Mesh objects without using InstancedMesh. You will learn the implementation process, how memory reuse works on the GPU, and the critical performance trade-offs regarding draw calls and CPU overhead.

The Implementation

To share a BufferGeometry among multiple meshes, you instantiate the geometry once and pass that same reference to the constructor of each new Mesh.

import * as THREE from 'three';

// 1. Create a single geometry and material instance
const sharedGeometry = new THREE.BoxGeometry(1, 1, 1);
const sharedMaterial = new THREE.MeshStandardMaterial({ color: 0x00ff00 });

// 2. Instantiate thousands of meshes using the shared references
const meshCount = 5000;
for (let i = 0; i < meshCount; i++) {
    const mesh = new THREE.Mesh(sharedGeometry, sharedMaterial);
    
    // Position each mesh individually
    mesh.position.set(
        (Math.random() - 0.5) * 100,
        (Math.random() - 0.5) * 100,
        (Math.random() - 0.5) * 100
    );
    
    scene.add(mesh);
}

How It Works Under the Hood

When you create a BufferGeometry, Three.js uploads its vertex attributes (positions, normals, UVs) to the GPU’s Video RAM (VRAM) as WebGL buffers.

By passing the sharedGeometry reference to multiple meshes, you prevent Three.js from duplicating this data in VRAM. Each mesh simply points to the same GPU memory address for its vertex data. The only unique data stored for each individual mesh is its transformation matrix (position, rotation, and scale) and its WebGL draw state.

Advantages of This Approach

Performance Limitations

While sharing geometry saves a massive amount of GPU memory, it does not reduce the number of draw calls.

In WebGL, every individual mesh in your scene requires its own draw call. Rendering 5,000 individual meshes using this method results in 5,000 draw calls per frame. This can quickly bottleneck the CPU, leading to a drop in frame rate.

If your meshes are static and do not need to be moved independently, merging the geometries into a single mesh using BufferGeometryUtils.mergeGeometries() is a more performant alternative. If they must move independently and CPU overhead becomes a bottleneck, you should use InstancedMesh, which reduces the overhead to a single draw call.