Three.js Cameras & Controls — Complete Guide to Every Camera Type and Control Scheme
The camera defines how the viewer sees your Three.js scene — it’s the audience’s window into your 3D world. Three.js provides multiple camera types for different perspectives and a rich set of controls for user interaction.
What You’ll Learn
By the end of this tutorial, you’ll understand the difference between Perspective and Orthographic cameras, master six control schemes (Orbit, Trackball, Fly, FirstPerson, DeviceOrientation, PointerLock), build multi-viewport layouts, animate camera transitions, and create a complete Camera Studio mini-project.
Why Cameras & Controls Matter
Choosing the wrong camera or control scheme breaks user experience. A product viewer needs OrbitControls — free rotation around a target. A first-person game needs PointerLockControls. At DodaTech’s Durga Antivirus Pro, the 3D threat dashboard uses multiple cameras: a PerspectiveCamera for the main threat landscape view and an OrthographicCamera for the mini-map overlay, both controlled via custom OrbitControls with constrained zoom limits.
flowchart LR
A[Camera Type] --> B{Perspective or Orthographic?}
B --> C[PerspectiveCamera: Realistic, 3D scenes]
B --> D[OrthographicCamera: Isometric, 2D, CAD]
C --> E[Add Controls]
D --> E
E --> F{Control Scheme}
F --> G[Orbit: Product viewer]
F --> H[Fly: Flight sim]
F --> I[FirstPerson: FPS game]
F --> J[Trackball: Unconstrained]
G --> K[Multi-Viewport Layout]
H --> K
I --> K
K --> L[Camera Studio Project]
style G fill:#44aa88,color:#fff,stroke:none
Camera Fundamentals — The Eye Analogy
Think of the camera as your eyes:
- Position = where you stand.
- LookAt = where you’re looking.
- Field of View = how wide you open your eyes. Narrow = focused (like binoculars). Wide = panoramic (can feel distorted).
- Near/Far clipping = how close/far you can see. Things too close or too far vanish.
PerspectiveCamera — Realistic Human Vision
PerspectiveCamera mimics how human eyes work: objects farther away appear smaller. This is the camera you’ll use 90% of the time.
var camera = new THREE.PerspectiveCamera(
75, // fov — Field of view (degrees). 50-75 is natural.
window.innerWidth / window.innerHeight, // aspect — width divided by height
0.1, // near — anything closer is not rendered
1000 // far — anything farther is not rendered
);Parameters explained:
fov (Field of View): Vertical angle in degrees. 50-75 feels natural. Below 30 = telescopic/zoomed. Above 90 = fish-eye/dramatic. Experiment with the slider in the Camera Studio project below.
aspect: Must match your canvas aspect ratio (width/height). If they don’t match, objects look stretched horizontally or vertically. Always update this in the resize handler.
near / far: These define the clipping planes — the visible depth range. Objects closer than
nearor farther thanfarare not rendered (they’re “clipped”). Keepnearas large as possible andfaras small as possible to avoid z-fighting (a flickering artifact when depth precision runs out).
camera.position.set(5, 3, 5); // Move camera to (5, 3, 5)
camera.lookAt(0, 0, 0); // Point at the origin
camera.zoom = 1.5; // Zoom in 1.5x
camera.updateProjectionMatrix(); // ⚠️ ALWAYS call after changing fov, aspect, near, far, or zoom
OrthographicCamera — Isometric / 2D View
No perspective distortion. Objects stay the same size regardless of distance. Think of a blueprint or a 2D platformer.
var frustumSize = 10;
var aspect = window.innerWidth / window.innerHeight;
var camera = new THREE.OrthographicCamera(
-frustumSize * aspect / 2, // left
frustumSize * aspect / 2, // right
frustumSize / 2, // top
-frustumSize / 2, // bottom
0.1, // near
1000 // far
);
camera.position.set(5, 5, 5);
camera.lookAt(0, 0, 0);When to use Orthographic vs Perspective:
| Use Case | Camera | Why |
|---|---|---|
| 3D games, product viewers | Perspective | Depth perception matters |
| 2D games, UI overlays | Orthographic | No distortion, exact measurements |
| CAD / architectural plans | Orthographic | Parallel lines stay parallel |
| Mini-maps | Orthographic | Bird’s-eye with consistent scale |
| Data visualization | Perspective | More visually engaging |
Switching Between Camera Types
function switchCamera(type) {
var pos = camera.position.clone();
var target = new THREE.Vector3(0, 0, 0);
if (type === "perspective") {
camera = new THREE.PerspectiveCamera(50, w / h, 0.1, 1000);
} else {
var s = 10;
camera = new THREE.OrthographicCamera(-s * w/h, s * w/h, s/2, -s/2, 0.1, 1000);
}
camera.position.copy(pos);
camera.lookAt(target);
}Controls — Letting Users Move the Camera
Controls map user input (mouse, touch, keyboard) to camera movement. Most controls require controls.update() in the animation loop.
OrbitControls — The Universal 3D Viewer
This is the most widely used control in Three.js. The camera orbits around a target point — think of a satellite orbiting Earth.
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
var controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // Smooth inertia — feels polished
controls.dampingFactor = 0.05; // (0-1, lower = smoother motion)
controls.autoRotate = false;
controls.autoRotateSpeed = 2.0; // Degrees per second when autoRotating
controls.target.set(0, 0, 0); // The point the camera orbits around
// Limit rotation (polar angle = up/down angle)
controls.minPolarAngle = 0; // 0 = looking straight down
controls.maxPolarAngle = Math.PI; // PI = looking straight up
// Limit zoom distance
controls.minDistance = 1;
controls.maxDistance = 50;
controls.screenSpacePanning = true; // Pan parallel to screen vs ground plane
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.update(); // Apply settings
Key interactions: Left-drag = rotate, scroll = zoom, right-drag = pan. Touch: one finger = rotate, pinch = zoom, two-finger = pan.
Why damping matters: Without enableDamping: true, movement stops instantly when you release the mouse — it feels mechanical. Damping adds momentum, making the experience feel polished and professional.
TrackballControls — Unconstrained Orbit
Like OrbitControls but without an “up” reference. You can spin completely upside-down.
import { TrackballControls } from "three/addons/controls/TrackballControls.js";
var controls = new TrackballControls(camera, renderer.domElement);
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.staticMoving = false; // True = instant stop, false = damping
controls.dynamicDampingFactor = 0.05;
controls.target.set(0, 0, 0);
controls.update();When to use: Inspecting 3D models from any angle, VR previews, anywhere the user needs full 6-DOF movement.
FlyControls — Free Flight
First-person flight simulation. WASD to move, mouse to look. No gravity — you float freely.
import { FlyControls } from "three/addons/controls/FlyControls.js";
var controls = new FlyControls(camera, renderer.domElement);
controls.movementSpeed = 10;
controls.rollSpeed = 0.5; // How fast the camera rolls (tilts sideways)
controls.dragToLook = true; // Hold mouse button to look around
controls.autoForward = false; // Auto-move forward without pressing W
function animate() {
controls.update(delta); // ⚠️ Pass clock delta in seconds!
renderer.render(scene, camera);
}Controls: W/S = forward/back, A/D = strafe left/right, R/F = up/down, Q/E = roll.
FirstPersonControls — FPS-Style
Traditional first-person shooter controls. Movement locked to horizontal plane — no roll.
import { FirstPersonControls } from "three/addons/controls/FirstPersonControls.js";
var controls = new FirstPersonControls(camera, renderer.domElement);
controls.movementSpeed = 10;
controls.lookSpeed = 0.1;
controls.lookVertical = true;
controls.constrainVertical = true;
controls.verticalMin = 1.0; // Radians from top
controls.verticalMax = 2.0; // Radians from top
controls.lon = 0; // Horizontal angle
controls.lat = 0; // Vertical angle
function animate() {
controls.update(delta);
renderer.render(scene, camera);
}Key difference from FlyControls: FirstPersonControls keeps the camera upright (no roll). FlyControls allows full 3D rotation including roll.
DeviceOrientationControls — Mobile AR
Uses the device’s gyroscope for head-tracking on mobile.
import { DeviceOrientationControls } from "three/addons/controls/DeviceOrientationControls.js";
var controls = new DeviceOrientationControls(camera);
// iOS 13+ requires explicit permission via user gesture
if (typeof DeviceOrientationEvent !== "undefined" &&
typeof DeviceOrientationEvent.requestPermission === "function") {
DeviceOrientationEvent.requestPermission()
.then(function(state) {
if (state === "granted") controls.connect();
});
} else {
controls.connect();
}
function animate() {
controls.update();
renderer.render(scene, camera);
}PointerLockControls — Immersive FPS
Locks the mouse pointer for games. Mouse movement directly controls camera rotation.
import { PointerLockControls } from "three/addons/controls/PointerLockControls.js";
var controls = new PointerLockControls(camera, renderer.domElement);
document.addEventListener("click", function() {
controls.lock(); // Request pointer lock
});Multi-Viewport Layouts (Split Screen)
Render different camera views to different regions of the same canvas.
var leftCamera = new THREE.PerspectiveCamera(50, 0.5, 0.1, 1000);
leftCamera.position.set(-4, 2, 4);
leftCamera.lookAt(0, 0, 0);
var rightCamera = new THREE.PerspectiveCamera(50, 0.5, 0.1, 1000);
rightCamera.position.set(4, 2, 4);
rightCamera.lookAt(0, 0, 0);
function render() {
var w = renderer.domElement.width;
var h = renderer.domElement.height;
var halfWidth = Math.floor(w / 2);
// Left half — set viewport and scissor, then render
renderer.setViewport(0, 0, halfWidth, h);
renderer.setScissor(0, 0, halfWidth, h);
renderer.setScissorTest(true);
renderer.render(scene, leftCamera);
// Right half
renderer.setViewport(halfWidth, 0, halfWidth, h);
renderer.setScissor(halfWidth, 0, halfWidth, h);
renderer.render(scene, rightCamera);
}Why scissor test? Without setScissorTest(true), each viewport render clears the entire canvas. The scissor test restricts clearing to the specific viewport rectangle.
Camera Transitions & Animation
Lerp (Linear Interpolation) — Simple & Built-in
var targetPosition = new THREE.Vector3(10, 5, 10);
var targetLookAt = new THREE.Vector3(0, 0, 0);
var speed = 0.03;
function animate() {
camera.position.lerp(targetPosition, speed); // Move toward target
var currentLook = new THREE.Vector3();
camera.getWorldDirection(currentLook);
var desiredLook = new THREE.Vector3().copy(targetLookAt).sub(camera.position).normalize();
currentLook.lerp(desiredLook, speed);
camera.lookAt(camera.position.clone().add(currentLook));
renderer.render(scene, camera);
requestAnimationFrame(animate);
}GSAP — Full Easing Control
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<script>
gsap.to(camera.position, {
x: 10, y: 5, z: 10,
duration: 2,
ease: "power2.inOut",
onUpdate: function() {
camera.lookAt(0, 0, 0);
camera.updateProjectionMatrix();
}
});
</script>Common Mistakes
1. Near/far clipping too far apart
Setting near = 0.0001 and far = 100000 sounds safe but causes z-fighting — surfaces flicker because the depth buffer ran out of precision. Rule: keep far / near < 10000.
// Bad — depth fighting on distant objects
var camera = new THREE.PerspectiveCamera(75, w/h, 0.0001, 100000);
// Good — tight range for the scene
var camera = new THREE.PerspectiveCamera(75, w/h, 0.1, 1000);2. Wrong aspect ratio
If camera.aspect doesn’t match the canvas size, objects appear stretched. Always update on resize and don’t forget updateProjectionMatrix().
3. Forgetting controls.update() in the loop
Controls with damping or auto-rotate silently do nothing without controls.update() being called every frame.
4. Camera inside a mesh
If the camera is inside an object (e.g., walking through walls), set near small enough (0.01) but watch for z-fighting.
5. iOS DeviceOrientation not connecting
iOS 13+ blocks device orientation without user permission. Always wrap .connect() in a button click handler.
6. OrbitControls target vs lookAt confusion
controls.target is the orbit center. If you call camera.lookAt() directly, OrbitControls fights it. Update controls.target instead.
7. Multi-viewport without scissor test
Each viewport render clears the entire canvas without setScissorTest(true). Always set the scissor rectangle per viewport.
Practice Questions
Q1: What happens if camera.aspect doesn’t match the canvas aspect ratio?
A: Objects appear stretched horizontally or vertically. The camera’s projection matrix doesn’t match the render surface.
Q2: Why does OrbitControls need controls.update() in the animation loop?
A: Controls with enableDamping: true simulate inertia. The damping state changes every frame and must be recalculated. Without update(), the controls stay frozen in their last state.
Q3: When would you use OrthographicCamera instead of PerspectiveCamera? A: For 2D games, CAD tools, isometric views, mini-maps, and any situation where you need objects to stay the same size regardless of distance.
Q4: What causes z-fighting and how do you prevent it?
A: Z-fighting is flickering between overlapping surfaces due to insufficient depth buffer precision. Fix by tightening near/far planes or using renderer.logarithmicDepthBuffer = true.
Q5: How do you prevent OrbitControls from going through the floor?
A: Limit controls.maxPolarAngle to something less than Math.PI (e.g., Math.PI / 2.1).
Challenge: Build a scene with four viewports (top-left, top-right, bottom-left, bottom-right). Each viewport shows the same scene from a different camera position (front, side, top, and perspective). Use OrthographicCamera for three views and PerspectiveCamera for one.
FAQ
Try It Yourself
Copy the Camera Studio HTML from the mini project above (in the original file). Save as camera-studio.html and open in any browser. Experiment with switching between Perspective/Orthographic cameras and Orbit/Trackball/Fly/FirstPerson controls.
What’s Next
| Topic | Description | Link |
|---|---|---|
| Geometries & Materials | Deep dive into shapes and PBR materials | https://tutorials.dodatech.com/frontend/libraries/threejs/threejs-geometries-materials/ |
| Lights & Shadows | Light types, shadow maps, and strategies | https://tutorials.dodatech.com/frontend/libraries/threejs/threejs-lights-shadows/ |
| Animation | Clocks, easing, TWEEN.js, and GSAP | https://tutorials.dodatech.com/frontend/libraries/threejs/threejs-animation/ |
| Interactivity & UI | Raycasting, click detection, and overlays | https://tutorials.dodatech.com/frontend/libraries/threejs/threejs-interactivity-ui/ |
| Three.js Basics | Scene, Camera, Renderer fundamentals | https://tutorials.dodatech.com/frontend/libraries/threejs/threejs-basics/ |
| JavaScript Fundamentals | The language behind the scenes | JavaScript |
| WebGL | The low-level API Three.js wraps | WebGL |
You now have a complete understanding of Three.js cameras and controls — from the Perspective vs Orthographic choice to six different control schemes and multi-viewport layouts. This knowledge powers the multi-camera threat visualization dashboard in Durga Antivirus Pro, where different camera angles reveal different aspects of the security landscape.
What’s Next
Congratulations on completing this Threejs Cameras Controls tutorial! Here’s where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Apply what you learned by building something real
- Explore related topics — Check out other tutorials in the same category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro