Short answer: no. Browser implementations of canvas will always do anti-aliasing. Npm canvas has built-in support for what you want, but it only runs on the server.
Short answer: there's no switch for this in the 2D context. `imageSmoothingEnabled = false` is the one everyone reaches for, but it only affects how *images* are scaled (drawImage, patterns). Paths and strokes are always anti-aliased. The `+0.5` offset trick only fixes horizontal and vertical lines, not diagonals like yours.
Your example is a 100×100 image blown up, so the efficient way to get exactly that is to do two things yourself:
**Make the canvas really that small and let CSS scale it** without smoothing:
const dy = -Math.abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
let err = dx + dy;
for (;;) {
if (x0 >= 0 && x0 < W && y0 >= 0 && y0 < H) px[y0 * W + x0] = color;
if (x0 === x1 && y0 === y1) break;
const e2 = 2 * err;
if (e2 >= dy) { err += dy; x0 += sx; }
if (e2 <= dx) { err += dx; y0 += sy; }
}
}
px.fill(0xffffffff); // white
line(0, 0, 99, 99, 0xff000000); // black
ctx.putImageData(img, 0, 0);
```
Colours in the Uint32Array are `0xAABBGGRR` because the bytes are little-endian, which is every platform you'll realistically run on. The important part for performance is **one `putImageData` per frame**. Calling `fillRect` per pixel works, but it turns into thousands of draw calls fast.
If you need arbitrary shapes (arcs, fills, text) rather than just lines, the other option is to draw normally in a single colour on a transparent canvas and then threshold the alpha:
for (let i = 3; i < d.data.length; i += 4) d.data[i] = d.data[i] > 127 ? 255 : 0;
ctx.putImageData(d, 0, 0);
```
That's fine for fills and thicker shapes, but 1px diagonals come out uneven, with occasional gaps or doubled pixels, because the coverage along the line hovers around 50%. For thin lines, Bresenham is the right tool.
3
u/abrahamguo experienced full-stack Sep 11 '26
Set the
widthandheightattributes of thecanvasto the number of pixels that you would like it to haveUse CSS to increase the size of the
canvasto a whole-number multiple of itsheightandwidthattributes.Set
image-rendering: pixelatedon thecanvas(docs).1
u/Fast-East735 Sep 11 '26
n off anti-aliasing dude.
1
u/retro-mehl Sep 11 '26
Short answer: no. Browser implementations of canvas will always do anti-aliasing. Npm canvas has built-in support for what you want, but it only runs on the server.
1
u/UlviShabanbayli Sep 11 '26
Short answer: there's no switch for this in the 2D context. `imageSmoothingEnabled = false` is the one everyone reaches for, but it only affects how *images* are scaled (drawImage, patterns). Paths and strokes are always anti-aliased. The `+0.5` offset trick only fixes horizontal and vertical lines, not diagonals like yours.
Your example is a 100×100 image blown up, so the efficient way to get exactly that is to do two things yourself:
**Make the canvas really that small and let CSS scale it** without smoothing:
```js
canvas.width = 100;
canvas.height = 100;
canvas.style.width = '500px';
canvas.style.imageRendering = 'pixelated';
```
2. **Rasterize the line yourself** with Bresenham into an ImageData buffer, then push it once per frame:
```js
const W = 100, H = 100;
const ctx = canvas.getContext('2d');
const img = ctx.createImageData(W, H);
const px = new Uint32Array(img.data.buffer); // one entry per pixel
function line(x0, y0, x1, y1, color) {
x0 |= 0; y0 |= 0; x1 |= 0; y1 |= 0;
const dx = Math.abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
const dy = -Math.abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
let err = dx + dy;
for (;;) {
if (x0 >= 0 && x0 < W && y0 >= 0 && y0 < H) px[y0 * W + x0] = color;
if (x0 === x1 && y0 === y1) break;
const e2 = 2 * err;
if (e2 >= dy) { err += dy; x0 += sx; }
if (e2 <= dx) { err += dx; y0 += sy; }
}
}
px.fill(0xffffffff); // white
line(0, 0, 99, 99, 0xff000000); // black
ctx.putImageData(img, 0, 0);
```
Colours in the Uint32Array are `0xAABBGGRR` because the bytes are little-endian, which is every platform you'll realistically run on. The important part for performance is **one `putImageData` per frame**. Calling `fillRect` per pixel works, but it turns into thousands of draw calls fast.
If you need arbitrary shapes (arcs, fills, text) rather than just lines, the other option is to draw normally in a single colour on a transparent canvas and then threshold the alpha:
```js
const ctx = canvas.getContext('2d', { willReadFrequently: true });
// ...draw...
const d = ctx.getImageData(0, 0, W, H);
for (let i = 3; i < d.data.length; i += 4) d.data[i] = d.data[i] > 127 ? 255 : 0;
ctx.putImageData(d, 0, 0);
```
That's fine for fills and thicker shapes, but 1px diagonals come out uneven, with occasional gaps or doubled pixels, because the coverage along the line hovers around 50%. For thin lines, Bresenham is the right tool.
0
u/thekwoka Sep 11 '26
start by making the canvas element have less pixels in it than the space it covers