Font formats are confusing because they accumulated 35 years of platform wars between Apple, Microsoft, and Adobe. TrueType was Apple and Microsoft's answer to Adobe's Type 1 monopoly in 1991. OpenType was Adobe and Microsoft's reconciliation in 1996. WOFF was the web's demand for smaller files in 2010. WOFF2 was Google's improvement on WOFF in 2014. Each format solved a real problem, and now we're left with four formats that all contain the same glyphs but differ in compression, features, and compatibility.
The good news: the decision tree is simple. Web fonts use WOFF2. Desktop fonts use TTF or OTF. Everything else is edge cases. This guide covers the technical differences, practical tradeoffs, and the one format shift (variable fonts) that's actually worth paying attention to.
TTF (TrueType): Universal Compatibility
TrueType was jointly developed by Apple and Microsoft in 1991 to break Adobe's stranglehold on digital typography. Apple needed a scalable font format for the Mac; Microsoft needed one for Windows. Rather than pay Adobe's licensing fees for Type 1 fonts, they built their own.
Technical Architecture
TTF stores glyph outlines as quadratic Bezier curves — defined by on-curve points and off-curve control points. Quadratic curves are simpler (and faster to rasterize) than the cubic Bezier curves used by PostScript/OTF, but they need more points to describe the same shape. A smooth curve that takes 4 points in cubic form might need 6-8 in quadratic.
TrueType's distinguishing feature is hinting: bytecode instructions embedded in the font that adjust glyph rendering at small sizes and low resolutions. Microsoft invested heavily in TrueType hinting for screen display — fonts like Verdana and Georgia were hand-hinted pixel-by-pixel at common sizes (10px, 11px, 12px). This made TrueType fonts look crisp on the 96-DPI CRT monitors of the 1990s and 2000s.
On modern high-DPI displays (Retina, 4K), hinting matters less because there are enough pixels to render curves smoothly without manual adjustment. But for low-DPI displays and small text sizes, well-hinted TrueType fonts still render more crisply than unhinted PostScript-based OTF fonts.
When to Use TTF
- Desktop installation — Every operating system, office suite, and design application supports TTF. It's the safest format for fonts you install system-wide
- Application embedding — Game engines (Unity, Unreal), mobile apps, and embedded systems almost universally support TTF. OTF support varies
- Maximum compatibility — If you're distributing a font and don't know what the recipient's system supports, TTF is the lowest-common-denominator choice
- Low-DPI rendering — Well-hinted TTF fonts look better than OTF at small sizes on non-Retina screens
OTF (OpenType): Professional Typography
OpenType launched in 1996 as a joint Adobe-Microsoft effort to unify the font landscape. The key innovation: an OTF file can contain either TrueType outlines (quadratic Bezier) or PostScript outlines (CFF/CFF2, cubic Bezier) in the same container format. In practice, when people say "OTF" they usually mean OpenType with PostScript outlines — the variant that uses cubic curves.
Cubic Bezier Curves
PostScript-flavored OTF uses cubic Bezier curves to describe glyph outlines. Cubic curves offer one more control point per segment than quadratic (two control points instead of one), allowing smoother curves with fewer points. For complex glyphs — ornamental capitals, script fonts, CJK characters — cubic curves produce smaller and more precise outlines.
A typical Latin font might be 5-15% smaller as OTF (PostScript) than TTF because the cubic curves need fewer data points to describe the same shapes. For CJK fonts with thousands of complex glyphs, the difference can reach 30-40%. This is why Adobe and most type foundries prefer the PostScript flavor of OpenType for their font libraries.
OpenType Layout Features
Both TTF and OTF support OpenType layout features — but OTF fonts from professional foundries are far more likely to include them. These features control advanced typographic behavior:
liga— Standard ligatures (fi, fl, ffi)dlig— Discretionary ligatures (ct, st)smcp— Small capitalsonum— Old-style (lowercase) numeralsswsh— Swash alternatesss01-ss20— Stylistic sets (alternate character designs)frac— Fractions (1/2 rendered as a proper fraction glyph)kern— Kerning pairs
In CSS, you access these via font-feature-settings: "liga" 1, "smcp" 1; or the higher-level font-variant-ligatures, font-variant-caps, etc. In design apps (InDesign, Illustrator, Figma), they appear in the OpenType or typography panel.
When to Use OTF
- Professional design work — When you need ligatures, stylistic alternates, small caps, and other advanced features for print or web typography
- Complex scripts — Arabic, Devanagari, and other scripts with contextual shaping rules are best served by OpenType's GSUB/GPOS tables
- CJK fonts — The cubic curve advantage reduces file sizes significantly for fonts with thousands of complex characters
- When file size matters on desktop — OTF fonts are typically 5-15% smaller than equivalent TTF fonts for Latin scripts
WOFF and WOFF2: Web Font Formats
WOFF (Web Open Font Format) isn't a new glyph format — it's a compressed container around a TTF or OTF file. The font outlines, hinting instructions, and OpenType features inside are identical. WOFF just adds compression and a metadata block (for license information) on top.
WOFF 1.0: The First Web Font Standard
WOFF 1.0 (2010) uses zlib/Deflate compression — the same algorithm as gzip. It typically produces files 40% smaller than raw TTF/OTF. WOFF was created because serving raw TTF or OTF files on the web was wasteful (no compression) and legally fraught (font foundries wanted a format that signaled "this is licensed for web use" rather than desktop installation).
WOFF 1.0 is supported by every browser since IE9 (2011). It's universally safe, but there's no reason to use it over WOFF2 in 2026.
WOFF 2.0: The Web Standard
WOFF 2.0 (2014) replaces Deflate with Brotli compression, plus font-specific preprocessing that transforms font tables into more compressible forms before compression. The result is files 30% smaller than WOFF 1.0 and 50-60% smaller than raw TTF.
Concrete example: Inter Regular (a popular UI font) as TTF is 316 KB, as WOFF is 184 KB, and as WOFF2 is 132 KB. For a CJK font like Noto Sans JP Regular, TTF is 4.7 MB, WOFF is 2.9 MB, and WOFF2 is 1.6 MB. Those savings matter when fonts block text rendering.
Browser support is at 97%+ globally. Every modern version of Chrome, Firefox, Safari, and Edge supports WOFF2. The only browsers that don't are IE11 (dead) and very old Android WebView versions. Use WOFF2 for all web fonts. This is not a nuanced decision.
Do You Still Need a WOFF Fallback?
In 2020, the standard advice was to provide WOFF2 with a WOFF fallback in your @font-face declaration. In 2026, the WOFF fallback serves the roughly 0.5% of browsers that support WOFF but not WOFF2. Whether that 0.5% matters depends on your audience.
If you're building for a general web audience, WOFF2-only is fine. If you support enterprise environments with locked-down browsers or older Android devices, add the WOFF fallback — it costs nothing except an extra file on your server. A TTF fallback after WOFF is almost never needed.
@font-face {
font-family: 'Inter';
src: url('/fonts/inter.woff2') format('woff2'),
url('/fonts/inter.woff') format('woff');
font-display: swap;
}
Variable Fonts: One File, Every Weight
Variable fonts (OpenType 1.8, 2016) are the most significant change in font technology since OpenType itself. A single variable font file contains a continuous range of styles — weight, width, slant, and custom axes — that would traditionally require 8-20 separate font files.
File Size Impact
A traditional font family with 9 weights (Thin through Black) in regular and italic = 18 files. As WOFF2, that might total 2.4 MB. The variable font equivalent containing all 18 styles in one file typically weighs 300-500 KB as WOFF2. That's a 4-8x reduction in total payload.
The saving isn't 18x because the variable font contains interpolation data (delta tables) that static fonts don't. But the deltas are far smaller than the redundant glyph data across 18 near-identical files. For web performance, variable fonts are a decisive win if you use more than 2-3 weights/styles of the same typeface.
Using Variable Fonts in CSS
Variable fonts work with all container formats — TTF, OTF, WOFF, and WOFF2. On the web, use WOFF2 as always:
@font-face {
font-family: 'Inter Variable';
src: url('/fonts/inter-variable.woff2') format('woff2-variations');
font-weight: 100 900;
font-display: swap;
}
/* Use any weight, not just predefined stops */
body { font-weight: 370; }
h1 { font-weight: 720; }The font-weight: 100 900 in @font-face tells the browser the supported range. You can then set any integer weight in that range — not just the traditional 100/200/300/400/500/600/700/800/900 stops. Intermediate weights like 350 or 550 render correctly because the browser interpolates between the font's master designs.
Custom axes use font-variation-settings: font-variation-settings: "wght" 450, "wdth" 85;. Standard axes (weight, width, slant, italic, optical size) have dedicated CSS properties; custom axes (like grade, casual, cursive in Recursive) require font-variation-settings.
Browser and Application Support
Variable font support is excellent: Chrome 66+, Firefox 62+, Safari 11+, Edge 17+ — all released before 2019. As of 2026, 96%+ of browsers support variable fonts. Major design tools (Figma, Sketch, Adobe CC, Affinity) all support them. The only holdouts are some older PDF generators and email clients.
Google Fonts serves variable fonts automatically when available. If you self-host, use the variable version to reduce total font payload. The only reason to prefer static fonts is if you use exactly one weight and style — in that case, the static file is slightly smaller than the variable file because it has no interpolation data.
Dead Formats: EOT and SVG Fonts
EOT (Embedded OpenType) was Microsoft's proprietary web font format, supported only by Internet Explorer. IE11 was the last browser to support it, and IE is dead. Remove EOT from your @font-face stack if it's still there — it's dead weight.
SVG fonts stored glyph outlines as SVG path data inside an XML document. They were briefly used for iOS Safari before it supported WOFF, and they're technically still needed for colored emoji in some older Safari versions. For text fonts, SVG fonts have been deprecated by every browser. Don't use them.
If you inherited a project with a four-format font stack (EOT, WOFF, WOFF2, TTF), simplify it to WOFF2 only, or WOFF2 + WOFF if you need the marginal fallback coverage.
Self-Hosting vs Google Fonts
Google Fonts serves fonts from its CDN with automatic WOFF2 delivery, subsetting by language, and format negotiation. Convenient. But two developments have weakened its performance advantage:
- Cache partitioning (Chrome 86, 2020): Chrome no longer shares cached resources across sites. A font cached from Google Fonts on Site A is re-downloaded on Site B. The "everyone shares the Google Fonts cache" benefit is gone
- Third-party request overhead: Loading from fonts.googleapis.com requires DNS lookup + TCP connection + TLS handshake to a different origin. Self-hosted fonts use the existing connection to your origin, eliminating 100-300ms of connection overhead
Self-hosting WOFF2 files is now faster in most cases. Download the fonts from Google Fonts, convert to WOFF2 if not already (convert TTF to WOFF2 here), and serve them from your own domain with appropriate cache headers (Cache-Control: public, max-age=31536000, immutable).
Subsetting is the other optimization that matters. If your site is English-only, you don't need Greek, Cyrillic, Vietnamese, or CJK glyphs. Subsetting strips unused character ranges, which can reduce a CJK font from 1.6 MB to 30 KB if you only need Latin characters. Google Fonts does this automatically based on the text= parameter or unicode-range splitting. For self-hosted fonts, use pyftsubset (part of fonttools) or tools like glyphhanger.
Font Conversion: What's Safe
| Conversion | Quality | Notes |
|---|---|---|
| TTF to WOFF2 | Lossless | Just adds Brotli compression. No glyph data changes. Convert here |
| OTF to WOFF2 | Lossless | Same — compression wrapper only. Convert here |
| WOFF2 to TTF | Lossless | Just decompression. The output is the original TTF. Convert here |
| WOFF to WOFF2 | Lossless | Decompress WOFF, recompress as WOFF2. Convert here |
| TTF to OTF | Slight loss possible | Converts quadratic to cubic curves. Usually imperceptible, but curve conversion is technically lossy. Convert here |
| OTF to TTF | Slight loss possible | Converts cubic to quadratic curves — needs more points, may subtly alter curves. Convert here |
The key principle: converting between container/compression formats (TTF/OTF to/from WOFF/WOFF2) is lossless. The glyphs, metrics, hinting, and features are preserved exactly. Converting between outline types (TTF quadratic vs OTF cubic) involves mathematical curve conversion that can introduce sub-pixel differences — practically invisible, but technically lossy.
Web Font Performance
Fonts block text rendering. When the browser encounters text styled with a web font that hasn't loaded yet, it has three options: show nothing (FOIT — Flash of Invisible Text), show a fallback font and swap later (FOUT — Flash of Unstyled Text), or show the fallback permanently if the font takes too long.
Best practices:
font-display: swap— Show fallback text immediately, swap to the web font when it loads. Users see content instantly with a brief font change. This is the right default for body textfont-display: optional— Use the web font only if it's already cached; otherwise use the fallback permanently. Best for subsequent page loads — zero layout shift, zero FOUT- Preload critical fonts:
<link rel="preload" href="/fonts/inter-var.woff2" as="font" type="font/woff2" crossorigin>tells the browser to start downloading the font before it discovers it in CSS. Saves 100-500ms on LCP for text-heavy pages - Limit font weights: Each weight/style is a separate HTTP request (unless using variable fonts). Two weights (regular + bold) is the practical minimum for most sites. Going beyond 3-4 weights starts hurting performance
- Subset aggressively: If you only need ASCII + basic punctuation, subset the font down. A full Inter WOFF2 is 132 KB; subsetted to Latin-only it's ~35 KB; subsetted to ASCII-only it's ~18 KB
Format Comparison Table
| Feature | TTF | OTF | WOFF | WOFF2 |
|---|---|---|---|---|
| Typical size (Latin, Regular) | 250-350 KB | 200-300 KB | 150-200 KB | 100-140 KB |
| Compression | None | None (CFF is compact) | zlib/Deflate (~40%) | Brotli (~55%) |
| Web use | Works but wasteful | Works but wasteful | Good (legacy) | Optimal |
| Desktop use | Universal | Universal | No | No |
| Curve type | Quadratic Bezier | Cubic Bezier (CFF) | Either (wrapper) | Either (wrapper) |
| Variable font support | Yes (since 2016) | Yes (CFF2, since 2016) | Yes | Yes |
| OpenType features | Full support | Full support | Full support | Full support |
| Browser support | All | All | All (IE9+) | 97%+ (no IE) |
| Hinting | Bytecode (detailed) | Type 1 (basic) | Preserved | Preserved |
Font format choice is one of the few technical decisions with a genuinely clear answer. WOFF2 for the web. TTF or OTF for the desktop. Variable fonts when you need multiple weights. Everything else is legacy.
If you have fonts in the wrong format, the conversions are clean: TTF to WOFF2, OTF to WOFF2, WOFF to WOFF2 for web optimization, or WOFF2 to TTF when you need a desktop-installable file. The glyphs are identical across all these formats — you're just changing the container and compression.