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
| Property | Type | Default | Description |
|---|---|---|---|
controls.enabled | boolean | true | Enable/disable all input |
controls.target | Vector3 | (0,0,0) | Orbit pivot point |
controls.enableDamping | boolean | false | Smooth inertia (requires update() in loop) |
controls.dampingFactor | number | 0.05 | Inertia amount |
controls.enableRotate | boolean | true | Allow orbiting |
controls.enableZoom | boolean | true | Allow dolly/zoom |
controls.enablePan | boolean | true | Allow panning |
controls.autoRotate | boolean | false | Auto-spin around target |
controls.autoRotateSpeed | number | 2.0 | Degrees per second |
controls.minDistance | number | 0 | Minimum zoom distance |
controls.maxDistance | number | Infinity | Maximum zoom distance |
controls.minPolarAngle | number | 0 | Restrict vertical angle (top clamp, radians) |
controls.maxPolarAngle | number | Math.PI | Restrict vertical angle (bottom clamp) |
controls.minAzimuthAngle | number | -Infinity | Restrict horizontal rotation |
controls.maxAzimuthAngle | number | Infinity | Restrict horizontal rotation |
controls.rotateSpeed | number | 1.0 | Rotation speed multiplier |
controls.zoomSpeed | number | 1.0 | Zoom speed multiplier |
controls.panSpeed | number | 1.0 | Pan speed multiplier |
controls.screenSpacePanning | boolean | true | Pan in screen space vs. world XZ |
controls.keyPanSpeed | number | 7 | Arrow-key pan speed |
controls.keys | object | {LEFT,UP,RIGHT,BOTTOM} | Arrow key codes |
controls.mouseButtons | object | {LEFT:ROTATE, MIDDLE:DOLLY, RIGHT:PAN} | Mouse button mapping |
controls.touches | object | {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
changeevent 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
| Control | Input | Best For |
|---|---|---|
OrbitControls | Mouse, touch | Model viewers, scene exploration |
TrackballControls | Mouse | Full 3D free rotation |
ArcballControls | Mouse, touch | CAD-style rotation with inertia |
FlyControls | Mouse + keyboard | Fly-through, space |
FirstPersonControls | Mouse + keyboard | Simple FPS |
PointerLockControls | Pointer lock + keyboard | True FPS games |
TransformControls | Mouse gizmo | Editor / scene builder |
DragControls | Mouse | Drag objects in the scene |
Gotchas
controls.update()is mandatory every frame whenenableDamping,autoRotate, or any control that has continuous motion is enabled. Skipping it causes jitter or no movement.
Mixing
TransformControlsandOrbitControlson the same canvas causes conflicts — disableOrbitControlsduring drag via thedragging-changedevent.
PointerLockControlsrequires a user gesture (click) to lock — browsers blocklock()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.targetmust be updated withcontrols.update()after you set it — just settingtarget.set(...)alone doesn't reposition the camera.