Zach’s ugly mug (his face) Zach Leatherman

It’s Dangerous to Go Stallone. Take Glyphhanger

March 01, 2018

Originally posted as It’s Dangerous to Go Stallone. Take Glyphhanger on the Filament Group Lab. Title by Scott Jehl


Psst.

Hey, you.

Yeah, you.

I’m gonna tell you a secret. A well guarded and little known secret—whispered about in hallway tracks of web performance conferences around the world. A web font performance trick that will take your font loading to the next level.

Here it is—are you ready? Smaller web fonts load faster. I know, I know. Mind blowing stuff.

Okay, fine–I’ll concede. This isn’t as groundbreaking or as secretive as I originally claimed. But it is helpful!

Practically speaking, making web fonts smaller is not always straightforward. The most straightforward optimization is to use compressed web font files, specifically the WOFF2 format but also its predecessor WOFF for compatibility.

A less well known, but very useful option—and the one I’d like to discuss in-depth today—is removing potentially unnecessary characters from a font file. This is a process called subsetting.

Subsetting

Let’s start with the most popular font on Google Fonts, Roboto. For experiment’s sake (although you can download it from Google Fonts) let’s download an unoptimized copy straight from the source, the Roboto GitHub repository.

Opening up the roboto-android.zip file, we can find Roboto-Regular.ttf—a completely unoptimized desktop font version of Roboto. It weighs in at 306 KB and includes 3,359 distinct glyphs (according to Font Drop). That’s a hefty font. Not a hefty weight—we’re not using the bold version of Roboto—I only mean that the file size is large. Was that clear? Hopefully. I think you get what I mean.

Font Drop also reports that Roboto contains support for over 40 languages:

Afrikaans Albanian Azerbaijani Belarusian Bosnian Bulgarian Catalan Croatian Czech Danish Dutch English Estonian Finnish French German Greek Hungarian Icelandic Italian Latvian Lithuanian Macedonian Maltese Norwegian Ossetic Polish Portugese Romanian Russian Serbian Slovak Slovenian Spanisch Swedish Turkish Ukrainian Uzbek Vietnamese Zulu

Does your content necessitate characters for those languages? Or are those characters unnecessary and unlikely to appear on your site? This isn’t a hypothetical question—let’s answer it!

Use glyphhanger

We can test our site to see what glyphs are used at a URL using a tool we’ve developed called glyphhanger.

Find glyphhanger on GitHub or npm.

npm install -g glyphhanger

At its simplest, glyphhanger can take a URL and return a Unicode range for all of the content used on the site.

For example (don’t include the $ if you copy these commands):

$ glyphhanger https://www.filamentgroup.com/
U+20,U+21,U+26,U+28,U+29,U+2B-38,U+3A,U+3F-47,U+49,U+4C-50,U+52-55,U+57-5A,U+61-70,U+72-77,U+79,U+7C,U+A9,U+DC,U+2014,U+2019,U+25A2

Let’s take Roboto-Regular.ttf and subset it this Unicode range. glyphhanger can help with this too:

Subsetting functionality in glyphhanger requires you to install fonttools, a python project. The glyphhanger README includes installation instructions for Mac OS X.

$ glyphhanger https://www.filamentgroup.com/ --subset=Roboto-Regular.ttf --formats=woff2,woff
Subsetting Roboto-Regular.ttf to Roboto-Regular-subset.woff (was 298.45 KB, now 14.09 KB)
Subsetting Roboto-Regular.ttf to Roboto-Regular-subset.woff2 (was 298.45 KB, now 10.63 KB)

Note that in the above example, --formats=woff2,woff means to output both a WOFF and WOFF2 subset file. Valid options include: woff2, woff, woff-zopfli (for better WOFF compression compared to woff), and ttf.

Make the files usable site-wide

This works great for a single page on our site. But to be fair, we want to use one set of web font files that works with all of the pages on our site—not just for the home page. There are a couple of ways to make this work:

  1. Pass in multiple URLs
  2. Use the glyphhanger Spider to find URLs for you
  3. Pass in a white-list of characters or a unicode-range

Let’s look at a few examples:

Pass in multiple URLs

$ glyphhanger https://www.filamentgroup.com/ https://www.filamentgroup.com/code/

Use the Spider to find URLs for you

Use the --spider option to find all of the a[href] attribute values that contain local URLs and spider those pages as well. This will report the union of all of the Unicode ranges.

$ glyphhanger https://www.filamentgroup.com/ --spider --spider-limit=10

Configure the maximum limit with --spider-limit (default is 10).

Pass in a whitelist

Use the --whitelist option to pass in a Unicode range or a list of characters you’d like to include by default in addition to any URLs that get interpreted. At time of writing, glyphhanger (v3.0) also includes shortcuts for --US_ASCII and --LATIN.

Using --whitelist makes the URL argument optional.

$ glyphhanger --whitelist="ABCD"
U+41-44
$ glyphhanger --US_ASCII
U+20-7E

Let’s subset our Roboto font using a whitelist.

$ glyphhanger --whitelist="U+20-7E" --subset=Roboto-Regular.ttf --formats=woff2
U+20-7E
Subsetting Roboto-Regular.ttf to Roboto-Regular-subset.woff2 (was 298.45 KB, now 10.78 KB)

Nice, right?

Subset to a specific font-family

Let’s say, instead of Roboto, we have a specific monospace web font we’d like to use in our code blocks and syntax highlighted code on our site. The Unicode range for monospace code content may be very different from the characters used for other content on our site. glyphhanger can help here too. We can pass in a comma separated list of one or more font-family names to fetch only the content in nodes using those font-family values.

$ glyphhanger --family='Consolas,monospace' https://www.filamentgroup.com/lab/preload-ctm.html
U+A,U+20-22,U+26,U+28,U+29,U+2B-30,U+38,U+3A-3E,U+41-43,U+45,U+4A,U+4C-4E,U+53,U+54,U+5B,U+5D,U+61-79,U+7B-7D

It works with --subset too. You can also use --json to see all ranges organized by font-family. "*" is the universal list of all characters on the page.

$ glyphhanger https://www.filamentgroup.com/code/ --json
{"*": "U+20,U+21,U+26,U+28,U+29,U+2B-38,U+3A,U+3F-4A,U+4C-59,U+61-7A,U+A9,U+B0,U+2019,U+201C,U+201D","Source Sans Pro": "U+20,U+21,U+26,U+28,U+29,U+2B-38,U+3A,U+3F-4A,U+4C-59,U+61-7A,U+A9,U+B0,U+2019,U+201C,U+201D","Consolas": "U+20,U+28,U+29,U+3A,U+40,U+61,U+63-66,U+69,U+6B-70,U+72-75,U+78-7A"}

Output CSS that you can use

glyphhanger will even make your @font-face blocks in CSS for you. Using --css will output a @font-face block to the console. Of course, it’ll add the unicode-range, but it’ll also add the right src with the correct file names returned from --subset. If you’re using --family, it’ll add a font-family value too.

$ glyphhanger --US_ASCII --family='Source Sans Pro' --subset=Roboto-Regular.ttf --formats=woff2,woff --css
U+20-7E
Subsetting Roboto-Regular.ttf to Roboto-Regular-subset.woff (was 298.45 KB, now 14.37 KB)
Subsetting Roboto-Regular.ttf to Roboto-Regular-subset.woff2 (was 298.45 KB, now 10.78 KB)

@font-face {
  font-family: Source Sans Pro;
  src: url(Roboto-Regular-subset.woff2) format("woff2"), url(Roboto-Regular-subset.zopfli.woff) format("woff");
  unicode-range: U+20-7E;
}

A Web Font Utility Belt

glyphhanger is a useful tool when working with web fonts—it can help optimize your font files very quickly. But don’t get too hasty. Take proper care to perform due diligence before subsetting:

  1. Make sure subsetting agrees with the licensing restrictions on the font file.
  2. Don’t be too aggressive with your subset. If you’re using web fonts for user generated content or content that is likely to change in the future, your Unicode range should be broad enough to accommodate this.

Download glyphhanger and give it a try! We welcome any and all feedback at our Issue Tracker. What else would you like to do with your web fonts?


< Newer
imaging-heap, Measuring Responsive Image Efficiency
Older >
“A letter about Google AMP”

Zach Leatherman IndieWeb Avatar for https://zachleat.com/is a builder for the web at IndieWeb Avatar for https://fontawesome.com/Font Awesome and the creator/maintainer of IndieWeb Avatar for https://www.11ty.devEleventy (11ty), an award-winning open source site generator. At one point he became entirely too fixated on web fonts. He has given 83 talks in nine different countries at events like Beyond Tellerrand, Smashing Conference, Jamstack Conf, CSSConf, and The White House. Formerly part of CloudCannon, Netlify, Filament Group, NEJS CONF, and NebraskaJS. Learn more about Zach »

9 Reposts

Pablo Lara HPablo Lara HPablo Lara HIndieWeb Avatar for http://studio-rgb.ruIndieWeb Avatar for https://flmarket.rumeduz'Jeffrey KnoxIndieWeb Avatar for https://calendar.perfplanet.comIndieWeb Avatar for https://jonas.brusman.se
4 Comments
  1. Mike Aparicio

    They’re a little over 40k each. The biggest thing is to preload them: web.dev/codelab-preloa… I don't recall having to do much to get Glyphhanger working (trust me, I am useless with CLIs). This post from @zachleat made it pretty straightforward. zachleat.com/web/glyphhange…

  2. Katie Langerman

    I'm wondering if maybe my font files aren't caching. I inlined the CSS font declarations in a style tag github.com/langermank/ux-…

  3. Mike Aparicio

    It's the site live? I could take a look. Maybe there are other gains to be had elsewhere. Feel free to DM me if you're not comfortable sharing the link publicly.

  4. Zach Leatherman

    the tricky thing about font loading is that your font files are too slow and the rest of your site is too fast 😅 You’re racing first render on 6.1 kB or HTML/CSS against 71.4 kB of fonts I think your options are: 1. subset and preload the subset 2. make your site slower 😅

Shamelessly plug your related post

These are webmentions via the IndieWeb and webmention.io.

Sharing on social media?

This is what will show up when you share this post on Social Media:

How did you do this? I automated my Open Graph images. (Peer behind the curtain at the test page)