How to Resize Three.js PerspectiveCamera

When building 3D applications with Three.js, handling browser window resizing is crucial to prevent your scene from looking stretched or squished. This article provides a quick, step-by-step guide on how to dynamically calculate the correct aspect ratio for a PerspectiveCamera and update the WebGLRenderer whenever the user resizes their browser window.

The Problem with Window Resizing

A PerspectiveCamera relies on a fixed aspect ratio (width divided by height) to project 3D coordinates onto a 2D screen. If the browser window dimensions change and you do not update this ratio, the rendered 3D scene will distort. To fix this, you must listen for the window’s resize event, recalculate the aspect ratio, update the camera’s projection matrix, and resize the renderer.

The Implementation Steps

To properly handle a window resize in Three.js, you need to implement a window event listener that performs three key actions:

  1. Recalculate the Aspect Ratio: Divide the new width of the container (usually window.innerWidth) by the new height (window.innerHeight).
  2. Update the Projection Matrix: Assign the new aspect ratio to the camera and call camera.updateProjectionMatrix(). Three.js caches projection calculations, so this method is required to apply the changes.
  3. Resize the Renderer: Update the size of the WebGL canvas using renderer.setSize() so that the output matches the new window dimensions.

Code Example

Here is the complete JavaScript code to implement this behavior:

// 1. Setup your standard Three.js variables (assumed to be initialized elsewhere)
const width = window.innerWidth;
const height = window.innerHeight;

const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);

// 2. Create the resize event listener function
function onWindowResize() {
  // Get the new dimensions of the window
  const newWidth = window.innerWidth;
  const newHeight = window.innerHeight;

  // Update camera aspect ratio
  camera.aspect = newWidth / newHeight;
  
  // Recalculate the projection matrix
  camera.updateProjectionMatrix();

  // Update renderer size
  renderer.setSize(newWidth, newHeight);
  
  // Optional: Update device pixel ratio for High-DPI/Retina screens
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
}

// 3. Register the listener
window.addEventListener('resize', onWindowResize);

Handling Custom Containers

If your Three.js canvas is inside a specific HTML <div> rather than filling the entire window, replace window.innerWidth and window.innerHeight with the container’s client dimensions:

const container = document.getElementById('canvas-container');

function onContainerResize() {
  const width = container.clientWidth;
  const height = container.clientHeight;

  camera.aspect = width / height;
  camera.updateProjectionMatrix();
  renderer.setSize(width, height);
}

In this scenario, using a ResizeObserver on the container element is often more reliable than listening to the window’s resize event.