Three.js LoadingManager: Track Asset Loading Progress

In web-based 3D applications, loading heavy assets like 3D models, textures, and audio files can heavily impact user experience. This article explores the Three.js LoadingManager, explaining how it serves as a central hub to globally track the loading progress of all your scene assets, and how you can use its built-in event hooks to build functional loading screens.

What is the LoadingManager?

The LoadingManager is a built-in Three.js class designed to coordinate and monitor the loading status of multiple external files. Instead of tracking each loader (such as GLTFLoader, TextureLoader, or FontLoader) individually, you can pass a single LoadingManager instance to all of them. The manager then aggregates their progress, giving you a unified way to handle loading states.

How Global Tracking Works

When you instantiate a loader in Three.js, you can optionally pass a LoadingManager as an argument to its constructor. Once passed, the loader automatically registers its network requests with that manager.

If you do not pass a custom manager to your loaders, Three.js automatically assigns them to a global default instance called THREE.DefaultLoadingManager. You can configure this default manager directly if you want to track all loading activity across your entire application without passing a manager to every individual loader.

Key Callback Functions

The LoadingManager provides four essential callback properties that you can define to handle different stages of the loading process:

Code Implementation

Here is a practical example of how to set up and use the LoadingManager with multiple loaders:

import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';

// 1. Create the LoadingManager
const loadingManager = new THREE.LoadingManager();

// 2. Define the callbacks
loadingManager.onStart = function (url, itemsLoaded, itemsTotal) {
    console.log(`Started loading: ${url}. Loaded ${itemsLoaded} of ${itemsTotal} files.`);
};

loadingManager.onProgress = function (url, itemsLoaded, itemsTotal) {
    const progressPercent = (itemsLoaded / itemsTotal) * 100;
    console.log(`Loading: ${progressPercent.toFixed(0)}%`);
    // Update your HTML progress bar element here
};

loadingManager.onLoad = function () {
    console.log('All assets loaded successfully! Ready to render scene.');
    // Hide loading screen and start the animation loop here
};

loadingManager.onError = function (url) {
    console.error(`There was an error loading ${url}`);
};

// 3. Pass the manager to your loaders
const textureLoader = new THREE.TextureLoader(loadingManager);
const gltfLoader = new GLTFLoader(loadingManager);

// 4. Load your assets
const colorTexture = textureLoader.load('textures/color.jpg');
gltfLoader.load('models/character.gltf', (gltf) => {
    scene.add(gltf.scene);
});

By leveraging the LoadingManager, you avoid writing complex asynchronous tracking logic yourself. It keeps your code clean and provides a reliable way to ensure your users are greeted with a smooth, informative loading experience.