If you ever used 3D software you’ve probably used an orbit camera. It’s the control scheme where you drag the mouse to rotate around the target. It feels simple: just rotate around a point. But once you try to implement it yourself, the details of 3D rotations quickly get in the way.
When I first got into graphics I spent a lot of time building small demos to learn the basics of the pipeline. An orbit camera quickly rose to the top of my list because it made navigating those experiments so much easier. But back then, the explanations I found were either incomplete or jumped straight to quaternions. Some even tossed in a mysterious “clamp” to stop the camera from flipping upside down without ever explaining why it was necessary.
In this article, I’ll revisit the problem the way I wish it had been explained to me back then. I’ll show how to build an orbit camera using spherical coordinates: what they are, why they’re a natural fit, and the quirks that come with them. By the end, you’ll have a working implementation. You’ll also see why spherical coordinates aren’t the final word, and how their limits point toward more general solutions.
From Cartesian to polar coordinates
One of the simplest ways to implement orbit controls is with spherical coordinates, and it’s also the most common approach in model viewers. When I first came across them, they seemed like a somewhat abstract way of describing positions in space, but they turned out to be more intuitive than I expected. Spending a little time on spherical coordinates is worth it because they map directly to camera movement and appear throughout graphics. The easiest way to understand the math is to start with polar coordinates in 2D.
I’ll assume you’re familiar with vectors. In graphics, we often picture them as arrows in space that represent displacement: direction and magnitude. We usually represent vectors with scalar values for each axis (e.g.,
Like Cartesian coordinates, polar coordinates in 2D space are also described by two scalars: a radius and an angle
Converting between Cartesian and polar coordinates is straightforward. To go from Cartesian to polar, we use the same operations we already know for computing a vector’s magnitude and direction. The magnitude gives us the radius
To go the other way, we resolve the vector into its
This shows that Cartesian and polar coordinates are simply two different ways of describing the same vector. One emphasizes components along axes, the other emphasizes distance and direction. If you’re new to polar coordinates, you might be wondering why we don’t use them everywhere in graphics.
In graphics we rely heavily on linear transformations like translation, rotation, scaling, and projection. These are neatly expressed as matrix–vector multiplications in Cartesian space. In polar space, the same operations quickly become messy. For example, translating a point isn’t as simple as just adding to
Polar coordinates have a couple of quirks. At the origin (
Polar coordinates are very common in game development3. Any time you want to implement a tracking or chasing mechanic where one object follows a target, it’s often easier to work in polar space. But a much more common use case that’s relevant to us is moving around a sphere. But before we can get there, we need to move from polar coordinates in 2D to spherical coordinates in 3D.
Extending to spherical coordinates
Like Cartesian coordinates, extending polar coordinates into 3D requires one more value. We add a second angle, which gives us an extra degree of freedom and leads to spherical coordinates4. A point is then represented by three values: the radius, a horizontal angle (azimuth), and a vertical angle (polar). Together,
Most resources use the symbols
In mathematics and physics, the horizontal angle is often measured in the
Footnotes
-
Most languages provide
atan2(y, x)
to compute the angle. Preferatan2
overatan(y/x)
because it uses the signs ofand to pick the correct quadrant and handles . ↩ -
In 2D graphics, angles are typically normalized to the range
. When the radius is , the angle is undefined, so it’s common to set by convention. ↩ -
The first time I ran into polar coordinates in 2D was while reading an old article in the early 2000s called Sin & Cos: The Programmer’s Pals!. It described the usefulness of polar coordinates in the context of a top-down racing game like Micro Machines. ↩
-
The reason we give this system a new name rather than calling it “3D polar coordinates” is that there is another way to generalize polar into three dimensions: Instead of adding an angle, we could add another length value, giving us cylindrical coordinates. ↩