Make Responsive

responsive

If you've ever opened a Three.js scene on your phone and seen a stretched or squished mess, you know the pain. Your scene looks great on your full-screen monitor, but resize the window or pull it up on a phone — and suddenly everything's broken.

The fix is simple: you need to update the camera and renderer whenever the window (or your canvas container) changes size.

the basic resize handler

function resize() {
    const width = canvas.clientWidth;
    const height = canvas.clientHeight;

    // Update the camera's aspect ratio so it doesn't stretch
    camera.aspect = width / height;
    camera.updateProjectionMatrix();

    // Update the renderer size to match
    renderer.setSize(width, height);
}

window.addEventListener('resize', resize);

That's it. Three things:

  1. Update the camera's aspect ratio — width / height so objects don't look squished
  2. Tell the camera to recalculate — camera.updateProjectionMatrix() applies the change
  3. Resize the renderer — renderer.setSize(width, height) matches the canvas to the container

using ResizeObserver (better for containers)

If your canvas is inside a container that might change size on its own (like a sidebar that expands, or a panel that resizes), window.addEventListener('resize', ...) won't catch it. That's where ResizeObserver comes in:

const observer = new ResizeObserver(resize);
observer.observe(canvas.parentElement);

// Cleanup when your component unmounts
return () => observer.disconnect();

This watches the canvas's parent element and fires your resize function whenever it changes size — no matter why.

cleaning up

If your scene lives inside a Svelte or React component, you need to remove the event listener when the component goes away, otherwise it keeps running invisibly:

// Using addEventListener
window.addEventListener('resize', resize);

// Cleanup
return () => window.removeEventListener('resize', resize);
// Using ResizeObserver
const observer = new ResizeObserver(resize);
observer.observe(canvas.parentElement);

// Cleanup
return () => observer.disconnect();

full example

// Create your scene, camera, renderer…

function resize() {
    const width = window.innerWidth;
    const height = window.innerHeight;
    camera.aspect = width / height;
    camera.updateProjectionMatrix();
    renderer.setSize(width, height);
}

resize(); // run once on load
window.addEventListener('resize', resize);

That's all you need. Your scene now adapts to any screen size.