Navigate

Search topics across all sections

GitHub
Controls

Camera Controls

CameraControls is the upgraded version of OrbitControls. It gives you the same orbit, zoom, and pan, but adds smooth animated transitions, auto-framing, and full programmatic control. If your app needs to fly the camera from point A to point B, this is the component you want.

You build a product viewer with OrbitControls. The client asks you to add a 'click on a part to zoom in' feature. You try setting the camera position directly, but it snaps instantly. There is no way to animate the transition. You end up rewriting half your camera logic to switch to CameraControls.

terminal
Camera jumps to new position with no animation. OrbitControls.target.set() provides no transition support.

Real-world

Think of CameraControls as a drone camera. OrbitControls is like spinning a globe on your desk — simple and direct. But a drone can do much more. You give it a flight plan: "Fly from here to there, point at that building, and zoom in smoothly."

That flight plan is setLookAt. You tell the drone where to go (camera position) and what to look at (target position), and it glides there gracefully. You can even chain multiple flight plans for a guided tour.

The setLookAt Flight Plan

Every animated camera move follows the same pattern.

Get ref

useRef to access controls

Call setLookAt

(posX, posY, posZ, targetX, targetY, targetZ, animate)

Camera flies

Smooth transition over smoothTime seconds

Promise resolves

Chain the next move with await

Guided Walkthrough

Let us build a camera system that can fly between views.

Step 1 — Set up CameraControls with a ref

Experience.tsxTSX
import { CameraControls } from '@react-three/drei'
import { useRef } from 'react'

function Experience() {
  const controls = useRef(null)

  return (
    <CameraControls
      ref={controls}
      smoothTime={0.5}
      makeDefault
    />
  )
}

The ref gives you access to all the camera methods. smoothTime controls how long transitions take. makeDefault keeps everything in sync just like with OrbitControls.

Step 2 — Fly to a position with setLookAt

ViewButtons.tsxTSX
const goToFront = () =>
  controls.current?.setLookAt(
    0, 0, 5,     // camera position
    0, 0, 0,     // look-at target
    true          // animate the transition
  )

const goToTop = () =>
  controls.current?.setLookAt(
    0, 5, 0.01,  // slightly off-axis to avoid gimbal lock
    0, 0, 0,
    true
  )

setLookAt takes six numbers: where to put the camera, and where to point it. The last argument enables smooth animation. Without it, the camera teleports.

Step 3 — Chain transitions for a guided tour

GuidedTour.tsxTSX
async function guidedTour(controls) {
  await controls.setLookAt(10, 8, 10, 0, 0, 0, true)
  await new Promise(r => setTimeout(r, 2000))
  await controls.setLookAt(2, 1, 2, 1, 0, 0, true)
  await controls.setLookAt(5, 5, 5, 0, 0, 0, true)
}

Every transition method returns a Promise. Use await to wait for one move to finish before starting the next. Add pauses with setTimeout between stops for a cinematic feel.

What you just learned

CameraControls gives you orbit + zoom + pan, plus smooth animated transitions

setLookAt is the core method: tell it where to go and what to look at

smoothTime controls how long transitions take (0.3 to 1.0 seconds feels natural)

All methods return Promises so you can chain transitions with await

Use fitToBox to automatically frame an object into view

Question

CameraControls can do everything OrbitControls does, plus animated transitions. So why would you ever pick OrbitControls? Think about bundle size, simplicity, and the principle of using the smallest tool that gets the job done.

Think about it...

You call setLookAt(0, 0, 5, 0, 0, 0, true) and then immediately call setLookAt(10, 10, 10, 0, 0, 0, true). What happens?

Try These Challenges

Put what you learned into practice. Try each challenge in the demo above using the Leva controls, then check the solution.

Try This!

Beginner

Set smoothTime to 2 -- slow-motion camera flight!

Try This!

Intermediate

Click all 3 objects quickly -- does the camera queue up?

Try This!

Beginner

Set transitionSpeed to 5 -- snappy transitions!

These are the patterns that trip up developers most often. Switch between Wrong and Fixed to compare the code side by side.

1
Using OrbitControls when you need animated transitions
Camera snaps instantly instead of gliding smoothly
Don't do this
ProductViewer.tsxTSX
const controls = useRef<OrbitControlsImpl>(null)

const focusOnPart = (pos) => {
  // OrbitControls has no animation system!
  // This snaps instantly — jarring for users
  controls.current.target.set(...pos)
  controls.current.update()
}
OrbitControls has no built-in animation. Setting the target or position snaps the camera instantly. CameraControls wraps the camera-controls library which provides smooth, configurable transitions. If your app needs click-to-focus, guided tours, or preset views, CameraControls is the right choice.
2
Forgetting to set smoothTime
Transitions feel too abrupt or too sluggish
Don't do this
SmoothTransition.tsxTSX
const controls = useRef<CameraControlsImpl>(null)

// Uses default smoothTime — might feel snappy
controls.current?.setLookAt(5, 5, 5, 0, 0, 0, true)
The smoothTime property controls transition duration in seconds. The default may feel too fast for cinematic moves or too slow for quick switches. Set it as a prop for the default, or change it dynamically before specific transitions. Values between 0.3 and 1.0 seconds feel natural for most cases.
3
Using both OrbitControls and CameraControls
Two control systems fighting over the same camera
Don't do this
Experience.tsxTSX
function Experience() {
  return (
    <>
      {/* Both attach listeners and move the camera! */}
      <OrbitControls makeDefault />
      <CameraControls makeDefault />
    </>
  )
}
Both controls attach event listeners to the canvas and move the camera. Having both active causes jitter, broken transitions, and unpredictable input. Pick one. CameraControls can do everything OrbitControls can plus animated transitions, so it is a safe default for most projects.

Best Practices

Set smoothTime

Configure smoothTime in seconds. Values between 0.3 and 1.0 feel natural. Change it dynamically before specific transitions if needed.

Use fitToBox

Call fitToBox(mesh, true, padding) to automatically frame any object into view. Perfect for product viewers where models vary in size.

Chain with await

All transition methods return Promises. Use await to sequence camera moves for guided tours and cinematic fly-throughs.

Add collision boundaries

Use distance limits, polar/azimuth constraints, and colliderMeshes to prevent the camera from going through walls or flying off into space.