Skip to content
pixi-silk
GitHub repository
Docs

Text

SilkText draws labels and readouts from a runtime SDF glyph atlas, with outlines, glows, gradients and tabular numbers. It lives in pixi-silk/text.

Updated pixi-silk 1.0.1View as Markdown

SilkText from pixi-silk/text draws a string from distance-field glyphs, so a label under a zooming camera keeps its edges where PixiJS Text turns into a stretched bitmap.

Draw a label#

Turn off animate and drag the zoom slider. Both sides zoom around the same point.
import { SilkText } from 'pixi-silk/text';

const label = new SilkText({
    text: 'Frame 1',
    fontFamily: 'Inter, system-ui, sans-serif',
    fontSize: 13,
    fontWeight: 600,
    fill: 0xffffff,
    anchor: 0.5,
});

app.stage.addChild(label);
label.text = 'Frame 2'; // laid out again before the next frame

PixiJS Text draws the string once into a canvas and shows that as a texture. SilkText stores each glyph as a signed distance field instead, and the shader turns the distance into coverage per screen pixel. How it works covers the idea. All texts share one atlas texture.

Change one field with its setter (label.fontSize = 18) or several with label.set({ ... }). Width, height, bounds and hit tests use the text box, as with PixiJS Text.

Add an outline, a glow or a gradient#

Change the outline, glow and weight. Watch the readout digits as the number changes.

An outline or a glow reads the same distance field at a different distance, so it needs no filter.

import { horizontal } from 'pixi-silk';
import { SilkText } from 'pixi-silk/text';

const title = new SilkText({
    text: 'BIG WIN',
    fontSize: 48,
    fontWeight: 900,
    fill: horizontal(['#ffd60a', '#ff9f0a']),
    outline: { width: 3, color: 0x3a1c00 },
    shadow: { color: 0x000000, alpha: 0.6, blur: 6, offsetY: 4 },
});
const readout = new SilkText({ text: '72.4', fontSize: 32, tabularNumbers: true });
OptionDefaultWhat it does
fontFamily, fontSize, fontWeight, fontStylesystem UI font, 16, 400, normalAny CSS font. Glyphs are baked once at a fixed size
fillwhiteA colour, or a linear, radial or conic gradient over the text box
letterSpacing, lineHeight0, fontSpacing in local units. Line height 0 uses the font’s own
align, wordWrap, wordWrapWidth, breakWordsleft, off, 100, offLine layout, as in PixiJS Text
anchor0Point of the text box at the position
outlinenone{ width, color, alpha }, outside the glyph edge
shadownone{ color, alpha, blur, offsetX, offsetY }. Without an offset it is a glow
dilate0Grows or thins the glyphs, in local units
tabularNumbersoffDigits share one width, so a changing number does not jitter
kerningonPair kerning measured from Canvas

Outlines and glows reach about a quarter of the font size. Past that the distance field runs out and the effect is clamped. For a wider halo, put a blurred SilkGraphics shape behind the text.

Small sizes#

At 11 to 13 px a texel of the atlas is smaller than a screen pixel. SilkText then averages four samples per pixel, so thin stems do not flicker. The bottom left of the first demo shows both kinds of text at 11, 12 and 13 px.

Glyph shapes and widths match PixiJS Text, because both come from the same Canvas font and measureText. Some platforms hint Canvas text to the pixel grid, so at 1x and no zoom its stems can look a little crisper. From 2x, or under a camera, SilkText wins.

Use SilkText inside widgets#

Widgets that show a value take a label option, a function that builds their text. Pass silkLabel() for widgets that sit under a zooming camera. The widget never imports SilkText itself, so apps that do not use it do not ship it.

import { silkLabel } from 'pixi-silk/text';
import { ValuePill } from 'pixi-silk/ui/pill';

const pill = new ValuePill({ label: silkLabel({ tabularNumbers: true }) });

pill.show('42 px');

To let people click and type into a text, wrap it in a TextEditor. See Editing text.

Fonts and the atlas#

Each glyph is drawn with Canvas2D at 48 px the first time it appears, then turned into a distance field. The atlas is one 8-bit texture, 1024 texels wide, that doubles in height up to 4096. When it is full it starts over, and every text bakes what it needs again.

Load web fonts before the first text uses them, for example with document.fonts.load('600 16px Inter'). Glyphs baked while a font was still loading are baked again once it is ready.

For a different trade-off, make your own atlas and pass it to the texts that should use it:

import { SdfAtlas, SilkText } from 'pixi-silk/text';

const big = new SdfAtlas({ size: 64, radius: 24 }); // sharper corners far in, longer glows
const heading = new SilkText({ text: 'Overview', fontSize: 40, atlas: big });

Where it stops#

Corners round off slightly at extreme zoom, because a single-channel distance field cannot hold a sharp corner. At the default 48 px bake the rounding is about a fiftieth of the font size.

Emoji and colour fonts come out as one-colour silhouettes. There is no shaping: no ligatures, no right-to-left text and no combining marks, and each code point is one glyph. Latin, Greek and Cyrillic UI text works. Use PixiJS Text for emoji, Arabic and Indic scripts.

SilkText runs on WebGL2 and WebGPU. Pixi’s WebGL1 fallback is not supported.

API reference: SilkText, SdfAtlas, silkLabel