Hand Coding a Line Spirograph

I recently discovered, to my delight, Veerle Pieter’s delightful website. I was reading through her tutorials when I found her article on spirographs, and I very much enjoyed reading about this technique in Illustrator.

I've always been a fan of spirographs, they seem to epitomize the link between mathematics and art. Illustrator, on the other hand, I do not like very much. Adobe products in general aren’t my favorite to use, I prefer leaner applications.

This article got me thinking: SVG is certainly the perfect medium for creating spirographs. I'm not talking about creating them in Illustrator, I'm talking coding them by hand. It seems like SVG has many features that would make this perhaps even easier than in Illustrator. So I set out to find out.

I started out with a simple rectangle:

<svg width="150" height="150" viewBox="0 0 150 150">
<rect x="25" y="25" rx="10" width="100" height="100" />
</svg>

If you've never seen SVG code before, this is a good place to start. We have a few things going on here: an svg element, and a rect element.

The svg element defines that we're creating an SVG image, with a width and height of 150px, and a viewBox that matches that. If you're unfamiliar with viewBox, it defines the coordinate system for the SVG image. When hand coding SVG, I find it easiest to do exactly what I've done above, and keep all of these numbers the same (150px, in this case). This makes SVG generally a little easier to work with.

If you want to learn more about SVG coordinate systems, take a look at Sara Soueidan’s excellent article on the subject

With that in mind, we also have a rect element, which will draw a rectangle for us. This rect will be 100px square, and it will start 25px down and to the right of the top left corner (that's what the x and y attributes do, they reposition based on the top left corner). Then we have the rx attribute, which rounds the corners of the rectangle.

Those three lines of code, in practice, look like this:

Not bad, but for a spirograph, we need an outline of the rectangle. For this we need to take advantage of two more attributes, stroke and fill. I can add these two attributes to my rect, like this:

<rect x="25" y="25" rx="10" width="100" height="100" stroke="red" fill="none" />

Now here's where we get tricky. For a spirograph we want to repeat the same element again and again, and the only thing we want to change is the rotation of that element. We could copy/paste and change what we like, which is what you would do in Illustrator, but when hand-authoring SVG there's a better way available to us.

If we want to reuse an element in SVG, there's an element for that: <use>. It's pretty simple to do, we have to add an ID to the element we want to reuse, and add a use element that references that ID, like this:

<rect id="base-shape" x="25" y="25" rx="10" width="100" height="100" stroke="red" fill="none" />
<use xlink:href="#base-shape" transform="rotate(18)" />

The best thing about this technique is that it then becomes effortless to change your spirograph to whatever shape you want. All you have to do is change that base element, and every other element will follow. For a simple example, we can change the border radius of the whole spirograph by changing one number:

<rect ... rx="40" ... />

Cool. Now that we have the framework set up, it's time to get to work.

One problem here that you may have noticed: the rectangles aren't rotated from a central point. If we try to add more rectangles, it won't look like a spirograph, it will look like this:

The problem here is that these rectangles are rotating from the top left corner, and instead we want them to rotate from the center. To do this we can use a CSS attribute called transform-origin. This will allow us to rotate around the center of the rectangles, rather than the top left. After adding that our example looks like this:

Hey, that looks pretty neat! Let's try it again, but with less border radius:

It's simple, but the results are nice, and the code is understandable. Here's the full code of this final example for your consideration:

<svg width="150" height="150" viewBox="0 0 150 150" class="ex-4">
<style>
.ex-4 use {
transform-origin: 50% 50%;
}
</style>
<rect id="base-shape-4" x="25" y="25" rx="20" width="100" height="100" stroke="red" fill="none" />
<use xlink:href="#base-shape-4" transform="rotate(18)" />
<use xlink:href="#base-shape-4" transform="rotate(36)" />
<use xlink:href="#base-shape-4" transform="rotate(54)" />
<use xlink:href="#base-shape-4" transform="rotate(72)" />
</svg>

I chose to use five shapes that are each rotated by increments of 18deg. This is adjustable though, you can really rotate your shapes by any number. It looks best if your rotation angle is able to evenly divide the number 90 though, so everything is spaced out evenly.

Here's the same example with nine shapes, rotated by ten degrees each:

Tons of flexibility in this technique!

Let’s Try Some Different Shapes! #

Now let’s have some fun. We have a basic framework for a simple line spirograph above, now we should be able to create endless variations by tweaking a few things here and there. Here are a few ideas:

It might be a stretch to call some of these "spirographs", but the idea is the same, and with this technique, they're quite simple to make.

Each one of the techniques above uses a slightly different technique. The first example uses an ellipse element:

<ellipse cx="75" cy="75" rx="60" ry="15" stroke="red" fill="transparent" stroke-width="2"/>

The second uses a zig-zag path, and many <use> elements, 19 by my count (I kept duplicating them until it looked right. Sometimes trial and error is the fastest way) Here's what that looks like:

<!-- zig zaggy -->
<polyline points="60 110 65 120 70 115 75 130 80 125 85 140 90 135 95 150 100 145" stroke="red" fill="transparent" stroke-width="3"/>
<!-- 19 `use` elements -->

The third example is a little more complicated. I used two ellipse elements, one of them duplicated from the first example. Then I wrapped them in a g tag and added the duplication ID there, so each <use> element clones both ellipses. Those two ellipses alone look like this:

I liked this idea, but didn't like the weird partially overlapping bits in the middle. So I added a circle with a wide border, to hide the undesirable bits. Here's what that looks like (I turned the border blue here for visibility):

The code:

<svg width="150" height="150" viewBox="0 0 150 150">
<g id="base-shape">
<ellipse cx="75" cy="75" rx="40" ry="7" stroke="red" fill="transparent" stroke-width="1"/>
<ellipse id="base-shape-5" cx="75" cy="75" rx="60" ry="15" stroke="red" fill="transparent" stroke-width="1"/>
</g>
<!-- use elements -->
<circle cx="75" cy="75" r="40" stroke="white" fill="transparent" stroke-width="22.5"/>
</svg>

Those are a few things I discovered using this technique in a couple hours of messing with it. There is so much more to be done with this technique, I hope you give it a shot and let me know what you find! Here's a pen to get you started (you can edit here, or click "Edit on CodePen" to fork and create your own version):

See the Pen Basic SVG Spirograph by Timothy Miller (@webinspect) on CodePen.

← Home

Changelog
  • Attempt to fix css tag 404
  • Add inclusive lang checker, remove non-inclusive language from blog posts
  • Edits and publishes new article about Spirographs
  • Fixes weird bug with code highlighting and adds draft for new article