How to Clone Meshes and Models in Three.js

Published on 02 Jan, 2025 | ~5 min read

In this tutorial, we’ll dive into cloning models and creating both regular and deep copies of meshes.

Dr. Alphonse Mephesto approves this article
Dr. Alphonse Mephesto approves this article

Cloning Meshes

First, set up a Three.js project with a basic scene that includes a mesh. Want to save time? Check out my Three.js boilerplate—it’s ready to go!

import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// Sets the color of the background.
renderer.setClearColor(0xfefefe);

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
  45,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);

// Sets orbit control to move the camera around.
const orbit = new OrbitControls(camera, renderer.domElement);

// Camera positioning.
camera.position.set(0, 8, 14);
// Has to be done everytime we update the camera position.
orbit.update();

const dLight = new THREE.DirectionalLight(0xffffff, 1);
scene.add(dLight);
dLight.position.set(-2, 10, 2);

const geometry = new THREE.BoxGeometry(3, 3, 3);
const material = new THREE.MeshPhongMaterial({ color: 0x00ff00 });

const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

function animate() {
  renderer.render(scene, camera);
}

renderer.setAnimationLoop(animate);

window.addEventListener('resize', function () {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
});

Creating a new Mesh Instance

To create a clone, you could say we just need to make a new Mesh instance and reuse the same geometry and material, right?

const geometry = new THREE.BoxGeometry(3, 3, 3);
const material = new THREE.MeshPhongMaterial({ color: 0x00ff00 });

const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

const clone1 = new THREE.Mesh(geometry, material);
scene.add(clone1);
clone1.position.x = -5;

Yep, that totally works!

Mesh copy
Mesh copy

This approach, however, comes with two drawbacks.

The first issue is that we can’t replicate the transformations applied to the original mesh.

mesh.scale.set(0.3, 0.3, 0.3);

const clone1 = new THREE.Mesh(geometry, material);
scene.add(clone1);
clone1.position.x = -5;
No scale applied to the clone
No scale applied to the clone

This might not seem like a big deal if you’re just making a copy or two, but imagine needing a couple dozen. Reapplying the same transformations to all of them manually would be anything but fun.

The second drawback is that we can’t duplicate the children of the original mesh.

const geometry = new THREE.BoxGeometry(3, 3, 3);
const material = new THREE.MeshPhongMaterial({ color: 0x00ff00 });

const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
mesh.scale.set(0.3, 0.3, 0.3);

const child = new THREE.Mesh(geometry, material);
child.scale.set(0.3, 0.3, 0.3);
mesh.add(child);
child.position.y = 7;

const clone1 = new THREE.Mesh(geometry, material);
scene.add(clone1);
clone1.position.x = -5;
No copy of the child mesh
No copy of the child mesh

clone()

A solution to both of these issues is using the clone() method.

const geometry = new THREE.BoxGeometry(3, 3, 3);
const material = new THREE.MeshPhongMaterial({ color: 0x00ff00 });

const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
mesh.scale.set(0.3, 0.3, 0.3);

const child = new THREE.Mesh(geometry, material);
child.scale.set(0.3, 0.3, 0.3);
mesh.add(child);
child.position.y = 7;

const clone1 = mesh.clone();
scene.add(clone1);
clone1.position.x = -5;
Cloning using the clone() method
Cloning using the clone() method

By the way, the clone() method takes an argument that lets us prevent cloning the original object's children.

const clone1 = mesh.clone(false);
Prevent cloning children
Prevent cloning children

Deep Cloning

Let’s make a new copy, but this time, we’ll try changing its color.

const clone2 = clone1.clone();
clone2.position.x = 5;
scene.add(clone2);
clone2.material.color.setHex(0xff0000);
Original material is affected by the color change
Original material is affected by the color change

As you can see, the color—actually, the entire material—of the original mesh has also changed. That’s because all the copies share the same material as the original mesh.

To fix that, we need to create a new material for the clone.

const clone2 = clone1.clone();
clone2.position.x = 5;
scene.add(clone2);
const material2 = new THREE.MeshPhongMaterial({ color: 0xff0000 });
clone2.material = material2;
Clone with a new material
Clone with a new material

By the way, the clone() method works with materials too, not just meshes. So, if you’re using something like a MeshPhysicalMaterial with lots of properties and want your new material to inherit all of them, you can just use clone().

const clone2 = clone1.clone();
clone2.position.x = 5;
scene.add(clone2);

clone2.material = material.clone();
clone2.material.color.setHex(0xff0000);

Cloning Models

You might be tempted to use the same approach for cloning your loaded models—by that, I mean using the clone() method.

Actually, let’s give that a shot. Load a model and use clone() to create a clone of it.

const loader = new GLTFLoader();
loader.load('/Stag.gltf', function (gltf) {
  const model = gltf.scene;
  scene.add(model);

  const clone1 = model.clone();
  scene.add(clone1);
  clone1.position.x = -5;
});
Model clone not appearing as it should be
Model clone not appearing as it should be

As you can see, only the horns of the model are visible. Furthermore, things can get even messier when the model is animated.

What we can conclude from this is that clone() isn’t suitable for cloning models.

"Yeah, so how can we do that?"

To do that, we need to use a special package called SkeletonUtils. This module has its own clone() method that works for cloning models.

import * as SkeletonUtils from 'three/examples/jsm/utils/SkeletonUtils.js';
const loader = new GLTFLoader();
loader.load('/Stag.gltf', function (gltf) {
  const model = gltf.scene;
  scene.add(model);

  const clone1 = model.clone();
  scene.add(clone1);
  clone1.position.x = -5;

  const clone2 = SkeletonUtils.clone(model);
  scene.add(clone2);
  clone2.position.x = 5;
});
Cloning using SkeletonUtils.clone()
Cloning using SkeletonUtils.clone()

Wrap Up

And that’s all you need to become the Frankenstein of the Three.js object cloning department.

Keep cloning! See ya!

Buy me a coffee

Credits and Resources

Related Content