• Modern UX

    Edit and navigate faster in the terminal with Warp's IDE-like input editor.

  • Warp AI

    AI suggests what commands to run and learns from your documentation.

  • Agent Mode

    Delegate tasks to AI and use natural language on the command line.

  • Warp Drive

    Save and share interactive notebooks, workflows, and environment variables.

  • All Features

No Glyph Left Behind: Font Fallback in a WASM Terminal

Daniel Peng

Thumbnail for No Glyph Left Behind: Font Fallback in a WASM TerminalNo Glyph Left Behind: Font Fallback in a WASM Terminal

We’re building Warp, the intelligent terminal with AI and your team’s knowledge built-in. Warp is built in Rust, and we recently took advantage of Rust’s ability to cross-compile to WebAssembly (WASM) to bring Warp to the web. Enabling Warp in the browser brings us closer to our vision of making the terminal more accessible and collaborative!

As part of this effort, we had to dive deep into the world of fonts and Unicode. At some point, you’ve probably been using your computer and encountered text with some weird boxes. What’s going on when that happens?

A primer on font fallback

Whenever your computer renders a character, it searches through font files for a matching graphical representation of that character, called a glyph. Each font file contains hundreds or even thousands of glyphs. A typical Western font might contain glyphs for the latin alphabet, numbers, punctuation, and some symbols.

Thumbnail for null

A preview of some of the glyphs available in Helvetica.

But what about non-Western scripts, like Arabic or Chinese? What about emoji or icons? It turns out, it’s not practical for fonts to include glyphs for every possible character – there are over 150,000 characters defined in the latest Unicode standard. The font files would get impractically large, and asking a font designer to create all these glyphs would be a herculean task.

Searching for the right glyph to represent a character is done via a mechanism called font fallback. Typically, applications will have a cascading list of available fonts. If a glyph is not available in the application’s default font, it will check the next font in the font fallback chain, repeating all the way to the end. You might be familiar with CSS’s implementation: the font-family property accepts a list of fonts that act as its font fallback chain.

Thumbnail for null

A CSS font fallback chain.

As a last resort, if none of the fonts contain a glyph for a character, the application will choose how to handle the missing character. Some applications will render nothing at all, while others will render the empty rectangle (sometimes known as the “tofu” character due to its resemblance to a block of tofu). So when you see those weird boxes in the middle of some text, that’s the application saying it doesn’t know how to render that character.

Constraints when building a web WASM app

Having a robust font fallback mechanism is important for Warp. The value is clear for supporting different scripts, symbols, and emoji. Warp has a global user base and supporting these types of input is the standard for any modern user-facing application. As a terminal, we also want to support the many users that use Nerd Fonts, developer-focused patched fonts containing a large number of extra icons. These icons are commonly used in plugins for editors such as Neovim and in popular custom prompts like Powerlevel10k.

However, this calculus is complicated by building a WASM application for the browser. On the native version of Warp, we have access to fonts installed on your system and OS-level libraries that create a font-fallback chain for us. The browser has no such access to system fonts, for security reasons. Our in-house UI framework renders Warp directly to an HTML canvas via the GPU, bypassing any chance of us using CSS’s font fallback mechanism. This necessitated us to build our own font fallback implementation for the web.

There’s one more major constraint to consider: our WASM binary size. The entire WASM executable must be loaded before Warp can run in the user’s browser. Time-to-first-load is a critical part of the user experience, so keeping our binary size small was a priority (for more on how we reduced our WASM binary size, see our other blog post).

Where we landed

We built an asynchronous font fallback mechanism into our UI framework, allowing consumers of the UI framework to define their own font fallback mappings. Each mapping consists of a range of Unicode points and an associated URL to a font that contains glyphs for that Unicode range.

Thumbnail for null

Some example font fallback mappings that we’ve defined for Warp.

When the UI framework tries to render a character that doesn’t have a glyph in any of the bundled fonts, it will check the supplied font fallback mappings to see if the application has defined a fallback font. If it does, it will fetch the font from the URL, add the font file to the font fallback chain, evict our local glyph caches, and re-render the scene when the font has been loaded. On the next render, the UI framework knows to use the loaded font!

Thumbnail for null

An example of a font being loaded asynchronously in Warp on the web.

By loading in these fallback fonts lazily, we avoid bloating our binary size with a plethora of fallback fonts. In practice, we find that lazy loading doesn’t degrade user experience. Loading the fonts on most internet connections is fast and browser caching means the cost is usually only paid on first load.

We had to determine which fonts we actually wanted to fall back to, since we don’t have the user’s system fonts to rely on. For this, we looked to the Noto font family. Noto, short for “no tofu”, is a free set of fonts designed to cover all the scripts encoded by Unicode along with comprehensive emoji and symbol support, making it perfect for our needs. Choosing fonts from the same family means we also maintain a consistent look and feel in Warp, even across different scripts. The one non-Noto font we included in our fallback fonts was a patched version of Hack that includes Nerd Font icons. These icons are used in many popular plugins that developers use to customize their terminals. Including Nerd Font icons allows these plugins to look and feel as they would on a native version of Warp.

Thumbnail for null

A Powerlevel10k prompt in Warp on web, before and after font fallback.

Unicode itself partitions the range of Unicode points into blocks. Each block represents a contiguous range of characters that generally are used to support a single language or symbol type. This was a useful starting point for determining which Unicode ranges we wanted to map to which fallback fonts.

Thumbnail for null

Sharing a session in Warp on the web, with 3 different fallback fonts in use: Noto Sans SC, Noto Color Emoji, and Hack Nerd Font!

As a result of our efforts, we were able to build out a web version of Warp with support for rendering the different scripts, icons, and symbols that you would expect from a native terminal, while not compromising on user experience. Font fallback was a fun example of the types of technical challenges we faced when building for WASM: needing to build out something from scratch that developers would usually take for granted. We’re excited to be launching Warp on the web and getting this into the hands of our users!

Related

Thumbnail for Why it took us 11 months to move a single line of textWhy it took us 11 months to move a single line of text

Why it took us 11 months to move a single line of text

By Warp Team

Trusted by hundreds of thousands of professional developers

Download Warp to get started

Download for Mac
Thumbnail for null