Getting Carried Away with Canvas
January 27, 2015
Despite having going too far on a few projects with lots of visual stuff going on, I recently wrestled with what was supposed to be the simple task of dropping some rotating spheres from a promo video onto a splash page. There were late night rewrites and hard-learned lessons, so I’ll walk you through what it took to make this happen.
Here’s the vision: Shaded spheres spin in place on top of a semi-transparent overlay. The quadrants of the spheres are either fully opaque, punch out the overlay so the background shows through, or are invisible. I know. That makes no sense. See below.
Step 1: Choose the technology
Feel free to brainstorm your own approach, but as for technology, it’s either SVG or Canvas. Here are the tradeoffs:
SVG: Vector graphics. Lines and shapes are the discrete entities. Performance scales with the number of objects. You get clipping masks and blending modes, but there’s a concern SVG may choke if it tries to rasterize a bunch of circles over and over. Verdict: Probably possible, but potentially problematic.
Canvas: Raster graphics. It’s all pixels. Performance scales with the size of the canvas. You can do blending modes and pixel effects like you would in Photoshop. The killer feature is being able to rasterize the circles once and then just add and subtract them intelligently to achieve the sphere effect. Verdict: Definitely possible and unlikely to be less performant than SVG.
So that’s it. There’s way more subtlety to performance than is captured by my sweeping generalizations, but this step is all about knowing the difference enough to make an executive decision and go with it. In the end, canvas felt safer because there’s more room for low-level optimization.
Step 2: Do the math
Viewed from the side like this, these spheres are just composed of ellipses and circles. Don’t worry; I did the math just to be sure. Here’s how I’m breaking it down in my head:
That’s all there is to it. Down below you’ll see how we can use canvas buffers and canvas’s blending modes (think: Photoshop’s multiple, subtract, overlay, etc. layer modes) to pull this off. As for figuring out where these ellipses need to be, I’ll do you a favor and spare you the convoluted logic it takes to actually get the job done.
Step 2.5: Do your homework
If you only take away one thing about drawing with canvases, let it be this:
Draw shapes like lines, paths, and circles as rarely as possible. If you can, draw them once on an offscreen canvas and use
drawImageto spit them out (pixel-for-pixel, if you can) onto a canvas on the page.
No, seriously. I’m not advocating premature optimization for everything, but the approach below is the result of a last-minute late-night rewrite because once I got this all working with the obvious method of drawing arcs on every frame, I found that Safari completely 100% locked up because it couldn’t keep up with the number of arcs I was rasterizing on each frame.
Step 3: Draw it
Enough talk. Here’s how the pieces come together. The only step I’ve left a little vague is adding and subtracting the half-circles to get the moon phases in the second step. But that’s mostly just more bookkeeping. As for the other steps, you can play with the blending modes to see how it changes the result.
What did we learn?
Lots of possibilities here. One you wrap this stuff inside an animation running at 60 fps it tends to become a real cpu hog, but it’s great for small animations and widgets—apart from the setup at least. To help out that part, I’m slowly putting together a library of functions to facilitate the process. You can get the source code here: