Stem 7 · v1.001 · OFL 1.1

A pixel font by David Hatley

Stem 7

A pixel font that refuses to blur. Set it at 16, 32, 48, 72 or 120 px and every edge lands on a whole pixel. Other sizes work too. They just look disappointed in you.

120 glyphs a style3 styles3.4 KB a WOFF25 sizes0 grey pixels
00Type tester

Try it

Every edge on a whole pixel. 0123456789

48 px · 6 px cells · click the text to edit it

01How it was made

It started as a logo

My mark is a d and an h sharing one stem, drawn on 7 × 7 cells. Its stem set the cap height, its bar set the x-height, and the rest of the alphabet had to fit around it. Type dh in Stem 7 and you get the mark back.

Plate 1 · The mark is the grid7 cells to the cap

The mark · 7 × 7

Caps are 7 cells, the x-height is 4, the descender is 2. The bar of the mark sits on row 4, so every bar, dash, arrow and operator in the font sits there too.

02The source

Drawn in a text file

No font editor. Each glyph is nine rows of X and . in a plain text file, with the reason for the drawing written above it. Diffs are readable and every argument with myself is in version control.

Plate 2 · src/regular.s7
regular.s7

The built font at 120 px

One CLI reads these files and builds the TTF and WOFF2. It also lints the drawings, derives the kerning, renders proofs and counts grey pixels.

03The em

Eight cells to the em

The em is 2,400 units, or 8 cells of 300. So one cell is always the font size divided by eight, and at the five house sizes that comes out to a whole number of pixels.

Plate 3 · One cell = size / 8UPM 2400

Font hinting is the usual fix for blurry pixel fonts. I tested it, and Chromium on Linux ignored the hints in every rendering mode, so the outlines do all the work themselves.

04The test

Zero grey pixels

A crisp pixel font has two colours: ink and not ink. Any pixel in between is the rasteriser guessing. This loupe renders the font in your browser right now and counts the guesses.

Plate 4 · Live, in this browser
0grey pixels of 0
Share of grey pixels by size, before and after
Build16324872120
The first build (1,000 UPM)63.5%35.5%25.9%18.4%0%
Stem 7 1.001, Regular00000
Stem 7 1.001, Bold00000

Grey pixels are marked in vermilion. The table comes from the build's own test, which renders every style at every size in headless Chromium and fails the build if one grey pixel shows up.

05Grid

The one with gaps

Grid draws every cell as its own square, with a gap cut from the top and right. No single gap stays whole at every size, so Grid is a variable font: the gap is half a cell up to 40 px and a third of a cell from 48 px. Browsers pick it automatically.

Plate 5 · Stem 7 Grid, opsz 8–160gap in px

At 16 and 32 px it is crisp but light, so it reads best from 48 px up.

06Revisions

Three rounds of arguing with myself

The first draft looked fine as an alphabet and fell apart in words. place read as ploce, and 64×64 had a lowercase x in the middle. Each fix below has its reason written into the source.

Plate 6 · Round 0 → 1.001before · after

Grey is round 0, black is the shipped drawing. Most fixes came from setting real words and reading them at 1:1, not from staring at the alphabet.

07Bold

Bold, by hand

The bold draft is a rule: double every column that carries a stem. That works for H and falls apart on diagonals, curves and dots. 64 of the 120 bold glyphs were redrawn by hand.

Plate 7 · Auto-bold draft → drawnchanged cells in cobalt

Lint now fails any Bold glyph with a stroke one cell thin, which is how the rule's misses were found.

08Spacing

Kerning in whole cells

Kerning comes from the drawings, not from my eyes at 2 a.m. For each pair, compare the facing edges row by row and average the gap. Under 3 cells: leave it. 3 to 5: pull in one cell. 5 or more: two. Never closer than one cell, and never into a mouth like C's.

Plate 8 · Row gaps

09Character set

Every character

Basic Latin, curly quotes, dashes, arrows, the mark and seven project glyphs. Pick one to see how it is built.

10In use

Good at small, exact things

HUDs, counters, labels, numbers that change while you watch. Figures are tabular, so they don't jiggle. Not recommended for your novel.

HP12/12LV07
A game HUD at 32 px
00:00:00
A clock at 72 px. Tabular figures, no jiggle
nibblePROTOTYPE · 2026
A title card. The glyph is U+E003, in the font
PLATE 2.1 · TILE RULESDRAFT
SEED 4127
A plate header and seed strip at 16 px
$ stemctl crispRegular  16  0 grey  PASSRegular 120  0 grey  PASSall crisp
Build output, set in its own font
A menu board. Dot leaders not included
11Install

Get it

TTF and WOFF2 for Regular, Bold and Grid, a ready-made stem7.css and the licence. One zip, 30 KB. No account, no newsletter, no "pay what you want" guilt screen.

Download stem7.zip

Desktop: open the TTF files and install. Web: use the WOFF2 files.

12Small print

Changelog and licence

VersionWhat changed
1.001F now kerns against a, c, e, g, m, n, o, r, u, w and y. The source asked for it all along; the build quietly dropped those 11 pairs. A new test shapes every pair with HarfBuzz so it can't happen again.
1.000Regular, hand-drawn Bold and variable Grid. 0 grey pixels at all five sizes.

Free for anything, commercial included, under the SIL Open Font License 1.1. There is no reserved font name, so subset it, convert it, rename it. Just don't sell the font files on their own.