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.

Preview of the MSDF text demo
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) | | -------------------- | ------------------------------------ | ------------------------------------------------------ | | Import | insomni | insomni/text-ttf (+ insomni-msdf-text) | | Font source | Any CSS family the browser resolves | A specific .ttf / .otf you load | | Bundle cost | Zero font bytes | opentype.js + MSDF generator | | Kerning | No (always 0) | Yes | | Extreme-zoom corners | SDF rounds sharp corners | Stays crisp (MSDF) | | Best for | UI / dataviz labels at typical sizes | Display 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:

| Field | Type | Default | Notes | | ------------ | ------- | -------------- | ------------------------------------------------ | | text | string | — | The string. | | x, y | number | — | Anchor; y is the top of the first line (Y-up). | | fontSize | number | 16 | World units per em. | | color | Color | black | Fill color. | | align | string | "left" | "left" | "center" | "right". | | maxWidth | number | — (no wrap) | Hard wrap width in world units. | | lineHeight | number | fontSize*1.2 | Line spacing. | | simple | boolean | false | Skip 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.