Bitmap Fonts
The system font and custom .btfont bitmap fonts: format, loading, and BMFont conversion.
BLIT386 ships with a built-in system font and supports custom .btfont bitmap fonts with variable-width glyphs,
per-character offsets, Unicode characters, and either embedded or external textures.
Built-in system font
The engine includes a 6×14 monospace bitmap font covering printable ASCII (characters 32-126) plus a set of extra glyphs
(dashes, accented Latin punctuation, media icons, arrows, Greek capitals, and more) - see
scripts/system-font-extra-chars.mjs for the full list. It requires no file loading and is available immediately after
initialization:
// Draw text with the system font (palette index selects the color)
.(new (10, 10), 1, 'Hello World!');
// Measure text dimensions
const = .('Hello'); // returns Vector2i(width, height)Editing the system font
The glyph data is stored as bit patterns in src/assets/fonts/systemFontData.ts. To edit it visually:
pnpm run system-font:export # exports current data to assets/system-font.png (96×154, 16×11 grid of 6×14 cells)
# open assets/system-font.png in a pixel editor and redraw glyphs
pnpm run system-font:convert # reads the PNG and regenerates systemFontData.tsWhite pixels become foreground (palette index 1), black pixels become transparent (palette index 0). Only the top 6 bits of each byte are used (6-pixel glyph width within an 8-bit row).
Custom bitmap fonts (.btfont)
For proportional fonts, Unicode support, or custom aesthetics, load a .btfont file:
Quick start
import { , , , } from 'blit386';
// Load a font and register its colors in the palette
const = new (256);
const = await .('fonts/MyFont.btfont');
.().(); // required before draw
.();
// Render text (optional 4th arg is paletteOffset, not a Color32)
.(, new (10, 10), 'Hello World!');
.(, new (10, 30), 'Team B', 16); // shift glyph indices by 16
// Measure text width
const = .('Hello');
const = .('Hello'); // { width, height } (TextSize)
// Access font properties
.(.); // "MyFont"
.(.); // 14
.(.); // 15
.(.); // 98Font file format (.btfont)
The .btfont format is a JSON file with the following structure:
{
"name": "PragmataPro",
"size": 14,
"lineHeight": 15,
"baseline": 13,
"texture": "PragmataPro14.png",
"glyphs": {
" ": { "x": 169, "y": 54, "w": 5, "h": 17, "ox": -2, "oy": -1, "adv": 7 },
"A": { "x": 80, "y": 18, "w": 9, "h": 17, "ox": -1, "oy": -1, "adv": 7 },
"×": { "x": 0, "y": 54, "w": 9, "h": 17, "ox": -1, "oy": -1, "adv": 7 }
}
}Properties
Prop
Type
Glyph properties
Prop
Type
Texture options
The texture field supports two formats:
{
"texture": "MyFont.png"
}The path is resolved relative to the .btfont file location. This is convenient during development as you can update
the PNG without regenerating the JSON.
Loading limits
BitmapFont.load() enforces the same resource limits as sprite sheets. See API: Assets for the full
table. In short:
.btfontJSON must be1 MiBor smaller; glyph maps are capped at8192entries.- Embedded PNG payloads must stay within
512 KiBof base64 data (after thedata:image/png;base64,prefix). - Font atlas textures obey the
8192per-side and16,777,216total-pixel decoded limits. - Invalid glyph metrics (non-integer values, negative sizes, rectangles outside the atlas) throw
AssetLimitErrorbefore rendering.
When validation fails, loading throws AssetLimitError with a beginner-friendly message. Prefer a separate PNG texture
for large atlases instead of embedding multi-megabyte base64 in the JSON file.
Converting from BMFont format
Most bitmap font tools export to the BMFont format (.fnt + .png). Use
the included conversion script to convert these to .btfont:
Using the conversion script
# From project root
node scripts/convert-bmfont.mjs <input.fnt> <output.btfont> [--embed]
# Or via pnpm. Arguments after `--` are forwarded; the separator itself is ignored:
pnpm run convert-font -- <input.fnt> <output.btfont> [--embed]
# Examples:
node scripts/convert-bmfont.mjs tmp/MyFont.fnt examples/fonts/MyFont.btfont
node scripts/convert-bmfont.mjs tmp/MyFont.fnt examples/fonts/MyFont.btfont --embedOptions:
--embed- Embed the texture as base64 (recommended for production)
Running on different platforms
The conversion script works best when run directly in your terminal:
cd /path/to/blit386
node scripts/convert-bmfont.mjs input.fnt output.btfont --embedRun from a real terminal
For best results with file I/O operations, run Node.js scripts directly from the terminal (not through an IDE's embedded shell).
Manual conversion
If you prefer to convert manually:
Parse the .fnt XML file
Extract:
- Font info:
face,sizefrom<info>. - Metrics:
lineHeight,basefrom<common>. - Glyphs: all
<char>elements.
Convert each character element to a glyph entry
<char id="65" x="80" y="18" width="9" height="17" xoffset="-1" yoffset="-1" xadvance="7" />Becomes:
"A": { "x": 80, "y": 18, "w": 9, "h": 17, "ox": -1, "oy": -1, "adv": 7 }Set the texture path
Point it at the PNG filename, or embed it as base64.
Creating bitmap fonts
Recommended tools
BMFont (Windows)
- The original bitmap font generator.
- Export as XML format, then convert to
.btfont.
Hiero (cross-platform)
- Java-based, works on Mac/Linux/Windows.
- Export as BMFont format.
ShoeBox (cross-platform)
- Adobe AIR application.
- Can create bitmap fonts from existing images.
Tips for creating fonts
-
Use power-of-two texture sizes (256×128, 512×256, etc.) for best GPU compatibility.
-
Include an outline (1-2px) for better readability on varied backgrounds.
-
Export with padding between characters to prevent bleeding artifacts.
-
Use white glyphs on a transparent background - palette slots are assigned at indexize time; recolor at draw time via
paletteOffsetonBT.printFont(), not RGBA tinting. -
Include common symbols beyond ASCII:
×,÷,·,-,…, etc.
API reference
BitmapFont
BitmapFont Since 0.1.0
TextSize Since 0.1.0
declare class {
static (: string): <>;
readonly : string;
readonly : number;
readonly : number;
readonly : number;
readonly : number;
readonly : readonly number[];
(: string): Glyph | null;
(: string): number;
(: string): TextSize;
(: string, : TextSize): TextSize;
(: string): boolean;
(): ;
}Exported type TextSize is { width: number; height: number }.
Fallback glyph
Include a glyph keyed by U+FFFD (the Unicode replacement character) in a .btfont file's glyphs map, and
getGlyph() / getGlyphByCode() (and measureText(), which shares their lookup) substitute it for any character
missing its own entry, instead of silently skipping the character. A skipped character does not advance the pen position
either, so a missing glyph without a fallback can make the character after it overdraw the one before it - a defined
fallback keeps both the glyph and the spacing correct. hasGlyph() is unaffected: it still reports whether a character
has its own glyph, not whether the fallback would cover it.
A font with no U+FFFD entry keeps the previous behavior exactly. The built-in system font (BT.systemPrint) defines
one, alongside a set of extra glyphs beyond plain ASCII - see codePoints below for the live, always-current list, or
scripts/system-font-extra-chars.mjs in the engine source for the same list annotated with human-readable labels.
Listing covered characters
codePoints returns every Unicode code point a font defines a glyph for, ascending - derived live from the same glyph
map getGlyph() / hasGlyph() read, so it can never list a character the font doesn't actually render. Use it to build
a glyph browser or a coverage check instead of hand-maintaining a duplicate character list next to the font:
for (const of .) {
.(.());
}BT.printFont()
.(, , );
.(, , , ); // Per-draw index shift (default 0); not a Color32Examples
Multi-line text
const = ['Line 1', 'Line 2', 'Line 3'];
let = 10;
for (const of ) {
.(, new (10, ), );
+= .;
}Centered text
const = 'Centered';
const = .();
const = ..;
const = .(( - ) / 2);
.(, new (, 10), );Per-character palette offsets
let = 10;
for (let = 0; < .; ++) {
const = [];
const = % 2 === 0 ? 0 : 16; // alternate palette ranges
.(, new (, 10), , );
const = .();
+= ? . : .;
}Troubleshooting
API history
| Symbol | Since | Last changed | Status |
|---|---|---|---|
BitmapFont | 0.1.0 | 1.7.0 | stable |
TextSize | 0.1.0 | – | stable |
1.7.0 - Fri Sep 11 2026
- Changed
BitmapFontAdded the `codePoints` getter, returning every Unicode code point the font defines a glyph for (ascending, derived live from the same glyph map `getGlyph()` and `hasGlyph()` read).
1.5.0 - Tue Aug 11 2026
- Changed
BitmapFont`getGlyph` / `getGlyphByCode` (and `measureText`, which now shares their lookup) substitute a font-defined fallback glyph for a missing character instead of returning `null`, when the font's glyph map has an entry keyed by `U+FFFD`.
0.1.0 - Sat Dec 13 2025
- Added
BitmapFont - Added
TextSize
See also
API: Assets
API: Rendering
Palette Guide
API: Palette
Last updated on October 18, 2026