cubic-bezier(.2, .8, .9, .6)

Bezier curves are great tools to represent curvatures and have many applications such as computer graphics, fonts, and animations. If you’ve dealt with CSS before, you’ve probably ran into Bezier curves. For example, In CSS you can use a timing function expressed as a Bezier curve — as shown above — where the x-axis is time and the y-axis is distance.

Why use them?

Let’s put ourselves in a perspective of a computer. We’re asked to draw a curve that will consume 100 pixels. If we were given the points upfront, it would be very convenient to draw the curve. Unfortunately, that’s not going to happen for many reasons — The obvious being the overhead of storing the points.

It would be nice if we could call a function 100 times, collecting the points each time. Let’s create a method signature for our dream function:

/**

* @param {Number} t Number from 0 to 1.

* @return {Array} Point on the curve

*/

function getCurveLocation(t) { ... }

The function takes a number from 0 to 1, and since we’re asked to draw 100 points, we’ll call getCurveLocation 100 times with values evenly spaced.

var points = []

for(var i = 0; i <= 100; i++) {

points.push(getCurveLocation(i * .01))

}

We’re all done. With this setup we can represent our curve in an arbitrary amount of pixels, i.e., it’s really simple to do scaling — We’d just increase or decrease the loop count to get the desired amount of pixels and then render to the screen.

The Definition

How does getCurveLocation work? That’s where Bezier curves come into play. The general definition is the following:

General formula for Bezier curve of degree n

Let’s break down the formula, and keep in mind that the method signature at the beginning would be an implementation of this. It’s ok if you don’t understand each part at first — The example later on will hopefully bring everything together.

B is function that accepts a number, t, from 0 to 1 and returns a point on the curve.

Σ is a loop that sums each iteration. n represents the degree of the Bezier curve. The higher the degree, the more complicated shapes you can make. Most of the time, quadratic(2) and cubic(3) are all you need. i is an integer from 0 to the polynomial degree and will increment each loop.

Represents binomial coefficients. If we expanded out a binomial, for example (x+y)^n, the number in front of each term is what the expression represents. We can take shortcuts here. Regardless of the binomial, for example, if n=3 and i=2, we can evaluate this expression quickly without expanding everything out by using combinatorics — how many ways can we choose 2 items from a set of 3 items — or we could look at Pascal’s Triangle by focusing on the 4th row and 3rd term.

Represents a specific term within the polynomial. Pi is a control point.

Intuition

Let’s create a Bezier curve with the following points taken from the example at the beginning:

P0 = (0,0)

P1 = (.2, .8)

P2 = (.9, .6)

P3 = (1, 1)

Since there’s 4 points, we’ll have a cubic bezier curve — The degree is of order 3. Therefore 𝝨 will do 4 loops. Let’s describe each loop, and remember that 𝝨 iterates and sums — So the result is a concatenation of each loop.

As a guide, here’s Pascal’s triangle so we can easily look up the coefficients:

Pascal’s Triangle

Loop 1:

Loop 2:

Loop 3:

Loop 4:

Final polynomial:

Let’s plot this equation with our points substituted in for P

You may be thinking this curve isn’t too useful, but the meaningful part is on the domain from 0 to 1.

fourWith domain constraint

That should look familiar — it’s the Bezier curve we used in the beginning of the article. Having the domain from 0 to 1 is critical because interesting things happen at those two values. Let’s look at the polynomial when t=0

We get our first point! Let’s look at the polynomial when t=1

We get our last point! Regardless of where the first and last point are, t=0 will always return the first point and t=1 will always return the last point. This is really powerful because no matter how complex our polynomial gets we can easily analyze where the path begins and ends. Any other t value will not cancel the terms so nicely. We can still gain intuition on what happens when we change the other two points though. For example, here we’re changing the second point, P1

When P1 is increasing the curve is bending upward. However, it’s bending faster at the beginning than the end. We could take a partial derivative with respect to P1 to prove this, but let’s just focus in on the equation where we think it’s changing quickly vs where we think it’s changing slowly:

Let’s look at where it’s bending quickly, t=.25

Clearly P1 has more of a say on how the curve should behave than P2. Now let’s look at t=.75

Now P2 has a higher weight — It reversed. So as t increases the weights of each term change, and since each term has a unique control point, the control points will take turns having more control on the curve.

We’ve been using cubic bezier curves as examples throughout this article, but the same intuition can be applied to other degree bezier curves. For example, a quadratic Bezier curve has three points — Two endpoints and a control point:

The curve is changing more symmetrically as P1 changes thanks to less control points. Also as P1 becomes collinear with the endpoints, we get a straight line. I’ll leave that phenomenon for you to explore. Hopefully you’ve gained some insight and intuition behind Bezier curves. They fascinate me because they’re powerful equations that have a clean API — a simple input from 0 to 1 — and a simple response — a location on the curve.