Navigate
Search topics across all sections
OrbitControls
OrbitControls is the simplest way to let users explore a 3D scene. Drop it in, and people can drag to rotate, scroll to zoom, and right-click to pan. It is the default choice for almost every R3F project.
You add OrbitControls to your scene and everything works great. Then you add TransformControls to let users move an object. Now when you drag the gizmo, the entire camera spins at the same time. Both controls fight for the mouse, and the scene becomes unusable.
Camera orbits while TransformControls gizmo is being dragged. Both controls respond to the same mouse events simultaneously.
Real-world
Picture a globe on a desk stand. You grab the globe and spin it to see different continents. You scroll the stand up and down to zoom in on a country. You slide the whole stand left or right to re-center your view.
That is exactly what OrbitControls does. Your 3D object is the globe, and the camera orbits around it on an invisible stand. Left-drag to spin, scroll to zoom, right-drag to slide. The object stays put while you move around it.
How OrbitControls Works
Every mouse action maps to a camera movement around a fixed target point.
Left Drag
Orbit (rotate around target)
Scroll Wheel
Dolly (zoom in/out)
Right Drag
Pan (shift target sideways)
Camera Moves
Object stays still
Guided Walkthrough
Let us go from zero to a polished orbit setup in three steps.
Step 1 — Drop in OrbitControls
import { OrbitControls } from '@react-three/drei'
function Experience() {
return (
<>
<OrbitControls makeDefault />
<mesh>
<boxGeometry />
<meshStandardMaterial color="royalblue" />
</mesh>
<ambientLight intensity={0.5} />
<directionalLight position={[5, 5, 5]} />
</>
)
}One line and you get full orbit, zoom, and pan. The makeDefault prop tells R3F "these are my controls" so everything stays in sync.
Step 2 — Constrain the movement
<OrbitControls
makeDefault
minPolarAngle={Math.PI / 6} // can't look from above
maxPolarAngle={Math.PI / 2} // can't go underground
minDistance={2} // closest zoom
maxDistance={10} // farthest zoom
/>Polar angles control the vertical range. Distance limits prevent users from zooming through the object or losing it in the distance. Essential for product viewers.
Step 3 — Add damping and auto-rotate
<OrbitControls
makeDefault
enableDamping
dampingFactor={0.05}
autoRotate
autoRotateSpeed={2}
/>Damping adds smooth momentum so the camera glides after you release the mouse. Auto-rotate slowly spins the scene, which looks great on landing pages and product showcases.
What you just learned
OrbitControls gives you orbit, zoom, and pan with a single component
makeDefault registers the controls so R3F and other drei components stay in sync
Polar angles and distance limits constrain the camera to a comfortable range
Damping adds smooth momentum, auto-rotate keeps the scene spinning
Question
OrbitControls always rotates the camera around a target point. But what if you need the camera to fly smoothly from one spot to another, like clicking on a part of a car to zoom into it? Can OrbitControls do that? (Hint: check out CameraControls.)
Think about it...
You set maxPolarAngle to Math.PI / 2 on your OrbitControls. What happens when a user tries to drag the camera below the ground plane?
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
Enable autoRotate and set autoRotateSpeed to 10 -- dizzy cam!
Try This!
Beginner
Enable the constrained polar angle -- can you look underneath?
Try This!
Beginner
Toggle damping off -- feels snappy!
These are the patterns that trip up developers most often. Switch between Wrong and Fixed to compare the code side by side.
<OrbitControls />
<TransformControls>
<mesh />
</TransformControls>
// TransformControls can't disable OrbitControls
// because it doesn't know they exist<Canvas frameloop="demand">
<OrbitControls enableDamping dampingFactor={0.05} />
{/* Scene freezes mid-deceleration when you
release the mouse — no one calls invalidate() */}
</Canvas><perspectiveCamera ref={camRef} position={[5, 5, 5]} />
<OrbitControls />
// Attaches to the DEFAULT camera, not yours!Best Practices
Always use makeDefault
It costs nothing and prevents hard-to-debug conflicts with other drei components like TransformControls and GizmoHelper.
Enable damping
Always enable enableDamping for a polished feel. The default dampingFactor of 0.05 works well for most scenes.
Constrain for UX
Set maxPolarAngle to prevent going underground and minDistance/maxDistance to keep the object in a comfortable zoom range.
Switch to CameraControls for animation
If you need smooth programmatic camera transitions like click-to-focus or guided tours, CameraControls is the right tool for the job.