HTML 5 Canvas Lessons: Zooming and Reusing
I noticed an interesting bug today while working on a browser-based user interface rendered with the HTML 5 <canvas> element. A little rectangle was appearing fuzzy:
![]()
Let me zoom in a bit:

Whereas it should have appeared crisp, as in this:

My first thought was that it would be due to fractional coordinates. I have years of experience with drawing APIs that force integer coordinates, so I’m used to having the fractional part of a coordinate whacked off and making up the difference when necessary in a second pass. Canvas, on the other hand, supports fractional coordinates, which I’m told is the fancy thing to do these days. (How the fraction is converted to an actual pixel is depenendent on whatever drawing system is doing the heavy lifting somewhere down the stack.) When your coordinates are fractional, you can get this kind of fuzziness.
Because the interface I’m working with involves a few layers of rendering code, ensuring that integers ruled the roost took some time. (I did this by wrapping the various canvas functions with versions that dumped their parameters before calling back to the real functions.) But after quite a bit of poking around, I found no evidence of fractional coordinates. It was around this time I saw Vlad (Mozilla’s graphics guru) walking around the office and asked for some help.
We started looking for evidence of transforms that would introduce fractional coordinates–but ultimately came up empty handed. As we went through this process, he pointed out that the <canvas> context instances are reused, so it’s a really good idea to save() and restore() when obtaining a canvas to avoid polluting the context:
var ctx = canvas.getContext("2d");
ctx.save();
// painting here
ctx.restore();
I had assumed each call to getContext() produced a fresh, stateless context, so this was welcome news indeed.
But, we didn’t find a source for the fractional coordinates. And then we noticed when right-clicking and selecting “View Image” from the context menu, the resulting image that was physically smaller than the browser window.
And that’s when we noticed that I had zoomed in a click using Firefox 3’s fancy full page zoom feature, which was causing the image the be scaled up, and the blurriness.
May you avoid a similar fate.





[...] The debugging exersize was fun, and he shares it with you on his personal blog. [...]
Ajaxian » Watch out for the zoom; Debugging fun with Canvas
January 7, 2009 at 5:49 am
Thanks for the tip.
If you don’t mind me asking, what’s the project?
And what proportion of it is in canvas as opposed to HTML?
Heck, while I’m at it, what browsers are you targeting for it?
enefekt
January 7, 2009 at 5:57 am
The project targets any canvas-equipped browser, but I don’t want to spoil the surprise–I’ll talk more about it next week.
Ben Galbraith
January 7, 2009 at 7:16 am
Hi Ben,
I came across you post looking for “crisp canvas lines”. You describe the problem I’m facing, but zooming appears not to be the cause of my problem.
If you like, please take a look at the following example:
var ctx = document.getElementById(“canvas”).getContext(“2d”);
ctx.save();
// not crisp
ctx.moveTo(33,10);
ctx.lineTo(33,90);
ctx.stroke();
// crisp
ctx.fillRect(66,10,1,80);
ctx.restore();
When I use the “default” way of drawing lines, I get the impression that two anti-aliased(?)/semi-transparent lines are drawn right next to each other, resulting in a 2px dark-grey line, where I would expect to be able to draw a crisp 1px black line.
It almost looks like your first example and I would expect it to look like your expected example. The only way I can draw these crisp lines, is by using the fillRect method to fill a rect of width 1.
Do you know if this behaviour is “by design” ? How do you go about drawing these crisp lines ?
Thanks a lot in advance.
Christophe VG
April 1, 2009 at 4:07 am
Time to blame myself for not reading the documentation available: https://developer.mozilla.org/en/Canvas_tutorial/Applying_styles_and_colors#A_lineWidth_example nicely explains why this is happening and how to resolve it.
The result boils down to:
var ctx = document.getElementById(“canvas”).getContext(“2d”);
ctx.save();
// not crisp
ctx.moveTo(25,10);
ctx.lineTo(25,90);
ctx.stroke();
// one way to solve it
ctx.fillRect(50,10,1,80);
// the right way to solve it
ctx.moveTo(75.5,10);
ctx.lineTo(75.5,90);
ctx.stroke();
ctx.restore();
Christophe VG
April 3, 2009 at 2:28 am
[...] we started googling, but even then nothing really turned up. Until finally I found a blogpost by Ben Galbraith. The post seemed to describe the symptoms we encountered, but the cause was a different one – we [...]
Drawing Crisp Lines on the HTML5 Canvas « TheSoftwareFactory
April 14, 2009 at 11:15 am
I had a similar experience when I specified the canvas width with css rather than on the canvas object, making me wonder why the object on the canvas didn’t seem to be the number of pixels specified, with some fuzzy lines
Joshua Koo
December 8, 2009 at 10:19 pm