In my last post I shared my Paper Snowflake web app, which allows users to create and share virtual paper snowflakes. There is a significant amount of JavaScript code behind the application, and I’ll take a couple blog posts to explain some of the inner workings. Here I’ll concentrate on how multiple HTML5 canvases are used together to handle drawing and interactivity.

If you haven’t had the chance yet, give the application a try! Since the last post, I’ve added the ability to change background colors. Click here or on the screencap below to open up the application. After you make a snowflake, you can share the image to your computer or share it on Facebook. (It will work on mobile devices, but it’s still not optimized for touch events. That will have to wait for a future version.)

paper snowflake app screencap 2

You can also see this flickr set for some of the snowflakes I created with this app.

Using multiple canvases

Although the application is rather simple looking on the surface, there are actually several canvases in use, some on-screen and some off-screen. These canvases are placed on top of each other, or used to cut out shapes from other canvases by changing the globalCompositeOperation. In total there are ten different canvas instances used for various purposes, plus an array of six canvases containing the differently colored gradient backgrounds.

The interactive board

The interactive board (on the right side of the application) containing the folded paper triangle shape is made up of three canvases of equal size layered on top of each other. Layering the canvases is easy using CSS by using absolute positioning and different z-index values, and placing these canvases in one div that can be positioned as desired.

  • boardBackgroundCanvas: A colored background canvas, which only changes when the user selects a different background color.
  • polygonLayerCanvas: This canvas contains the polygon outlines as they are drawn by the user. The graphics consist of vertices (dots) and lines only. The filled in color is drawn into the boardCanvas below.
  • boardCanvas: A canvas containing the cut out triangle shape, which is white with a transparent background.

Here is an illustration of these three canvases:

paper snowflake thumbnails

When the user draws new polygons on the board (or edits existing polygons by dragging vertices), the cut-out shape is accomplished by drawing an opaque polygon onto the boardCanvas after setting the globalCompositeOperation to “destination-out”, which preserves existing content in the canvas where it doesn’t overlap drawn image.

The snowflake

When the user presses the “make snowflake!” button, several things happen to draw the snowflake on the left. In total, six canvases are used: two on-screen and four off-screen. These canvases are used together to create the snowflake with a folded-paper shading effect and colored background.

On-screen, a displayBackgroundCanvas holds a gradient-colored background (explained below), and on top of this is a displayCanvas holding the snowflake image and its shadow with a transparent background.

Off-screen, a snowflake shape is created by copying the cut-out triangle shape from the interactive board twelve times into a snowflakeShapeCanvas, using appropriate translations, rotations, and scaling transformations (the geometry of these transformations will be the subject of a future blog post). Once this off-screen snowflake shape is created, this shape is used to “cut out” the same shape from a textured and shaded paper image which is stored in a separate off-screen paperShadingCanvas (this shading is created programmatically – see below). This time we use the “destination-in” globalCompositeOperation, which preserves existing content in the canvas only wherever it overlaps the newly drawn image. We first draw the paperShadingCanvas into an off-screen bufferCanvas, set the globalCompositeOperation to “destination-in”, then draw the snowflakeShapeCanvas onto the bufferCanvas. See the illustration below:

paper snowflake thumbnails

Once we have this properly shaded snowflake stored in the buffer canvas off-screen, we assemble the on-screen snowflake image by drawing a shadow in another off-screen canvas first (explained below), then drawing this shadow and the shaded snowflake from the buffer into the on-screen displayCanvas. Behind this displayCanvas is a displayBackgroundCanvas, which contains the gradient colored background.

The shadow

Although the HTML5 canvas has built-in methods for drawing shadows, at the time of this writing it doesn’t work with bitmaps with transparency in current version of Chrome due to a bug (see here). So for cross-browser consistency I decided to draw my own shadows. Here is where the StackBlur for Canvas by Mario Klingemann (a.k.a. quasimondo) came to the rescue! (I changed the code slightly so it would take a canvas context as an argument rather than a canvas object). I wrote a makeShadow() method which copies the snowflakeShapeCanvas to a new canvas, while changing the color to a semi-transparent gray shadow color. This canvas is then blurred using quasimodo’s StackBlur. This shadow canvas can then be copied into the on-screen display canvas with an offset, creating the shadow effect behind the snowflake.

The paper shading

This web app uses no images loaded at runtime. The paper shading is drawn programmatically at the beginning and stored in an off-screen paperShadingCanvas, using the following method that I came upon after some experimentation, and which again uses quasimondo’s StackBlur:

  • Gray wedges drawn of various shadings to suggest folded paper,
  • noise is added to this image by randomly shifting pixel colors
  • The image is blurred with StackBlur by Quasimodo
  • more noise (subtler this time) is added.

The gradient backgrounds

The background colors behind the snowflake may look solid, but they are actually subtle gradients, which I found to look nicer. But the gradients are subtle enough that a “banding” effect could be noticeable. So I used my dithered gradients for the HTML5 canvas that I posted at this blog here. (See the radial version here, and dithered gradients for Processing here).

Since the code for drawing these gradients is computationally intensive (although still rather fast), the gradient backgrounds are drawn once at the beginning of the application and stored in an array of canvases, then drawn into the displayBackgroundCanvas as needed when the user selects a different color.

More to come!

There is a lot more going on in this application, and I plan on blogging about more of the code in the future. In particular, the use of transformations to create the snowflake symmetries, plus the code used for image saving and Facebook sharing are worthy of further discussion. Stay tuned!

Acknowledgment

My thanks again to Mario Klingemann (a.k.a. quasimondo) for his excellent StackBlur for Canvas!