Command Palette

Search for a command to run...

Hectal
Course outline
Topic 10.3Intermediate ~19 min · step 3 of 7

Loading Models

In one line

GLTFLoader.loadAsync(url) fetches and parses a model and gives you its scene. Then you measure it, scale it to real size, stand it on the floor, and handle the ways a load fails: a missing file, a file that is not a model, no network.

0/7 · 0%

Think of it like this

A parcel arrives. Before it goes on the shelf you open it, check it is what you ordered, and see how big it is. A model from a file is the same: load it, then measure and place it, and know what to do when the parcel never comes.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

GLTFLoader
The three.js add-on that reads .gltf and .glb files and builds three.js objects from them.
gltf.scene
The root Group of the loaded model: add this to your scene.
LoadingManager
An object that loaders report to, so you can follow progress across many files and know when all are done.
CORS
Cross-Origin Resource Sharing: the server's permission, in a response header, for pages on other sites to read a file. Without it the browser blocks the read.

Step by step

01Load two very different models

The first example loads the course's bike and the Fox, measures each with Box3, scales it to a chosen length, and stands it on the floor. The console prints each size as loaded, the scale used, and the lowest point after: 0.000 for both.

02Read the Fox's numbers

154.72 along z means the fox is about 155 units long. A fox is about 0.9 m long, so the file's unit is roughly a centimetre. scale × 0.0058 converts it. Real-world files come in metres, centimetres, inches or no unit at all: always measure.

03Make a load fail three ways

The second example asks for a misspelled file, a text file and a server that does not exist, and prints each error's name and message (with the site's address cut off). Note that only the first message says what happened in words a person could act on.

04Turn errors into decisions

A 404 is a bug in your paths or deployment (Topic 18.6). A SyntaxError usually means the server sent an HTML page instead of the model: a wrong path that a single-page app's server answers with index.html. Failed to fetch means no network, a blocked request or CORS: retrying may help. Topic 10.4 shows the visitor something useful in each case.

Try it yourself

  1. 1

    Leave the fox in centimetres

    In the first example, change the fox's target length 0.9 to 154.72. Its scale becomes x1.0000 and the fox is 155 m long: the picture shows two huge brown blocks (parts of the fox) hanging from the top edge, towering over the small bike, because the camera is now inside a 155 m animal.

  2. 2

    Turn the bike around

    In the first example add bike.rotation.y = Math.PI; after the line fox.position.x += 1.4;. The console is unchanged, but the bike now faces left: the handlebar and front wheel are on the left of the picture. The half turn happens about the model's own origin, so it also moves a little: a wrapper Group at the centre avoids that.

  3. 3

    Load a model from a data URL

    In the second example add a case ["a data URL", "data:text/plain,hello"]. It fails with SyntaxError: Unexpected token 'h', "hello" is not valid JSON: the loader accepted the URL and read its content as glTF JSON.

Code & diagrams

Load, measure, scale and stand on the floor New tab

The bike is already in metres; the fox is not. Both end with their lowest point on the floor. Fox model by PixelMannen (CC0), rigging and animation by tomkranis (CC BY 4.0), glTF conversion by @AsoboStudio and @scurest (CC BY 4.0); the licences are the subject of Topic 10.6.

idle

Console

Run the scene to see its console.

Expected console output

bike  as loaded: 1.83 x 0.92 x 0.56 | scale x0.9978 | lowest point now 0.000
fox   as loaded: 25.19 x 79.03 x 154.72 | scale x0.0058 | lowest point now 0.000
Three ways a load fails New tab

Console only. The address of the site is removed from the message, so the output is the same here and on your computer.

idle

Console

Run the scene to see its console.

Expected console output

a file that is not there:
  Error: fetch for "/data/3d/models/lumen-bikee.glb" responded with 404: Not Found
a file that is not a model:
  SyntaxError: Unexpected token '#', "# Assets o"... is not valid JSON
a server that is not reachable:
  TypeError: Failed to fetch

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Forget to wait for the model

In the first example change (await loader.loadAsync("/data/3d/models/lumen-bike.glb")).scene to loader.loadAsync("/data/3d/models/lumen-bike.glb").scene.

terminal
$ Run
── what you'll see ──
Uncaught TypeError: Cannot read properties of undefined (reading 'updateWorldMatrix')

Myth vs fact

Myth

If a model looks wrong, the file is broken.

Fact

Most "broken" models are fine: wrong units (a centimetre model is 100 times too big), a different up axis, or the origin far from the object. Measure it with Box3 before blaming the file.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    THREE.Cache.enabled = true (off by default) makes three.js keep downloaded files in memory; otherwise loading the same URL twice asks the browser again (its HTTP cache may answer), and either way the file is parsed again. For repeated parts, load once and clone() (use SkeletonUtils.clone for skinned models, Topic 9.4).

  • ▸

    loader.setPath("/data/3d/models/") sets a base path; loader.setResourcePath() sets where a .gltf's separate .bin and images are found. A .glb has everything inside, so it never needs a resource path.

Remember this

  1. 1

    The problem this solves: a model made by someone else is in their units, facing their direction, standing wherever their scene's origin was. Put it in the showroom as it is and it may be a hundred times too big, under the floor, or behind the camera.

  2. 2

    Loading. const gltf = await new GLTFLoader().loadAsync(url) resolves with an object: gltf.scene (a Group to add to your scene), gltf.animations (clips for Phase 9's mixer), gltf.cameras, gltf.asset (the file's version, generator, copyright) and gltf.parser. The callback form loader.load(url, onLoad, onProgress, onError) does the same with callbacks. Add-ons import from three/addons/loaders/GLTFLoader.js.

  3. 3

    Measure, scale, place. new THREE.Box3().setFromObject(model) gives its size in your units. The course's bike arrives 1.83 × 0.92 × 0.56 m (the handlebar is 0.56 m wide): right already. The Fox arrives 25.19 × 79.03 × 154.72: it was made in centimetres, so it needs scale × 0.0058 to be 0.9 m long. Then move it so its centre is over the origin and its lowest point is at y = 0, as the first example's fit() does.

  4. 4

    How a load fails, and what you see. A wrong path: Error: fetch for "/data/3d/models/lumen-bikee.glb" responded with 404: Not Found. A file that is not a model (an HTML error page, a text file): SyntaxError: Unexpected token '#', "# Assets o"... is not valid JSON, because the loader tried to read it as glTF JSON. No network: TypeError: Failed to fetch. A server that forgets CORS headers fails the same way as no network, with the real reason only in the browser's console. A wrong Content-Type on a .glb (for example text/plain) is harmless: the loader reads bytes and checks the file's own glTF magic, not the label. The case that hurts is a server that answers 200 with an HTML page.

  5. 5

    A model facing the wrong way. glTF says up is +Y and the front of a model faces +Z. A model from a tool that treats Z as up lies on its back (rotate it -90° about x), and a model that faces away needs a half turn (model.rotation.y = Math.PI). Put the loaded scene inside a Group and turn the group: the correction then lives in one place, and the model's own transform stays as the artist made it.

  6. 6

    Several files at once. A LoadingManager passed to loaders (new GLTFLoader(manager)) counts every item they start: models, and also textures inside models. onProgress(url, loaded, total) and onLoad() let one loading bar cover everything. In a real run with the Fox, a JPEG and one missing file the manager's total reached 6, not 3: the Fox's own texture counts too, and the missing file's error was reported twice. Treat the total as a growing number, not a fixed one.

Explain it without notes

01

A model loads but nothing appears. Name three things you would check, in order.

02

What does SyntaxError: Unexpected token '<' usually mean when loading a .glb, and how do you confirm it?

Practice

01

Write fitToLength(object, metres) that scales an object so its longest side has that length and puts its lowest point at y = 0.

02

Load the Fox and print the names of its animation clips and its node count.

Done when you can

  • Load a model with GLTFLoader and add its scene

  • Measure a model and fit it to a real size on the floor

  • Read the three common load errors and say what each means

  • Follow several loads with a LoadingManager