Touching the Canvas
Drawing pictures on the canvas is easy, unless you want to use your fingers. Read this tutorial to figure out how to wrangle the canvas with your own bare hands.
The canvas element in HTML is a brilliant tool which can be used for all sorts of creative projects — Games, art, advertisements — Anything that can be drawn and animated (OK, mainly just those three). You might think that taking a purely programmatic approach to rendering graphics is complicated or difficult — and you might be surprised! As for games, upon which this article you can make an awesome version of Snake in jQuery or Breakout in pure JavaScript in just a couple of hours and publish it to the web, where it will ‘just work’ on any of the hundreds of millions of devices with internet connections across the globe.
This immediate implementation and vast audience make trying out this sort of game development a no-brainer, right? Ah, but wait! There are no W, A, S, D, or arrow keys on a purely touchscreen device (I miss you, G1 and G2). So, how are our players to control their snakes?
Our only recourse is to use what smartphones do best — touch! But if you Google around, you’ll find that there are very few resources available to help out on that front — that’s why I’ve written this article. We’re going to walk through the process of adding a touch interface to the snake game from thecodeplayer.com (if you haven’t heard of this site yet, go check it out right now, it’s friggin’ awesome!).
You might not expect it, but JavaScript actually makes touch controls fairly simple to implement with its TouchEvent API. If you follow that link, you’ll see a handful of useful TouchEvent properties and types included in vanilla JS. Since we’re trying to make the tap controls for Snake (no holding required) we’re just going to be using the touchstart type. OK! Easy peas so far, right?
Enough talk, let’s lay down some lines! If you haven’t yet gone through the Snake tutorial above, go do it now or just copy and paste the code so you can follow along. I’ll wait.
…
…
…
Wow, great job! That game of Snake is possibly the best implementation I’ve ever not seen! But it’s not quite ready for our awesome touch controls. To simplify things for us, we’re going to make our game full-browser, meaning it will take the whole browser window without scrollbars. To achieve this, start by inserting the following lines in your css:
* {
overflow: hidden;
}body {
margin: 0;
}That will remove scrollbars from the view and remove the margins from your <body>, allowing the <canvas> to stretch all the way to the edges of the browser. Now, we’re going to delete the height and width attributes from our canvas element in the HTML — it should now look just like this:
<canvas id='canvas'></canvas>
And in our JS, we’ll change our canvasWidth and canvasHeight to equal the following:
var canvasWidth = canvas.width = document.defaultView.innerWidth;
var canvasHeight = canvas.height = document.defaultView.innerHeight;
These two lines point new variables at the canvas dimensions and then set those dimensions to the browser viewport’s inner height and width. And that’s it for basic setup, now let’s dive into the magic!
You may have noticed that your keyboard controls are handled by jQuery using the $.keydown function. Our touch controls, on the other hand, will be handled using vanilla JavaScript not because it’s impossible to do with jQuery (see the jQ Mobile library for more shiny-ness), but because we want to glean a deeper understanding of the fundamentals of touch events in JavaScript.
Let’s start by adding an event listener to our code.
// Insert this line after your canvas variable declaration
canvas.addEventListener('touchstart', screenTouched, false);
What we’re doing here is adding a listener which, on a touch event, will fire the screenTouched method (I know we haven’t written it yet — patience young padawan). The false at the end is there, basically, to prevent conflicts with multiple event listeners for the same event type on different levels of the DOM — so it’s not super important for us to understand right now.
So far, so good? Great! (Keep in mind you can feel free to ask me any questions you might have at any time by reaching out on Twitter)
The next step is to build our screenTouched function. But, before we do, let’s think about what we’re going to write. It’s a callback from an event listener, so when it is called after a touch event, it will be automatically populated with an ‘event’ object, or ‘e’ for short, as its only parameter. This e object will contain all of the information we need to know about the user’s tap. But before we start grabbing coordinates, let’s start with this:
function screenTouched(e) {
e.preventDefault();
}By calling preventDefault(), we’re telling mobile browsers not to do whatever it is they want to do when a user touches the screen. That means no double tap or pinch to zoom, no scrolling — the browser will do nothing except what we are about to tell it to do.
OK, that’s out of the way, let’s grab the actual location of the user’s touch:
function screenTouched(e) {
e.preventDefault();var x = e.changedTouches[0].pageX;
var y = e.changedTouches[0].pageY;
}
So easy, right? Every finger touched to the screen lives at a different index in the changedTouches array on the event object — what we’re doing here is grabbing the coordinates of the first finger to touch down in pixels, starting at (0, 0) in the top left corner, just like canvas.
This is awesome, we’ve registered a touch event and recorded its location on the canvas, all in under 10 lines of code! I hope you’re ready to get your hands dirty (well, dirtier), because shit’s about to get real.
You may have figured out by now that we can’t just use raw pixel data to determine which direction our user wants their snake to go — we’ll have to process it somehow to figure out the user’s intention, but don’t freak out! the math isn’t THAT complicated to anyone with a basic understanding of linear algebra, and one equation in particular which I’m sure you’ve seen about a thousand times before:
y = mx + b
That’s right, it’s our old friend the slope-intercept equation! How does it relate to our touch problem, you ask? Well, take a look at this picture:

Recognize the coordinate system? It’s just like our canvas! And the cross pattern divides it into the 4 quadrants we want our users to be able to tap to move down (A), right (B), up (C), and left (D). Here’s where the real math starts — we want to calculate which quadrant the user touches using just the information available to us.
So, let’s take a little informational inventory — we’re obviously going to need the X and Y locations of the user touch, but we’ll also need two more key bits of information, the M and the B of the linear formula, also known as slope and intercept. Fortunately, these are trivially easy to calculate!
To start with, if you recall your algebra, you know that the slope of a line starting at the origin (0, 0) is simply Y/X — in our scenario, Width/Height, not vice-versa, since our canvas is rotated 90 degrees clockwise from the classical coordinate system. We’ll go ahead and save this value to a variable immediately, since it won’t change after the game is loaded. Put this line anywhere after your canvasWidth and canvasHeight variable declarations:
var ratio = canvasWidth / canvasHeight;
Remember that the slope of lines going up vs down is just a difference of sign (m to -m), so we now have the slope of both dividing lines! See where we’re going with this, yet?
Figuring out our y-intercepts will be even easier — the first one is zero, obviously, since it starts from the origin, and the second one will be equal to the width of the canvas, for equally obvious reasons.
Now let’s tie all of that information together. Here are our two new equations for our cross-lines:
y = m * x // Line One (top-left to bottom-right)
y = -m * x + w // Line Two (top-right to bottom-left)
And with a little reorganization:
m = y / x
-m = (y - w) / x
Alright, we’re so close to turning this from math into code I can taste it! We’re setting both equations equal to the slope for a very good reason — we already know what it is! That means that when we receive our user touch coordinates, we can now instantly figure out whether it’s above or below each cross-line just by inputting the coordinates and checking against each equation above. If a point at (x, y) results in a value greater than m, we know that the point will be above the line, and vice-versa.
In our code, the above two equations will be the same as:
e.changedTouches[0].pageY / e.changedTouches[0].pageX = ratio
(e.changedTouches[0].pageX - canvasWidth) / e.changedTouches[0].pageY = -ratio
Which we can plug directly into an if/else block! Now, let’s go ahead and put this new algorithm we’ve developed into our screenTouched method, taking game logic from the previous keyboard function:
var ratio = canvasWidth / canvasHeight;
function screenTouched(e) {
e.preventDefault();var x = e.changedTouches[0].pageX;
var y = e.changedTouches[0].pageY;
if (x / y > ratio && (x - canvasWidth) / y > -ratio && d != 'left') {
d = 'right';
} else if (x / y < ratio && (x - canvasWidth) / y < -ratio && d != 'right') {
d = 'left';
} else if (x / y < ratio && (x - canvasWidth) / y > -ratio && d != 'up') {
d = 'down';
} else if (x / y > ratio && (x - canvasWidth) / y < -ratio && d != 'down') {
d = 'up';
}
}And there you have it, we’ve finally got a working game of snake that runs on traditional and touchscreen devices alike! Congratulations, you’ve just quadrupled your target market!
I hope you’ve enjoyed this tutorial and learned a bit about touch events in the browser and in general. If you find any bugs or possible optimizations in my code, please tell me about it! You can find me on Twitter or GitHub at any time.
Thanks for reading!