Circle Packing

Circle packing is such a fantastic effect, it looks infinitely complex, while also being mathematically beautiful. In this tutorial, we’re going to create a circle packing effect… Interestingly, this is a good example of an effect that isn’t particularly efficient to run, but at the same time, will still be very quick!

As usual, we’re going to begin with a small, clean canvas. We will also set the size of the canvas and adjust it based on the user’s device pixel ratio, or pixel density. This ensures that the final result is crisp on all monitors.

Now, I’m going to explain a little about our process, so we know which variables we will need. You’ll be able to see here that it’s not the most efficient, but it really gets the job done.

Our steps will be:

  1. Create a new Circle
  2. Check to see if the circle collides with any other circles we have.
  3. If it doesn’t collide, we will grow it slightly, and then check again if it collides.
  4. Repeat these steps until we have a collision, or we reach a “max size”
  5. Create another circle and repeat x times.

So, we have an array of circles, a totalCircles, a min & max circleSize and a createCircleAttempts. Let’s get this in code.

Now we will spec out our process. We will make a createCircle and doesCircleHaveACollision function, and then fill it in with our goals… including calling the createAndDrawCircle function once for each of our totalCircles variable.

This is the fun part, we can go through our functions and fill them in. If we tackle this in a step by step way, it will flow out really well.

First, we’ll start with creating a circle object, we’ll give it an x, y and a radius

Now, we’ll add it to our list of circles, and draw it… we didn’t really need to do this just yet, but being able to see what we’re coding render out really helps with the process.

Awesome, and there we have it, tiny circles all over our screen. Next, we can look at growing them. We will do this 1 unit at a time, and when they collide, we’ll take one step back, and break out of the loop.

Wow, what a mess we’ve made! Of course we know the reason for this. Currently our doesCircleHaveACollision function always returns false … we’ll need to fill that in.

The way that we tell if circles have a collision, is a little bit of trigonometry. We’re going to loop through each of the circles that are drawn and compare them to the current circle being drawn. If their radii combined, is greater than the distance between each of their centers, then we know there’s a collision.

To get the distance between the two center points, we will use pythagoras' theorem (whoa, that high school math coming in handy!)

Almost there! But another small gotcha, when we’re creating our circles, there’s also a chance that they’re appearing inside others.

We’re going to use a loop in the creation area now as well, its a little inefficient to randomly guess positions, but really at the end of the day, unless we were doing millions of circles, we won’t see any slow down.

If the circle doesn’t find a safe place to draw, the attempt is abandoned.

Wow, now we’ve got some beautiful circles, all packed in! There’s only one little step more to do, and that is to trigger a collision when they hit the wall as well as each other. We’ll break that into two if statements, one checking the top and bottom, and one checking the left and right.

And there we have it! It’s not the prettiest code, but it’s a great example of how some complex things can be reasoned out, thought about, and stepped through with relative ease & a little math.

↪ You can edit the code above, and have it run by pressing the arrow between the code and demo, but if you like, you can also play around a little with this code