Track Three.js Model Loading Progress with XHR

This article explains how to monitor the download progress of large 3D models in Three.js using the XMLHttpRequest (XHR) progress event. You will learn how to implement the onProgress callback function within Three.js loaders to calculate the loading percentage and update a user interface in real-time.

Implementing the onProgress Callback

In Three.js, loading managers and specific loaders (such as GLTFLoader, OBJLoader, or FBXLoader) accept a progress callback as the third argument of their .load() method. This callback receives a native browser ProgressEvent (often named xhr), which contains data about the download status.

Here is a practical example using GLTFLoader:

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

const loader = new GLTFLoader();

loader.load(
    'models/large-city.gltf',
    // 1. onLoad Callback
    function (gltf) {
        scene.add(gltf.scene);
        hideLoadingScreen();
    },
    // 2. onProgress Callback
    function (xhr) {
        if (xhr.lengthComputable) {
            const percentComplete = (xhr.loaded / xhr.total) * 100;
            updateLoadingBar(percentComplete);
            console.log(`Model is ${Math.round(percentComplete)}% loaded`);
        } else {
            // Fallback if total size is unknown
            console.log(`Loaded ${xhr.loaded} bytes`);
        }
    },
    // 3. onError Callback
    function (error) {
        console.error('An error happened during loading:', error);
    }
);

How the Code Works

The xhr object passed to the onProgress function has two critical properties: * xhr.loaded: The number of bytes already downloaded. * xhr.total: The total size of the file in bytes.

Dividing xhr.loaded by xhr.total and multiplying by 100 yields the exact percentage of the download completed.

The xhr.lengthComputable check is a safeguard. If the hosting server does not provide the file size, lengthComputable will be false, and xhr.total will equal 0.

Handling Server-Side Issues (Missing Content-Length)

For the progress bar to work correctly, the server hosting your 3D models must send the Content-Length header in its HTTP response.

If your progress bar jumps instantly from 0% to 100% or if xhr.lengthComputable is always false: 1. Ensure your web server (e.g., Nginx, Apache, or a cloud storage bucket like AWS S3) is configured to include the Content-Length header. 2. If using Cross-Origin Resource Sharing (CORS), ensure the server exposes the Content-Length header by setting the Access-Control-Expose-Headers: Content-Length response header.