A 3×3 font for Led Matrices

fontIcon

For the clock project I require text to appear in a small container, that is 4×4.

Since in this 4×4 I have to add vertical and horizontal spacing this ends with a 3×3 font.

Never in my life I designed a font, and if you check my hand writing… it is horrible. Luckily 3×3 only leads up to 2^9 options, 512 different characters, and I believed I could handle this.

After writing the font in a squared notebook, I checked that other fronts already exist, and, unsurprisingly they were very similar to mine.

Design constraints

I designed the font with low resolution in mind (a led matrix). In paper looks good enough, but presented with led lights at a correct distance reads much better.
It is a compromise to work on a specific environment.

The font

A picture is worth a thousand words:

caracters_lcase

It’s easy to see that for a 3×3 letter I had to compromise in a lot of things, so I obtained a font that can be read, but sometimes you need more imagination / training to know what’s going on in the display.

I am not a designer, so my decision process has been like: “have a beer and just check how each letter looks on a display”

So, just for fun, let’s comment on each letter:

A – A is Ok.
B – B resembles a lowercase b, since B was impossible.
C – C is perfect.
D – D is perfect too. Could have gone with something like lowercase d too.
E – E is a compromise. It tries to look like uppercase E, but it misses a separation.
F – F has the same problem as E.
G – G is terrible. You’ll be able to read it in a name, but by itself is not possible.
H – H is uppercase H, but could also work with lowercase h.
I – I is perfect.
J – J is easy to understand.
K – K is good.
L – L is good.
M – Duality with M and n. M is an uppercase M, of course diagonals are not possible at all.
N – N is the same as M without a dot.
O – O is clear as pie.
P – P is easy to get. But it can’t have the usual hole.
Q – Same as P but inverted. Resembling a lowercase q.
R – Lowercase r would be easier to represent, but uppercase R works too.
S – Like a snake. lacks width.
T – T is easy.
U – U is clear.
V – V is the same as A but inverted.
W – W is the same as M but inverted.
X – X is clear.
Y – Y is clear.
Z – Like S, it lacks width.

The numbers

The font also includes numbers of 4×3. From the clock design, I generate letters that leave spacing on top so I can use numbers 4 leds tall and 3 leds wide.

The good
Numbers like: 0, 1, 4, 6, 7, 9 are mostly easy to recognize.

The bad
3 and 8 compromise one of the typical spacings. But I believe are still easy to understand visually.

The ugly
2 and 5 are ugly, and there’s no denying that.

The matrices

In the blog post about the clock code, you’ll see that I’m programming in C. So for the font to work there, I generated 4×4 matrices of uint8_t type for each character.

This is not the ideal storage structure. But I have one motto: “Work first, optimize later”.

In this case, I only realized “Work first”, the optimization never happened at all. But you could store it in four uint8_t variables.

Let’s see a single example, just to get the idea, at the end you’ll a file with all the letters.

const char S_A[4][4] = {
    {0,0,0,0},
    {0,1,0,0},
    {1,0,1,0},
    {1,0,1,0},
};

The full source has some extra characters in 4×4 (because I used them in my project), and an enumeration that works as a map. It maps each ASCII value to the corresponding character matrix.

const char S_A[4][4] = {
    {0,0,0,0},
    {0,1,0,0},
    {1,0,1,0},
    {1,0,1,0},
};

const char S_B[4][4] = {
    {0,0,0,0},
    {1,0,0,0},
    {1,1,1,0},
    {1,1,1,0},
};

const char S_C[4][4] = {
    {0,0,0,0},
    {1,1,1,0},
    {1,0,0,0},
    {1,1,1,0},
};

const char S_D[4][4] = {
    {0,0,0,0},
    {1,1,0,0},
    {1,0,1,0},
    {1,1,0,0},
};

const char S_E[4][4] = {
    {0,0,0,0},
    {1,1,1,0},
    {1,1,0,0},
    {1,1,1,0},
};

const char S_F[4][4] = {
    {0,0,0,0},
    {1,1,1,0},
    {1,1,0,0},
    {1,0,0,0},
};

const char S_G[4][4] = {
    {0,0,0,0},
    {1,1,0,0},
    {1,0,1,0},
    {1,1,1,0},
};

const char S_H[4][4] = {
    {0,0,0,0},
    {1,0,1,0},
    {1,1,1,0},
    {1,0,1,0},
};

const char S_I[4][4] = {
    {0,0,0,0},
    {1,1,1,0},
    {0,1,0,0},
    {1,1,1,0},
};

const char S_J[4][4] = {
    {0,0,0,0},
    {0,1,1,0},
    {0,0,1,0},
    {1,1,1,0},
};

const char S_K[4][4] = {
    {0,0,0,0},
    {1,0,1,0},
    {1,1,0,0},
    {1,0,1,0},
};

const char S_L[4][4] = {
    {0,0,0,0},
    {1,0,0,0},
    {1,0,0,0},
    {1,1,1,0},
};

const char S_M[4][4] = {
    {0,0,0,0},
    {1,1,1,0},
    {1,1,1,0},
    {1,0,1,0},
};

const char S_N[4][4] = {
    {0,0,0,0},
    {1,1,1,0},
    {1,0,1,0},
    {1,0,1,0},
};

const char S_O[4][4] = {
    {0,0,0,0},
    {1,1,1,0},
    {1,0,1,0},
    {1,1,1,0},
};

const char S_P[4][4] = {
    {0,0,0,0},
    {1,1,1,0},
    {1,1,1,0},
    {1,0,0,0},
};

const char S_Q[4][4] = {
    {0,0,0,0},
    {1,1,1,0},
    {1,0,1,0},
    {1,1,0,0},
};

const char S_R[4][4] = {
    {0,0,0,0},
    {1,1,1,0},
    {1,1,0,0},
    {1,0,1,0},
};

const char S_S[4][4] = {
    {0,0,0,0},
    {0,1,1,0},
    {0,1,0,0},
    {1,1,0,0},
};

const char S_T[4][4] = {
    {0,0,0,0},
    {1,1,1,0},
    {0,1,0,0},
    {0,1,0,0},
};

const char S_U[4][4] = {
    {0,0,0,0},
    {1,0,1,0},
    {1,0,1,0},
    {1,1,1,0},
};

const char S_V[4][4] = {
    {0,0,0,0},
    {1,0,1,0},
    {1,0,1,0},
    {0,1,0,0},
};

const char S_W[4][4] = {
    {0,0,0,0},
    {1,0,1,0},
    {1,1,1,0},
    {1,1,1,0},
};

const char S_X[4][4] = {
    {0,0,0,0},
    {1,0,1,0},
    {0,1,0,0},
    {1,0,1,0},
};

const char S_Y[4][4] = {
    {0,0,0,0},
    {1,0,1,0},
    {0,1,0,0},
    {0,1,0,0},
};

const char S_Z[4][4] = {
    {0,0,0,0},
    {1,1,0,0},
    {0,1,0,0},
    {0,1,1,0},
};

const char S_0[4][4] = {
    {1,1,1,0},
    {1,0,1,0},
    {1,0,1,0},
    {1,1,1,0},
};

const char S_1[4][4] = {
    {0,1,1,0},
    {1,0,1,0},
    {0,0,1,0},
    {0,0,1,0},
};

const char S_2[4][4] = {
    {0,1,1,0},
    {1,0,1,0},
    {0,1,0,0},
    {1,1,1,0},
};

const char S_3[4][4] = {
    {1,1,1,0},
    {0,1,1,0},
    {0,0,1,0},
    {1,1,1,0},
};

const char S_4[4][4] = {
    {1,0,1,0},
    {1,1,1,0},
    {0,0,1,0},
    {0,0,1,0},
};

const char S_5[4][4] = {
    {1,1,1,0},
    {1,0,0,0},
    {0,1,1,0},
    {1,1,1,0},
};

const char S_6[4][4] = {
    {0,1,1,0},
    {1,0,0,0},
    {1,1,1,0},
    {1,1,1,0},
};

const char S_7[4][4] = {
    {1,1,1,0},
    {0,0,1,0},
    {0,1,0,0},
    {1,0,0,0},
};

const char S_8[4][4] = {
    {1,1,1,0},
    {1,0,1,0},
    {1,1,1,0},
    {1,1,1,0},
};

const char S_9[4][4] = {
    {1,1,1,0},
    {1,0,1,0},
    {1,1,1,0},
    {0,0,1,0},
};

const char S_NDEF[4][4] = {
    {1,1,1,1},
    {1,1,1,1},
    {1,1,1,1},
    {1,1,1,1},
};

const char S_SPACE[4][4] = {
    {0,0,0,0},
    {0,0,0,0},
    {0,0,0,0},
    {0,0,0,0},
};

const char S_DDOT[4][4] = {
    {0,0,0,0},
    {0,1,1,0},
    {0,0,0,0},
    {0,1,1,0},
};

const char S_EXCL[4][4] = {
    {0,1,1,0},
    {0,1,1,0},
    {0,0,0,0},
    {0,1,1,0},
};

const char S_QUE[4][4] = {
    {0,1,1,0},
    {1,0,0,1},
    {0,0,1,0},
    {0,0,1,0},
};

const char S_SLASH[4][4] = {
    {0,0,1,0},
    {0,0,1,0},
    {0,1,0,0},
    {0,1,0,0},
};

const char* ASCII_4x4[] = {
    *S_NDEF, //NULL //0
    *S_NDEF, //SOH
    *S_NDEF, //STX
    *S_NDEF, //ETX
    *S_NDEF, //EOT
    *S_NDEF, //ENQ //5
    *S_NDEF, //ACK
    *S_NDEF, //BEL
    *S_NDEF, //BS
    *S_NDEF, //TAB
    *S_NDEF, //LF //10
    *S_NDEF, //VT
    *S_NDEF, //FF
    *S_NDEF, //CR
    *S_NDEF, //SO
    *S_NDEF, //SI //15
    *S_NDEF, //DLE
    *S_NDEF, //DC1
    *S_NDEF, //DC2
    *S_NDEF, //DC3
    *S_NDEF, //DC4 //20
    *S_NDEF, //NAK
    *S_NDEF, //SYN
    *S_NDEF, //ETB
    *S_NDEF, //CAN
    *S_NDEF, //EM //25
    *S_NDEF, //SUB
    *S_NDEF, //ESC
    *S_NDEF, //FS
    *S_NDEF, //GS
    *S_NDEF, //RS //30
    *S_NDEF, //US
    *S_SPACE, //SPACE
    *S_EXCL, //!
    *S_NDEF, //"
    *S_NDEF, //# //35
    *S_NDEF, //$
    *S_NDEF, //%
    *S_NDEF, //&
    *S_NDEF, //'
    *S_NDEF, //( //40
    *S_NDEF, //)
    *S_NDEF, //*
    *S_NDEF, //+
    *S_NDEF, //'
    *S_NDEF, //- //45
    *S_NDEF, //.
    *S_SLASH, ///
    *S_0, //0
    *S_1, //1
    *S_2, //2 //50
    *S_3, //3
    *S_4, //4
    *S_5, //5
    *S_6, //6
    *S_7, //7 //55
    *S_8, //8
    *S_9, //9
    *S_DDOT, //:
    *S_NDEF, //;
    *S_NDEF, //< //60     *S_NDEF, //=     *S_NDEF, //>
    *S_QUE, //?
    *S_NDEF, //@

    *S_A, //A //65
    *S_B, //B
    *S_C, //C
    *S_D, //D
    *S_E, //E
    *S_F, //F //70
    *S_G, //G
    *S_H, //H
    *S_I, //I
    *S_J, //J
    *S_K, //K //75
    *S_L, //L
    *S_M, //M
    *S_N, //N
    *S_O, //O
    *S_P, //P //80
    *S_Q, //Q
    *S_R, //R
    *S_S, //S
    *S_T, //T
    *S_U, //U //85
    *S_V, //V
    *S_W, //W
    *S_X, //X
    *S_Y, //Y
    *S_Z, //Z //90

    *S_NDEF, //[
    *S_NDEF, //\
    *S_NDEF, //]
    *S_NDEF, //^
    *S_NDEF, //_ //95
    *S_NDEF, //`
    *S_NDEF, //`//PADDING (check table plz)

    *S_A, //a
    *S_B, //b
    *S_C, //c
    *S_D, //d //100
    *S_E, //e
    *S_F, //f
    *S_G, //g
    *S_H, //h
    *S_I, //i //105
    *S_J, //j
    *S_K, //k
    *S_L, //l
    *S_M, //m
    *S_N, //n //110
    *S_O, //o
    *S_P, //p
    *S_Q, //q
    *S_R, //r
    *S_S, //s //115
    *S_T, //t
    *S_U, //u
    *S_V, //v
    *S_W, //w
    *S_X, //x //120
    *S_Y, //y
    *S_Z, //z

    *S_NDEF, //{
    *S_NDEF, //|
    *S_NDEF, //} //125
    *S_NDEF, //~
    *S_NDEF //DEL
};

Closing

Designing real fonts it’s probably very difficult…

Advertisement

Leave a comment

Filed under code, curious, DIY

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.