Canvas Pixelization Problems


1. Antialiasing

If you are making a Javascript canvas game with pixelart graphics, you may have noticed that by default it adds antialiasing to drawn objects. Example:

This sucks, especially when working with small pixeled sprites (it looks worse at small scales). This can be resolved by forcing the page to "pixelize" the canvas or the image. Code:


canvas,img {
image-rendering: crisp-edges;
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-optimize-contrast;
image-rendering: optimize-contrast;
image-rendering: pixelated;
-ms-interpolation-mode: nearest-neighbor;}


This will try to make the browser render the image or canvas using NNA if the browser supports it (juicy and sharp). The result will look like something like this:

See? The image is more clear but there are still a few remaining problems: 1. The flowers on the canvas are scaled by fractions which causes blur (The screenshot is from a pseudo 3d racing game, it renders closer objects with bigger scale in a nutshell). 2. The built-in "ctx.fill" and "lineto" methods do not use NNA which means if they are not parallel with the Y or X axis, the context will render semi-transparent pixels thus will make the line blurry. So, what is the solution?...
...
There is no real solution, however we can create a fake one!

2. CRT!

Yes! CRT effect is the solution. Not only it helps to "pixelize" the game but it also gives the game a retro effect. Here is how to make a basic CRT effect:


html::after {
content: "";
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06));
background-size: 100% 5px/*crt x partition width*/, 5px/*crt x partition height*/ 100%;}


You can make a little vibration effect if you want to. Here is the css with the vibration effect:


html::after {
content: "";
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06));
background-size: 100% 5px/*crt x partition width*/, 5px/*crt x partition height*/ 100%;
animation: crtfl 0.15s infinite;
}
@keyframes crtfl {
15% {
opacity: 0.9;
}
50% {
opacity: 0.96;
}
55% {
opacity: 0.085;
}}


Pros of using CRT effect:
-It will help to mask semi-transparent pixels
-Gives a retro feeling to the game


Cons of using CRT effect:
-It won't resolve the sub-pixel rendering
-The vibration effect may be unpleasant.


After applying the CRT effect to the canvas it looks something like this. For now this is all we can do. Let's hope someone adds an option to disable sub-pixel rendering in the future. I hope this helped someone.

3. Bonus

As of 2023. 03. 23. there is a solution for drawing shapes and reducing the blurryness of those shapes. You can apply svg filters to your drawing context:


<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>


And here is the javascript code:


ctx.filter = "url(#remove-alpha)";



Go back to main page