How to Remove Objects from a Three.js Scene

In Three.js, removing an object from the scene graph during runtime requires more than just calling the .remove() method. To prevent memory leaks and ensure optimal performance, you must also properly dispose of the object’s geometry, materials, and textures. This article provides a straight-to-the-point guide on how to safely detach an object from your scene, release its WebGL resources, and clean up GPU memory.

Step 1: Remove the Object from the Parent

The first step is to disconnect the object from the scene graph so it is no longer rendered. You do this by calling the remove() method on the object’s parent (which is often the scene itself).

scene.remove(mesh);

Step 2: Dispose of Geometries and Materials

Simply removing a mesh from the scene does not free up the GPU memory allocated for its geometry and materials. To prevent memory leaks, you must explicitly call the .dispose() method on these components.

// Dispose of geometry
if (mesh.geometry) {
    mesh.geometry.dispose();
}

// Dispose of material(s)
if (mesh.material) {
    if (Array.isArray(mesh.material)) {
        mesh.material.forEach(material => disposeMaterial(material));
    } else {
        disposeMaterial(mesh.material);
    }
}

Step 3: Dispose of Textures

If your materials use textures, those textures must also be disposed of individually to free up VRAM.

function disposeMaterial(material) {
    // Dispose of textures associated with the material
    for (const key in material) {
        if (material[key] && typeof material[key].dispose === 'function') {
            material[key].dispose();
        }
    }
    // Dispose of the material itself
    material.dispose();
}

Creating a Reusable Clean-up Function

If you need to remove a complex object with nested children (like a loaded GLTF model), you should traverse the object hierarchy to ensure every child mesh is fully cleaned up.

function safelyRemoveObject(objectToRemove) {
    objectToRemove.traverse((child) => {
        if (child.isMesh) {
            // Clean up geometry
            if (child.geometry) {
                child.geometry.dispose();
            }

            // Clean up materials and textures
            if (child.material) {
                if (Array.isArray(child.material)) {
                    child.material.forEach((material) => {
                        cleanMaterial(material);
                    });
                } else {
                    cleanMaterial(child.material);
                }
            }
        }
    });

    // Remove from parent
    if (objectToRemove.parent) {
        objectToRemove.parent.remove(objectToRemove);
    }
}

function cleanMaterial(material) {
    // Dispose of any textures
    for (const key in material) {
        if (material[key] && material[key].isTexture) {
            material[key].dispose();
        }
    }
    // Dispose of the material
    material.dispose();
}

Using this recursive approach ensures that all GPU resources associated with the target object and its hierarchy are completely freed, maintaining high performance in your Three.js application.