Note: The following is excerpted from Josh Comeau’s CSS for JavaScript Developers course

The exact position of each character varies from browser to browser. Chrome’s commas are closer to the preceding word than Safari’s. But if you look closely, you’ll notice that most letters change slightly from browser to browser.

kerning

The reason why letters are arranged slightly differently between browsers is that browsers implement different kerning algorithms.

Kerning is the spacing between individual characters. Characters are gently pushed to the left or right. The process is part art, part science.

Browsers do not adjust the spacing of monospaced fonts because the characters need to be neatly arranged in columns.

With CSS, we have the option of using FONT-Kerning: None to completely disable resizing, but the browser doesn’t give us any fine-grained control over the kerning. The letter-spacing property allows us to increase/decrease the spacing between individual characters. It acts as a “spacing amplifier” — it amplifiers browser spacing.

How do I customize the spacing adjustment?

  1. usefont-kerning: noneDisables built-in kerning.
  2. withspanLabels wrap each letter.
  3. useletter-spacingMove the letters further to eachspanTag selects a custom value.

If you’re interested in improving your kerning skills, a super neat kerning mini-game can help you practice.

Rasterization of text

In addition to the browser’s role, the browser’s operating system also affects how typesetting renders.

To understand why text looks so different on different machines, we need to learn rasterization and anti-aliasing.

In the early days of computers, fonts were essentially collections of images. Each character is represented by a picture. Font is a huge gallery, with each font having a different size for an image. This is called a bitmap font.

Bitmap fonts are still popular today, but they are rare. For fonts, it is more common to use vector formats like TTF, OTF, SVG, and WOFF/Woff2. In vector fonts, we store a set of mathematical instructions for each character.

The main benefit of vector fonts is that they can be scaled to any size without pixelating and blurring the letters.

However, in order to convert vector fonts into characters on the screen, the browser has to figure out the color of each pixel, a process called rasterization.

There is a Wikipedia article on rasterization of fonts that delves into different text rendering methods.

The easiest way to do this is to fill in any pixel that the vector path passes through. In this example, we draw the letter “A” by coloring in the relevant pixels:

As you can imagine, this rasterization method produces pixel-toothed text:

To make text look smoother, browsers can apply “anti-aliasing” :

Basic anti-alias algorithm.

Anti-aliasing using hints embedded in the font file.

So why does our text look different on different machines? This is because the browser and operating system both play a role in rasterization and anti-aliasing. Different algorithms produce different results.

Can we change the algorithm it uses in CSS? Can… But it’s complicated.

Font smoothing

The WebKit-Font-Smoothing attribute allows you to switch between the anti-aliasing algorithm used by the browser. Unfortunately, it works only on MacOS, and only on Chrome/Safari/Edge (not Firefox).

“Subpixel Antialiasing” is a fancy technique that uses the R/G/B elements of the pixel to produce a more accurate anti-aliasing effect. This blowup from Wikipedia shows the technique:

Essentially, we can improve perceived resolution by “borrowing” a color from adjacent pixels. This extra thickness is useful for anti-aliasing algorithms.

Try to keep your head close to/away from the monitor. When you get close enough to the screen, you should be able to see the 3-ribbon. Away from the screen, the text should be white. This is a good demonstration of how the monitor works! Our eyes can see white by combining the three primary colors.

When this property was added to Apple devices more than a decade ago, it helped generate clearer text.

Much has changed since then, though.

First, our pixel arrangement becomes more complex. If you look at the screen of a modern device under a microscope, you will find some very creative arrangements of pixels.

For example, take a look at the Apple Watch and iPhone XS Max:

Apple Watch

iPhone XS Max

(Sources: Apple Watch and iPhone)

Another big thing has changed, too. Most monitors are “high resolution”. This means that each logical pixel is mapped to multiple physical pixels.

Modern iphones have a 3x device pixel ratio, which means that each logical pixel is represented by nine physical pixels.

Given how tiny modern pixels are, what good can we get from subpixel antialiasing? Apparently, Apple doesn’t think so.

Since the launch of MacOS Mojave in 2018, Apple has turned off the Subpixel Antialiasing feature by default. If you use a retina display, the subpixel antialiasing effect seems to do more harm than good, producing hazy, illegible text.

Confusingly, however, MacOS browsers like Chrome and Safari don’t “inherit” system defaults. We need to explicitly use Antialiased to enable anti-aliasing:

*, *:before, *:after {
  box-sizing: border-box;
  -webkit-font-smoothing: antialiased;
}
Copy the code

Other browsers

Smoothing is available only on MacOS Chrome/Safari/Edge.

MacOS Firefox offers an alternative attribute, Moz-Osx-Font Smoothing, but it only allows us to switch between Grayscale and Auto. Also, in most cases, Auto defaults to Grayscale. There doesn’t seem to be any difference.

On Windows, there is no such property. Windows does have a Subpixel-Antialiasing algorithm called ClearType, but it is not exposed in CSS.