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.