Three.js Loading Manager — Asset Loading and Progress Tracking
In this tutorial, you will learn about Three.js Loading Manager. We cover key concepts, practical examples, and best practices to help you master this topic.
Three.js LoadingManager provides centralized asset loading control with progress tracking, error handling, and start/complete callbacks for managing complex loading pipelines.
What You'll Learn
By the end of this guide, you will use LoadingManager to track loading progress, show a loading screen, handle errors for missing assets, preload multiple assets with dependencies, and integrate with GLTFLoader.
Why Loading Management Matters
Without a loading manager, assets load asynchronously with no feedback. In Durga Antivirus Pro, the threat dashboard shows a loading progress bar while models and textures download. Without this, users stare at a blank screen wondering if the app is broken.
flowchart LR
A[Page Load] --> B[Show Loading Screen]
B --> C[Create LoadingManager]
C --> D[Queue Assets]
D --> E[Loading Manager Tracks Progress]
E --> F[Update Progress Bar]
F --> G{All Loaded?}
G -->|No| E
G -->|Yes| H[Hide Loading Screen]
H --> I[Start Application]
Basic LoadingManager
var manager = new THREE.LoadingManager();
manager.onStart = function(url, itemsLoaded, itemsTotal) {
console.log('Started loading: ' + url);
};
manager.onProgress = function(url, itemsLoaded, itemsTotal) {
var progress = (itemsLoaded / itemsTotal) * 100;
console.log('Loading: ' + progress.toFixed(1) + '%');
};
manager.onLoad = function() {
console.log('All assets loaded!');
};
manager.onError = function(url) {
console.error('Error loading: ' + url);
};
var textureLoader = new THREE.TextureLoader(manager);
var cubeTextureLoader = new THREE.CubeTextureLoader(manager);
var gltfLoader = new GLTFLoader(manager);
Expected output: Console logs showing each asset as it loads, overall progress percentage, and a completion message when all assets finish.
Loading Screen With Progress Bar
var progressBar = document.getElementById('progress-bar');
var loadingScreen = document.getElementById('loading-screen');
var manager = new THREE.LoadingManager();
manager.onProgress = function(url, loaded, total) {
var percent = (loaded / total) * 100;
progressBar.style.width = percent + '%';
};
manager.onLoad = function() {
loadingScreen.style.display = 'none';
};
manager.onError = function(url) {
console.error('Failed to load: ' + url);
// Optionally show error on screen
};
Expected output: A loading screen with a progress bar that fills from 0% to 100% as each asset finishes loading.
Loading Multiple Asset Types
var manager = new THREE.LoadingManager();
var textureLoader = new THREE.TextureLoader(manager);
var gltfLoader = new GLTFLoader(manager);
var audioLoader = new THREE.AudioLoader(manager);
// Queue all assets
textureLoader.load('textures/diffuse.jpg');
textureLoader.load('textures/normal.jpg');
gltfLoader.load('models/character.glb');
gltfLoader.load('models/environment.glb');
audioLoader.load('audio/background.mp3');
Expected output: The manager tracks all 5 assets across different loaders as a single queue. Progress reflects total items.
Error Recovery
manager.onError = function(url) {
var retryButton = document.createElement('button');
retryButton.textContent = 'Retry: ' + url;
retryButton.onclick = function() {
textureLoader.load(url);
};
document.getElementById('error-area').appendChild(retryButton);
};
Expected output: When an asset fails to load, a retry button appears. Clicking it re-queues the failed asset.
Common Mistakes
1. Creating Separate Managers for Each Loader
Each manager tracks its own queue. Use a single manager shared across all loaders for unified progress tracking.
2. Not Handling onError
Users see a broken scene with no feedback. Always implement onError to show a fallback or retry option.
3. Zero Division on Progress
When itemsTotal is 0, the progress calculation divides by zero. Check itemsTotal > 0 before computing percentage.
4. Forgetting to Remove Loading Screen
If onLoad never fires (e.g., an asset silently fails), the loading screen persists forever. Add a timeout fallback.
5. Loading the Same Asset Multiple Times
The manager counts each load call separately. Cache loaded assets to avoid redundant downloads.
Practice Questions
Q1: What events does LoadingManager emit? A: onStart (when loading begins), onProgress (per-file progress), onLoad (all complete), onError (per-file error).
Q2: How do you share a LoadingManager across loaders?
A: Pass the manager instance as the first argument to each loader constructor: new THREE.TextureLoader(manager).
Q3: What happens when an asset fails to load? A: The onError callback fires with the failed URL. The manager continues loading remaining assets. onLoad fires when all non-failed assets complete.
Q4: How do you show a loading progress bar?
A: In onProgress, update the progress bar width as (loaded / total) * 100%.
Q5: How do you handle the case where onLoad never fires? A: Add a setTimeout fallback that hides the loading screen after a maximum timeout (e.g., 30 seconds).
Challenge: Build a scene with 10 different colored textures applied to spheres. Show a loading screen with a progress bar that fills smoothly from 0% to 100% as each texture loads. Add a retry button for failed textures.
FAQ
Try It Yourself — Loading Screen Demo
Build a page that simulates loading multiple assets with a visible progress bar.
<!DOCTYPE html>
<html>
<head>
<title>Loading Manager Demo</title>
<style>
body { margin: 0; overflow: hidden; font-family: sans-serif; }
#loading-screen { position: fixed; inset: 0; background: #0a0a1a; display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 100; color: white; }
#progress-container { width: 300px; height: 20px; background: #222; border-radius: 10px; overflow: hidden; margin: 20px; }
#progress-bar { height: 100%; width: 0; background: linear-gradient(90deg, #4ecdc4, #44aa88); transition: width 0.2s; }
#loading-text { font-size: 14px; color: #888; }
#scene-container { width: 100%; height: 100%; }
#error-container { margin-top: 10px; }
.retry-btn { margin: 4px; padding: 4px 8px; cursor: pointer; background: #333; color: white; border: 1px solid #555; border-radius: 4px; font-size: 12px; }
</style>
</head>
<body>
<div id="loading-screen">
<h2>Loading Scene</h2>
<div id="progress-container"><div id="progress-bar"></div></div>
<div id="loading-text">0%</div>
<div id="error-container"></div>
</div>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.170.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.170.0/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from "three";
var progressBar = document.getElementById('progress-bar');
var loadingText = document.getElementById('loading-text');
var loadingScreen = document.getElementById('loading-screen');
var errorContainer = document.getElementById('error-container');
var manager = new THREE.LoadingManager();
manager.onProgress = function(url, loaded, total) {
var pct = Math.round((loaded / total) * 100);
progressBar.style.width = pct + '%';
loadingText.textContent = pct + '%';
};
manager.onLoad = function() {
loadingScreen.style.display = 'none';
initScene();
};
manager.onError = function(url) {
var btn = document.createElement('button');
btn.className = 'retry-btn';
btn.textContent = 'Retry: ' + url.split('/').pop();
btn.onclick = function() {
btn.remove();
new THREE.TextureLoader(manager).load(url);
};
errorContainer.appendChild(btn);
};
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 100);
camera.position.set(0, 0, 5);
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
renderer.setPixelRatio(devicePixelRatio);
function initScene() {
document.body.appendChild(renderer.domElement);
var textureLoader = new THREE.TextureLoader(manager);
var urls = [
'https://threejs.org/examples/textures/uv_grid_opengl.jpg',
'https://threejs.org/examples/textures/planets/earth_atmos_2048.jpg',
'https://threejs.org/examples/textures/colors.png'
];
urls.forEach(function(url) {
textureLoader.load(url);
});
}
// Start initial empty scene
initScene();
function animate() {
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
window.addEventListener("resize", function() {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
</script>
</body>
</html>
What's Next
Create billboard sprites that always face the camera.
Sprites — Billboard sprites and sprite materials. Lines — Drawing lines and polylines in Three.js.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro