Blog posts > Convincing-looking 90s fonts in modern browsers

Convincing-looking 90s fonts in modern browsers

By caitlin and paul, 09-MAY-2020

We wanted to make a website that looked like it was from the 1990s. Sounds easy, right? Looking back from 2020, the aesthetic of the early World Wide Web stands out for the things it lacked: no CSS, emojis, fancy web fonts or non-web-safe colours. Black Times New Roman on a grey background, something like this:

That's simple and austere, but it doesn't really feel retro or nostalgic, does it? Initially, we weren't even sure what was lacking – we've clearly been living in the 21st century for too long.

But when you open the same HTML file in Internet Explorer 5, running in a Windows 2000 virtual machine, it looks quite different:

Seeing that brought back a strong wave of nostalgia. Beyond the old-timey browser chrome and the different default treatment of tables, the text itself looks really different:

Antialiasing is another thing that the 90s web lacked, and it makes a surprisingly big difference. Even in the early 2000s, when CRT monitors were still widespread, the Apple and Microsoft operating systems had made fundamentally different choices when it came to font rendering. Mac OS X offered smoothed, dithered text, using shades of grey pixels to suggest contours, while Windows followed a strongly hinted approach, with strokes well-aligned to pixels, giving a much sharper, high-contrast feel.

The left-hand image is a screenshot of Mac OS X 10.1 Puma, the right-hand image is of Windows 2000. Both date from the very early 2000s, but you can see the stark difference in approach to font rendering. These screenshots have not been modified at all, except for enlarging them to 200% with nearest-neighbour interpolation.

Ultimately there isn't a rational justification for why we decided to mimic the early 2000s Windows style of rendering, but seeing those old browsers in Windows virtual machines resonated most with us, felt most.. retro. And so, we decided to try and replicate that look and feel in modern browsers.

Idea #1: CSS

"Great", we thought, "we'll use CSS to take care of that". CSS in 2020 can do amazing things like rendering vector graphics and animations, so surely it should make short work of jagged fonts. But our hopes were quickly dashed by a web search. In the middle of page after page of StackOverflow posts from people who wanted to make their fonts less jagged, we found that there isn't really a universal way to disable antialiasing using CSS. There's a non-standard font-smooth property that didn't make the cut for the CSS3 spec, but that doesn't work in all browsers, and worst of all, the results didn't quite hit the mark: the pixels were visible, but the resolution was way too high. Developers often lament the difficulty of designing for Retina screens, but we were having the opposite problem.

It seems pretty settled that antialiasing is set at the OS level, and individual websites are discouraged from messing with that. Mostly that's because it's bad for accessibility, but in any case the font-smooth property is not part of the standards, and likely won't be in the future. And that's sensible. But making a website today that looks like it's from 1996 is not a sensible exercise either, so we continued on our quest for pixellated typographic perfection.

Idea #2: Importing bitmap font files

We couldn't make vector fonts look pixelated, but could we get around this by importing authentic 90s bitmap font files instead? We had a Windows 98 disk image with all of the system fonts including MS Serif: a riff on Times New Roman in the bitmap .FON format dating back to Windows 1.x. Unfortunately, we quickly found out that CSS @font-face doesn't work with .FON files, and none of the web-based font conversion tools that we could find would convert them to the vector formats that are usable with @font-face.

Idea #3: FontForge

While searching for tools to convert bitmap font files we came across FontForge - a free and open source font editor. It looked impressively fully-featured and had a huge number of supported formats, so we were cautiously optimistic that we could make it work for us.

Fontforge can convert fonts from vector to bitmap formats by creating bitmap strikes. Basically, a strike of a vector font, means its glyphs have been rasterised at a particular pixel size. Rasterisation at different sizes is important, because on an old-fashioned low-resolution screen, this is essentially what's happening: when rendering your text, the font is rasterised for your particular pixel density. For example, on a 75DPI screen, your 12 point font becomes 16 pixels high. These 16 pixels are big enough that you see them as jagged edges. However, if you're displaying 48 point text (a large heading, for example) on that same old-fashioned screen, you'll rasterise the font at 64 pixels, so already it'll seem much smoother. This will come back and complicate our life later on.

Notice in this screenshot (taken in Windows 2000) how at different point sizes, the rasterisation of the text is clearly different. This is why we can't simply make a bitmap font at one size and scale it up – every size we want a pixellated font, we need to have a bitmap strike available.

FontForge has the ability to work with other free software that traces the visual information conveyed by pixels into points, lines and curves, allowing you to convert bitmap fonts back into vector formats. This was promising!

We made a bitmap strike of Times New Roman at 16px, and it was a pixel-perfect match of what we saw in IE5!

Elated, we installed potrace and told FontForge to use it to trace our pixellated version of Times.

This was the result:

Potrace was doing its best to turn jagged pixels into smooth curves, and the result was a blobby mess.

Idea #4: Scaling up

Some further searching unearthed the idea of scaling up the bitmap font before tracing to give potrace a higher-resolution input to work with. In our 16px bitmap version of Times we created a second, 80px strike, so it looked the same but the resolution was five times higher. We held our breath and repeated the autotrace…

Success at last!

We were thrilled with the way this looked, but the whole process was very manual. We would need to do this for the regular, bold and italic variants of Times, and if we wanted headings to look pixel-perfect we would need to trace different strike sizes as well. We wondered, was there a way we could automate this?

Automating things

Ideally, even if we limited ourselves to Times New Roman only, we wanted to have Regular, Bold, Italic, and Bold Italic font weights available, and since we wanted text to look reasonable at various sizes in the browser, we would also need to create strikes at a few different pixel sizes at least. For example, body text at 16px, but also 24px and 32px for headings, to be able to display them with appropriate levels of jaggedness – not too little, but definitely also not too much. As illustrated above, you wouldn't want to strike at 16px and then scale up to heading size. That would already mean 4x3 = 12 variants of that one font alone. And of course, we wouldn't be satisfied if we didn't also have MS Comic Sans and Lucida Handwriting available for our users ;). This would've taken us hours, if not days, to accomplish manually.

We were full of hope, however, because FontForge is supposed to be scriptable using Python. Unfortunately, it doesn't act like a real library that you install with pip install fontforge or similar, you have to somehow invoke your Python script through FontForge, or something like that. It was too difficult for us. It also seemed as if for a font without any strikes (so, all vector TTFs), you couldn't create a first strike in Python. This might just be us misunderstanding it – either way, the interface was arcane at best. Luckily, there's also the scripting interface the author dryly refers to as "a legacy language [the author] came up with". That sounds about obscure enough for a project like ours. It turns out that the functions available when scripting FontForge are very close similes of performing the actions manually in the UI. So we dutifully came up with a procedure for generating one font – glossing over some details:

  1. Open the TTF file (e.g., Times New Roman from Windows 98)
  2. Create a bitmap strike at 16px
  3. Create bitmap strike at 5x desired pixel size (for a 16px font, strike at 80px) – FontForge automatically scales it up with nearest-neighbour interpolation
  4. Get rid of any vector information
  5. Invoke "autotrace" – in our case the potrace binary, which gives back vector information from input bitmap
  6. Save that result as a WOFF file (vector-based web-font, usable in modern browsers)

Although that sounded easy, we soon hit some snags. For example, while the UI allowed us to resize strikes in FontForge, inside a script step 3 would cause a hard segfault. That was quite disappointing, to say the least. We tried everything we could think of, but couldn't work around that bug, and as we illustrated above, it was crucial for our pixellated fonts. Just when we thought we were defeated, another approach surfaced: instead of trying to do the heavy lifting in FontForge, why not wrap or modify potrace instead? It turns out that somebody out there has already done that. We quickly set to work trying to integrate that into our font pipeline, and finally got it to work! It was slow to run and arduous to figure out, but definitely did the job much better than us manually poking at the FontForge UI. The results were quite rough out of the box, though. We had to apply heaps of little fixes like removing mysterious artefacts from the 'space' characters, copying the asterisk into the position of the 'middle dot' to get nice password mask, among other things. But with many tries and lots of tweaking for speed and aesthetics, we ended up with the fonts you're looking at right now on this website (be sure to check out the Home Page Builder Wizard® for more!). At least, assuming you have webfonts enabled and are using Chrome or Safari – we're still relying a bit on that deprecated font-smooth CSS property to make things pixel-perfect.

Further reading

If you want to learn more about fonts, here are a few links we found interesting: