# Owner: glow
# KeyID: E65A8520F36AEE13CFE4F56BEB8FE331063A4794
# Key: https://glow.li/pgp
# Verify: curl "https://glow.li/posts/using-pixel-fonts-in-a-browser-without-font-smoothing/index.html.asc" | gpg
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
- ---
date=2016-07-15
layout=post
title="Pixel fonts in a browser without font smoothing"
tags[]=Technology
tags[]=Web_Development
tags[]=Fonts
tags[]=Showcase
tags[]=CSS
tags[]=HTML
tags[]=Jekyll
tags[]=Ruby
oldurls[]=technology/2016/7/15/using-pixel-fonts-in-a-browser-without-font-smoothing/
css[]=pixel-zoom.css
- ---
[Tinier](https://squaregear.net/fonts/tinier.html) is a pretty cool font. It is a so called pixel font, and it is just about the smallest font I could find. The letters can be displayed at a font-size of `4px`. This means that each character is only 3 pixels tall.
= charfont('The wizard quickly jinxed the gnomes before they vaporized.', 'charfont-tinier'); ?>
= charfont('The wizard quickly jinxed the gnomes before they vaporized.', 'charfont-tinier'); ?>
I think this is pretty cool, so I tried to implement this on my website for the footer blurb. It turns out though, that pixel fonts look absolutely hideous in modern browsers.
Here is how it looked:

{.pixel-zoom}
Do you see these hideous colored pixels? It ruins the pixel font. The footer was originally white on blue, where the problem was even worse:

{.pixel-zoom}
How it should look:

{.pixel-zoom}
Nice, clean, black on white Pixels.
As with any modern software, browser employ font smoothing. This is done so text looks good on a low DPI screen. It is done with anti aliasing and sub pixel rendering and for normal fonts this is pretty great, but for pixel fonts it looks terrible. There is no way for a font to disable it, so it usually has to be done at a software level. Some browsers have a setting for disabling font smoothing. This is obviously pretty useless, since I can't expect readers of my blog to have turned this off and made all normal fonts look bad except my tiny blurb.
## There must be a CSS setting right?
Well, let me tell you about `font-smooth`. It was present in early drafts of CSS3, but has been removed from the specification since. It only works on [some browsers](https://caniuse.com/font-smooth) and only on the Mac OSX operating system. So it looks like there is no easy way to implement a pixel font without ugly rendering artifacts.
## But why don't you just use an image?
Yes, I could just write my text in GIMP and upload it as a picture. But I want it to be dynamically generatable. I want to do it in good old HTML and CSS.
## So I have decided to go the sprite map route instead
{.left}
Having it as a sprite map has many advantages including (and probably limited to):
- - You can still wrap text around newlines
- - You only have to have one image for any size and length of text.
- - Because the original characters are still in the HTML you can select and copy it
Here is how the HTML of a very short text looks:
```
= charfont('Oli', 'charfont-tinier'); ?>
```
As you can see every letter is just a `` with a special class and the original letter inside it. And since this is a [Jekyll](https://jekyllrb.com) blog, I wrote a short ruby plugin to achieve this, but it would probably work really well in JavaScript as well.
```
module Jekyll
class Charfont < Liquid::Tag
def initialize(tag_name, text, tokens)
super
html = ""
text.upcase.split("").each do |i|
if i == " "
classname="SP"
elsif i == "."
classname="dot"
else
classname=i
end
html = html + "" + i + ""
end
html = html + ""
@output=html
end
def render(context)
@output
end
end
end
Liquid::Template.register_tag('charfont', Jekyll::Charfont)
```
And here is the accompanying CSS:
```
.charfont-tinier span {
color: transparent;
background-image: url("../media/images/tinier.webp");
margin-left: calc(1px * var(--scale));
display: inline-block;
background-size: calc(5px * var(--scale));
width: calc(3px * var(--scale));
height: calc(4px * var(--scale));
overflow: hidden;
background-repeat: no-repeat;
image-rendering: optimizeSpeed;
image-rendering: -moz-crisp-edges;
image-rendering: -o-crisp-edges;
image-rendering: -webkit-optimize-contrast;
image-rendering: pixelated;
image-rendering: optimize-contrast;
-ms-interpolation-mode: nearest-neighbor
}
.charfont-tinier{
- --scale:1;
}
.charfont-big .charfont-tinier{
- --scale:5;
}
.charfont-tinier .charfontA {
background-position: 0px calc(-0px * var(--scale));
}
.charfont-tinier .charfontB {
background-position: 0px calc(-4px * var(--scale));
}
.charfont-tinier .charfontC {
background-position: 0px calc(-8px * var(--scale));
}
```
You can look at the whole CSS file [here](= $URL; ?>/css/charfont.css).
So, this is a very complicated solution to a problem that really should be as simple as `font-smooth`. And it still isn't prefect. Having it do proper line-wrapping would require a bit more complicated HTML and CSS. Also the `image-rendering` part doesn't work on all browsers, so when text size is large it would look like this:
{.zoom}
Edit:
Since writing this post Chrome has made some changes. It now enforces a minimum `font-size` of 6px. Therefore my previous method of using `em` and `font-size=1px` no longer works. I've updated the code in this post to use a css variable to define scale. This way I can still enlarge the font with CSS.
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCgAdFiEE5lqFIPNq7hPP5PVr64/jMQY6R5QFAmHoUJAACgkQ64/jMQY6
R5RUrQ/+OrOP3XST9EX1gTAXNaly4oM68pr78lNve9W7vQHSeRUTRVaQDXY+XYwW
yoyF/Pu9F2JAjHDE8nI+BjufHRi7KdJII2rE/QifWGss/iRs6WWJJ26i6tdAUjFL
iYANGfqhISJHo+pZsXa1R0m+sp+Mrzaf+7PBqF6G7jR83HpSTjc3gX9IlxUtYmaS
lV5eOI3+jehuH4kBkkcrk0IxDRjf4NJvlSiw5Fqs+tReLAsKuLs4x0tKjCOecaKr
NwJP4PDvSN9hBp8+J6k9h/gJeox56AJ4Fs3VknsLOVJSthO7BQhPWBOGW3SMnDkB
cSkrKhwnZzCYj8/tJTDYnCk91OeOzy4PJ8Aeg9RS90MTzHY1z3m9Tk5ZrT6igp2z
xz/VwMY0ydtxtM6QP5SwP8olMAG6R2Enoueuez0TZ64cDUx/ndshdthd2E2cJtZY
tkf7MKDJoU+rYhqyFEWoHjF0yJTlsMg8JEONuq/ekImnhtnP3HPHVW2h0hWxuHrk
2AsAsC4skWdwhJaDSq3CWfs8dZi+JG9WEkpq38kfN0pozM6ciKFwVzQzwEmqtR8D
Oynj91vqMWvxp19OzGFQDDjUMyNpeu7HufJI26i52FU6yFw8XTEG/BCQqQQW6w7J
EWAj9T/0WXGAovEp62KcdVGgDeAbMdqYSUA1+wGvVc8jUADsRRE=
=f5F/
-----END PGP SIGNATURE-----