threejs-syntax-controls

ALWAYS use 'three/addons/controls/...' for r160+. The legacy path 'three/examples/jsm/controls/...' still works but is deprecated.

Works with

Claude CodeCursorCodex CLIGitHub CopilotGemini CLI

Agent Skills format with YAML frontmatter. Claude Code reads it as-is.

---
name: "threejs-syntax-controls"
description: "ALWAYS use 'three/addons/controls/...' for r160+. The legacy path 'three/examples/jsm/controls/...' still works but is deprecated."
license: "MIT"
---

# threejs-syntax-controls

## Quick Reference

### Control Selection Decision Tree

| Use Case | Control | Why |
|----------|---------|-----|
| Inspect a 3D model from all angles | OrbitControls | Orbit/pan/zoom around a target point |
| Top-down map or 2D-style navigation | MapControls | Left-drag pans, right-drag rotates |
| Free-flight editor or space scene | FlyControls | Six degrees of freedom, WASD + mouse |
| First-person game with pointer lock | PointerLockControls | Hides cursor, captures mouse movement |
| First-person without pointer lock | FirstPersonControls | Mouse-look without browser lock API |
| Move/rotate/scale objects via gizmo | TransformControls | Interactive translate/rotate/scale handles |
| Drag objects along a plane | DragControls | Click-and-drag object repositioning |
| Unconstrained rotation (no gimbal lock) | ArcballControls | Full spherical rotation with animation |
| Unconstrained rotation (simpler) | TrackballControls | Like OrbitControls but no pole constraint |

### Import Paths (Three.js r160+)

```javascript
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { MapControls } from 'three/addons/controls/MapControls.js';
import { FlyControls } from 'three/addons/controls/FlyControls.js';
import { FirstPersonControls } from 'three/addons/controls/FirstPersonControls.js';
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';
import { TransformControls } from 'three/addons/controls/TransformControls.js';
import { TrackballControls } from 'three/addons/controls/TrackballControls.js';
import { ArcballControls } from 'three/addons/controls/ArcballControls.js';
import { DragControls } from 'three/addons/controls/DragControls.js';
```

**ALWAYS** use `'three/addons/controls/...'` for r160+. The legacy path `'three/examples/jsm/controls/...'` still works but is deprecated.

### Critical Warnings

**ALWAYS** call `controls.update()` in the animation loop when `enableDamping` or `autoRotate` is `true`. Failing to do so causes the camera to freeze after user interaction ends.

**ALWAYS** call `controls.dispose()` when removing controls. Failing to do so leaks DOM event listeners (pointermove, wheel, keydown) that cause memory leaks and ghost interactions.

**NEVER** attach two camera-control classes to the same camera simultaneously without disabling one. OrbitControls + FlyControls on the same camera causes erratic movement.

**ALWAYS** disable OrbitControls while TransformControls is dragging. Listen to the `dragging-changed` event and toggle `orbitControls.enabled`.

**ALWAYS** call `PointerLockControls.lock()` from a user gesture (click handler). Browsers reject pointer lock requests without user interaction.

**ALWAYS** pass `delta` time to `FlyControls.update(delta)`. Passing no argument or passing elapsed time causes speed to depend on frame rate.

---

## Controls Lifecycle

Every control follows the same lifecycle pattern:

```
construct --> configure --> attach to loop --> dispose on cleanup
```

### Step 1: Construct

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

**ALWAYS** pass `renderer.domElement` as the second argument. Passing `document` or `document.body` causes controls to capture events globally, breaking UI overlays.

### Step 2: Configure

```javascript
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.minDistance = 2;
controls.maxDistance = 50;
controls.target.set(0, 1, 0);
```

### Step 3: Update in Render Loop

```javascript
function animate() {
  requestAnimationFrame(animate);
  controls.update(); // REQUIRED when enableDamping or autoRotate is true
  renderer.render(scene, camera);
}
animate();
```

### Step 4: Dispose on Cleanup

```javascript
controls.dispose();
```

---

## OrbitControls

The most commonly used control. Orbits, pans, and zooms around a target point.

### Constructor

```javascript
new OrbitControls(camera: THREE.Camera, domElement: HTMLElement)
```

### Key Properties

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `enabled` | `boolean` | `true` | Enable/disable all interaction |
| `target` | `Vector3` | `(0,0,0)` | Orbit focus point |
| `enableDamping` | `boolean` | `false` | Smooth inertial movement |
| `dampingFactor` | `number` | `0.05` | Inertia strength (0-1) |
| `autoRotate` | `boolean` | `false` | Auto-rotate around target |
| `autoRotateSpeed` | `number` | `2.0` | Degrees/sec at 60fps |
| `enablePan` | `boolean` | `true` | Allow panning |
| `enableRotate` | `boolean` | `true` | Allow rotation |
| `enableZoom` | `boolean` | `true` | Allow zooming |
| `minDistance` | `number` | `0` | Min zoom distance (PerspectiveCamera) |
| `maxDistance` | `number` | `Infinity` | Max zoom distance (PerspectiveCamera) |
| `minZoom` | `number` | `0` | Min zoom (OrthographicCamera) |
| `maxZoom` | `number` | `Infinity` | Max zoom (OrthographicCamera) |
| `minPolarAngle` | `number` | `0` | Min vertical angle (radians) |
| `maxPolarAngle` | `number` | `Math.PI` | Max vertical angle (radians) |
| `minAzimuthAngle` | `number` | `-Infinity` | Min horizontal angle (radians) |
| `maxAzimuthAngle` | `number` | `Infinity` | Max horizontal angle (radians) |
| `screenSpacePanning` | `boolean` | `true` | Pan in screen plane (true) or horizontal plane (false) |
| `zoomToCursor` | `boolean` | `false` | Zoom towards cursor position |
| `panSpeed` | `number` | `1.0` | Pan speed multiplier |
| `rotateSpeed` | `number` | `1.0` | Rotation speed multiplier |
| `zoomSpeed` | `number` | `1.0` | Zoom speed multiplier |
| `mouseButtons` | `object` | `{LEFT: ROTATE, MIDDLE: DOLLY, RIGHT: PAN}` | Mouse button mapping |
| `touches` | `object` | `{ONE: ROTATE, TWO: DOLLY_PAN}` | Touch gesture mapping |
| `keys` | `object` | `{LEFT, UP, RIGHT, BOTTOM}` | Arrow key codes for panning |

### Methods

| Method | Signature | Description |
|--------|-----------|-------------|
| `update(deltaTime?)` | `(number?) => boolean` | Update controls state. MUST call every frame with damping/autoRotate |
| `dispose()` | `() => void` | Remove all event listeners |
| `saveState()` | `() => void` | Save current camera position/target/zoom |
| `reset()` | `() => void` | Restore to last saved state |
| `getDistance()` | `() => number` | Distance from camera to target |
| `getPolarAngle()` | `() => number` | Vertical angle in radians |
| `getAzimuthalAngle()` | `() => number` | Horizontal angle in radians |
| `listenToKeyEvents(el)` | `(HTMLElement) => void` | Enable keyboard panning |
| `stopListenToKeyEvents()` | `() => void` | Disable keyboard panning |

### Events

| Event | Trigger |
|-------|---------|
| `change` | Camera position or target changed |
| `start` | User interaction began (pointerdown) |
| `end` | User interaction ended (pointerup) |

---

## MapControls

Subclass of OrbitControls optimized for top-down map navigation.

| Button | OrbitControls | MapControls |
|--------|--------------|-------------|
| Left mouse | Rotate | Pan |
| Middle mouse | Dolly | Dolly |
| Right mouse | Pan | Rotate |

All properties, methods, and events are identical to OrbitControls. The ONLY differences are the default `mouseButtons` mapping and `screenSpacePanning` defaulting to `true`.

---

## FlyControls

Six-degrees-of-freedom flight camera. WASD for movement, QE for roll, RF for up/down.

### Key Properties

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `movementSpeed` | `number` | `1.0` | Translation speed |
| `rollSpeed` | `number` | `0.005` | Roll rotation speed |
| `dragToLook` | `boolean` | `false` | Require mouse drag to rotate (vs. always follow mouse) |
| `autoForward` | `boolean` | `false` | Move forward automatically |

### Methods

- `update(delta)` -- **ALWAYS** pass `delta` time from the clock. NEVER omit this argument.
- `dispose()` -- Remove event listeners.

---

## PointerLockControls

First-person camera using the Pointer Lock API. Hides and captures the mouse cursor.

### Key Properties

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `isLocked` | `boolean` | read-only | Whether pointer is currently locked |
| `maxPolarAngle` | `number` | `Math.PI` | Max vertical look angle |
| `minPolarAngle` | `number` | `0` | Min vertical look angle |
| `pointerSpeed` | `number` | `1.0` | Mouse sensitivity multiplier |

### Methods

| Method | Description |
|--------|-------------|
| `lock()` | Request pointer lock (MUST call from user gesture) |
| `unlock()` | Exit pointer lock |
| `connect()` | Attach event listeners |
| `disconnect()` | Remove event listeners |
| `dispose()` | Full cleanup (calls disconnect) |
| `getObject()` | Returns the controlled camera |
| `getDirection(target)` | Write look direction into target Vector3 |
| `moveForward(distance)` | Move camera forward |
| `moveRight(distance)` | Move camera sideways |

### Events

| Event | Trigger |
|-------|---------|
| `change` | Camera orientation changed |
| `lock` | Pointer lock acquired |
| `unlock` | Pointer lock released |

**ALWAYS** implement your own WASD movement in the animation loop. PointerLockControls handles look direction only, NOT position.

---

## TransformControls

Interactive gizmo for moving, rotating, and scaling scene objects.

### Key Properties

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `mode` | `string` | `'translate'` | `'translate'`, `'rotate'`, or `'scale'` |
| `space` | `string` | `'world'` | `'world'` or `'local'` coordinate space |
| `showX` | `boolean` | `true` | Show X axis handle |
| `showY` | `boolean` | `true` | Show Y axis handle |
| `showZ` | `boolean` | `true` | Show Z axis handle |
| `size` | `number` | `1` | Gizmo visual scale |
| `translationSnap` | `number|null` | `null` | Snap to grid (units) |
| `rotationSnap` | `number|null` | `null` | Snap rotation (radians) |
| `scaleSnap` | `number|null` | `null` | Snap scale increment |
| `dragging` | `boolean` | read-only | User is currently dragging |

### Methods

| Method | Description |
|--------|-------------|
| `attach(object)` | Attach gizmo to a scene object |
| `detach()` | Remove gizmo from current object |
| `setMode(mode)` | Set transform mode |
| `setSpace(space)` | Set coordinate space |
| `setSize(size)` | Set gizmo scale |
| `setTranslationSnap(snap)` | Set position snap |
| `setRotationSnap(snap)` | Set rotation snap |
| `setScaleSnap(snap)` | Set scale snap |
| `getRaycaster()` | Access internal raycaster |
| `dispose()` | Cleanup |

### Events

| Event | Trigger |
|-------|---------|
| `change` | Gizmo visual changed |
| `dragging-changed` | `event.value` is `true` when dragging starts, `false` when it ends |
| `objectChange` | Attached object's transform was modified |
| `mouseDown` | Pointer pressed on gizmo |
| `mouseUp` | Pointer released from gizmo |

### Critical Integration Pattern

**ALWAYS** disable camera controls during gizmo drag:

```javascript
transformControls.addEventListener('dragging-changed', (event) => {
  orbitControls.enabled = !event.value;
});
```

**ALWAYS** add TransformControls to the scene: `scene.add(transformControls.getHelper())` or `scene.add(transformControls)`.

---

## Brief Overview: Other Controls

### ArcballControls
Unconstrained rotation with animation states. No polar angle limit -- the camera rotates freely in all directions. Best for CAD-style model inspection.

### TrackballControls
Like OrbitControls without the polar angle constraint. The camera can rotate past the poles. Properties: `rotateSpeed`, `zoomSpeed`, `panSpeed`, `staticMoving`, `dynamicDampingFactor`.

### DragControls
Drag scene objects along a plane. Constructor: `new DragControls(objects, camera, domElement)`. Events: `dragstart`, `drag`, `dragend`, `hoveron`, `hoveroff`.

### FirstPersonControls
Mouse-look camera without pointer lock. Properties: `movementSpeed`, `lookSpeed`, `activeLook`, `constrainVertical`, `verticalMin`, `verticalMax`.

---

## Reference Links

- [references/methods.md](references/methods.md) -- Full API signatures for all controls
- [references/examples.md](references/examples.md) -- Working code examples for each control type
- [references/anti-patterns.md](references/anti-patterns.md) -- What NOT to do

### Official Sources

- https://threejs.org/docs/#examples/en/controls/OrbitControls
- https://threejs.org/docs/#examples/en/controls/MapControls
- https://threejs.org/docs/#examples/en/controls/FlyControls
- https://threejs.org/docs/#examples/en/controls/PointerLockControls
- https://threejs.org/docs/#examples/en/controls/TransformControls

More General & Other skills

← All General & Other skills

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free · No signup · No trial clock

SEE THE DIRECTORY