3x5 Font for those with good eyesight

While creating Dark & Under, I created a little 3 x 5 font for the Arduboy.

It has the upper- and lower-case alphabet, numbers and the exclamation mark and the period. You can easily add extra characters if you like.

There is a #define that allows you to remove the lower case letters. Doing so strips out 108 bytes.
A sample program is shown below:

#include <Arduboy2.h>
#include "src/fonts/Font3x5.h"

Arduboy2Base arduboy;
Font3x5 font3x5 = Font3x5();

void setup() {



void loop() {

  if (!(arduboy.nextFrame())) return;

  font3x5.setCursor(12, 12);



You can also pass a line height to the constructor, as shown below:

Font3x5 font3x5 = Font3x5( 11 );

The two images below show the standard line height - 8 - and 11.

34%20pm 44%20pm

Check it out!

I also have a 4x6 font here > https://github.com/filmote/Font4x6

As a comparison …



Wow i love it.
We could make a full low ascii compliant variant with 128 letters for it in just 240 bytes.

128*5/8 = 80 times 3 for each column is 240.

It should be super simple by taking tinyfont as a base.

This reminds me, months ago I started on a font framework for the Arduboy but never got round to finishing it.


I didn’t use the lower case in my game - only the upper-case letters, numbers and the two punctuation marks. If someone wants to extend it please do however I like the idea of a super small foot print.


Actually, the font is nominally 3x5 but you may notice that some of the characters go below the line, such as the upper case W and the lower case g, j, p, q and y.

Would need to become :

128*6/8 = 96 times 3 for each column is 288.

But I have actually used the full 8 bits so that I can then use the standard drawSelfMasked() and drawErased(). I need to look at TinyFonts - are you ‘packing’ multiple characters into a byte? Is the code to unpack this and render heavier than using drawSelfMasked()? My assumption is that most people will be using the drawXX() functions in there programme anyhow.

That we can catch with a range exception where we move thy baseline by 1 px. I do that too for ascii 97-122. :smiley:

Yes, the font is 4px wide so i store 2 symbols in 4 bytes.
Masking is done super simple with binary operations and write the byte directly to the image buffer so i’m not dependent on any external drawing method.

uint8_t colData = pgm_read_byte(letter++);
if (c % 2 == 0) {
  // mask upper sprite
  colData &= 0x0f;
  // for every odd character, shift pixels to get the correct character
  colData >>= 4;

Had the same idea with tinyfont.
Everything is prepared for that. The font should have 2 additional bytes at the start for width and height and the code has to be modified to work with them instead of fixed 4x4. That would also mean that a symbol can go over multiple bytes which needs to be calculated. The math was the reason why i didn’t do it because i saw no demand for putting in the work. :sweat_smile:

Oh, I see you move all characters down one - even those that don’t need it? That’s clever.

My approach was slightly different.
Me being me, my approach involved templates.
So instead of baking the width and height into the font bytes, information about the font is stored as constants on a struct/class, e.g.

struct TinyFontInfo
  constexpr static const int Width = 5;
  constexpr static const int Height = 8;
  // Some other stuff...
struct Filmote5x8Info
  constexpr static const int Width = 5;
  constexpr static const int Height = 8;
  // Some other stuff...

Which is then passed into a template:

using TinyFontRenderer = FontRenderer<TinyFontInfo>;
using Filmote5x8Renderer = FontRenderer<Filmote5x8Info>;

The good thing about this is that it generates less code for individual fonts and would probably run faster, but the bad thing is that using lots of different fonts might end up generating more code than in your system.

Either way, I never finished the system.
I got stuck at deciding how to handle mapping the character indices to the ascii range.
Most Arduboy fonts only cover a subset of the ascii range.
I was going to fix that by using a per-font ascii char to font index function, but I got distracted before working out all the details (e.g. handling spaces and tabs).

(In hindsight, I think I was trying to be too generic and accommodating. I need to practise being mean enough to force people to adhere to rules that make life easier for me :P)

I aligned every lowercase letter in the bitmap so that is baseline is shifted up. So i realign ech lower case letter and punctuation. :slight_smile:


I can relate! Additionally, I believe that the effort we would put into this is in no relation to its use.

1 Like


I like to design code to be as generic as possible though.
If you make the code generic then it becomes a lot easier to re-purpose it for other systems/devices.

1 Like

That’s what i love about you ;D


A bit of a code, bromance going on here. We’ll just give you some privacy if you like?

1 Like

Ignoring the fact I’m heterosexual;

it would never work out - I’m married to programming.
Programming Mug

(Besides which, it’s only natural to want to bask in my epicness. :P)


Reminds me of this font which I used in MicroCity:

It is a 4x6 font which is actually really 3x5 with extra white space, but uses one bit to flag descenders like j and q to shift down one pixel. Each character is 2 bytes.


Just realised you haven’t got round to adding a licence for this.

If you use the github UI to add a a file called ‘LICENCE’ or ‘LICENSE’ it will give you a button to click so you can pick one to add easily.
That way it will show up on the main page of the repo as if it had been there since the start.


Done. the old BSD-3 clause.


I’m sure I’m missing this… but where do you define the width of the space character?



It seems that despite being a member variable, it’s never actually altered in any way, shape or form, so it might as well be a constant.

1 Like