advanced · 30 minute study
Loading a model is an asynchronous lifecycle
A valid model can still arrive after the user leaves, point at missing textures, appear outside the camera, or leak GPU resources when revisited. Loading needs a state and ownership contract.
Useful preparation · 1 prerequisite
Use this as a map, not a lock. If these ideas are familiar, continue.
- A texture is data with a meaning and a budget — A small downloaded image can occupy a large GPU allocation, and a plausible-looking material can still interpret its data incorrectly. Learn representation, color and memory together.
First, build a mental model
The asset is an external result that arrives later. Your scene owner decides whether that result still belongs to the current view, how it becomes visible, and when its resources can be released. A resolved promise is only one milestone in that lifecycle.
- glTF describes scene nodes, meshes, materials, textures, animations and related data. GLB is its binary container form and can package resources together. Inspect actual dependencies and supported extensions instead of assuming every .glb has no external requirements.
- GLTFLoader is an explicit Three.js addon. It returns an asset with a scene hierarchy and associated data; adding that scene is distinct from fetching and parsing it. Optional compression extensions may need compatible decoder setup.
- Use visible loading, error and ready states. An HTTP failure, decoding error, unsupported extension, missing dependency and invisible-but-loaded model have different investigations. Do not convert all of them into a spinner that never ends.
- When requests overlap, identify the current request. If an older result arrives after a newer selection, do not mount it over the current one. Rejecting a stale result is logical cancellation; it does not necessarily abort network or decoder work.
- Removing an object from the scene does not dispose its GPU resources. Geometries, materials and textures need an ownership-aware release policy. Shared resources must survive until their other owners finish using them.
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
const loader = new GLTFLoader();
let currentRequest = 0;
// scene and showStatus are supplied by the view owner.
// releaseOwnedAsset is YOUR ownership-aware helper, not a Three.js API.
async function showModel(url, releaseOwnedAsset) {
const request = ++currentRequest;
showStatus("loading");
try {
const asset = await loader.loadAsync(url);
if (request !== currentRequest) {
releaseOwnedAsset(asset);
return;
}
scene.add(asset.scene);
showStatus("ready");
} catch (error) {
if (request === currentRequest) showStatus("error: inspect the asset request");
}
}
function invalidatePendingRequest() { currentRequest += 1; }
// The view separately owns replacement/removal of already mounted assets.- Request A
The first model gets identity 1. Before it finishes, the user chooses another model, creating identity 2.
- B wins
The second result arrives while identity 2 is current. It may enter the active scene and report ready.
- A arrives late
The first result sees that identity 1 is stale. It is released by its resource owner instead of replacing the user's newer choice.
Production thinking
Take this beyond the example.
Use immutable asset URLs or deliberate versioning, verify deployed paths and enforce a useful failure state. Account for shared resources before disposal. Test rapid navigation, repeated mounting, slow and failed requests, oversized bounds and unsupported extensions. A local validator can establish file structure; only the target loader and scene can establish the full runtime integration.
Sources and further reading
Your evidence
unseen is a local learning signal, not a credential or proof of production competence.
Explain the mental model, then explicitly acknowledge it. Opening a page is not evidence.
Supported retries can complete this lesson. Independent evidence and delayed review determine the skill stage.
Next lesson · Treat the asset export as a build →