Inspirograph

January 26, 2015  |  6 minutes to read


A quick note for context: this post was stolen from a portfolio site I built a few years back. It’s about my side project Inspirograph.


A drawing generated by Inspirograph
An example of some squiggles generated by Inspirograph.

Inspirograph began as an entertaining and nostalgic way to familiarize myself with D3.js and TypeScript. It quickly grew to be one of the most ambitious personal projects I’ve attempted. Inspirograph forced me to get creative with the mechanics of the user interface; it was of the utmost importance that manipulating the gears was a tangible, physical, and effortless experience.

Generating the gears

The simplest way to generate the gear visuals would have been to create a series of .PNGs, one for each available size. However, I wanted the flexibility to easily create new gear sizes in the future (and potentially let users create custom gears). This meant programmatically assembling SVG command strings into an SVG path that described the gear’s toothy perimeter. To accomplish this, I had to dust off some of the trigonometry I hadn’t touched since 10th grade Geometry. The result was rather headache-inducing, but ultimately allowed for a great deal of flexibility down the road.

Here’s an example of the 30-tooth gear, one of the smaller gear options:

A gear with 30 teeth

Creating new gears is as simple as passing D3 a new GearOptions object that specifies the gear’s radius, tooth count, and number of holes to punch out of the body of the gear.

Working with paths

Under the surface, Inspirograph assembles its SVG definitions by concatenating string after string of command definitions that describe each angle and line of the gear. In order to avoid directly manipulating strings, I wrote a few simple utility objects to wrap the SVG creation process in a compile-time safety net. So instead of doing this:

var svg = 'M ' + options.radius + ' 0 ';
for (var i = 0; i < options.toothCount; i++) {
    svg += 'A' + options.radius + ' ' + options.radius + ' ' + 0 + ' ' + 0 + ' ' + 1 + ' ' + newX + ' ' + newY;
    // etc....
}

return svg;

… I was able to take advantage of TypeScript’s static typing and write this much safer and less error-prone version:

var pathBuilder = new SVG.PathBuilder(),
pathBuilder.addCommand(new SVG.MCommand(options.radius, 0));
for (var i = 0; i < options.toothCount; i++) {
    pathBuilder.addCommand(new SVG.ACommand(options.radius, options.radius, 0, false, true, newX, newY));
    // etc....
}

return pathBuilder.toString();

I was initially tempted to forgo this extra abstraction and just build the strings by hand, but the extra time it took to write this abstraction quickly paid off when I moved on to more complex SVG drawings (like the Ring Gear and the Beam).

Making it natural

Making Inspirograph easy to use turned out to be much more difficult that I originally thought. Initially, I used a simple click and drag algorithm that based the rotating gear’s position on the mouse’s angle with respect to the center of the screen. This proved to be overly simplistic, however. For example, take this gear combination:

A ring gear with another gear inside it

If the user begins dragging on the left edge of the inner gear, the mouse is actually to the right of the center of the screen. This caused the gear to unexpectedly jump depending on where the user initiated the drag. To solve this, I temporarily “move” the center of the screen relative to the start location of the pointer. Then, as the user begins to drag, I slowly ease this virtual “center” back to the true center of the screen. You can witness this yourself by turning on the “cursor tracker” option by pressing T.

Playing around

Watching friends, family, and total strangers create new and unusual creations using Inspirograph was by and large and the most rewarding part of the project. While I usually stuck to simple, elegant designs with pleasing color themes:

An example of an Inspirograph drawing

An example of an Inspirograph drawing

… others were much more bombastic:

An example of an Inspirograph drawing

An example of an Inspirograph drawing

Check out the gallery for yourself at nathanfriend.io/inspirograph/gallery.

Reception

Inspirograph turned out to be a much bigger hit than I expected. Since its release (in October of 2014), Inspirograph has received over 3,500,000 page hits and more than 100,000 creations have been submitted to the gallery. (Editor’s note: these figures were accurate when this post was originally written in January of 2015. They’re probably larger now.) Fortunately, I host Inspirograph on Microsoft’s Azure platform, which allowed me to quickly scale my server’s hardware to meet the sudden demand.

Inspirograph was featured by the Adobe Inspire magazine and Chrome Experiments, made appearances on Reddit and Hacker News, and enjoyed a warm reception on Twitter:


Other posts you may enjoy:

Shell-ing With TypeScript

June 17, 2018  |  2 minutes to read

My First "Hello, World!"

June 7, 2018  |  5 minutes to read

Live Reloading An Angular 2+ App Behind NGINX

May 14, 2018  |  4 minutes to read

Fantastic Fast Fonts with system-ui

April 27, 2018  |  1 minute to read

Smart Date Searching with Solr

June 26, 2017  |  5 minutes to read