Playable ads guide

Custom Fonts in Playable Ads: Subsetting, Missing Glyphs and Silent Fallbacks

Brand guidelines specify a typeface, the design comp uses it, and the playable ships in Arial, or in the right face on one phone and something else on the next. Fonts are one of the few assets in a playable that fail without an error of any kind. They are also, unsubsetted, one of the heaviest things a creative can carry. Both problems are solvable, and neither is solved by the usual web habit of linking a font from a service.

Why the font has to travel inside the ad

A playable may not make network requests, so a stylesheet link to a font service is either blocked by the container or flagged at review. Relying on the device is not an option for a brand face either: a family the device does not have silently becomes whatever the device substitutes, which differs between iOS, Android and each network's webview. The only reliable font in a playable is one embedded in the file.

What a font costs

A complete TTF is routinely 200 to 500 KB. Against a 5 MB network cap that is merely wasteful. Against Meta's requirement that index.html stay under 2 MB, one unsubsetted font can take a quarter of the entire budget before a single image is added. Every weight and style is a separate file, so a comp using regular, medium, bold and black is asking for four of them.

Subsetting, which cuts the file down to only the characters the ad draws, fixes most of that. A 23-character subset of Georgia, measured for a call-to-action button, comes to about 13 KB as WOFF2. That is the difference between a font being a significant line in the budget and being a rounding error.

Subset to everything the ad draws

The subset has to contain every character that can appear on screen, and the ones that are easy to miss are the ones that never appear in the copy as typed.

  • Every scene, including the end card and any overlay shown during an interaction, not just the opening scene.
  • Every translation. A subset built from the English copy is missing every accented and non-Latin character the other languages introduce, and that failure is visible only to people reading those languages.
  • Dynamic text. A countdown needs the ten digits and the colon; a formatted number needs digits, grouping separators, the decimal point and the percent sign; a counter needs every digit it can reach. None of those appear in any authored string.
  • Text generated from data, such as cards built from a table of rows, whose content lives in the table rather than in a text object.
  • Case transforms. If a style renders text in capitals, the capitals are what gets drawn, whatever was typed.

That means the subset has to be made at export time, from the finished content, not once, when the font is uploaded. Keep the original font file with the project and cut it down on every build. A subset made at upload is missing every character typed afterwards, and each of those renders as an empty box.

Ship WOFF2, and do not compress it again

WOFF2 is the right format to embed. It is already compressed internally with Brotli, which makes it the smallest widely supported font format, and it also means compressing it again gains nothing. We measured exactly that: deflating the 13 KB Georgia subset above made it five bytes larger. A font should not ride the same compression path as scripts and data.

Count the encoded size

An embedded font is base64-encoded inside the HTML, and base64 adds a third. The 13 KB subset occupies roughly 17 KB of the file. A size estimate that reports the font's size on disk understates what it costs against the cap.

Missing glyphs fall back to a different font on every phone

This is the failure most teams never notice. Display typefaces frequently cover letters, digits and basic punctuation and nothing more. When the ad asks for a character the font does not contain, the browser does not fail; it draws that one character from a fallback chosen by the device. Luckiest Guy has no rightwards arrow, and neither does Georgia. So a button reading SEE MY REWARD → draws thirteen characters in the brand face and the arrow in whatever that phone substitutes: one design on an iPhone, another on each Android webview.

It looks like an inconsistency between the preview and the ad, and it is not: the file is identical, the devices are not. The fix is to check the font's coverage against the copy before building, then replace the symbol with an image or a shape, pick a face that has it, or rewrite the copy. The same applies to currency signs outside the basic set. Emoji should never be relied on in a brand font at all. They come from the operating system and look different on every platform by design.

Weights you do not have are faked

Ask for bold from a file that contains only the regular weight and the browser synthesises one by smearing the outlines. The result reads as a rendering fault rather than a design choice. Offer only the weights the uploaded file actually contains (one, for most display faces) and treat a variable font as a range along its weight axis rather than a fixed ladder of names.

Load before you measure

Canvas-based renderers, which most playables use, measure text when the text object is created and never measure it again. If the font has not finished loading at that moment, the text is laid out against the fallback's widths, and when the real font arrives, the right glyphs appear in the wrong positions, wrapped at the wrong points. Nothing throws. Because it is a race, it resolves differently on a fast development machine than on a slow phone, which is where it shows up. Wait for every embedded font to load before building any text.

Checking that a font really rendered is less obvious than it sounds. A test asserting that the text appeared passes when the font silently fell back. The reliable check is to measure the rendered width and compare it with the same string in the fallback font: if they are equal, the font was not used.

Licensing

A font licence that covers a website does not automatically cover embedding the font in a file distributed through third-party ad networks. Many commercial foundries sell separate licences for apps, digital ads or embedding, and those are the terms to check before the design is approved. Fonts under the SIL Open Font License generally permit embedding; check whether the licence reserves the font's name for modified versions, since a subset is technically a modified file.

How Playable Ads Maker handles it

Upload a WOFF2, WOFF, TTF or OTF file in the font manager. The project keeps the original, and every export subsets it from the finished content (every scene, the translations, cards generated from data, timer digits and the characters formatted numbers can produce) then embeds it as base64 WOFF2. Coverage is read from the font when it is uploaded, so characters the face does not contain are flagged in the font manager and in the pre-export check. The weight menu lists only the weights the file contains, and both the editor preview and the exported ad wait for fonts to load before laying out any text.

Create playable end cards in minutes—no code required.

Open Playable Ads Maker