DEV Community

Cover image for Loading states for a heavy 3D model - do not show an empty canvas
Dominik
Dominik

Posted on

Loading states for a heavy 3D model - do not show an empty canvas

Even a well-compressed product model is a few MB, and for a second or two on a normal connection the user is looking at an empty canvas. That gap is where people decide the page is broken and leave. A configurator needs a loading state as much as it needs the model.

GLTFLoader gives you progress for free

The loader reports bytes as they arrive:

loader.load(
  '/models/pool.glb',
  (gltf) => { scene.add(gltf.scene); hideLoader() },
  (event) => {
    if (event.lengthComputable) {
      const pct = (event.loaded / event.total) * 100
      setProgress(pct)
    }
  },
  (err) => showError()
)
Enter fullscreen mode Exit fullscreen mode

A real percentage, a real bar, and the user knows something is happening.

The gotcha: total is often zero

event.total is only set when the server sends a Content-Length header. With gzip transfer encoding or chunked responses, it frequently comes back 0, and your percentage is Infinity or NaN. That is why so many Three.js loaders show a bar stuck at zero.

Handle both cases: use lengthComputable to decide between a real bar and an indeterminate spinner.

(event) => {
  if (event.lengthComputable && event.total > 0) {
    setProgress((event.loaded / event.total) * 100)
  } else {
    showIndeterminate()   // bytes are flowing, size unknown
  }
}
Enter fullscreen mode Exit fullscreen mode

If you control the server, send Content-Length (do not gzip already-compressed .glb, it does not help and it hides the size). Then you get a real bar everywhere.

Fill the canvas before the model arrives

The bar is the minimum. Better: show the product's poster image, or a low-poly placeholder, or the configurator UI in a skeleton state, so the first paint is never empty. The model fades in when ready. The pool configurator shows its UI and a progress state immediately, so the few MB download never looks like a stall.

Perceived performance is performance. A model that loads in 1.5 seconds with nothing on screen feels slower than one that loads in 2 seconds with a bar and a placeholder.

I am Dominik Groński / GroDev, a studio in Poznan, Poland building custom 3D configurators and Laravel back-ends for manufacturers.

Top comments (0)