Runtime Font Loading

SkillDocs & knowledge

Gum runtime font loading (MonoGame/KNI), three loading paths (custom, font-property cache, in-memory generation), lookup cascade, FontCache naming. Triggers: TextRuntime font properties, BitmapFont loading, CustomSetPropertyOnRenderable.UpdateToFontValues, IInMemoryFontCreator.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Runtime Font Loading skill

What this skill tells your AI

The instructions your AI receives, as published by vchelaru/gum in .claude/skills/gum-runtime-fonts/SKILL.md and read by ahel’s review.

Gum renders text using BitmapFont — a .fnt descriptor file plus one or more .png texture atlases. There are three ways to get a BitmapFont onto a TextRuntime, each with different tradeoffs. Path 3 (in-memory generation, typically KernSmith) is the recommended route for new projects; pre-generated .fnt files on disk are the older path.

For crisp text under camera/layer zoom, see docs/code/files-and-fonts/font-oversampling.mdTextRuntime.UseFontOversampling rebuilds the font bigger automatically when zoom changes; unrelated to the three loading paths below.

.fnt encodings

BMFont defines three encodings for the same data, and ParsedFontFile picks a branch off the first character. Gum's tool and KernSmith both emit text, so that is the only branch normal users reach. XML requires hand-authoring a file from BMFont's XML export and deserializes via XmlSerializer (so it is not Native AOT safe); binary throws outright. Treat gaps confined to the XML or binary branch as near-zero user impact.

Three Font Loading Paths

Path 1: Custom Font File (UseCustomFont = true)

User provides a pre-built .fnt file directly:

textRuntime.UseCustomFont = true;
textRuntime.CustomFontFile = "fonts/MyFont.fnt";
  • File path resolves relative to FileManager.RelativeDirectory (typically Content/)
  • Loaded via new BitmapFont(path), cached in LoaderManager
  • If the file doesn't exist, load silently skips — element gets DefaultBitmapFont
  • No property-to-filename mapping; user controls the exact file

Path 2: Font Property Cache Lookup (UseCustomFont = false, the default)

Six properties combine into a deterministic filename in FontCache/:

PropertyDefaultEffect on filename
Font / FontFamily"Arial"Base name (spaces → underscores)
FontSize18Base size number
OutlineThickness0_o{N} suffix if non-zero
UseFontSmoothingtrue_noSmooth suffix if false
IsItalicfalse_Italic suffix if true
IsBoldfalse_Bold suffix if true

Naming formula: FontCache/Font{size}{name}[_o{N}][_noSmooth][_Italic][_Bold].fnt

Examples: FontCache/Font18Arial.fnt, FontCache/Font24Times_New_Roman_o1_Bold.fnt

BmfcSave.GetFontCacheFileNameFor() produces this name. Every property setter on TextRuntime (Font, FontSize, etc.) calls UpdateToFontValues(), which regenerates the filename and attempts to load.

Key gotcha: Unless an IInMemoryFontCreator or IRuntimeFontService is registered, the .fnt file must already exist in FontCache/. Users often set FontSize = 24 expecting it to work, but silently get DefaultBitmapFont because Font24Arial.fnt was never generated. There is no error or warning — the text just renders in the default font.

All platforms (MonoGame/KNI/FNA and Raylib) raise CustomSetPropertyOnRenderable.PropertyAssignmentError when a wired InMemoryFontCreator throws, or declines and nothing else resolves a usable font — but it's a static event with no default subscriber, so even that diagnostic is silent unless the consumer subscribes to it themselves.

Path 3: In-Memory Font Creation (IInMemoryFontCreator) — New

Generates a BitmapFont entirely in memory at runtime — no pre-built .fnt files needed. The loading code already checks for this; it slots into the cascade between embedded resources and disk-based generation.

When registered on CustomSetPropertyOnRenderable.InMemoryFontCreator, font-property changes (Path 2) automatically create fonts on demand. This eliminates the FontCache pre-population requirement.

Lookup Cascade

When UseCustomFont = false and a font property changes, UpdateToFontValues tries these sources in order:

  1. LoaderManager cache — already-loaded BitmapFont by full path
  2. Embedded resource — MonoGameGum ships Font18Arial (plus Bold/Italic/Bold_Italic variants) as embedded resources; these are the default fonts
  3. IInMemoryFontCreator — generates BitmapFont in memory, no disk I/O
  4. IRuntimeFontService — generates .fnt/.png files on disk, then falls through to step 5 (typically tool-only, not used in game code)
  5. Disk loadnew BitmapFont(fullPath) if the file exists
  6. DefaultBitmapFont fallbackText.DefaultBitmapFont (Font18Arial, set during SystemManagers initialization)

The result is cached in LoaderManager so subsequent lookups for the same font properties hit step 1.

Wiring

SystemManagers initialization (called by GumService.Initialize) sets up the font system:

  • Loads embedded Font18Arial as Text.DefaultBitmapFont
  • Wires GraphicalUiElement.UpdateFontFromPropertiesCustomSetPropertyOnRenderable.UpdateToFontValues

Game code can optionally set CustomSetPropertyOnRenderable.InMemoryFontCreator to enable Path 3.

Key Files

FilePurpose
MonoGameGum/GueDeriving/TextRuntime.csUser-facing font properties; each setter calls UpdateToFontValues()
RenderingLibrary/Graphics/Text.csRenderable; holds BitmapFont instance and static DefaultBitmapFont
RenderingLibrary/Graphics/Fonts/BitmapFont.csLoads .fnt + .png textures; stores character metrics
RenderingLibrary/Graphics/Fonts/BmfcSave.csGetFontCacheFileNameFor() — deterministic cache filename from properties
Gum/Wireframe/CustomSetPropertyOnRenderable.csUpdateToFontValues() — orchestrates the lookup cascade
RenderingLibrary/Graphics/Fonts/IInMemoryFontCreator.csInterface for runtime font generation without disk I/O
RenderingLibrary/Graphics/Fonts/IRuntimeFontService.csInterface for disk-based font generation (typically tool-only)
RenderingLibrary/SystemManagers.csWires font delegates and loads default embedded font

Signals

GitHub stars
620
Forks
80
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
gum-runtime-fonts
Source
github.com/vchelaru/gum