Displaying a model in a browser is a solved problem; making the experience good is where the work is.
The stack
A WebGL library handles rendering, camera and loading. Above it sit the decisions that matter: framing, controls, lighting, and load behaviour.
Framing
The model should arrive filling the frame, centred, at a sensible angle. That means computing its bounding box and positioning the camera from it, rather than hard-coding a position that only suits one model. A viewer that opens with the object off-screen or tiny loses people instantly.
Controls
- Damped orbit. Momentum and easing make rotation feel physical; instant snapping feels broken.
- Zoom bounds derived from model size, so the user cannot end up inside the mesh or a kilometre away.
- Proportional speed. Rotation and zoom speed scaled to the object's size, so a small object and a large one both feel the same to manipulate.
These three account for most of the difference between a viewer that feels good and one that feels cheap.
Lighting
A neutral environment map does more for perceived quality than any number of individual lights. It gives PBR materials something to reflect, which is what makes them read as materials rather than as coloured shapes.
Loading
Show progress. Show something before the model arrives. Loading a multi-megabyte asset with a blank canvas is the most common mistake, and a progress indicator with a real percentage is meaningfully better than a spinner.
Practical details that get missed
- Handle WebGL being unavailable — some devices and configurations have none.
- Dispose resources when the viewer unmounts, or a single-page app leaks GPU memory.
- Respect reduced-motion preferences for auto-rotation.
- Test on a phone, where most of the traffic is.