Traverse Three.js Scene Graph to Find Objects

In Three.js, managing and locating specific 3D objects within a complex hierarchy can be challenging. This article explains how to efficiently traverse the entire Three.js scene graph using the built-in traverse method, allowing you to search, filter, and manipulate specific objects based on their properties, names, types, or custom user data.

Understanding the Scene Graph

A Three.js scene is structured as a hierarchical tree graph. The root node is typically the Scene object, which contains child nodes like meshes, lights, cameras, and groups. Each of these child nodes can, in turn, have their own children.

To find a specific object or perform an action on a subset of objects, you must recursively visit every node in this tree.

Using the traverse Method

Three.js provides a built-in method called traverse on all objects that inherit from Object3D. When you call traverse on a parent object (such as the main Scene), it executes a callback function for the parent and all of its descendants recursively.

Here is the basic syntax:

scene.traverse((object) => {
    // This function runs for every object in the scene
    console.log(object.type);
});

Finding Objects by Type

To perform actions on specific types of objects, such as meshes or lights, check the properties of the object inside the callback:

scene.traverse((object) => {
    if (object.isMesh) {
        // Apply changes only to meshes
        object.material.color.setHex(0xff0000);
    }
});

Using flags like isMesh, isLight, or isGroup is highly performant and the recommended way to filter by type in Three.js.

Finding Objects with Custom Properties

You can also use the userData object to store custom identifiers and search for them during traversal. This is useful for identifying interactive objects.

// Example: Mark an object as interactive when creating it
myMesh.userData.isInteractive = true;

// Later, search the scene for interactive objects
const interactiveObjects = [];

scene.traverse((object) => {
    if (object.userData && object.userData.isInteractive) {
        interactiveObjects.push(object);
    }
});

Built-in Shortcuts: getObjectByName and getObjectById

If you only need to find a single object and you already know its name or unique ID, Three.js provides optimized shortcut methods that handle the traversal internally.

Finding by Name

Assign a unique name to your object:

const playerMesh = new THREE.Mesh(geometry, material);
playerMesh.name = "player_character";
scene.add(playerMesh);

Retrieve it later from anywhere in the scene graph:

const player = scene.getObjectByName("player_character");
if (player) {
    // Object found
}

Finding by ID

Every Object3D is assigned a unique auto-incremented ID upon creation. You can retrieve an object directly using this ID:

const objectId = myMesh.id;
const retrievedObject = scene.getObjectById(objectId);

Performance Considerations

While traversal is highly useful, it can be performance-heavy if executed frequently.