This is an issue with the Chrome text engine.
There is a technique you can use to get around this though:
- Setup an off-screen canvas double the size of the on-screen canvas
- Draw the text to the off-screen canvas in scale: font size and position
- Draw the off-screen canvas to main canvas scaled down (half in this case).
The difference is subtle (as expected) but improves the quality. Here is an enlargement:
Sidenotes: CSS does not affect content of the canvas, only elements. Image smoothing is enabled by default and affects only images but not text or shapes (we will use this though for this technique).
var scale = 2; // scale everything x2.
var ocanvas = document.createElement('canvas');
var octx = ocanvas.getContext('2d');
ocanvas.width = canvas.width * scale; // set the size of new canvas
ocanvas.height = canvas.height * scale;
// draw the text to off-screen canvas instead at double sizes
octx.fillStyle = "BLACK";
octx.font = "bold " + (100 * scale) + "px Arial";
octx.fillText("A quick brown fox jumps over the lazy dOg", 50*scale, 200*scale);
// key step is to draw the off-screen canvas to main canvas
context.drawImage(ocanvas, 0, 0, canvas.width, canvas.height);
What happens is that we are introducing interpolation in the mix here. Chrome will draw the text very rough also to the off-screen canvas, but after the text becomes rasterized we can deal with it as an image.
When we draw the canvas (aka image) to our main canvas at half the size, interpolation (sub-sampling) kicks in and will low-pass filter the pixels giving a smoother look.
It will of course take more memory but if result is important that is a small price to pay nowadays.
You will probably notice that other values than 2 doesn't work so well. This is because the canvas typically uses bi-linear interpolation rather than bi-cubic (which would allow us to use 4). But I think 2x serves us well in this case.
Why not using transform, ie. scale()
? Chrome's text engine (or the way it's used) does not rasterize the text at 1x and then transforms it. It takes the vectors, transforms them and then rasterize the text which will give the same result (ie. scale 0.5, draw double).