Tom MacWright

tom@macwright.com

Math for Pictures

Here’s the small subset of math that I use for the vast majority of visualizations - ideally it’s a good introduction to those who want to make pretty things with d3js, canvas, or other tools.

In essence, I’ll try to explain four parts of Javascript’s built-in Math object:

  • Math.cos(a), which we use to get x positions from angles
  • Math.sin(a), which we use to get y positions from angles
  • Math.atan2(y, x), which turns x, y positions back into angles
  • Math.PI, which we use to convert radians to degrees and back, and use for the angles in a circle

Of course, each of these parts is usable for much, much more - but let’s start here.

Radians

As a preface, let’s get confortable with the units that these functions take: radians. The degree angles in a circle go from 0° to 360°. In the land of radians, that’s 0 radians to 2π radians. So, halfway around is π and 90° is π/2.

Core Math functions use radians exclusively. For code that bridges the gap and accepts degrees as input, you’ll usually see a chunk like this, that defines D2R and R2D, which convert radians R to degrees D and back again.

Math.sin & Math.cos

Before diving in to Math.sin() and Math.cos(), let’s think quickly about what they are.2

Math.sin() and Math.cos() are functions that take one numeric argument and return a numeric value. While you can give them any number and they’ll give a value, the values repeat every 2 * Math.PI, because that’s their wavelength.

Sine, Cosine, and Unit Circles

Many visualizations are composed of circles, circle fitting, or ‘radial’ layouts. For instance, this geometry daily redo, as well as this one, and this one, and this one - all circle-based.

To just draw circles, SVG has a circle element. But it’s more important to reason about them - to know that, at a certain angle, where is a point on a circle.

This is where Math.sin() and Math.cos() jump in.1

The fundamental circle you can draw with the functions is a unit circle: a circle with a radius of 1. (and thus a diameter of 2) Once you make a unit circle, it’s easy to scale it up or down.

In terms of a unit circle, using Math.sin() and Math.cos() is simple: the sine function generates y coordinates, and cosine generates x coordinates.

We call it a unit circle because the distance from the center to each point is 1.

Both functions take as an argument an angle in radians, and return a number which you can interpret as a distance from the center of the circle. The center of the circle, for a unit circle, is at the coordinate 0, 0, and so it radiates outwards to 1 and -1 in x and y dimensions.

One of the classic tricks with sine, cosine, and so on is the pattern:

  • sine: opposite / hypotenuse
  • cosine: adjacent / hypotenuse
  • tangent: adjacent / adjacent

These make a lot of sense in this context if you think of a triangle radiating out of a unit circle, and since the hypotenuse is always equal to 1, sine and cosine simply provide the values of opposite and adjacent sides.

Radial Shapes from Circles

With this basic knowledge, you can draw other kinds of shapes as well.

Here’s an equilateral triangle, drawn by choosing three angles - 0°, 120°, and 240°, from a unit circle. Besides just being an easy way to compute this, you also know that this triangle will be inscribed in a circle made from the same formula.

Here’s a square, made the exact same way, which you can also guarantee is inscribed in a circle of radius 90.

So: in this narrow usage of trigonometry, sine and cosine are used to go from angles into coordinates. How do you go back?

atan2 and Angles

Let’s do the opposite: going from coordinates to angles. Doing this is useful for a number of reasons - like if you have a ‘dial’ control in which the user can drag a circular UI element, and you want to know what angle they’re currently dragging it to. Or if you have existing data and you want to do something that requires working with the angles from a → b → c, like my running map.

Sine, cosine, and tangent have inverse versions: arcsine, arctan, and arccos - but they’re not that convenient. Why? Because, while an angle going into sine and cosine will give you a point in x & y, giving arcsine a y coordinate and arccos an x coordinate is not enough, because there’s no one-to-one mapping - the point x=0 could be at the the top of the sphere or at the bottom, and arccos doesn’t know.

In school, you would learn how to figure out what quadrant the point is in, and use a different little equation for each. Luckily there’s some new math:

atan2 is awesome: it takes a coordinate and gives you the angle to it, and handles the quadrant problem internally. The only catch is that atan2 takes arguments in an atypical y, x order.

Here’s an example of looping this around - this uses Math.atan2 to find the angle from a point a picture to the mouse position, and then draws a line at that angle using Math.cos and Math.sin:

atan2 is the goldmine that made my running map possible and sparked this renewed fascination with math.

Further Reading

Nature of Code is a great exploration of other realms of math. Bret Victor’s Kill Math is essential. Trig without Tears is great.

The examples in this article are powered by bl.ocks.org, jsfiddle, and my project mistakes.io. You might also find tributary.io useful for prototyping. The lead illustration was done in Context Free.