Three.js Cheatsheet

Controls

Use this Three.js reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

OrbitControls

The most commonly used control. Orbit around a target with mouse/touch.

import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

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

controls.target.set(0, 0, 0);   // look-at / orbit pivot
controls.update();               // REQUIRED after any manual camera or target change

// In render loop
function animate() {
  requestAnimationFrame(animate);
  controls.update(); // needed only if enableDamping or autoRotate is true
  renderer.render(scene, camera);
}

OrbitControls Properties

PropertyTypeDefaultDescription
controls.enabledbooleantrueEnable/disable all input
controls.targetVector3(0,0,0)Orbit pivot point
controls.enableDampingbooleanfalseSmooth inertia (requires update() in loop)
controls.dampingFactornumber0.05Inertia amount
controls.enableRotatebooleantrueAllow orbiting
controls.enableZoombooleantrueAllow dolly/zoom
controls.enablePanbooleantrueAllow panning
controls.autoRotatebooleanfalseAuto-spin around target
controls.autoRotateSpeednumber2.0Degrees per second
controls.minDistancenumber0Minimum zoom distance
controls.maxDistancenumberInfinityMaximum zoom distance
controls.minPolarAnglenumber0Restrict vertical angle (top clamp, radians)
controls.maxPolarAnglenumberMath.PIRestrict vertical angle (bottom clamp)
controls.minAzimuthAnglenumber-InfinityRestrict horizontal rotation
controls.maxAzimuthAnglenumberInfinityRestrict horizontal rotation
controls.rotateSpeednumber1.0Rotation speed multiplier
controls.zoomSpeednumber1.0Zoom speed multiplier
controls.panSpeednumber1.0Pan speed multiplier
controls.screenSpacePanningbooleantruePan in screen space vs. world XZ
controls.keyPanSpeednumber7Arrow-key pan speed
controls.keysobject{LEFT,UP,RIGHT,BOTTOM}Arrow key codes
controls.mouseButtonsobject{LEFT:ROTATE, MIDDLE:DOLLY, RIGHT:PAN}Mouse button mapping
controls.touchesobject{ONE:ROTATE, TWO:DOLLY_PAN}Touch gesture mapping

OrbitControls Methods

controls.update();               // process damping / autoRotate
controls.reset();                // reset to initial camera state (saved at creation)
controls.saveState();            // save current state as reset target
controls.dispose();              // remove event listeners
controls.getDistance();          // distance from camera to target
controls.getAzimuthalAngle();    // current horizontal angle (radians)
controls.getPolarAngle();        // current vertical angle (radians)

OrbitControls Events

controls.addEventListener('start',  () => console.log('interaction started'));
controls.addEventListener('change', () => renderer.render(scene, camera));  // demand render
controls.addEventListener('end',    () => console.log('interaction ended'));

Use the change event for demand rendering (only render when user interacts) to save GPU on idle scenes.

FlyControls

import { FlyControls } from 'three/addons/controls/FlyControls.js';

const controls = new FlyControls(camera, renderer.domElement);
controls.movementSpeed = 10;
controls.rollSpeed     = Math.PI / 24;
controls.autoForward   = false;
controls.dragToLook    = false;

// REQUIRED: pass delta every frame
const clock = new THREE.Clock();
function animate() {
  requestAnimationFrame(animate);
  controls.update(clock.getDelta()); // ← delta in seconds
  renderer.render(scene, camera);
}

FirstPersonControls

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

const controls = new FirstPersonControls(camera, renderer.domElement);
controls.movementSpeed = 5;
controls.lookSpeed     = 0.05;
controls.lookVertical  = true;

const clock = new THREE.Clock();
function animate() {
  requestAnimationFrame(animate);
  controls.update(clock.getDelta());
  renderer.render(scene, camera);
}

PointerLockControls (FPS-style)

import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';

const controls = new PointerLockControls(camera, document.body);
scene.add(controls.object); // controls wraps camera in a yaw object

document.addEventListener('click', () => controls.lock());

controls.addEventListener('lock',   () => console.log('pointer locked'));
controls.addEventListener('unlock', () => console.log('pointer unlocked'));

// Move using WASD in the render loop
const keys = {};
document.addEventListener('keydown', (e) => (keys[e.code] = true));
document.addEventListener('keyup',   (e) => (keys[e.code] = false));

const speed = 5;
function animate() {
  requestAnimationFrame(animate);
  const delta = clock.getDelta();
  if (controls.isLocked) {
    if (keys['KeyW']) controls.moveForward( speed * delta);
    if (keys['KeyS']) controls.moveForward(-speed * delta);
    if (keys['KeyA']) controls.moveRight(-speed * delta);
    if (keys['KeyD']) controls.moveRight( speed * delta);
  }
  renderer.render(scene, camera);
}

TrackballControls

import { TrackballControls } from 'three/addons/controls/TrackballControls.js';

const controls = new TrackballControls(camera, renderer.domElement);
controls.rotateSpeed = 1.0;
controls.zoomSpeed   = 1.2;
controls.panSpeed    = 0.8;
controls.noZoom      = false;
controls.noPan       = false;
controls.staticMoving = false;  // true = no inertia
controls.dynamicDampingFactor = 0.2;

// Update in loop (requires delta)
const clock = new THREE.Clock();
function animate() {
  requestAnimationFrame(animate);
  controls.update();
  renderer.render(scene, camera);
}

Trackball allows full free rotation including rolling (no up-lock). OrbitControls keeps "up" locked to Y — use Trackball when you need free 3D rotation.

ArcballControls

import { ArcballControls } from 'three/addons/controls/ArcballControls.js';

const controls = new ArcballControls(camera, renderer.domElement, scene);
controls.enableAnimations = true;
controls.enableGrid       = false;
controls.setGizmosVisible(true);

// Update in loop
function animate() {
  requestAnimationFrame(animate);
  controls.update();
  renderer.render(scene, camera);
}

TransformControls (Editor Gizmo)

import { TransformControls } from 'three/addons/controls/TransformControls.js';

const gizmo = new TransformControls(camera, renderer.domElement);
gizmo.attach(mesh);        // attach to an object
scene.add(gizmo);

// Switch modes
gizmo.setMode('translate'); // 'translate' | 'rotate' | 'scale'
gizmo.setSpace('world');    // 'world' | 'local'
gizmo.setSize(1);

// Disable OrbitControls while dragging gizmo
gizmo.addEventListener('dragging-changed', (event) => {
  orbitControls.enabled = !event.value;
});

// Keyboard shortcuts (common pattern)
window.addEventListener('keydown', (e) => {
  if (e.code === 'KeyT') gizmo.setMode('translate');
  if (e.code === 'KeyR') gizmo.setMode('rotate');
  if (e.code === 'KeyS') gizmo.setMode('scale');
  if (e.code === 'Escape') gizmo.detach();
});

DragControls

import { DragControls } from 'three/addons/controls/DragControls.js';

const objects = [meshA, meshB, meshC];
const drag    = new DragControls(objects, camera, renderer.domElement);

drag.addEventListener('dragstart', (event) => {
  orbitControls.enabled = false;
  event.object.material.opacity = 0.5;
});

drag.addEventListener('dragend', (event) => {
  orbitControls.enabled = true;
  event.object.material.opacity = 1;
});

drag.addEventListener('drag', (event) => {
  // event.object.position is updated automatically
});

drag.dispose(); // cleanup

Controls Comparison Table

ControlInputBest For
OrbitControlsMouse, touchModel viewers, scene exploration
TrackballControlsMouseFull 3D free rotation
ArcballControlsMouse, touchCAD-style rotation with inertia
FlyControlsMouse + keyboardFly-through, space
FirstPersonControlsMouse + keyboardSimple FPS
PointerLockControlsPointer lock + keyboardTrue FPS games
TransformControlsMouse gizmoEditor / scene builder
DragControlsMouseDrag objects in the scene

Gotchas

controls.update() is mandatory every frame when enableDamping, autoRotate, or any control that has continuous motion is enabled. Skipping it causes jitter or no movement.

Mixing TransformControls and OrbitControls on the same canvas causes conflicts — disable OrbitControls during drag via the dragging-changed event.

PointerLockControls requires a user gesture (click) to lock — browsers block lock() calls not triggered by an event.

After calling controls.dispose(), remove the controls reference — it only removes event listeners, it doesn't null out the camera reference.

OrbitControls.target must be updated with controls.update() after you set it — just setting target.set(...) alone doesn't reposition the camera.