A jQuery + Canvas Scratch Off

I’ve worked on Flash scratch off card applications for many years, and the other day I wondered if it was possible to get the same effect without Flash. I’m a huge jQuery fan — it never fails to make my JavaScript coding easier, but I know that jQuery doesn’t have the ability alone to simulate such an effect. This lead me to the red-headed stepchild element in the HTML universe, <canvas>. The canvas element has always been one of those tags that I’ve known about but never really gotten into, mainly because there’s so much that it can do that it’s more than just your average HTML element. In a nutshell, the canvas element allows you to draw using JavaScript. Perfect. Now all I had to do was learn canvas.

Let me say that canvas support is better than ever with all major browsers now supporting it. In the past this was always a weak point for canvas as IE8 had no canvas support (without hacks), so the case for using canvas was limited. Until now. Unfortunately the documentation for canvas is a little fragmented and getting a good grip on all the nuances and features can be a bit frustrating. But this article isn’t about me teaching you canvas, it’s about how canvas can be used to build a scratch off card without having to use a lick of Flash.

What shocked me the most was how basic the final code I came up with was that accomplished the effect. The Actionscript for a Flash scratch off is no walk in the park, but the canvas version is very neat and clean. In fact, the function that does the heavy lifting is just six lines long. Six lines! And the weird part there is that none of the savings happens to come from jQuery. jQuery does show its strength in the rest of my code by handing the various mouse movements and DOM manipulations, in a total of about 45 lines of code. Awesome.

So I present to you a jQuery/Canvas scratch off that requires no Flash. Move your mouse over the scratch card and you’ll see a coin follow your mouse. Click and drag and you’ll soon be on your way to scratching to reveal the prize below the top layer. Voilà!

How it Works

Instead of a line by line breakdown of the code I’m going to give an overview of the entire process. We start by loading the images we’ll be using via JavaScript. Then we place a <canvas> tag on our page with the width and height set to match the dimensions of the images we’ll be using for our top and bottom layers (the top layer refers to the layer that would be scratched off on a real scratch off card and the bottom layer is what would be revealed). When the page loads, we begin the bulk of our scripting. We create and append a second “overlay” canvas element on top of the original canvas to hold our coin image. We tie listeners for the mousedown, mouseup, and mousemove events to this overlay canvas so that our coin image follows the mouse whenever it moves and that the scratch off effect is triggered only when the mousedown event occurs.

The mousedown event is where the scratch effect takes place. This bit of code comes from a trick I borrowed from my Flash scratch off creation days. While it looks like you’re removing parts of the “top” layer to reveal the “bottom” layer, you’re actually drawing parts of the bottom layer on top of the top layer using the canvas element’s clip() method. Basically we’re drawing an arc (circle) and then clipping it to unmask, or reveal, part of the bottom layer image which is then drawn over the top image on our main canvas. In other words, we first draw our circle shape and then combine that with the bottom image so that only that part of the bottom image is used. We then apply that clipped image and draw it onto our main canvas, thus creating the scratch off effect. We repeatedly save and restore our clipping path to build it up as we continually click and drag. Did I mention that the effect takes just six lines of code?!

Unfortunately neither the canvas element nor jQuery have any built-in collision detection like Flash does. If either one did then this would have allowed me to find out how much scratching someone had done and complete the process for them when they had scratched enough. For example, if they had scratched away 80% of the top layer (actually drawing 80% of the bottom layer but let’s not split hairs) then we could remove the rest of the little pieces automatically. Not a necessity by any means, but it would have been a nice touch. If anyone knows of an easy way to do this that doesn’t add tons of code or eat memory then let me know.

So there you have it. All you need are three images (top layer, bottom layer, and coin) and this code. You could conceivable get rid of the overlay canvas and coin, however I’ve found it a little more user-friendly to have the coin in there so that the user doesn’t just see the mouse cursor poking holes in the image. You could also use PHP to dynamically create the bottom image so that it randomly changed.


This code is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) (http://creativecommons.org/licenses/by-nc-sa/3.0/). For commercial use, please email admin@catchmyfame.com.

10 thoughts on “A jQuery + Canvas Scratch Off”

  1. Since your coin has a fixed size, you could calculate the amount of scratching based on that: increment a variable by one each time a new coin-sized part is ‘revealed’, and when it reaches a certain threshold, you fade out the rest. (although, you’d have to build in some sort of position detection, ie only increment the counter if the position of your coin isn’t on a previous position. Maybe an array of hashes with cooordinates would be more appropiate)

  2. I’m doing something very similar for a mobile creative for Oreo cookie. In that unit, the screen is covered with fudge and the user has to wipe off at least 70% of the screen in order to reveal the page underneath.

    To accomplish this 70% functionality, I’m basically keeping track of the number of pixels the user has uncovered. Everytime part of the scratch-off is uncovered, I loop through the x and y coordinates and use them as indexes in a two-dimensional array. If the value it returns is undefined, then I know it’s a new entry and I can increment my “revealed” counter. If it’s set to true, then I know the user has already uncovered it and I don’t need to increment the counter. Once my counter reaches 70% or more of the total number of pixels on the scratch-off card, I run a function that fades out the rest of the fudge and hides the canvas to reveal the page underneath.

    It works very well on desktop browsers. However, I’m still working out some of the bugs for mobile devices. I think it might have to do with my canvas tag implementation, so this article might be the answer I was looking for. Thanks!

  3. Great, I am looking to launch a contest to give away some amazon gift cards. So I am trying to see what the best approach is.

    I would have to manipulate the images in order for one to display a winner one but this could generate all cards to be given away at once if several people scratches at the same time.

    This approach would also required for the user to take a screen shot of the image so is not that automated.

    But overall, great script!

  4. I am so glad to have discovered this page! It worked perfectly for my needs.

    Now, here’s how I took care of detecting how much of the page has been uncovered. I picked an arbitrary increment (say, 25 pixels) and visualized that the area consisted of 25×25-pixel squares. Within the initialization routine, I created a two-dimensional global array, initialized to “false,” and count the number of cells in it. A count of the number of squares filled is initially zero.

    Within the “ScratchOff” routine, take the (x,y) coordinate and divide by (say) 25, truncating to an integer (“caution! see below!”). If that array-element is still ‘false,’ the cell is being visited for the first time: set it to ‘true,’ increment the count of cells uncovered, and recalculate the percent. (Do anything else you need to do, there.)

    One very(!) interesting finding: the standard Android “internet” browser-app DOESN’T IMPLEMENT “Math.trunc()!” :-O

    But, this works:
    function trunc(x) {
    return x >> 0;

Leave a Reply

Your email address will not be published. Required fields are marked *