Why manual camera numbers fail
A camera position like `[3, 2, 5]` is not a strategy; it is a coincidence that worked for one mesh. Imported models can be a few centimeters tall, hundreds of units wide, or offset far from the origin. If the viewer keeps a fixed camera distance, some assets will fill the screen while others vanish into the grid.
The reliable approach is geometric. After the object is loaded, compute a bounding box from the visible scene graph. The box gives you size and center. The center becomes the camera target. The size drives distance. The field of view tells you how much vertical space the camera can see from a given distance.
Use the largest dimension as the promise
For simple viewers, start with the largest of width, height, and depth. This creates a conservative fit that works for cubes, tall bottles, chairs, characters, and flat panels. If your content is always product photography, you may choose height as the primary dimension. If your content is architectural, width and depth may matter more than height.
The vertical distance calculation starts with the camera's field of view. A larger FOV sees more at the same distance; a smaller FOV needs to move farther back. The horizontal fit also depends on aspect ratio. On a narrow phone screen, the same camera may crop a wide object unless you calculate both vertical and horizontal fit and choose the larger distance.
Move the controls target too
If the camera moves but OrbitControls keeps targeting the old origin, dragging the mouse will feel wrong. The model appears to swing away from the viewport because the orbit center is not the object center. Always copy the bounding-box center to the controls target after the camera is fitted.
Also update near and far planes after fitting. A near plane that is too large can slice the front of a model; a far plane that is too small can clip the back or shadow helpers. Derive them from the computed distance so tiny and huge models both remain visible.
When to override the automatic fit
Automatic fit is a baseline, not a composition engine. Product pages often need a little headroom, character viewers may need the face weighted higher than the feet, and floor-based scenes may need to keep the object grounded. Keep an offset parameter and expose it as a slider in tools. That gives authors a predictable way to make the fit more generous or tighter.
The best result is a two-step flow: first fit the camera from math, then let the user make a small artistic adjustment. This keeps the tool useful without pretending there is one perfect camera for every asset.
Check the framing math with known dimensions
The calculator starts with a 2-unit object, a 5-unit distance, 1.777 aspect ratio, and 10 percent margin.
- 24.81 degree vertical FOV
- 42.71 degree horizontal FOV
- 3.91 units visible width
Aim at the measured center
Moving only the camera leaves controls orbiting around the previous target.
camera.position.z = 5;
camera.lookAt(0, 0, 0);const box = new THREE.Box3().setFromObject(object);
const center = box.getCenter(new THREE.Vector3());
const size = box.getSize(new THREE.Vector3());
fitCameraToBounds(camera, controls, center, size);OrbitControls damping after camera fit
Set controls.target to the measured object center in the same operation that moves the camera. When enableDamping is true, call controls.update() from the render loop until the controls settle; otherwise the camera can appear to ignore the new target or stop between two states. Start with a small dampingFactor and evaluate it on the same desktop and mobile viewports used for the framing check.
Keep one owner for animation. A fit transition, OrbitControls damping, and a second independent requestAnimationFrame loop can compete over camera position. Apply the fitted position and target, update the projection matrix, then let the existing controls loop converge from that known state.
Calculator verification record
The values are deterministic outputs from the formula shown in the tool.
| Input or output | Value |
|---|---|
| Object height | 2 |
| Camera distance | 5 |
| Framing margin | 10% |
| Vertical FOV | 24.81 degrees |
Verify wide and tall assets separately
A vertical fit alone can crop a wide object on a narrow viewport.
- Measure a Box3 after world matrices are current.
- Calculate both vertical and horizontal fit distances.
- Choose the larger distance and update camera near and far planes.
- Move OrbitControls target to the same center, then test the mobile viewport.
Perspective fit helper
function fitCameraToObject(camera, object, controls, offset = 1.35) {
const box = new THREE.Box3().setFromObject(object);
const size = box.getSize(new THREE.Vector3());
const center = box.getCenter(new THREE.Vector3());
const maxSize = Math.max(size.x, size.y, size.z, 0.01);
const fitHeightDistance = maxSize / (2 * Math.tan(THREE.MathUtils.degToRad(camera.fov) / 2));
const fitWidthDistance = fitHeightDistance / camera.aspect;
const distance = offset * Math.max(fitHeightDistance, fitWidthDistance);
const direction = camera.position.clone().sub(controls.target).normalize();
camera.position.copy(center).add(direction.multiplyScalar(distance));
camera.near = distance / 100;
camera.far = distance * 100;
camera.updateProjectionMatrix();
controls.target.copy(center);
controls.update();
}
Sources and further reading
Update record
- : Rewritten around a reproducible demo, explicit test record, code comparison, and known platform limits.
- : Added an OrbitControls damping section and routed the consolidated damping URL to that tested camera-control guidance.