From Asset to Experience: Loading, Motion, Input, and Extension

A Three.js scene becomes an application when external content, time, and user intent begin changing the scene graph. This chapter follows that path from a runtime asset through animation, input, and image effects, then shows where addons and render-on-demand decisions belong. The practical goal is to choose an application path that matches the asset and device instead of treating every loader, control, or effect as part of the core engine.

A runtime asset arrives as a scene, not a single mesh

The current Three.js manual recommends glTF or GLB where possible because the format is designed for runtime delivery. It can carry meshes, materials, textures, cameras, lights, skins, skeletons, morph targets, and animations in a form intended to be transmitted and rendered. That does not mean every glTF file is perfect or that every application should accept its hierarchy unchanged. It means the format gives the loader enough structured information to build a useful runtime object graph.

GLTFLoader is an addon. After it parses an asset, its result includes a default scene and may include additional scenes, cameras, animations, metadata, and a parser. The application can add the default scene to its own scene, find named nodes, inspect descendants, and attach behavior to the parts it needs. This is a very different workflow from loading a flat triangle list and inventing all relationships afterward.

The loader also has extension boundaries. Compressed mesh data may require a Draco decoder. Compressed texture data may require a KTX2 loader. Other compression or format features use registered plugins or decoders. These components are not invisible implementation details: they are part of the application’s loading contract. An asset may be valid while still failing to load because the application did not provide the decoder required by an extension it declares.

A successful parse is only the start of a usable asset. The loaded hierarchy preserves authored names, parent transforms, origins, and scales. A car group may contain the individual cars. A character’s animation may target named bones. Exporters may add orientation or correction nodes. If an application immediately assumes that the root is a neutral world-space object, it may attach a path, camera, or animation to the wrong frame.

The right response is inspection rather than guesswork. Traverse the loaded hierarchy, identify the nodes that represent the behavior the application needs, and check their local transforms before writing runtime logic. A model can display all its textures and still rotate around a useless origin. It can be visually correct and operationally inconvenient. The asset pipeline crosses from an authoring tool into Three.js, but Three.js cannot infer the application’s intended pivot, scale convention, or naming policy.

This is also why “load the model” is not the same as “own the model.” The application decides whether to re-parent a subtree under a new pivot, which resources are shared, when the asset is visible, and when it can be disposed. The loader supplies runtime objects; the application supplies their role in the larger scene.

Loading is an asynchronous state transition, so the application must decide what the user sees before it completes. It can show a placeholder, keep a shell scene active, or wait until the required assets are ready before adding the final object. Texture and model requests may finish at different times, and a loading manager can coordinate a group of resources. An error callback is not optional defensive decoration: a missing URL, unavailable decoder, or failed texture request changes whether the scene can be trusted. A runtime viewer should have a clear failure state rather than silently presenting an empty canvas.

The same asset can also have more than one valid use. One application may keep the imported hierarchy intact because it wants the authored cameras and animation clips.