Related lab bench

Inspect the asset in the GLB Viewer

Use the live viewer to check bounds, material readability, animation clips, and model scale before the scene becomes harder to debug.

Open GLB Viewer Check before publishing

Start with the asset contract

GLB is usually the simplest delivery format for a browser viewer because geometry, materials, textures, and animations can live inside one binary file. That does not mean every GLB is ready for a scene. Files exported from Blender, Cinema 4D, Spline, or a CAD converter can arrive at wildly different scales, with pivots placed far from the visible mesh or with materials that only look correct under a specific environment.

Before you design UI around a model, create a small loader bench. The bench should answer four questions: how large is the object, where is its center, how many meshes and vertices does it contain, and whether animation clips exist. These facts are more useful than a pretty preview because they tell you whether the file can survive responsive layouts, mobile GPUs, and product-page constraints.

Normalize before styling

A common mistake is to add postprocessing, orbit controls, and lighting before the imported object is understood. Instead, load the asset, compute a Box3 around it, move the object so the box center is near the origin, then decide whether the model needs a uniform scale. If a preview stage handles this work consistently, every model starts from the same framing assumptions.

The next layer is material diagnosis. If a model appears black, the first suspect is not the loader. Check whether the material depends on an environment map, whether color management is configured, and whether the lighting setup is strong enough for rough PBR surfaces. A single hemisphere light plus a key and rim light is often enough for inspection, while product renders may need image-based lighting.

Handle animations deliberately

GLTFLoader returns animation clips separately from the scene graph. A viewer should not assume the first clip should play forever; it should list clip names and durations, then allow a user or page author to choose. This is especially important for character files where idle, walk, and gesture clips may all be bundled into one export.

Use AnimationMixer only after the model is in the scene and the clips have been inspected. Keep a small update loop that advances the mixer by delta time, not by a fixed number. That keeps animation speed stable across machines. When a model is removed, stop mixer actions and revoke object URLs so local preview tools do not leak memory during repeated uploads.

A viewer checklist that catches most issues

The useful viewer is a diagnostic surface, not a gallery. Show filename, file size, scene children, mesh count, material count, bounding-box size, center, animation clips, and whether the camera was fitted automatically. Add a reset button that returns the model to normalized position and camera framing. These details turn a vague export problem into something a designer or developer can fix quickly.

For AdSense and search, the same checklist also creates meaningful page content: explain the problem, show what to inspect, and link to focused tools. A thin page that only says 'drop a GLB here' is less useful than a page that teaches what the preview is measuring and why the measurements matter.

Inspect the same asset before integration

The lab sample is a deliberately small control asset. Its fixed metrics make loader, camera, and material regressions easier to separate from asset complexity.

  • 1 mesh and 12 triangles
  • 2.00 x 2.00 x 2.00 bounds
  • Local file loading stays on-device
Open the GLB Viewer sample

Keep loader configuration beside the loader

A loader with implicit decoder paths is difficult to move between local, staging, and production builds.

Fragile pattern
const loader = new GLTFLoader();
loader.load('/model.glb', ({ scene }) => root.add(scene));
Testable pattern
const loader = new GLTFLoader(manager);
loader.setDRACOLoader(dracoLoader);
loader.setKTX2Loader(ktx2Loader.detectSupport(renderer));
loader.load(url, onLoad, onProgress, onError);

DRACO, KTX2, and texture color space

Configure DRACOLoader and KTX2Loader before the first model request, keep decoder paths beside the loader setup, and call KTX2Loader.detectSupport(renderer) with the renderer that will display the asset. Decoder files are additional production dependencies, so verify their URLs, cache headers, and cross-origin behavior instead of assuming the compressed GLB is self-contained.

Compression is not automatically a win for every asset. Compare transfer size, decoder cost, first visible frame, and texture memory against an uncompressed control model. Mark base-color and emissive textures as sRGB color data; keep normal, roughness, metalness, and occlusion maps in their non-color data space. Test these assumptions under a known light rig before changing material values.

Control asset record

These values come from the sample GLB generated during the site build.

MeasurementRecorded value
Meshes1
Vertices24
Triangles12
Bounds2.00 x 2.00 x 2.00

Reproduce an import failure without guessing

Keep the file, loader configuration, and console output together for one narrow test.

  1. Load the built-in sample and record its metrics.
  2. Load the project asset with the same renderer and camera.
  3. Compare network errors, decoder paths, bounds, material count, and animation clips.
  4. Change one loader or asset assumption, then repeat the comparison.

Minimal import flow

const loader = new GLTFLoader();
loader.load(url, (gltf) => {
  const model = gltf.scene;
  const box = new THREE.Box3().setFromObject(model);
  const center = box.getCenter(new THREE.Vector3());
  const size = box.getSize(new THREE.Vector3());

  model.position.sub(center);
  scene.add(model);
  fitCameraToBox(camera, controls, size);

  console.table({
    width: size.x.toFixed(2),
    height: size.y.toFixed(2),
    depth: size.z.toFixed(2),
    clips: gltf.animations.length
  });
});

Sources and further reading

Update record

  • : Rewritten around a reproducible demo, explicit test record, code comparison, and known platform limits.
  • : Added a dedicated DRACO, KTX2, and texture color-space section so consolidated compression and texture URLs land on the maintained answer.

Related guides