nanoFont - an extremely small pixel font format

nanoFont is a pixel font format, with an editor and renderer, designed to take up the least amount of space without sacrificing on style.

nanoFont

Overview

In previous projects I have rendered text to an HTML canvas element using a solution limited to characters like this:

With nanoFont, with no compromise, I can now render them more like this:

or, with a little more data and code, like this:

The font data and rendering code combined is under 800 bytes for nanoFont, similar to tinyfont.js, but the full ASCII character set is included instead of just uppercase letters, numbers and a few other characters. Any nanoFont file can also be loaded for a wide variety of different font styles.

The Extended version of nanoFont is still under 1200 bytes for the code and the full ASCII set of characters for the example font shown (Enchanted Sword by Jeti). It also adds the option to align text and returns the dimensions of the printed text.

You can take a look at the nanoFont editor, check out a demonstration of a variety of fonts and settings and see a minimal code example and another example with a bit more functionality using the extended version, or read on for more background and technical detail.

Here is a comparison of before and after retrofitting nanoFont to my recent game Exit the Castle, still fitting into the 13k limit for the competition:

Previous solution

The format

In my last two games for the js13kGames Competition I needed a solution for graphically rendering text without using too much of the 13 kilobyte file size limit that it imposed.

My games are in a pixel art style partly to make meeting the size limit easier but also because I like the aesthetic of it. The font used would therefore be pixel based rather than vector.

Each character in a pixel font can be represented as a string of binary digits. Where there is a 1 a pixel is drawn, where there is a 0 there is not. For example the letter A could look like this:

010
101
101
111
101

If you take the sequence of each column and join them together you have a single row like this:

01111 + 10010+01111

This results in the binary string 011111001001111 which is 15 characters long. Storing that in JavaScript would take 15 bytes. So for the full ASCII character set that would be 1410 bytes. That's more than 10% of the competition storage limit just for the font data, without considering the code needed to display anything.

It can be reduced though by storing the binary string as the Unicode character that it represents. The binary is just a number and the Unicode character set assigns every character with a number, or code point.

Using UTF-16 encoding, the 15 binary bits can be encoded to get the character at its code point.

The JavaScript static method String.fromCodePoint() can be used to get the string for a given code point.

index.js

So we now have the character 㹏 which represents the binary data to draw the character A in our pixel font. This character takes up 3 bytes in the UTF-8 encoding used for this page and the web generally, only two bytes in UTF-16.

It can easily be decoded by reversing the process.

The codePointAt() method can be used to get the integer that is the Unicode code point for a given character in a string.

index.js

The leading zeros of the binary string are lost during the encoding but they can easily be replaced as we know the length needs to be 15 digits.

This is the solution that is used in tinyfont.js by Timur Manyanov and that is what I implemented for my games.

The distribution version of tinyfont.js only takes up 713 bytes for the font data including a JavaScript function that uses it to render text to a canvas element. I was very happy with the result I got using it in my games.

Limitations

Despite how good it is, it does come with some limitations. Significantly, it is restricted to uppercase letters, numbers and a few other characters and the style of the font is inevitably very simplistic.

I wondered if there would be a way to add the rest of the ASCII character set and improve the look of the font.

Adding characters is problematic. Not only is the small grid size limiting but the encoding process doesn't always result in a useable encoded character as some are control characters and others are supplementary. In those cases the decimal value can be stored but that is less than optimal.

Improving the font requires a bigger grid which requires more bytes to be encoded resulting in multiple encoded characters for each font glyph and more chance that some of those characters won't be useable. The limit of this solution seems to have been reached.

A new approach

Storing font data in an image file

I thought that a better alternative might be to store the font as an image file. I looked at the PBM image file format. The P4 type of PBM file stores data in binary, one bit per pixel, 1 and 0 for black and white. The only other information stored is the width and height of the image.

After some minimal testing it was clear that a significant amount of unnecessary data would be stored. Wanting to have characters of different heights, there would be space around them the size of the largest character on the same row. Also with images of small widths there is a high percentage of redundant data. Each row is stored in however many bytes are needed to accommodate the bitcount. So for an image with a pixel width of ten it takes two bytes per row, the remaining six bits in each second byte being wasted, just set to 0, which soon adds up.

How about my own file format?

One bit per pixel, each glyph with its own width and height, so only the necessary bits are stored. The offset from the top is also then required, to put it back in its context correctly. The letter spacing is assumed to be a fixed width, so no horizontal offset is specified.

A maximum width and height is needed to know how many bits have to be used to store their values. A maximum 7x7 grid would use a minimum of three bits - binary 111 is 7 in decimal - but if a requirement is set that every glyph has to be at least 1x1 then it can go from 1-8 instead of 0-7 and an 8x8 grid can be used. Likewise a 16x16 grid could use four bits each for width, height and offset.

That could work. So…

Introducing nanoFont

Format

The file format for nanoFont is as follows:

Editor

nanoFont editor

To make it easy to create fonts in this new format I put together an editor. You can select which character to work on in the grid on the right and then edit it in the zoomed in version on the left. Left click to set a pixel, right click to delete. A reminder of the character being worked on is in the bottom right. An example sentence at the bottom shows the use of each letter.

The interface includes 'load' and 'save' buttons, for loading and saving font files, and next to those the width of the space character can be set.

Finally the '8' and '16' buttons on the right allow for switching between the different sized grids.

On a side note, the text used in the buttons is of course loaded in from a nanoFont file.

Try out the nanoFont editor.

Code

Once you have created a font file you will want to be able to render it onto a web page, or into your own game for the next js13kGames Competition.

There is a choice of two JavaScript classes to handle that.

One is absolutely minimal, only renders 8x8 fonts and has the same functionality as tinyfont.js, that is the position of the text, its size and color.

nanofont-minimal.js - 461 bytes

Here is an example of nanoFont using this class.

The other option is slightly larger but can render fonts of either type (8x8 or 16x16) and adds the option to align either the left, right or center of the text at the specified x position. It also returns the width and height of the rendered text, which can be useful.

nanofont-extended.js - 604 bytes

Here is an example of the nanoFont Extended class in action.

Conclusion

The options for rendering pixel fonts using minimal data and code seemed to be either UTF-16 encoding like with tinyfont.js or using an image format like PNG or PBM.

The intention with nanoFont is to get as close as possible to the best aspects of UTF-16 encoding, with its minimal storage requirements, but also image files, with their unlimited creativity.

I hope you manage to make use of it in your own projects and look forward to seeing the results, so let me know if you do.