Building Concentric Circles with Nice Color Palettes For Fun and Science

I was looking through my applications folder the other day, hoping to clean house a bit. I came across an app I used to love but haven’t used in forever: Simple Desktops. It’s an app for changing your wallpaper once in a while to a random, simple, classy design. One came up like this:

concentric circles 1

I was just kinda in the mood, so I set about re-creating it how I would do it with front end code.

You know what’s good at drawing circles?

SVG is. I figured the easiest way to draw some concentric circles was the <circle> element. Easier still, make the circles center at 0, 0 on a grid and just increase the radius.

<svg viewBox="-50 -50 100 100">
  <!-- first circle -->
  <circle cx="0" cy="0" radius="20" />
</svg>

I knew I wanted five circle groups and for each circle to have five circles within. That would be tedious to write by hand and harder still to tweak. So I reached for our programmatic friend the for loop.

How do you write a for loop in SVG though?

Use an HTML preprocessor! I went for Jade. A simple Jade loop looks like this:

- for (var x = 1; x < 6; x++)
  p Print stuff five times.

Our loops will only be slightly more complicated in that there are two of them nested. And what’s inside the loop is a <circle> element in which we are hoping to affect the radius of the circle with the current value of the loop. Jade can do it!

- for (var y = 1; y < 6; y++)
  <svg viewBox="-50 -50 100 100">
  - for (var x = 1; x < 6; x++)
    circle&attributes({
      "cx": 0,
      "cy": 0,
      "r": 60-(x*10)
    })
  </svg>

This looks a little funny. Especially that &attributes bit. That’s what they call “exploded” attributes. I like it though. Setting attributes in an JavaScript object format like that is kinda nice.

When that compiles, it ends up like this:

concentric circles 2

I’m sure you can see how easy it would be to tweak the code in those loops to adjust lots of code quickly.

Wicked easy centering and layout with flexbox

Setting the 5 <svg>s in a row is very easy. In our little demo, just making the <body> a flex container will do.

Vertical centering will happen automatically if we expand their height with flex: 1. By default, SVG will make it’s drawing area centered and as big as it can be within the area that the <svg> occupies (the “viewport”).

html, body {
  height: 100%;
  overflow: hidden;
}
body {
  display: flex;
  padding: 0 50px;
}
svg {
  flex: 1;
  padding: 20px;
}

Getting ourselves some NICE color palettes

There is a clever way to generate a random color (hex code) in JavaScript:

'#'+Math.floor(Math.random()*16777215).toString(16);

Random colors usually aren’t very attractive though, especially five random colors brought together.

There is a neat little library called Please.js that we could use to get pleasing colors. They even have an API for palettes:

Please.make_scheme({
  scheme_type: 'analogous'
});

concentric circles 3

But… this is still maybe not as nice as a hand-created and community-curated color palette. Which is exactly what COLOURlovers has.

concentric circles 4

And fortunately, they have an API. The thought process for using it is something like:

1. We want palettes specifically, so:

http://www.colourlovers.com/api/palettes

2. We want good ones specifically, so:

http://www.colourlovers.com/api/palettes/top

3. We want to use the palettes in JavaScript, so JSON would be nice, so:

http://www.colourlovers.com/api/palettes/top?format=json

4. We want exactly 5 of them, so:

http://www.colourlovers.com/api/palettes/top?format=json&numResults=5

5. We want different results every time, so:

That’s slightly trickier. Fortunately their API supports an “offset”, meaning we can get our five results starting wherever in their ordering. So let’s randomize that.

Let’s generate a number between 1-50:

var rand = Math.floor(Math.random() * (50 - 1)) + 1;

Then we can append that to the end of the URL we’re building:

var url = "http://www.colourlovers.com/api/palettes/top?format=json&numResults=5&resultOffset=" + rand;

We want to use this in the browser, so:

That means doing this API request with Ajax. In doing so, we’re subject to the browser’s Cross Domain Restrictions. These can be lifted by the API provider by setting what are called CORS headers, but for whatever reason, COLOURlovers doesn’t set those. Another classic way around that is “JSONP”, but let’s not go there right this minute.

Instead, we’ll use a proxy server to fetch the data and re-serve it to us with proper CORS headers. crossorigin.me is useful for that. You simply prepend their URL to the one we’ve been building:

var url = "http://crossorigin.me/http://www.colourlovers.com/api/palettes/top?format=json&numResults=5&resultOffset=" + rand;

Get the Color Data

Now that we have the URL that returns the data we need, let’s use a jQuery Ajax method to go get it.

$.getJSON("http://crossorigin.me/http://www.colourlovers.com/api/palettes/top?format=json&numResults=5&resultOffset=" + rand, function(data) {

  console.log(data);

});

Got it!

concentric circles 5

Colorizing the SVGs

That data object has everything we need to do the colorization. We’re going to loop over all the <svg>s and all the <circle>s inside as a nested loop.

jQuery’s .each() will be useful for that, which also gives us a parameter to track iterations. We’ll use those to pluck out individual colors from the arrays and apply them as the fill attribute to the <circle>s

$("svg").each(function(x) {
  // x will be 0, 1, 2, etc.
  $(this)
    .find("circle")
    .each(function(y) {
      // y will be 0, 1, 2, etc.
      $(this).attr("fill", "#" + data[x].colors[y]);
    });
  console.log("Palette #" + x + ": " + data[x].colors)
  // "Palette #1: 351330,424254,64908A,E8CAA4,CC2A41"
});

Ta da!

See the Pen Color Palette Circles by Chris Coyier (@chriscoyier) on CodePen.

Comments

  • Jordan Arentsen

    Really enjoyed this! I would be excited for another article investigating the animation of these beautiful concentric circles into a nice loader animation / network indicator.

  • Ashley Sheridan

    Curious why you went with an SVG approach instead of a radial gradient? A CSS approach would have been arguably easier to read (using percentages instead of fixed values) and could still benefit from a loop in a pre-processor to avoid manually typing everything?

    • chriscoyier

      No reason. Could be divs with 50% border-radius. Could be clipping paths. Could be generated images. I enjoy thinking in SVG.

    • Pat Hartl

      That damn SVG, it’s so hot right now

    • http://TedSlater.com/ Ted Slater

      SVG is really cool!

      That said, this generates something very similar, but with simple (albeit kludgy) CSS:

      border-radius: 50%;
      height: 13px;
      width: 13px;
      box-shadow: 0 0 0px 10px #F44336,0 0 0px 20px rgb(241, 239, 165),0 0 0px 30px rgb(211, 206, 61),0 0 0px 40px rgb(247, 120, 37),0 0 0px 50px rgb(85, 66, 54);
      background: rgb(96, 185, 154);

  • MrChris

    For science? That’s stretching it a bit☺

    • http://mimoYmima.com/ Brent Lagerman

      svg = Science Vector Goodness

  • Sebastián Sánchez

    Let me try to do that.

Related Articles