How to Clone a Mesh with Unique Material in Three.js
In Three.js, cloning a 3D mesh using the default
.clone() method duplicates the mesh object but keeps
references to the original geometry and material. If you modify the
material of the cloned mesh, the original mesh will change as well. To
prevent this, you must explicitly clone the material while keeping the
geometry shared. This article provides a direct, efficient guide and
code example on how to clone a mesh in Three.js so that it shares the
original geometry for performance but uses a completely independent
material.
The Problem with Default Cloning
When you call originalMesh.clone(), Three.js performs a
shallow clone of the Mesh object. The resulting clone
points to the exact same instances of Geometry and
Material in memory.
// This changes the color of BOTH originalMesh and clonedMesh
const clonedMesh = originalMesh.clone();
clonedMesh.material.color.setHex(0x00ff00); To give the cloned mesh its own visual properties (like a different color, opacity, or texture) without wasting memory by duplicating the heavy vertex data of the geometry, you must manually clone the material.
The Solution: Explicitly Cloning the Material
To create a clone with shared geometry and a unique material, follow this pattern:
// 1. Clone the original mesh structure
const clonedMesh = originalMesh.clone();
// 2. Explicitly clone the material so it becomes unique
clonedMesh.material = originalMesh.material.clone();
// 3. Now you can safely modify the clone's material independently
clonedMesh.material.color.set(0x00ff00); // Only the clone turns greenHandling Multi-Materials
If your mesh uses an array of materials (common with complex models or models loaded from GLTF/OBJ files), assigning a single cloned material will break the rendering. You must check if the material is an array and clone each material individually:
const clonedMesh = originalMesh.clone();
if (Array.isArray(originalMesh.material)) {
// Clone each material in the array
clonedMesh.material = originalMesh.material.map(material => material.clone());
} else {
// Clone the single material
clonedMesh.material = originalMesh.material.clone();
}Why This Approach is Best Practice
- Memory Efficiency: Geometry data contains vertex positions, normals, and UV maps, which consume significant GPU memory. By sharing the geometry, you keep your application fast and memory-efficient.
- Visual Independence: Cloning the material generates a new set of draw calls for the GPU, allowing you to change shaders, textures, colors, or blending modes on the cloned object without affecting any other instances.