Skip to content

Text

insomni ships two text paths that share one rendering shader and one authoring surface (Layer.pushText and friends). They differ only in how glyphs are rasterized into the atlas:

  1. Raster (default) — system fonts via Canvas 2D + a CPU-computed SDF. Zero font bytes, no opentype.js. This is what insomni gives you out of the box.
  2. MSDF / TTF (opt-in) — true multi-channel SDF from TTF/OTF outlines, with kerning and cross-platform-consistent shapes. Imported from insomni/text-ttf, which pulls in insomni-msdf-text.

Both produce a GlyphAtlas you attach to a layer; the layer’s text methods shape strings against that atlas.

MSDF text Three lines at different sizes, rendered from a system font with zero font downloads via RasterFont + SdfGlyphAtlas.
Raster (default)MSDF / TTF (insomni/text-ttf)
Importinsomniinsomni/text-ttf (+ insomni-msdf-text)
Font sourceAny CSS family the browser resolvesA specific .ttf / .otf you load
Bundle costZero font bytesopentype.js + MSDF generator
KerningNo (always 0)Yes
Extreme-zoom cornersSDF rounds sharp cornersStays crisp (MSDF)
Best forUI / dataviz labels at typical sizesDisplay typography, brand fonts, exact cross-OS layout

Both paths satisfy two interfaces so the shaper and renderer don’t care which kind you use:

  • TextFontfamily, unitsPerEm, ascender, descender, lineGap, and getKerning(left, right). Metrics are em-relative and normalized.
  • GlyphAtlas — owns the GPU atlas texture and lazily rasterizes glyphs on first request. Key members: texture, font, pxRange, atlasFontSize, atlasSize, getGlyph(codepoint), measureText(text, options), preload(text), and version (bumped on each new glyph allocation).

GlyphAtlasOptions configures an atlas: atlasSize (default 2048), atlasFontSize (em size in atlas texels, default 64), pxRange (SDF distance range in texels, default 8), and label.

measureText(text, options) takes MeasureTextOptions (fontSize, optional maxWidth, lineHeight, fontFamily, fontWeight, fontStyle, simple) and returns TextMetrics (width, height). The simple flag mirrors the same flag on the shape so a measured width matches what the layout will produce.

Build a RasterFont from a CSS family, then an SdfGlyphAtlas over it:

import { createLayer, loadSystemFont, SdfGlyphAtlas, rgba } from "insomni";
const font = await loadSystemFont("system-ui");
const atlas = new SdfGlyphAtlas(renderer, font, { atlasFontSize: 64 });
const labels = createLayer({ space: "ui", atlas });
labels.pushText({
text: "Hello, insomni",
x: 24,
y: 24,
fontSize: 16,
color: rgba(0.1, 0.1, 0.1, 1),
});
renderer.render([labels]);
  • RasterFont implements TextFont over a system font. loadSystemFont(family, options?) returns one (resolves immediately — Canvas metrics are synchronous). family is any CSS font-family ("sans-serif", "system-ui", "monospace", …). RasterFontOptions: family (label override), samplePx (metric sampling size, default 64), weight, style. Kerning always returns 0.
  • SdfGlyphAtlas is the GlyphAtlas for raster fonts: it renders each glyph onto an OffscreenCanvas, runs a CPU distance transform, and splats the single-channel SDF into all four atlas channels so the MSDF shader works unchanged. Construct with new SdfGlyphAtlas(owner, rasterFont, options?).

Import the loaders from insomni/text-ttf. This entry point re-exports insomni-msdf-text’s Font / loadFont plus insomni’s MsdfGlyphAtlas adapter, and pulls opentype.js + the MSDF generator into your bundle:

import { createLayer } from "insomni";
import { loadFont, MsdfGlyphAtlas } from "insomni/text-ttf";
const inter = await loadFont("/fonts/inter.ttf");
const atlas = new MsdfGlyphAtlas(renderer, inter);
const labels = createLayer({ space: "ui", atlas });
labels.pushText({ text: "Crisp at any zoom", x: 24, y: 24, fontSize: 18 });

loadFont accepts a URL or an ArrayBuffer (FontSource) and takes FontOptions. The resulting Font is a TextFont with real kerning; MsdfGlyphAtlas is its GlyphAtlas.

For non-layer consumers (custom pipelines, SVG export, measurement), the MSDF shaper is exported from the core barrel under prefixed names:

  • shapeMsdfText(text, font, atlas, x, y, options) → an MsdfShapedBlock (glyphs, bbox, height, lineCount, maxLineWidth).
  • MsdfShapeOptionsfontSize, align, maxWidth, lineHeight, simple.
  • MsdfShapedGlyph — one positioned glyph (glyph, worldX/Y, worldW/H).
  • MsdfGlyph — an atlas glyph entry.

The TextPack class (with TextBlockMetrics and the MsdfTextShape alias of its TextShape) is the GPU-backed per-layer text buffer used internally; it owns the glyph instance buffer and a style table. Most callers never touch it directly — Layer.pushText manages packing for you.

A layer created with { atlas } exposes the text methods (they throw without an atlas):

  • pushText(shape, flags?) — full shaper: kerning, multi-line (\n), and word wrap (maxWidth). Returns layout metrics (glyph count + world bbox).
  • pushString(shape) — fast single-line, no-kerning append (equivalent to pushText with shaping off).
  • pushAnchoredString(shape, flags?) — world-anchored, screen-sized text: the anchor stays in world coordinates and the shader projects it per-frame, so panning never repacks the glyphs. Em size and offsets are CSS px.
  • pushStringsBulkInto(batch) — structure-of-arrays fast path for many short single-line labels with no per-string allocation.

pushText / pushString consume a text shape with these fields:

FieldTypeDefaultNotes
textstringThe string.
x, ynumberAnchor; y is the top of the first line (Y-up).
fontSizenumber16World units per em.
colorColorblackFill color.
alignstring"left""left" | "center" | "right".
maxWidthnumber— (no wrap)Hard wrap width in world units.
lineHeightnumberfontSize*1.2Line spacing.
simplebooleanfalseSkip kerning / multi-line / wrap for speed.

Outline, shadow, and gradient are described per-block by these exported types (stored per text block, looked up per-glyph in the fragment shader):

  • TextStyle{ fill, outline?, shadow?, gradient? }.
  • TextOutline{ color, width, softness? } (width/softness in screen px).
  • TextShadow{ color, dx, dy, softness? } (offset/softness in screen px).
  • TextGradient{ from, to, angle? } (angle radians; 0 = left→right).
  • BlockGeometry{ originX, originY, width, height }, the block’s world-space content box that a gradient samples within.