cyberangles blog

Three.js Tutorial: How to Automatically Center and Resize GLTF Models After Import (On-the-Fly with OrbitControls)

When working with 3D models in Three.js, one of the most common frustrations is importing a GLTF model only to find it off-center, too large, or too small—ruining the user experience. Manually adjusting position and scale for every model is tedious and error-prone, especially when working with dynamic or user-uploaded assets.

In this tutorial, we’ll solve this problem by creating an automated workflow to center and resize GLTF models immediately after import. We’ll use Three.js’s built-in tools like Box3 (for bounding boxes) and integrate seamlessly with OrbitControls to ensure smooth interaction around the model. By the end, you’ll have a reusable function that works for any GLTF model, saving you hours of manual adjustments.

2025-12

Table of Contents#

  1. Prerequisites
  2. Setting Up the Three.js Scene
  3. Importing GLTF Models
  4. Calculating Model Bounds with Bounding Boxes
  5. Automatically Centering the Model
  6. Resizing the Model to Fit the View
  7. Integrating with OrbitControls
  8. Full Example Code
  9. Troubleshooting Common Issues
  10. Conclusion
  11. References

Prerequisites#

Before diving in, ensure you have:

  • Basic knowledge of JavaScript and Three.js (familiarity with Scene, Camera, Renderer, and Mesh).
  • A development environment (e.g., VS Code with Live Server).
  • The following Three.js libraries (we’ll use CDNs for simplicity):
    • Three.js core (three.min.js).
    • GLTFLoader (to import GLTF models).
    • OrbitControls (for interactive camera controls).

Setting Up the Three.js Scene#

First, let’s create a basic Three.js scene. This includes a scene, camera, renderer, and lights (critical for visibility).

Step 1: HTML Structure#

Create an HTML file with a canvas element (where the 3D scene will render) and include the required Three.js libraries via CDN:

<!DOCTYPE html>
<html>
<head>
    <title>Auto-Center & Resize GLTF Model</title>
    <style>
        body { margin: 0; }
        canvas { display: block; }
    </style>
</head>
<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/loaders/GLTFLoader.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/controls/OrbitControls.js"></script>
    <script>
        // Three.js code will go here
    </script>
</body>
</html>

Step 2: Initialize Scene, Camera, and Renderer#

In the <script> tag, set up the core Three.js components:

// Scene
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xf0f0f0); // Light gray background
 
// Camera (PerspectiveCamera: fov, aspect ratio, near, far)
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
 
// Renderer
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
 
// Lights (required to see the model!)
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); // Soft white light
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(5, 5, 5); // Position light above/behind the camera
scene.add(directionalLight);

Step 3: Handle Window Resizing#

Add a listener to adjust the camera and renderer when the window resizes:

window.addEventListener('resize', () => {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
});

Importing GLTF Models#

Next, we’ll import a GLTF model using GLTFLoader. For this tutorial, use a sample GLTF model (e.g., from Khronos Group’s GLTF Sample Models).

Step 1: Load the Model#

Use GLTFLoader to import the model. Add error handling for failed loads:

const loader = new THREE.GLTFLoader();
 
// Load a GLTF model (replace with your model's path)
loader.load(
    'models/Duck.glb', // Sample model (Duck.glb)
    (gltf) => {
        // Called when the model loads successfully
        const model = gltf.scene;
        scene.add(model); // Add model to the scene
 
        // We'll process centering/resizing here later!
    },
    (xhr) => {
        // Optional: Progress callback
        console.log((xhr.loaded / xhr.total * 100) + '% loaded');
    },
    (error) => {
        // Error callback
        console.error('An error occurred loading the model:', error);
    }
);

At this point, the model will load but may be off-center or incorrectly sized. Let’s fix that!

Calculating Model Bounds with Bounding Boxes#

To center and resize the model, we first need to find its bounding box—a 3D box that encloses all vertices of the model. Three.js provides Box3 for this.

What is a Bounding Box?#

A Box3 (axis-aligned bounding box) defines the minimum and maximum coordinates (min and max) of a 3D object. For example, a cube from (1,1,1) to (3,3,3) has min = (1,1,1) and max = (3,3,3).

Step 1: Compute the Bounding Box for the GLTF Model#

GLTF models often have nested objects (e.g., meshes, bones). Use Box3.setFromObject() to automatically traverse all objects in the model and compute the overall bounding box:

// Inside the GLTFLoader success callback:
const model = gltf.scene;
scene.add(model);
 
// Compute bounding box
const boundingBox = new THREE.Box3().setFromObject(model);

setFromObject(model) traverses all child objects of model and calculates the smallest box that contains them all.

Automatically Centering the Model#

Now that we have the bounding box, we can find the model’s center and reposition it to the origin (0,0,0).

Step 1: Find the Model’s Center#

Use boundingBox.getCenter() to get the 3D coordinates of the model’s current center:

const center = new THREE.Vector3();
boundingBox.getCenter(center); // center now holds (x, y, z) of the model's center

Step 2: Translate the Model to the Origin#

To center the model, move it so its center aligns with (0,0,0). Subtract the center coordinates from the model’s position:

model.position.sub(center); // Moves model so its center is at (0,0,0)

Why? If the model’s center is (2, 3, 4), model.position.sub(center) sets model.position to (-2, -3, -4), shifting the model so its center is at (0,0,0).

Resizing the Model to Fit the View#

Even if centered, the model might be too large (clipping the camera) or too small (hard to see). We’ll scale it to fit within the camera’s view.

Step 1: Calculate the Model’s Size#

Use boundingBox.getSize() to get the model’s dimensions (width, height, depth):

const size = new THREE.Vector3();
boundingBox.getSize(size); // size = { x: width, y: height, z: depth }

Step 2: Determine the Maximum Dimension#

To ensure the model fits, use the largest dimension (width, height, or depth):

const maxDim = Math.max(size.x, size.y, size.z); // Largest dimension of the model

Step 3: Compute the Scale Factor#

We want the model to fit within a "target size" (e.g., 5 units). The scale factor is targetSize / maxDim:

const targetSize = 5; // Adjust this to control how large the model appears
const scaleFactor = targetSize / maxDim;
model.scale.set(scaleFactor, scaleFactor, scaleFactor); // Uniform scaling

Step 4: Reposition the Camera#

After scaling, adjust the camera’s position to ensure the entire model is visible. Use the scaled size to set the camera’s distance from the origin:

// Position camera along the z-axis, far enough to see the scaled model
camera.position.z = maxDim * scaleFactor * 2; // Adjust multiplier (e.g., 2) for comfort

Integrating with OrbitControls#

OrbitControls lets users pan, zoom, and rotate the camera around a target point. To ensure controls work with our centered model, set the target to the model’s center (origin, (0,0,0)).

Step 1: Initialize OrbitControls#

Add OrbitControls to the scene:

const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // Smooth camera movement
controls.dampingFactor = 0.05;

Step 2: Set Controls Target#

By default, OrbitControls orbits around (0,0,0) (our model’s new center). Confirm this with:

controls.target.set(0, 0, 0); // Orbit around the origin (model's center)
controls.update(); // Apply changes

Now, dragging the mouse will orbit around the model’s center!

Full Example Code#

Here’s the complete code, combining all steps:

<!DOCTYPE html>
<html>
<head>
    <title>Auto-Center & Resize GLTF Model</title>
    <style>
        body { margin: 0; }
        canvas { display: block; }
    </style>
</head>
<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/loaders/GLTFLoader.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/controls/OrbitControls.js"></script>
    <script>
        // Scene
        const scene = new THREE.Scene();
        scene.background = new THREE.Color(0xf0f0f0);
 
        // Camera
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
 
        // Renderer
        const renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);
 
        // Lights
        const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
        scene.add(ambientLight);
        const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
        directionalLight.position.set(5, 5, 5);
        scene.add(directionalLight);
 
        // OrbitControls
        const controls = new THREE.OrbitControls(camera, renderer.domElement);
        controls.enableDamping = true;
        controls.dampingFactor = 0.05;
        controls.target.set(0, 0, 0); // Orbit around origin
 
        // Load GLTF Model
        const loader = new THREE.GLTFLoader();
        loader.load(
            'models/Duck.glb', // Replace with your model path
            (gltf) => {
                const model = gltf.scene;
                scene.add(model);
 
                // Step 1: Compute bounding box
                const boundingBox = new THREE.Box3().setFromObject(model);
 
                // Step 2: Center the model
                const center = new THREE.Vector3();
                boundingBox.getCenter(center);
                model.position.sub(center);
 
                // Step 3: Resize the model
                const size = new THREE.Vector3();
                boundingBox.getSize(size);
                const maxDim = Math.max(size.x, size.y, size.z);
                const targetSize = 5; // Adjust based on desired size
                const scaleFactor = targetSize / maxDim;
                model.scale.set(scaleFactor, scaleFactor, scaleFactor);
 
                // Step 4: Position camera to see the model
                camera.position.z = maxDim * scaleFactor * 2; // Distance from model
            },
            (xhr) => console.log((xhr.loaded / xhr.total * 100) + '% loaded'),
            (error) => console.error('Model load error:', error)
        );
 
        // Window resize handler
        window.addEventListener('resize', () => {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        });
 
        // Animation loop
        function animate() {
            requestAnimationFrame(animate);
            controls.update(); // Required for damping
            renderer.render(scene, camera);
        }
        animate();
    </script>
</body>
</html>

Troubleshooting Common Issues#

1. Model is Invisible#

  • Lights: Ensure AmbientLight and DirectionalLight are added to the scene (models are black without light).
  • Camera Position: If the camera is inside the model, adjust camera.position.z to be larger.

2. Bounding Box is Empty#

  • Nested Objects: setFromObject() may fail if the model has zero-scale objects. Verify the model’s geometry in a tool like Blender.
  • Model Not Loaded: Ensure the model path is correct (use absolute paths if needed).

3. OrbitControls Not Working#

  • Target: Confirm controls.target is set to (0,0,0) (the model’s center).
  • Renderer DOM Element: Pass renderer.domElement to OrbitControls (required for mouse input).

Conclusion#

You now know how to automatically center and resize GLTF models in Three.js! By computing the bounding box, translating the model to the origin, scaling it to fit the view, and integrating OrbitControls, you can ensure any imported model looks polished and interactive.

This workflow is critical for dynamic applications (e.g., user-uploaded models) where manual adjustments aren’t feasible. Experiment with targetSize and camera position to fine-tune results!

References#