Cause
This is due to smoothing.
The canvas is still in progress, and the browser has various implementations for anti-aliasing processing.
Possible solutions
1
You can try disabling anti-aliasing for images in Firefox as follows:
context.mozImageSmoothingEnabled = false;
In Chrome:
context.webkitImageSmoothingEnabled = false;
and add a class to an element like this (should work with Opera):
canvas { image-rendering: optimizeSpeed; // Older versions of FF image-rendering: -moz-crisp-edges; // FF 6.0+ image-rendering: -webkit-optimize-contrast; // Webkit image-rendering: -o-crisp-edges; // OS X & Windows Opera (12.02+) image-rendering: optimize-contrast; // Possible future browsers. -ms-interpolation-mode: nearest-neighbor; // IE }
In this browser test, which I did to see the effect of disabling anti-aliasing:
ANTI-ALIAS BROWSER TEST
2
Translate the entire canvas by 0.5 points.
ctx.translate(0.5, 0.5);
This does not always work and may contradict other translations. However, you can add a fixed offset each time:
ctx.translate(scrollX + 0.5, scrollY + 0.5);
3
Another option is a compromise, which you either impose on the tiles with one additional pixel, which I do not recommend because of the extra work that you will support.
4
This method draws fragments that are slightly scaled, so they overlap:
ctx.drawImage(tile, x, y, 65, 65); //source tile = 64x64
This may be enough to cover the failure. In combination with disabling anti-aliasing (or using the nearest neighbor), it will not affect most of the tile graphics, but this can reduce performance due to scaling.
If you turn off anti-aliasing (and this did not work on its own), the overhead will be minimal, as some of them will interpolate the image.
5
Just draw all positions offset -1 (i.e. grid = 63x63). Of course, this will ruin everything with regard to checks, so ...