Remote Software Engineer at Stripe and cellist based out of Ontario. Previously at GitLab. Fascinated with building usable, delightful software.
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 Inspiral Web.
Inspiral Web 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. Inspiral Web 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.
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:
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.
Under the surface, Inspiral Web 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 Inspiral Web 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:
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.
Watching friends, family, and total strangers create new and unusual creations using Inspiral Web was by and large and the most rewarding part of the project. While I usually stuck to simple, elegant designs with pleasing color themes:
… others were much more bombastic:
Check out the gallery for yourself at nathanfriend.com/inspiral-web/gallery.
Inspiral Web turned out to be a much bigger hit than I expected. Since its release (in October of 2014), Inspiral Web 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 Inspiral Web on Microsoft’s Azure platform, which allowed me to quickly scale my server’s hardware to meet the sudden demand.
Inspiral Web was featured by the Adobe Inspire magazine and Chrome Experiments, made appearances on Reddit and Hacker News, and enjoyed a warm reception on Twitter:
If this doesn't make you happy, nothing will: http://t.co/JTFue1zBIc Digital spirograph! (via @SarDSta)
— Jennifer Howard (@JenHoward) October 15, 2014
This is insanely cool: Inspirograph http://t.co/7KbwteLl2G You're welcome.
— Avinash Kaushik (@avinash) October 16, 2014
Yes! @NathanAFriend recreated our favorite childhood toy for the web: the spirograph! Prepare to lose hours to this: http://t.co/LVKMNl9M5U
— Brand New School (@BrandNewSchool) October 16, 2014
digital spirograph
DIGITAL SPIROGRAPH
D*I*G*I*T*A*L S*P*I*R*O*G*R*A*P*H
http://t.co/MIU6fmUn4R
— #MGKmergate (@mightygodking) October 14, 2014
Got things you need to do? Like eat or sleep? Put if off for a while with this gorgeous digital Spirograph. http://t.co/Wca7ZIqVNe
— craig burston (@skipratmedia) October 19, 2014
Other posts you may enjoy:
October 14, 2024 | 3 minutes to read
May 31, 2024 | 6 minutes to read
June 26, 2023 | 14 minutes to read
January 25, 2022 | 6 minutes to read
August 26, 2021 | 2 minutes to read