Integrate FirstPersonControls in Three.js

This guide explains how to integrate FirstPersonControls into a Three.js project to recreate a classic first-person video game camera. You will learn how to import the controller, initialize it with your camera and renderer, configure its movement and looking sensitivity, and update it within the animation loop using a clock.

1. Import the Required Modules

To use FirstPersonControls, you must import it in addition to the core Three.js library. Modern versions of Three.js locate these add-ons in the addons directory.

import * as THREE from 'three';
import { FirstPersonControls } from 'three/addons/controls/FirstPersonControls.js';

2. Set Up the Clock and Renderer

FirstPersonControls requires a time delta (the time elapsed between frames) to calculate smooth, frame-rate-independent movement. You need to instantiate a THREE.Clock to track this time.

// Initialize the clock
const clock = new THREE.Clock();

// Standard setup
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

3. Initialize FirstPersonControls

Instantiate the controls by passing the camera and the renderer’s DOM element.

const controls = new FirstPersonControls(camera, renderer.domElement);

4. Configure Control Settings

Customize the movement speed, look speed, and behavior to match your desired video game style.

// Adjust how fast the camera moves (units per second)
controls.movementSpeed = 50;

// Adjust how fast the camera rotates with the mouse
controls.lookSpeed = 0.1;

// Prevent the camera from looking upside down
controls.lookVertical = true;

// Constrain vertical looking angles (optional)
controls.verticalMin = 1.0; 
controls.verticalMax = 2.0;

// Allow camera rotation only when clicking and dragging (false), 
// or constantly tracking the mouse position (true)
controls.activeLook = true;

5. Update Controls in the Animation Loop

To make the camera move and look around, you must update the controls on every frame. Pass the elapsed time delta from your clock into the controls.update() method.

function animate() {
    requestAnimationFrame(animate);

    // Get the time elapsed since the last frame
    const delta = clock.getDelta();

    // Update the controls
    controls.update(delta);

    // Render the scene
    renderer.render(scene, camera);
}

animate();

6. Handle Window Resizing

If the browser window is resized, the controls need to be notified to recalculate their internal dimensions.

window.addEventListener('resize', () => {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
    
    // Update controls to match new dimensions
    controls.handleResize();
});