Methodology

How we score font alternatives

BrandSweets ranks free, open-licensed fonts as alternatives to commercial typefaces with a fixed, published formula — not a model, not a vote, not a vibe. The same two fonts always produce the same number, every axis of the score is shown on the page, and you can reproduce any result from the data we publish. This page is the formula.

The code that implements it is src/server/fonts/alternatives.ts; the measurements come from scripts/fonts/enrich-metrics.mjs; scripts/fonts/audit-alternatives.ts checks every reference font's top three against the rules below and fails on any cross-family or sub-0.45 result. All are versioned with this document.

The score

For a commercial reference font C and an open font O:

score(C, O) = 0.35 · classification
            + 0.25 · metrics
            + 0.20 · traits
            + 0.10 · useCase
            + 0.05 · language
            + 0.05 · variable

Every axis is a number in [0, 1], so the total is too. A total of 1.0 would mean "indistinguishable on everything we measure"; in practice the best matches land around 0.85–0.95 and a different-family font is capped at 0.2 (see the gate below).

0. The family gate

Before any of the six axes is weighed, every candidate has to pass a family gate. Classes roll up into coarse families:

FamilyClasses
sansgrotesque, neo-grotesque, geometric, humanist, industrial, rounded, techno
serifold-style, transitional, didone, scotch, contemporary-serif
slabslab, clarendon, geometric-slab
glyphicglyphic
monospacemonospace
scriptscript, brush, handwriting
blackletterblackletter
stencilstencil
pixelpixel
display-fatfat-face
displaydisplay, retro

A font's families are the families of its classes, in order. display is a modifier, not a lineage — a "geometric, display" face is a sans that happens to be cut for headlines — so it only counts as a family when it is the only one. An unclassified open font falls back to its catalogue category (sans → sans, mono → monospace, handwriting → script, …).

Two rules, and the audit script (scripts/fonts/audit-alternatives.ts) enforces both across every reference font:

  • Specialist families bind. blackletter, script, glyphic, stencil, monospace and pixel are families where "roughly the same shape" means nothing unless you are actually in the family. A reference whose primary family is specialist (Trajan → glyphic, Old English Text → blackletter, Courier → monospace) lists only fonts that share that family — everything else is excluded outright, not merely marked down. Symmetrically, an open font whose primary family is specialist (a stencil, a pixel face, a script) only appears on a page that includes that family, even if a secondary "display" class would otherwise overlap.
  • Mismatches are capped. For every other reference, a candidate that shares no family keeps its axis scores for transparency but its total is capped at 0.2, so a pixel font can never out-rank a real serif on a serif page just by matching on role and language coverage.

The gate outcome is printed on every result ("Same family: sans.", "… is a stencil face; Windsor is serif — total capped at 0.2.") so the cap is never silent. Curated picks (below) are a human's call and are not gated — the audit flags them instead if they ever cross families.

No close alternative. When nothing in a reference font's family scores at least 0.45, we mark the reference noAlternatives rather than pad the page: it renders a short, honest "no close free alternative yet" page with the nearest faces by style for orientation, is noindex, and is left out of the sitemap and the "a free alternative to …" links on open font pages. (At the time of writing no reference needs this — every one of the 164 has a same-family #1 above 0.45.)

1. Classification — weight 0.35

Typographic class is the strongest single predictor that two faces will feel interchangeable, so it carries the most weight.

RelationshipScore
Same class (e.g. both neo-grotesque)1.0
Sibling classes (see table)0.6
Same category (both sans, both serif …) but different class0.3
Different category0

Sibling pairs are hand-defined and symmetric:

grotesque ↔ neo-grotesque, grotesque ↔ industrial, neo-grotesque ↔ geometric, geometric ↔ rounded, humanist ↔ glyphic, industrial ↔ techno, old-style ↔ transitional, old-style ↔ contemporary-serif, transitional ↔ contemporary-serif, transitional ↔ scotch, scotch ↔ didone, slab ↔ clarendon, slab ↔ geometric-slab, display ↔ fat-face, display ↔ retro, script ↔ brush, script ↔ handwriting, brush ↔ handwriting.

The class vocabulary (grotesque, neo-grotesque, geometric, humanist, industrial, rounded, techno, old-style, transitional, didone, scotch, contemporary-serif, glyphic, slab, clarendon, geometric-slab, monospace, display, fat-face, stencil, script, brush, handwriting, blackletter, retro, pixel) is shared by the commercial and open catalogues. Classification is hand-authored for the commercial reference list and for the ~450 most-used open families, including every blackletter, glyphic, stencil, pixel and formal-script face the gate depends on; unclassified open families can only earn the same-category 0.3 on this axis, which naturally keeps them below the vetted set.

2. Metrics — weight 0.25

We compare real proportions. For the open catalogue these are measured from the font files with opentype.js; for commercial fonts (which we never download or host) they are publicly documented or well-known approximate values, flagged approx in the data.

Four axes, each producing a distance in [0, 1]; the score is 1 − mean(distances) over the axes both fonts have:

AxisDistance
x-height ratio (x-height ÷ em)min(1, |Δ| ÷ 0.12)
cap-height ratio (cap-height ÷ em)min(1, |Δ| ÷ 0.10)
width class (condensed / normal / wide)0 same, 0.5 one step apart, 1 two steps
stroke contrast (low / medium / high)0 same, 0.5 one step apart, 1 two steps

So an x-height that differs by 0.03 em costs a quarter of that axis; a difference of 0.12 em or more costs the whole axis.

Two guards keep this axis honest:

  • Caps-only references (Trajan, Copperplate, Stencil, Bank Gothic) have no lowercase and therefore no x-height. Their data carries none, and the comparison is skipped rather than faked with the cap height — otherwise every alternative with a lowercase would be a full axis away.
  • Broken measurements are dropped. An open-font x-height outside 0.35–0.75 em or cap height outside 0.55–0.85 em is not a Latin text or display face measured correctly — it is a ruled "guides" cut whose x outline includes the guide lines, a barcode or charted font, or a redacted face. Those values are omitted at generation time and again at load (METRIC_BOUNDS in catalog.ts), so the axis simply has one fewer term for that font. The audit script reports how many were dropped.

How the open-font measurements are made (regular upright cut, Latin):

  • x-height / cap-height: the OS/2 table's sxHeight / sCapHeight, cross-checked against the outline height of the glyphs x and H; when the table disagrees with the outline by more than 15% (common in display and script fonts) the outline wins.
  • Width class: the OS/2 usWidthClass when the font sets one (1–4 condensed, 6–9 wide). Otherwise a proportion proxy: mean advance width of a–z ÷ cap height, so a small-x-height Garamond isn't misread as condensed. Below 0.65 → condensed, 0.88 and above → wide. Calibration: Oswald 0.49, Roboto Condensed 0.62 (condensed); Roboto 0.70, Inter 0.74, Montserrat 0.82 (normal); Unbounded 0.92, Syncopate 1.27 (wide).
  • Stroke contrast: from the glyph o (fallback O). We flatten the outline to line segments, cast three horizontal scanlines at 40/50/60% of the bowl's height (the first entry/exit pair is the thickness of the left stem) and three vertical scanlines at 40/50/60% of its width (the first pair is the thickness of the top hairline), and divide the median thick stroke by the median thin stroke. Below 1.5 → low, above 2.5 → high. Calibration: Inter 1.11, Roboto 1.17 (low); Merriweather 1.91, Libre Baskerville 2.10 (medium); Playfair Display 3.54, Bodoni Moda 4.21 (high).

This is a heuristic, not typographic truth — it's designed to rank faces that look alike, and it is one axis of six. We publish the thresholds so you can argue with them.

3. Traits — weight 0.20

Jaccard overlap (|A ∩ B| ÷ |A ∪ B|) between the two fonts' trait sets. Commercial traits are hand-authored. Open-font traits are the curated moods on our deep-tagged families plus traits derived deterministically from the data: condensed / wide from the width class, monoline / high-contrast from the contrast class, and class-implied traits such as geometric, humanist, neutral, industrial, classic, elegant, technical.

4. Use case — weight 0.10

Jaccard overlap of intended roles (display, heading, body, ui, code, longform). Open fonts use their curated use-fit where we have it, otherwise a category default (sans → body/ui/heading, serif → body/longform/heading, mono → code/ui, display → display/heading, handwriting → display).

5. Language — weight 0.05

The share of the reference's script subsets (latin, latin-ext, cyrillic, greek, vietnamese, …) that the alternative covers — coverage, not Jaccard, so an alternative that supports more scripts than the reference is never penalised for it. A small weight on purpose: coverage matters for a usable substitute, but it says nothing about how the font looks. The page tells you which subsets the alternative lacks.

6. Variable — weight 0.05

1.0 when both are variable, both are static, or the open font is variable and the commercial one isn't (a superset). 0 only when the commercial font is variable and the alternative ships static instances.

Curated picks

For the most-searched commercial fonts (Helvetica, Gotham, Proxima Nova, Futura, Garamond, Bodoni …) we also hand-pick up to three alternatives with a one-line reason a designer would recognise. Curated picks are always listed first and are labelled as curated, with their computed score shown alongside so you can see where the formula and the designer agree — or don't. Everything after them is pure formula.

Ordering and ties

Results are sorted by score, descending. Exact ties break on the open font's popularity (descending), then alphabetically by slug — so the list is fully deterministic and never depends on evaluation order.

What we don't do

  • We do not run a model, an embedding, or any learned ranking.
  • We do not host, embed, subset or redistribute commercial fonts. Commercial names are used only to identify the typeface (nominative use); each page links to the rights holder.
  • We do not claim an alternative is a "clone" or "metric-compatible" unless the alternative's own project says so (e.g. Arimo → Arial, Carlito → Calibri, Tinos → Times New Roman, Gelasio → Georgia, Cousine → Courier New).

Reproducing a score

Every alternative page shows the six axis scores, the weighted contributions (which sum to the total unless the family gate capped it), the gate outcome, and the sentence behind each axis — e.g. "x-height 0.55 vs 0.52 for Helvetica (close); cap height 0.73 vs 0.72 for Helvetica (near-identical); both normal width; both low stroke contrast." The first number is always the alternative's, the second the reference's. The open-font metrics are in the catalogue data we serve; the commercial reference values are listed on each commercial font's page, marked approximate where they are.

If you think a classification, a metric or a curated pick is wrong, tell us — corrections go straight into the data files.

mcpConnect your agent — the same score over MCP
live endpoint

find_alternatives returns exactly the numbers described above — total, six axis scores, weighted contributions and the sentence behind each — so an agent can justify a pick the same way this page does. Read-only, no key.

endpointhttps://mcp.brandsweets.com/fonts/mcp
claude mcp add --transport http brandsweets-fonts https://mcp.brandsweets.com/fonts/mcp
try this prompt

find a free alternative to Helvetica and show me the score breakdown

Want the agent to remember your picks and write them into a living brand? Create a free BrandSweets workspace.