HTML5 Canvas and Anti-aliasing

122,723

Solution 1

Anti-aliasing cannot be turned on or off, and is controlled by the browser.

Can I turn off antialiasing on an HTML <canvas> element?

Solution 2

You may translate canvas by half-pixel distance.

ctx.translate(0.5, 0.5);

Initially the canvas positioning point between the physical pixels.

Solution 3

It's now 2018, and we finally have cheap ways to do something around it...

Indeed, since the 2d context API now has a filter property, and that this filter property can accept SVGFilters, we can build an SVGFilter that will keep only fully opaque pixels from our drawings, and thus eliminate the default anti-aliasing.

So it won't deactivate antialiasing per se, but provides a cheap way both in term of implementation and of performances to remove all semi-transparent pixels while drawing.

I am not really a specialist of SVGFilters, so there might be a better way of doing it, but for the example, I'll use a <feComponentTransfer> node to grab only fully opaque pixels.

var ctx = canvas.getContext('2d');
ctx.fillStyle = '#ABEDBE';
ctx.fillRect(0,0,canvas.width,canvas.height);
ctx.fillStyle = 'black';
ctx.font = '14px sans-serif';
ctx.textAlign = 'center';

// first without filter
ctx.fillText('no filter', 60, 20);
drawArc();
drawTriangle();
// then with filter
ctx.setTransform(1, 0, 0, 1, 120, 0);
ctx.filter = 'url(#remove-alpha)';
// and do the same ops
ctx.fillText('no alpha', 60, 20);
drawArc();
drawTriangle();

// to remove the filter
ctx.filter = 'none';


function drawArc() {
  ctx.beginPath();
  ctx.arc(60, 80, 50, 0, Math.PI * 2);
  ctx.stroke();
}

function drawTriangle() {
  ctx.beginPath();
  ctx.moveTo(60, 150);
  ctx.lineTo(110, 230);
  ctx.lineTo(10, 230);
  ctx.closePath();
  ctx.stroke();
}
// unrelated
// simply to show a zoomed-in version
var zCtx = zoomed.getContext('2d');
zCtx.imageSmoothingEnabled = false;
canvas.onmousemove = function drawToZoommed(e) {
  var x = e.pageX - this.offsetLeft,
    y = e.pageY - this.offsetTop,
    w = this.width,
    h = this.height;
    
  zCtx.clearRect(0,0,w,h);
  zCtx.drawImage(this, x-w/6,y-h/6,w, h, 0,0,w*3, h*3);
}
<svg width="0" height="0" style="position:absolute;z-index:-1;">
  <defs>
    <filter id="remove-alpha" x="0" y="0" width="100%" height="100%">
      <feComponentTransfer>
        <feFuncA type="discrete" tableValues="0 1"></feFuncA>
      </feComponentTransfer>
      </filter>
  </defs>
</svg>

<canvas id="canvas" width="250" height="250" ></canvas>
<canvas id="zoomed" width="250" height="250" ></canvas>

And for the ones that don't like to append an <svg> element in their DOM, you can also save it as an external svg file and set the filter property to path/to/svg_file.svg#remove-alpha.

Solution 4

I haven't needed to turn on anti-alias because it's on by default but I have needed to turn it off. And if it can be turned off it can also be turned on.

ctx.imageSmoothingEnabled = true;

I usually shut it off when I'm working on my canvas rpg so when I zoom in the images don't look blurry.

Solution 5

Here's a workaround that requires you to draw lines pixel by pixel, but will prevent anti aliasing.

// some helper functions
// finds the distance between points
function DBP(x1,y1,x2,y2) {
    return Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
}
// finds the angle of (x,y) on a plane from the origin
function getAngle(x,y) { return Math.atan(y/(x==0?0.01:x))+(x<0?Math.PI:0); }
// the function
function drawLineNoAliasing(ctx, sx, sy, tx, ty) {
    var dist = DBP(sx,sy,tx,ty); // length of line
    var ang = getAngle(tx-sx,ty-sy); // angle of line
    for(var i=0;i<dist;i++) {
        // for each point along the line
        ctx.fillRect(Math.round(sx + Math.cos(ang)*i), // round for perfect pixels
                     Math.round(sy + Math.sin(ang)*i), // thus no aliasing
                     1,1); // fill in one pixel, 1x1
    }
}

Basically, you find the length of the line, and step by step traverse that line, rounding each position, and filling in a pixel.

Call it with

var context = cv.getContext("2d");
drawLineNoAliasing(context, 20,30,20,50); // line from (20,30) to (20,50)
Share:
122,723
KRouane
Author by

KRouane

Updated on July 05, 2022

Comments

  • KRouane
    KRouane almost 2 years

    How to turn on the anti-aliasing on an canvas.

    The following code doesn't draw a smooth line:

    var context = mainCanv.getContext("2d");
    if (context) {
       context.moveTo(0,0);
       context.lineTo(100,75);
    
       context.strokeStyle = "#df4b26";
       context.lineWidth = 3;
       context.stroke();
    }
    
  • Perry Monschau
    Perry Monschau almost 11 years
    I don't think getAngle is necessary for drawing lines. All you need to do is divide the difference between the two points by the 'dist' and multiply that by 'i'. Am I wrong?
  • Overcode
    Overcode almost 11 years
    yes you're correct. I just used angles for clarity..? I guess
  • Gaurav
    Gaurav over 10 years
    imageSmoothingEnabled applies to pattern fills and drawImage, it does not affect general anti-aliasing. whatwg.org/specs/web-apps/current-work/multipage/…
  • zachdyer
    zachdyer about 10 years
    That's not true. It can be turned on and off with ctx.imageSmoothingEnabled.
  • Gaurav
    Gaurav about 10 years
    imageSmoothingEnabled applies to pattern fills and drawImage, it does not affect general anti-aliasing. whatwg.org/specs/web-apps/current-work/multipage/…
  • zachdyer
    zachdyer about 10 years
    Oh in that case maybe it's the video card that would handle that.
  • Richard
    Richard about 10 years
    Note that if you clear your canvas using the method of setting the width to itself: canvas.width = canvas.width this will reset the transzlation matrix and you'll need to translate by half a pixel again. You can avoid this by using clearRect() instead.
  • Tim Hettler
    Tim Hettler about 10 years
    This is the only answer that actually solves OPs issue
  • Bjorn
    Bjorn over 9 years
    It clearly helps with straight lines, but diagonal lines still look pretty terrible.
  • steve
    steve over 8 years
    holy toledo you just saved me a ton of time! about a year ago i started work on a pixel based painting web app, and got frustrated with the inability to disable anti-aliasing. i think the only solution id found at the time ran so slow that it was hopeless, but yours runs amazingly fast and does exactly what i need it to! thank you!!!
  • kristianp
    kristianp about 7 years
    Drawing lines pixel by pixel shouldn't need calls to sin and cos. Use Bresenham's algorithm, it's a piece of graphics history: en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
  • Octopus
    Octopus about 7 years
    It just moves where the aliasing occurs.
  • rococo
    rococo almost 7 years
    Note that floating point pixel values greatly reduce performance, so if you are drawing frequently this may not be a good idea
  • jedierikb
    jedierikb over 5 years
    Looking to do something similar - map translucent to opaque pixels... stackoverflow.com/questions/53755451/…
  • goat
    goat about 5 years
    Terrific answer - you even included a zoom to emphasize the difference!
  • Kit
    Kit about 4 years
    This solution worked perfectly for me, and for all angles. Edge and Chrome.
  • Jeremy Thille
    Jeremy Thille about 4 years
    "It's now 2018", not for Internet Explorer. And it's actually 2020 by the time of this comment, and still not for Internet Explorer. Unfortunately, this dinosaur is STILL used by many people and some projects STILL require to support it...
  • Kaiido
    Kaiido about 4 years
    @JeremyThille so? Should we deprive the rest of the world from modern techs because some companies are still using IE? I also have to support it for some clients, we just made clear to them that they won't have the "optimal experience". There are other solutions in this page, pick one if it's really a must for your project.
  • Jeremy Thille
    Jeremy Thille about 4 years
    Oh, absolutely. I am fully for dropping IE, this is my everyday battle. Your solution is good, but IE drags us all :(
  • Kaiido
    Kaiido about 4 years
    @JeremyThille Yes but latest IE was released in 2013, so having my answer saying at the beginning "it's now 2018" shouldn't deceive the readers. Your comment felt like it did deceive you. I may misread it though.
  • Jeremy Thille
    Jeremy Thille about 4 years
    My point is the same as yours. It's now 2018, and now 2020, we should be able to use modern methods and APIs, but... unfortunately it's not 2018 yet for many people who still use IE for some reason :( Many people still live in 2013. Thought of the day.
  • i336_
    i336_ over 3 years
    FWIW the HTML5 canvas is not GPU accelerated
  • i336_
    i336_ over 3 years
    Just for the record/out of curiosity, what version of IE was this talking about? 11? ...10?
  • Kaiido
    Kaiido over 3 years
    @i336_ all versions. The thread is from 2020 and it's been said in it that the last release of IE was made in 2013.
  • i336_
    i336_ over 3 years
    Ah, I see. That logical structure there sailed right over my head, thanks :) - and wow, I just realized that means everything's in a time-warp to (as of right now) 8 years ago (and then with a bunch of inconsistencies on top). That's quite the legacy entrenchment... :(