Bitmap Font Maker

Turn a grid font sheet and character order into PNG, BMFont and JSON.

Draw pixel glyphs locally or import a PNG sheet, BDF, or editable project. The maker writes one PNG atlas and matching BMFont text, XML and version-3 binary descriptors; editable projects also include the v2 source and, within stated limits, a TrueType pixel-outline font. A live missing-character count and kerning preview help check the actual text you plan to render. No file is uploaded. Exported text, XML, binary, PNG and TTF from two CC0 BDFs were independently reopened; engine loading of the new BDF path remains a separate check.

Drop image files hereProcess many files at once
No upload · processed on this device

What a .fnt file says, line by line

info names the face and size and records padding and spacing. common holds lineHeight (the distance in pixels between two lines of text), base (pixels from the top of a line to the baseline), scaleW and scaleH (the texture size) and pages. page id=0 file="font.png" names the texture, relative to the .fnt. Each char line gives id (the Unicode code point in decimal), x y width height (the glyph's rectangle in the texture), xoffset yoffset (where to draw that rectangle relative to the pen position and the top of the line) and xadvance (how far the pen moves afterwards). kerning first second amount lines adjust particular pairs; The editable project records pair kerning, while a plain image sheet has no kerning metadata to import.

To draw a string an engine starts the pen at x = 0, and for every character copies its rectangle to (pen + xoffset, line top + yoffset) and adds xadvance to the pen; the next line starts lineHeight lower. In the fixed-grid mode every advance is the cell width, which suits monospaced pixel fonts. The measured mode keeps only each cell's opaque pixels (alpha above 8), puts them back with the offsets and advances by the ink's right edge plus the glyph spacing; an empty cell such as the space still advances.

Character sets decide the atlas size. ASCII is 95 characters (U+0020–U+007E) and Latin-1 191; precomposed Hangul alone is 11,172 syllables and the main CJK ideograph block 20,992 code points, so the Korean and Japanese presets take only the characters that occur in text you paste. A signed distance field (SDF) stores, per texel, the distance to the glyph's edge instead of its coverage, so a shader can cut a sharp edge at other sizes; Nerulio encodes 128 as the edge and spread pixels of distance across the byte range, computes the exact distance per glyph at 4× resolution, and ships a single-channel SDF, not MSDF.

lineHeight
Pixels from one line of text to the next.
base
Pixels from the top of a line down to the baseline.
xoffset / yoffset
Where the glyph rectangle is drawn relative to the pen and the line top.
xadvance
How far the pen moves after the glyph.
SDF spread
How many pixels of distance the 0–255 range covers; 128 is the glyph edge.

Example: the same three glyphs in grid and measured mode

A 24 × 8 sheet with 8 × 8 cells for A, i and a space, baseline 7. These are the lines the maker writes:

common    lineHeight=8 base=7 scaleW=24 scaleH=8 pages=1
grid      char id=65  x=0  y=0 width=8 height=8 xoffset=0 yoffset=0 xadvance=8
          char id=105 x=8  y=0 width=8 height=8 xoffset=0 yoffset=0 xadvance=8
measured  char id=65  x=1  y=1 width=5 height=6 xoffset=1 yoffset=1 xadvance=7   (1 + 5 + spacing 1)
          char id=105 x=11 y=1 width=1 height=6 xoffset=3 yoffset=1 xadvance=5   (3 + 1 + 1)
          char id=32  x=16 y=0 width=0 height=0 xoffset=0 yoffset=0 xadvance=3   (empty cell: round(8 ÷ 3))
draw "iA i" measured   i at 0 + 3 = 3 → pen 5;  A at 5 + 1 = 6 → pen 12;  space → pen 15;  i at 18 → width 20 px
draw "iA i" grid       4 glyphs × 8 = 32 px

The measured font sets i 5 px apart instead of 8, which is why proportional text looks natural. font.json carries the same numbers as glyphs[].xAdvance, xOffset and yOffset.

Load the font in an engine

  1. Keep font.fnt and font.png together: the page line names the PNG by a relative file name, so rename both or neither.
  2. Godot 4: copy both files into the project; Godot imports the .fnt as a bitmap font. Assign it to a Label or other Control as its font theme override. Bitmap fonts have a fixed design size, so draw pixel fonts at that size or whole multiples.
  3. PixiJS 8: await Assets.load('font.fnt'), then create new BitmapText({ text, style: { fontFamily: 'Nerulio Grid' } }) — the family is the face in the info line (Nerulio Grid, Nerulio Measured, or Nerulio plus your font file name).
  4. Phaser: use this.load.bitmapFont(key, 'font.png', 'font.xml'). The ZIP contains the XML descriptor alongside the PNG; test the loaded result in your Phaser version.

Check the result

  • Open font.fnt in a text editor: chars count equals the number of char lines, and every rectangle lies inside scaleW × scaleH.
  • The sample line under the sheet is drawn with the font's own metrics: letters that overlap or drift apart there will do the same in the engine.
  • Run the missing glyph checker with your localisation file against this font before shipping.

Troubleshooting

SymptomLikely causeHow to checkFix
The engine loads the font but shows nothingfont.png was renamed or moved away from the .fntThe page id=0 file= line and the actual file namePut the PNG next to the .fnt with the name the page line gives
Phaser fails to parse the fontPhaser's load.bitmapFont expects the XML variant of BMFontThe console error when the loader parses the dataUse the included font.xml next to font.png, then check Phaser console and file paths.
Letters touch or sit too far apart in measured modeThe glyph spacing value, or faint pixels above alpha 8 widening a glyph's ink boxCompare xadvance of a narrow and a wide glyph in the .fntChange the glyph spacing, or clean faint pixels off the sheet
Font-file mode: info size is larger than the size you choseThe size written is the cell height (ascent + descent + padding), not the font size you typedCompare info size with the Font size fieldWhere an engine uses size, set it by hand to the size you rendered
Font-file mode: some letters look like another typefaceA character your TTF lacks was drawn by the browser's fallback font and baked into the sheetLook at the glyph sheet for letters in a different styleRemove those characters, or pick a font file that contains them
Font-file mode: a pixel font comes out blurryCanvas text is anti-aliased, so edges get partial alphaZoom into the sheet: grey edge pixelsDraw the pixel font as a sheet and use the grid or measured mode
Grid mode says the character order does not fitMore characters than cells, a repeated character, or an empty orderColumns × rows of the sheet against the number of charactersRemove duplicates or use a larger sheet; a strip narrower than a cell at the edge is ignored

Other ways to do it

OptionWhen it is the better choice
Godot's own image-font import (import type Font Data (Image Font))The font is only for Godot and the sheet is in a simple order: no extra files, and the import dock also takes character ranges, margins and kerning pairs.
AngelCode BMFont or another BMFont-compatible generatorYou need verified multi-page atlases, effects, or advanced font features; this editor currently writes one atlas page.
A dynamic font (the TTF itself) in the engineText size changes a lot or the language needs thousands of glyphs; Godot documents MSDF rendering for dynamic fonts, not for bitmap fonts.

What this page does not cover

  • One atlas page; local TTF/OTF baking and BDF/project editing currently cap at 1,024 glyphs. No verified multi-page CJK output or OTF download; SDF remains Beta. A coverage count does not prove that Hangul composition is legible.
  • The editable TTF uses pixel-square outlines; two CC0 BDFs reopened with fontTools, but default FreeType hinting moved the detached dots in i and j. Unity TextMeshPro loading is unverified.
  • The existing local-font bake can use browser fallback glyphs and antialiasing. Inspect every downloaded atlas in the target engine.

Versions and sources

Measured in Chromium for docs/UI-LAB.md: a fixed-grid font writes char id=12354 x=8 y=0 width=8 height=8 for あ in a 16 × 8 font.png; an independent parser in the test read the .fnt header and every glyph record back equal to the JSON; measured mode gives tight rects, at least three distinct advances and an advancing empty cell; an ASCII-95 atlas at 32 px from a TTF built in 28 ms (350 × 380 sheet) and its SDF in 1174 ms. The font-file issues listed above are known, unfixed issues of this release. BMFont fields follow the AngelCode documentation; engine loading follows Godot 4.7, PixiJS 8 and the Phaser API.

Official documentation used

Example

Original geometric sample · 96 × 64
Original geometric sample · 96 × 64
Processed sample · 96 × 64
Processed sample · 96 × 64

Example generated with this tool. ZIP tools show their preview; inspect the downloaded archive for every file.

Bitmap Font Maker

Turn a grid font sheet and character order into PNG, BMFont and JSON.

How it works

  1. Add images
  2. Adjust settings and review the result
  3. Download a new file

Supported formats

PNG / JPG / WebP / AVIF / HEIC → PNG + FNT + JSON (ZIP) — Input decoding depends on browser support and optional engines.

What this tool does

Turn a grid font sheet and character order into PNG, BMFont and JSON.

Implementation and verified quality

advanced · fixed grid BMFont. Quality qualification is in progress. Pro/Flagship quality has not been established.

Before you export

Fixed-cell grids only. No kerning, variable advance or automatic Godot resource generation.

Frequently asked questions

What are this tool’s limitations?

Fixed-cell grids only. No kerning, variable advance or automatic Godot resource generation.

Are my files uploaded?

This tool processes the files you open on your device without uploading them. Images you explicitly attach to community posts are uploaded separately. Optional engines and models are downloaded from third parties; those requests expose connection information such as your IP address.

Will my work be saved automatically?

No. Images and editing state stay in this tab; media output can use local temporary storage. Changing language preserves your work; reloading, closing the tab, or following a link to another page can clear it.