@implicit-invocation/three-2d-font
v0.10.0
Published
TTF/OTF to bitmap-font atlas tooling for three-2d: portable parser, rasterizers, atlas packer and CLI.
Maintainers
Readme
three-2d-font
TTF/OTF → bitmap-font atlas tooling for @implicit-invocation/three-2d: a portable parser, two rasterizers, an atlas packer, a runtime packing API and a CLI.
⚠️ Alpha (0.x) — APIs may change before 1.0.
Install
bun add @implicit-invocation/three-2d-font @implicit-invocation/three-2d threePeer dependencies: three. Depends on @implicit-invocation/three-2d and opentype.js (never exposed in the public API).
CLI (recommended for production)
three-2d-font pack ./Inter-Regular.ttf --size 32 --charset latin --output ./assets/inter-32
# → inter-32.png + inter-32.json (format "three-2d-bitmap-font", version 1)Options: --size, --charset latin|ascii|digits, --chars "<literal>", --supersample, --padding, --family/--weight/--style, --max-atlas, --stroke <px>.
Text outlines and shadows (--stroke)
three-2d-font pack ./Inter-Bold.ttf --size 64 --stroke 16 --family "Inter Display" --weight 800 --output ./assets/display-64Bakes a second channel: the red channel holds a signed distance field (0.5 at the glyph edge, ±stroke/2 + 1 px to either side) while coverage stays in alpha — the JSON gains "stroke": { "maxWidth": 16 } and the glyph padding grows to fit it. three-ui then renders -webkit-text-stroke of any width up to maxWidth (scaled with the display size) and soft text-shadow from the same atlas; wider requests clamp with a one-time warning, and fonts without the channel simply have no stroke. The atlas must be loaded without alpha premultiplication (the field lives in transparent texels). API equivalent: bakeBitmapFont(data, { size, stroke: { maxWidth } }). The distance transform runs on the CPU from the rasterizer's coverage, so both rasterizers support it.
Runtime packing
import { packBitmapFont } from '@implicit-invocation/three-2d-font'
const font = await packBitmapFont(fontBytes /* ArrayBuffer */, {
renderer, // optional: bake on the GPU (Three render target); omit to use the CPU rasterizer
size: 32,
characters: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
supersample: 4,
})
font.draw(batch, 'Hello', 10, 10) // three-2d BitmapFontArchitecture
ArrayBuffer → FontParser → ParsedFont → GlyphRasterizer → AtlasPacker → THREE.Texture | PNG + JSONOpenTypeFontParser— opentype.js adapter; input is anArrayBuffer(nofetch, no DOM).ThreeGlyphRasterizer— default portable rasterizer: outlines →ShapeGeometry→ supersampled render target (no Canvas2D).CpuGlyphRasterizer— pure-JS scanline rasterizer (CLI, tests, headless).- Atlas entries are keyed by font glyph ID, not just Unicode, leaving room for shaping/ligatures.
Pair kerning is read when the font exposes a kern table; GPOS-only kerning is not applied yet.
Runtime support
Core (@implicit-invocation/three-2d-font): browser, Node, Bun, React Native (no DOM, no Canvas2D). @implicit-invocation/three-2d-font/node and the CLI need Node/Bun (node:fs, node:zlib).
Examples
bun run build:fonts at the repo root bakes the Inter fixtures used by every example.
