Web Font Performance: Weighing @font-face Options and Alternatives

1823 13

Web fonts are a key ingredient in today’s website designs; at my employer (AOL) it is a given redesigns will feature downloadable fonts. The days of maintaining a sprite full of graphic text headlines are behind us. We’ve moved on—but what approach yields the best performance?

The goal of this article is to look at the various web font implementation options available, benchmark their performance, and arm you with some useful tips in squeezing the most bang for your font byte. I will even throw in a new font loader as a special bonus!

Font Hosting Services vs. Rolling Your Own

There are two approaches you can take to get licensed, downloadable fonts on to your web pages: font hosting services and do-it-yourself (DIY).

Font Hosting Services like Typekit, Fonts.com, Fontdeck, etc., provide an easy interface for designers to manage fonts purchased, and generate a link to a dynamic CSS or JavaScript file that serves up the font. Google even provides this service for free. Typekit is the only service to provide additional font hinting to ensure fonts occupy the same pixels across browsers.

The DIY approach involves purchasing a font licensed for web use, and (optionally) using a tool like FontSquirrel’s generator to optimize its file size. Then, a cross-browser implementation of the standard @font-face CSS is used to enable the font(s). This approach ultimately provides the best performance.

Both approaches make use of the standard @font-face CSS3 declaration, even when injected via JavaScript. JS font loaders like the one used by Google and Typekit (i.e. WebFont loader) provide CSS classes and callbacks to help manage the “FOUT” that may occur, or response timeouts when downloading the font.

What the FOUT?

FOUT, or “Flash of Unstyled Text,” was coined by Paul Irish and is the brief display of the fallback font before the web font is downloaded and rendered. This can be a jarring user experience, especially if the font style is significantly different.

FOUT of some form exists in all versions of Internet Explorer and Firefox 3.6 and lower. Check out my my demo.

Here are my recommendations for avoiding the FOUT:

Host the fonts on a CDN
GZIP all font files except .woff (already compressed)
Cache all font files for 30+ days by adding a future expires cache header
Remove excess glyphs (characters) from the font files
Ensure @font-face is the first rule of the first stylesheet on the page (IE)
Still have a FOUT? Read on, a JavaScript font loader may be in order.

Removing Excess Font Glyphs

Font Squirrel has an awesome tool that lets you take a desktop font file and generate its web counterparts. It also allows you to take a subset of the font, significantly reducing file size.

To show just how significant, I added Open Sans and tried all three settings:

From the table above, it should be obvious that the byte size is directly correlated to the # of glyphs (characters) in the font file.

I suggest you follow along with me at www.fontsquirrel.com/generator!

The Basic setting leaves the characters untouched. Optimal reduces the characters to around 256, the Mac Roman character set. We are able to see the greatest savings by selecting Expert mode and only including the Basic Latin set, then manually adding in characters we need.

Here are my recommended Expert FontSquirrel settings (screenshot):

    • Under Rendering, uncheck Fix Vertical Metrics
    • Under Subsetting, check Custom Subsetting…
    • Under Unicode Tables, check only Basic Latin
      Note: This assumes the fonts will only use English characters; for other languages add the characters you need.
    • If you are typography nerd, copy and paste ‘ ’ “ ” into the Single Characters field
    • Verify your Subset Preview; adjust if needed

FontSquirrel settings

  • Under Advanced Options, give your font a suffix based on the subset (i.e. latin)

JavaScript Font Loaders

Typekit and Google joined forces to create an open source WebFont Loader that provides CSS and JavaScript hooks indicating a font’s status as it downloads. This can be useful in normalizing the FOUT across browsers by hiding the text and adjusting CSS properties so that both fonts occupy the same width.

The three states it tracks are loading, active, and inactive (timeout). Corresponding CSS classes (wf-loading, wf-active, and wf-inactive) can be used to control the FOUT by first hiding headings and then showing them when once downloaded:

h1 {
 visibility: hidden;
}
.wf-active h1 {
 visibility: visible;
}

JavaScript hooks for these same events are also available via callbacks in the configuration object:

 WebFontConfig = {
 google: {
 families: [ 'Tangerine', 'Cantarell' ] // Google example
 },
 typekit: {
 id: 'myKitId' // Typekit example
 },
 loading: function() {
 // JavaScript to execute when fonts start loading
 },
 active: function() {
 // JavaScript to execute when fonts become active
 },
 inactive: function() {
 // JavaScript to execute when fonts become inactive (time out)
 }
};

The WebFont loader also includes callbacks for fontactive, fontloading, and fontinactive that is fired each time a font updates, giving you control at a font level. For more information, check out the WebFont Loader documentation.

Introducing Boot.getFont, a fast and tiny Web Font Loader

I haven’t seen one out there (leave a comment if I missed it) so I wrote a little font loader that provides the same hooks for loading fonts called getFont as part of my Boot library.

It weighs in at 1.4 K after GZIP (vs. 6.4 KB Google, 8.3 KB Typekit) and easily fits into your existing library. Simply change the “Boot” string at the end of the file to update the namespace (i.e., jQuery).

Fonts are loaded via a JavaScript function, and a callback can be supplied that executes once the font has finished rendering.

Boot.getFont("opensans", function(){
 // JavaScript to execute when font is active.
});ae748df820b0

Boot.getFont provides similar CSS classes to the WebFont Loader but at a font level, affording precise control:

.wf-opensans-loading {
 /* Styles to apply while font is loading. */
}
.wf-opensans-active {
 /* Styles to apply when font is active. */
}
.wf-opensans-inactive {
 /* Styles to apply if font times out. */
}

You can easily configure it to grab fonts based on your directory structure by loading a configuration object:

// Global
Boot.getFont.option({
 path: "/fonts/{f}/{f}-webfont" // {f} is replaced with the font name
});
// Font-specific
Boot.getFont({ path: "http://mycdn.com/fonts/{f}/{f}-wf" }, "futura" );

I haven’t had time to document all the goods, but the library is available here if you are interested.

Development: boot.getfont.js
Production: boot.getfont.min.js

Gentlefonts, start your engines!

Now that we are armed with the knowledge needed to ensure fast-loading fonts, let us take a look at the performance of the implementation options.

I set up the following test pages, loading the same web font (Open Sans), spanning DIY and various hosting options at Typekit and Google:

    • System: Our control test; this page does not load any fonts and uses Arial.
    • FontSquirrel Optimal: FontSquirrel generator’s recommended ‘Optimal’ setting and FontSpring’s cross-browser @fontface declaration. Fonts hosted on the same server as the web page like most small websites.
    • FontSquirrel Expert: Used recommended tips above to trim font file size using the FontSquirrel Generator, I replaced the ‘Optimal’ font kit in the above test with a minimal ‘Basic Latin’ character set.
    • FontSquirrel Expert (CDN): Same as the above test, however fonts are hosted from a CDN on a different domain.
    • Boot.getFont: This test updated the ‘FontSquirrel Expert’ test to use my Boot.getFont JavaScript library.
    • Boot.getFont (CDN): Same as Boot.getFont test, except font files are hosted from a CDN on a different domain.
    • Google Web Fonts Standard: I chose Google to represent a free font hosting service, and since this is a speed test, and Google is all about speed, I figured they should be in the race. Google provides 3 implementation options, this being the default—a <link> element pointing to a dynamic stylesheet that loads the font(s). Note: I left out the ‘Import’ option as results were nearly identical to ‘Standard’ option.
    • Google Web Fonts JavaScript: This option includes the WebFont loader discussed above to load the fonts, hosted from Google’s servers.
    • Typekit: Here, I created a kit at Typekit and used the options that provided the smallest font file.

I used webpagetest.org and loaded each test page 10 times in Chrome, Firefox 7, IE7, IE8, and IE9 over a 1.5 mbps DSL connection. We are comparing implementation, so I took the fastest test to weed out network latency issues and other causes of variance in the data.

Here is how they stack up, ranked by the fastest time (ms) across browsers:

Fastest Load Times

Take some time to digest the data. To better compare implementations across browsers, check out these charts:

IE9

ie web font

Chrome

webfont benchmarks chrome

Firefox

webfont-benchmarks-firefox

The Do-It-Yourself implementations were consistently the fastest, especially when combined with a CDN. This is due to physics—less bytes, requests, and CPU overhead are required to serve the font.

It is interesting to compare Google Web Fonts (GWF) to Typekit since they use the same core loader, but that is where the similarities end:

Google Web Fonts in Firefox (1254ms): JS » CSS » Font

Typekit in Firefox (795ms): JS » CSS Data URIs

In browsers that support them, Typekit uses Data URIs in the CSS to load the font, whereas GWF first loads the JS, then the CSS, and finally the font. Typekit uses this approach in IE 8 and lower where Data URIs are not supported, ending up with slower load times in those browsers.
Google is also slower because of their multiple DNS lookups; Typekit rightly uses one domain for all assets.

I was impressed by the performance of Boot.getFont, which ended up being faster (sometimes by a hair, sometimes more) than the standard @font-face CSS in all cases. My hypothesis is that somehow the JS triggers a reflow/repaint that forces the fonts to download sooner in all browsers.

Final Thoughts

While this article could probably be split into several, I wanted a single place to document implementation choices, tips for optimizing them, and have some reference benchmarks. If other font providers want to hook me up with a free account (and host Open Sans, for consistency), I’d be happy to include them in another study at another time.

I was again dissappointed to see Google turn out another slow service. Google friends, take some notes from Typekit!

I am looking forward to hearing your thoughts and observations on this experiment, and to your recommendations for speeding up web fonts. Thanks for reading!

In this article

Join the Conversation

13 comments

  1. Aaron Peters Reply

    Dave,
    excellent article/research.
    Questions:
    1) what CDN did you use for Boot.getFont (CDN) and FontSquirrel Expert (CDN)
    2) are you sure the CDN was actually serving the file (cache HIT)?
    3) did you serve the files Gzipped from the CDN?
    4) what Webpagetest.org location did you use?
    – Aaron

    1. artzstudio Reply

      @Aaron – Thanks!
      1) Akamai
      2) Yup, since I took the fastest run of 10 in the results
      3) Yes.
      4) Dulles, VA (East Coast USA)

  2. Andy Davies Reply

    Funnily enough I’ve just been analysing a site for a client and I found that Typekit was adding 0.5s to the pre-render time in IE9
    Even though the versions of pre-IE9 versions use a separate font file, I was seeing earlier render start times as the font downloading doesn’t block render start in the same way that the CSS/datauri based approach does.
    Thanks for the article it fills in some of the gaps I have on the font stuff

    1. artzstudio Reply

      @Andy – In my experience, @fontface does not block rendering – in fact, that is why we have a FOUT. I’d be curious to hear more.

  3. Brad Dunzer Reply

    Dave,
    Very excellent article breaking down the differences in web font service loading and self-hosting. You missed our FOUT-B-GONE script that we wrote a year ago to automatically handle FF3.6 and IE9. http://www.extensis.com/en/WebINK/fout-b-gone/index.jsp – check it out.
    One thing you also need to note about self-hosting over services like Typekit, WebINK and others is rendering fonts in browsers can be a crazy mess at times. If the font is not built correctly fonts can stop rendering in a new version of browser. It is why each of our services have invested heavily into testing fonts before they go live and continue testing when new browsers arrive in the Alpha and Beta channels. Beyond that you also need to consider that services can deliver fonts optimized for OS/Browser combinations that standard self-hosting stacks cannot do. For instance Google sends fonts without hinting to Mac and iOS devices making the fonts sometimes 50% smaller. Typekit began a while ago sending WOFF/CFF instead of WOFF/TFF fonts to Windows computers to render cleaner looking fonts. And our Service WebINK will begin very soon doing both of these offerings and more in a very short time frame. We have also just introduced a new method of requesting fonts that bundles up all font requests into a single call, thus reducing the network traffic between a browser and our servers.
    So while self-hosting a font file might appear faster in some testing it might not be the best solution always.

    1. artzstudio Reply

      @Brad – The FOUT-B-Gone test page threw an error in IE8 when I went to try it out. I was also against the approach of waiting until all stylesheets were downloaded and then parsing their cssText using RegEx, then performing the hide and show of the text when ready — it struck me as inefficient and having unintended rendering consequences. Re: bundling fonts together, I’d worry that in serving them together they may not all be used on the page, wasting bytes; larger files also are subject to cache limits on mobile, something else we need to watch out for. I’d be curious to see Google’s unhinted fonts and their byte savings; exporting fonts without hinting in the FontSquirrel Generator did not yield savings (but I did notice Google’s EOTs were not hinted and gross looking, something I meant to mention in the article).

  4. Andy Walpole Reply

    It’s an interesting experiment. There’s little difference between my solutions of choice, FontSquirrel and Typekit. It would interesting to repeat the exercise on various mobile platforms.

    1. artzstudio Reply

      @Andy – Thanks for the suggestion, I’ll look into mobile testing.

  5. Jon Kemp Reply

    We are hosting our webfont files on a separate server that uses Akamai for caching, and the fonts do not work on Firefox and IE. I know that the Firefox problem is because of the cross domain policy. How do you implement webfonts on a CDN but still get around the cross domain origin policy?

    1. artzstudio Reply

      @Jon – You need to serve the fonts with the Access-Control-Allow-Origin header.

  6. Matti Schneider Reply

    Very good article.
    One thing, though: I believe you’re missing an important point, or at least a notice, in the FontSquirrel “Expert mode” tutorial.
    Indeed, for English-only content, with an English-only audience, the set of characters you gave is sufficient. However, remember most other languages use accented letters. And, IMHO, the few kb lost in adding accented characters are much worth the gains in maintainability. Even English content may use foreign phrases, or may quote one at some point. As a single example, “déjà vu” (French expression) is written “déjà vu”, not “deja vu”. If you’re in a use case where you care enough about typography to embed your own fonts, you most probably care enough to spell foreign words properly.
    I would not like to be the one in charge of adding elements to the charset, re-generating *all* font files, testing, and adding cache busting to _font-files_ every time a writer needs one foreign glyph

    1. artzstudio Reply

      @Matti – Great catch, I meant to point this out in the article, although I want to get people thinking harder about including only what they need, as the savings can quickly add up. Thanks!

  7. Brad Dunzer Reply

    Dave,
    Thanks for the notes about fout-b-gone. Yes it was built to handle the simple approach so it had some trade-offs.
    Regarding the bundling I mentioned. Its not that we are bundling the fonts together but the first request for them. Prior to our change we had users place a @font-face link for each font required in their CSS. Now one link makes a call for all of the fonts. Actually it makes a call for a dynamic CSS generation that writes the @font-face calls to the page and gets the fonts.
    One thing i was wondering in your comparison of services (google and typekit) how did you make sure that the font file sizes were the same? Each vendor can do specific things to a font and sizes can vary wildly for the same font face. But its pretty easy to see why the font for typekit comes back faster when the base64 version is returned with the CSS and thus there is no third step as is shown for Google. Did you also test based on downloading multiple fonts at once. Say with 4 font-faces which is a typical setup for a single font? It would be interesting to see how that larger single chunk of data effects pageload. Just as you were concerned about what you thought we introduced with combining fonts.
    Either way glad to see all the discussion.