You are here: O'Reilly Media ‣ CSS & HTML ‣ Dive Into HTML 5 ‣ Let's Call It a Draw(ing Surface)
⁂
TML 5 defines the <canvas> element as “a resolution-dependent bitmap canvas which can be used for rendering graphs, game graphics, or other visual images on the fly.” A canvas is a rectangle in your page where you can use JavaScript to draw anything you want.
| IE8* | IE7* | FF3.5 | FF3.0 | Saf4 | Saf3 | Chr3 | Op10 |
|---|---|---|---|---|---|---|---|
| ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| * Internet Explorer support requires the third-party explorercanvas library. | |||||||
So what does a canvas look like? Nothing, really. A <canvas> element has no content and no border of its own.
↜ Invisible canvas
The markup looks like this:
<canvas width="300" height="225"></canvas>Let’s add a dotted border so we can see what we’re dealing with.
↜ Canvas with border
You can have several <canvas> elements on the same page. Each canvas will show up in the DOM, and each canvas maintains its own state. If you give each canvas an id attribute, you can access them just like any other element.
Let’s expand that markup to include an id attribute:
<canvas id="a" width="300" height="225"></canvas>Now you can easily find that <canvas> element in the DOM.
var a_canvas = document.getElementById("a");⁂
| IE8* | IE7* | FF3.5 | FF3.0 | Saf4 | Saf3 | Chr3 | Op10 |
|---|---|---|---|---|---|---|---|
| ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| * Internet Explorer support requires the third-party explorercanvas library. | |||||||
Every canvas starts out blank. That’s boring! Let’s draw something.
⇜ Click to draw on this canvas
The onclick handler called this function:
function draw_b() {
var b_canvas = document.getElementById("b");
var b_context = b_canvas.getContext("2d");
b_context.fillRect(25,25,100,100);
}The 1st line of the function is nothing special; it just finds the <canvas> element in the DOM.
And then there’s this ⇝
function draw_b() {
var b_canvas = document.getElementById("b");
var b_context = b_canvas.getContext("2d");
b_context.fillRect(50, 25, 150, 100);
}
Every canvas has a drawing context, which is where all the fun stuff happens. Once you’ve found a <canvas> element in the DOM (by using document.getElementById() or any other method you like), you call its getContext() method. You must pass the string "2d" to the getContext() method.
☞Q: Is there a 3-D canvas?
A: Not yet. Individual vendors have experimented with their own three-dimensional canvas APIs, but none of them have been standardized. The HTML 5 specification notes, “A future version of this specification will probably define a 3d context.”
You have a <canvas> element, and you have its drawing context. The drawing context is where all the drawing methods and properties are defined. There’s a whole group of methods devoted to drawing rectangles:
fillStyle property can be a CSS color, a pattern, or a gradient. (More on patterns and gradients shortly.) The default fillStyle is solid black, but you can set it to whatever you like. Each canvas remembers its own properties as long as the page is open.fillRect(x, y, width, height) draws a rectangle filled with the current fill style.strokeStyle property is like fillStyle — it can be a CSS color, pattern, or gradient.strokeRect(x, y, width, height) draws an rectangle with the current stroke style. strokeRect doesn’t fill in the middle; it just draws the edges.clearRect(x, y, width, height) clears the pixels in the specified rectangle.Getting back to that code sample in the previous example…
Draw a rectangle ⇝
var b_canvas = document.getElementById("b");
var b_context = b_canvas.getContext("2d");
b_context.fillRect(50, 25, 150, 100);Calling the fillRect() method draws the rectangle and fills it with the current fill style, which is black until you change it. The rectangle is bounded by its upper-left corner (50, 25), its width (150), and its height (100). To get a better picture of how that works, let’s look at the canvas coordinate system.
⁂
The canvas is a two-dimensional grid. The coordinate (0, 0) is at the upper-left corner of the canvas. Along the X-axis, values increase towards the right edge of the canvas. Along the Y-axis, values increase towards the bottom edge of the canvas.
Canvas coordinates diagram ↷
That coordinate diagram was drawn with a <canvas> element. It comprises
First, we need to define the <canvas> element itself. The <canvas> element defines the width and height, and the id so we can find it later.
<canvas id="c" width="500" height="375"></canvas>Then we need a script to find the <canvas> element in the DOM and get its drawing context.
var c_canvas = document.getElementById("c");
var context = c_canvas.getContext("2d");Now we can start drawing lines.
⁂
| IE8* | IE7* | FF3.5 | FF3.0 | Saf4 | Saf3 | Chr3 | Op10 |
|---|---|---|---|---|---|---|---|
| ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| * Internet Explorer support requires the third-party explorercanvas library. | |||||||

Imagine you’re drawing a picture in ink. You don’t want to just dive in and start drawing with ink, because you might make a mistake. So you sketch lines and curves with a pencil, and once you’re happy with it, you trace over your sketch in ink.
Each canvas has a path. Defining the path is like drawing with a pencil. You can draw whatever you like, but it won’t be part of the finished product until you pick up the quill and trace over your path in ink.
To draw straight lines in pencil:
moveTo(x, y) moves the pencil to the starting point.lineTo(x, y) draws a line to an ending point.The more you call moveTo() and lineTo(), the biggest the path gets. These are “pencil” methods — you can call them as often as you like, but you won’t see anything on the canvas until you call one of the “ink” methods.
Let’s draw the off-white grid.
for (var x = 0; x < 500; x += 10) {
context.moveTo(x, 0);
context.lineTo(x, 375);
}⇜ Draw vertical lines
for (var y = 0; y < 375; y += 10) {
context.moveTo(0, y);
context.lineTo(500, y);
}⇜ Draw horizontal lines
Those were all “pencil” methods. Nothing has actually been drawn on the canvas yet. We need an “ink” method to make it permanent.
context.strokeStyle = "#eee";
context.stroke();stroke() is one of the “ink” methods. It takes the complex path you defined with all those moveTo() and lineTo() calls, and it actually draws it on the canvas. The strokeStyle controls the color of the lines, and this is the result:
Now let’s draw the horizontal arrow. All the lines and curves on a path are drawn in the same color (or pattern, or gradient — yes, we’ll get to those soon). We want to draw the arrow in a different color ink — black instead of off-white — so we need to start a new path.
A new path ↷
context.beginPath();
context.moveTo(0, 40);
context.lineTo(240, 40);
context.moveTo(260, 40);
context.lineTo(500, 40);
context.moveTo(495, 35);
context.lineTo(500, 40);
context.lineTo(495, 45);The vertical arrow looks much the same. Since the vertical arrow is the same color as the horizontal arrow, we do not need to start another new path. The two arrows will be part of the same path.
context.moveTo(60, 0);
context.lineTo(60, 153);
context.moveTo(60, 173);
context.lineTo(60, 375);
context.moveTo(65, 370);
context.lineTo(60, 375);
context.lineTo(55, 370);↜ Not a new path
I said these arrows were going to be black, but the strokeStyle is still off-white. (The fillStyle and strokeStyle don’t get reset when you start a new path.) That’s OK, because we’ve just run a series of “pencil” methods. But before we draw it for real, in “ink,” we need to set the strokeStyle to black. Otherwise, these two arrows will be off-white, and we could hardly see them!
context.strokeStyle = "#000";
context.stroke();And this is the result:
⁂
| IE8* | IE7* | FF3.5 | FF3.0 | Saf4 | Saf3 | Chr3 | Op10 |
|---|---|---|---|---|---|---|---|
| ✓ | ✓ | ✓ | ✗ | ✓ | ✗ | ✓ | ✗ |
| * Internet Explorer support requires the third-party explorercanvas library. | |||||||
You can draw lines on a canvas. You can also draw text on a canvas. Unlike text on the surrounding web page, there is no box model. That means none of the familiar CSS layout techniques are available: no floats, no margins, no padding, no word wrapping. (Maybe you think that’s a good thing!) You can set a few font attributes, then you pick a point on the canvas and draw your text there.
The following font attributes are available on the drawing context:
font can be anything you would put in a CSS font rule. That includes font style, font variant, font weight, font size, line height, and font family.textAlign controls text alignment. It is similar (but not identical) to a CSS text-align rule. Possible values include start, end, left, right, and center.textBaseline controls where the text is drawn relative to the starting point. Possible values are top, hanging, middle, alphabetic, ideographic, or bottom.textBaseline is tricky, because text is tricky. Well, English text is not tricky, but you can draw any Unicode character you like on a canvas, and Unicode is tricky. The HTML 5 specification explains the different text baselines:
The top of the em square is roughly at the top of the glyphs in a font, the hanging baseline is where some glyphs like आ are anchored, the middle is half-way between the top of the em square and the bottom of the em square, the alphabetic baseline is where characters like Á, ÿ, f, and Ω are anchored, the ideographic baseline is where glyphs like 私 and 達 are anchored, and the bottom of the em square is roughly at the bottom of the glyphs in a font. The top and bottom of the bounding box can be far from these baselines, due to glyphs extending far outside the em square.
For simple alphabets like English, you can safely stick with top, middle, or bottom for the textBaseline property.
Let’s draw some text! The default font is 10px sans-serif, which is a touch too small for my taste. Let’s increase that to 12px and make it bold. We do that by setting the font property on the drawing context.
context.font = "bold 12px sans-serif";
context.fillText("x", 248, 43);
context.fillText("y", 58, 165);↜ Change the font style
The fillText() method draws the actual text.
context.font = "bold 12px sans-serif";
context.fillText("x", 248, 43);
context.fillText("y", 58, 165);⇜ Draw the text
☞Q: Can I use relative font sizes to draw text on a canvas?
A: Yes. The default font is10px sans-serif. Sizes inemor percentages are measured against the default font.
For the text in the upper-left corner, I want the top of the text to be at y=5. But I’m lazy — I don’t want to measure the height of the text and calculate the baseline. Instead, I can set the textBaseline to top and pass in the upper-left coordinate of the text’s bounding box.
context.textBaseline = "top";
context.fillText("( 0 , 0 )", 8, 5);For the text in the lower-right corner, I want to be lazy again. I want the bottom-right corner of the text to be at coordinates (492,370) — just a few pixels away from the bottom-right corner of the canvas — but I don’t want to measure the width or height of the text. I can set textAlign to right and textBaseline to bottom, then call fillText() with the bottom-right coordinates of the text’s bounding box.
context.textAlign = "right";
context.textBaseline = "bottom";
context.fillText("( 500 , 375 )", 492, 370);And this is the result:
Oops! We forgot the dots in the corners. I’m going to cheat a little and draw them as rectangles. We’ll see how to draw circles a little later.
context.fillRect(0, 0, 3, 3);
context.fillRect(497, 372, 3, 3);⇜ Draw two “dots”
And that’s all she wrote!
⁂
| Feature | IE8* | IE7* | FF3.5 | FF3.0 | Saf4 | Saf3 | Chr3 | Op10 |
|---|---|---|---|---|---|---|---|---|
| linear gradients | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| radial gradients | ✗ | ✗ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| * Internet Explorer support requires the third-party explorercanvas library. | ||||||||
Earlier in this chapter, you learned how to draw a rectangle filled with a solid color, then a line stroked with a solid color. But shapes and lines aren’t limited to solid colors. You can do all kinds of magic with gradients and patterns.
The markup looks the same as any other canvas.
<canvas id="d" width="300" height="225"></canvas>First, we need to find the <canvas> element and its drawing context.
var d_canvas = document.getElementById("d");
var context = d_canvas.getContext("2d");Once we have the drawing context, we can start to define a gradient. A gradient is a smooth transition between two or more colors. The canvas drawing context supports two types of gradients:
createLinearGradient(x0, y0, x1, y1) paints along a line from (x0, y0) to (x1, y1).createRadialGradient(x0, y0, r0, x1, y1, r1) paints along a cone between two circles. The first three parameters represent the start circle, with origin (x0, y0) and radius r0. The last three parameters represent the end circle, with origin (x1, y1) and radius r1.Let’s make a linear gradient. Gradients can be any size, but I’ll make this gradient be 300 pixels wide, like the canvas.
Create a gradient object ↷
var my_gradient = context.createLinearGradient(0, 0, 300, 0);Because the y values (the 2nd and 4th parameters) are both 0, this gradient will shade evenly from left to right.
Once we have a gradient object, we can define the gradient’s colors. A gradient has two or more color stops. Color stops can be anywhere along the gradient. To add a color stop, you need to specify its position along the gradient. Gradient positions can be anywhere between 0 to 1.
Let’s define a gradient that shades from black to white.
my_gradient.addColorStop(0, "black");
my_gradient.addColorStop(1, "white");Defining a gradient doesn’t draw anything on the canvas. It’s just an object tucked away in memory somewhere. To draw a gradient, you set your fillStyle to the gradient and draw a shape, like a rectangle or a line.
Fill style is a gradient ↷
context.fillStyle = my_gradient;
context.fillRect(0, 0, 300, 225);And this is the result:
Suppose you want a gradient that shades from top to bottom. When you create the gradient object, keep the x values (1st and 3rd parameters) constant, and make the y values (2nd and 4th parameters) range from 0 to the height of the canvas.
x values are 0, y values vary ↷
var my_gradient = context.createLinearGradient(0, 0, 0, 225);
my_gradient.addColorStop(0, "black");
my_gradient.addColorStop(1, "white");
context.fillStyle = my_gradient;
context.fillRect(0, 0, 300, 225);And this is the result:
You can also create gradients along a diagonal.
both x and y values vary ↷
var my_gradient = context.createLinearGradient(0, 0, 300, 225);
my_gradient.addColorStop(0, "black");
my_gradient.addColorStop(1, "white");
context.fillStyle = my_gradient;
context.fillRect(0, 0, 300, 225);And this is the result:
⁂
| IE8* | IE7* | FF3.5 | FF3.0 | Saf4 | Saf3 | Chr3 | Op10 |
|---|---|---|---|---|---|---|---|
| ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| * Internet Explorer support requires the third-party explorercanvas library. | |||||||
Here is a cat:

⇜ An <img> element
Here is the same cat, drawn on a canvas:
A <canvas> element ⇝
The canvas drawing context defines several methods for drawing an image on a canvas.
drawImage(image, dx, dy) takes an image and draws it on the canvas. The given coordinates (dx, dy) will be the upper-left corner of the image. Coordinates (0, 0) would draw the image at the upper-left corner of the canvas.drawImage(image, dx, dy, dw, dh) takes an image, scales it to a width of dw and a height of dh, and draws it on the canvas at coordinates (dx, dy).drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) takes an image, clips it to the rectangle (sx, sy, sw, sh), scales it to dimensions (dw, dh), and draws it on the canvas at coordinates (dx, dy).The HTML 5 specification explains the drawImage() parameters:
The source rectangle is the rectangle [within the source image] whose corners are the four points
(sx, sy),(sx+sw, sy),(sx+sw, sy+sh),(sx, sy+sh).The destination rectangle is the rectangle [within the canvas] whose corners are the four points
(dx, dy),(dx+dw, dy),(dx+dw, dy+dh),(dx, dy+dh).
To draw an image on a canvas, you need an image. The image can be an existing <img> element, or you can create an Image() object with JavaScript. Either way, you need to ensure that the image is fully loaded before you can draw it on the canvas.
If you’re using an existing <img> element, you can safely draw it on the canvas during the window.onload event.
↶ using an <img> element
<img id="cat" src="images/cat.png" alt="sleeping cat" width="177" height="113">
<canvas id="e" width="177" height="113">
<script>
window.onload = function() {
var canvas = document.getElementById("e");
var context = canvas.getContext("2d");
var cat = document.getElementById("cat");
context.drawImage(cat, 0, 0);
};
</script>If you’re creating the image object entirely in JavaScript, you can safely draw the image on the canvas during the Image.onload event.
using an Image() object ↷
<canvas id="e" width="177" height="113">
<script>
var canvas = document.getElementById("e");
var context = canvas.getContext("2d");
var cat = new Image();
cat.src = "images/cat.png";
cat.onload = function() {
context.drawImage(cat, 0, 0);
};
</script>The optional 3rd and 4th parameters to the drawImage() method control image scaling. This is the same image, scaled to half its width and height and drawn repeatedly at different coordinates within a single canvas.
Here is the script that produces the “multicat” effect:
cat.onload = function() {
for (var x = 0, y = 0;
x < 500, y < 375;
x += 50, y += 37) {
context.drawImage(cat, x, y, 88, 56);
}
};
⇜Scale the image
All this effort raises a legitimate question: why would you want to draw an image on a canvas in the first place? What does the extra complexity of image-on-a-canvas buy you over an <img> element and some CSS rules? Even the “multicat” effect could be replicated with 10 overlapping <img> elements.
The simple answer is, for the same reason you might want to draw text on a canvas. The canvas coordinates diagram included text, lines, and shapes; the text-on-a-canvas was just one part of a larger work. A more complex diagram could easily use drawImage() to include icons or graphics.
But wait, there’s more! You can also use canvas properties to transform an image before drawing it.
⁂
Here is the same cat as before, but upside-down:
FIXME