Subpixel anti-aliased text on HTML5's canvas element
Asked Answered
J

6

53

I'm a bit confused with the way the canvas element anti-aliases text and am hoping you all can help.

In the following screenshot the top "Quick Brown Fox" is an H1 element and the bottom one is a canvas element with text rendered on it. On the bottom you can see both "F"s placed side by side and zoomed in. Notice how the H1 element blends better with the background:

example 'F'

Here's the code I'm using to render the canvas text:

        var canvas = document.getElementById('canvas');
        if (canvas.getContext){

            var ctx = canvas.getContext('2d');
            ctx.fillStyle = 'black';
            ctx.font = '26px Arial';
            ctx.fillText('Quick Brown Fox', 0, 26);
        }

Is it possible to render the text on the canvas in a way so that it looks identical to the H1 element? And why are they different?

Jeremiahjeremias answered 29/12, 2010 at 1:50 Comment(1)
+1 This is a great question! The ugly text on canvas elements is very noticeable to end users because it's in close proximity to "good" text that's elsewhere on the page.Maud
G
33

It's now possible to get sub-pixel font rendering by creating an opaque canvas context. In Safari and Chrome you can get this using this snippet:

var ctx = canvas.getContext("2d", {alpha: false})

I found this from this blog post.

Galloway answered 27/1, 2015 at 0:50 Comment(7)
That really improved the legibility of text in my project. Thanks.Rhyne
Any ideas how to get this to work in FF or IE (recent versions)?Culliton
@sunnymoon: The Adobe blog post says it is a method to get subpixel-antialiasing to canvas text. And it works in Chrome, but not in FF or IE. Test yourself: codepen.io/timo22345/pen/avvOmp. The textbox below should have subpixel-antialiasing.Culliton
@Timo, I'm on FF 40.0.3 and I see LCD subpixel antialiasing with colored edges (aka ClearType) in both canvases. So, it does work in FF, right?Silvey
@sunnymoon: What OS? I tested in OSX.Culliton
@sunnymoon: I tested on native Win7 x64 and it really works: alpha:false -> subpixel-antialiasing, alpha:true -> grayscale-antialiasing. However on virtualized (Parallels) Win7 x64 or on host OSX Mavericks doesn't work (= both are grayscale-antialiased in FF). New updated test: codepen.io/timo22345/pen/YywNyJ.Culliton
This worked for me in Firefox as of this writing. Looks much better. Thanks for the tip.Laceration
J
14

Answering my own question:

It is possible using the technique demonstrated on this site.

The only problem is that its too slow to implement in a production app. If anyone runs across a faster way please let me know.

Jeremiahjeremias answered 29/12, 2010 at 15:45 Comment(1)
Link seems to be broken. I believe this is it, though (via WayBack Machine) : web.archive.org/web/20120714122710/http://www.bel.fi/~alankila/…Rugose
L
9

Matt, I sat with the (same/similar) problem last week, which, in my case, turned out to be because of differences in pixel densities on the devices I was testing; I wrote about it tonight - http://joubert.posterous.com/crisp-html-5-canvas-text-on-mobile-phones-and

The link at posterous is dead, so here is the gist with the source code: https://gist.github.com/joubertnel/870190

And the snippet itself:

  // Output to Canvas without consideration of device pixel ratio
  var naiveContext = $('#naive')[0].getContext('2d');    
  naiveContext.font = '16px Palatino';
  naiveContext.fillText('Rothko is classified as an abstract expressionist.', 10, 20);

  // Output to Canvas, taking into account devices such as iPhone 4 with Retina Display
  var hidefCanvas = $('#hidef')[0];
  var hidefContext = hidefCanvas.getContext('2d');

  if (window.devicePixelRatio) {
    var hidefCanvasWidth = $(hidefCanvas).attr('width');
    var hidefCanvasHeight = $(hidefCanvas).attr('height');
    var hidefCanvasCssWidth = hidefCanvasWidth;
    var hidefCanvasCssHeight = hidefCanvasHeight;

    $(hidefCanvas).attr('width', hidefCanvasWidth * window.devicePixelRatio);
    $(hidefCanvas).attr('height', hidefCanvasHeight * window.devicePixelRatio);
    $(hidefCanvas).css('width', hidefCanvasCssWidth);
    $(hidefCanvas).css('height', hidefCanvasCssHeight);
    hidefContext.scale(window.devicePixelRatio, window.devicePixelRatio);               
  }

  hidefContext.font = "16px Palantino";
  hidefContext.fillText("Rothko is classified as an abstract expressionist.", 10, 20);
Lazos answered 15/3, 2011 at 2:38 Comment(2)
Posterous is no longer around, so this is a dead link.Hexose
This is the solution if you solved antialiasing and still see fat fonts. I solved antialiasing by subtracting the %1 of the value (broken pixel) then I also did this.Monahan
G
2

Here's a way of doing sub-pixel rendering for any canvas content (text, images, vectors, etc). http://johnvalentine.co.uk/archive.php?art=tft.

Outline of the method

It draws onto a canvas, which is then drawn to the screen to take advantage of RGB-striped subpixels. It works with alpha channels too. Note that this might not work if you are using a portrait display, non-striped pixels, or if your browser displays canvases at a lower resolution than your display.

There's scope for fine-tuning, but it's a big gain for a simple method.

Gotcher answered 17/7, 2013 at 23:18 Comment(0)
C
1

This is generically called subpixel anti-aliasing, or ClearType on Windows. I'm not aware of any OS/browser combinations that currently support this for Canvas.

I'd be interested to see some tests using sub-pixel offsets for the text to see if any browsers even use pixel-based hinting of the font rendering (aligning ascenders on pixel boundaries, for example). My assumption would be no.

Edit: My assumption was wrong; it would appear that Safari, Chrome, and Firefox all utilize some pixel font hinting. Safari and Chrome appear the same, snapping to whole pixel boundaries, but are different from Firefox (snapping to half-pixel boundaries?). See the visual results of testing (on OS X) here: http://phrogz.net/tmp/canvas_text_subpixel.html

Cardiogram answered 29/12, 2010 at 2:17 Comment(2)
Thanks for testing it out. You might also like this: bel.fi/~alankila/lcdJeremiahjeremias
@MattMazur Nice! You should post that as an answer so that you can accept it.Cardiogram
V
0

You could make the fonts a lot clearer with fairly easy technique.

You can scale the canvas in CSS twice as small:

canvas {
    transform-origin: left top;
    transform: scale(0.5);
}

In the HTML double the dimensions of the canvas:

<canvas width="(width*2)" height="(height*2)">

Finally draw everything on the canvas in double size.

You will notice that the fonts are a lot clearer.

This is not completely the same as H1 in the HTML but a looks lot better than normal font rendering.

Virgenvirgie answered 17/11, 2021 at 13:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.