
Pixijs Scene Text
- 3k installs
- 293 repo stars
- Updated June 4, 2026
- pixijs/pixijs-skills
pixijs-scene-text is a PixiJS v8 skill for Text, BitmapText, HTMLText, and split text animation with styling and performance guidance.
About
PixiJS Scene Text covers five v8 text rendering classes with distinct styling, performance, and animation trade-offs. Text renders canvas-quality styled labels for menus and dialog. BitmapText uses a glyph atlas for cheap per-frame updates such as scores and timers. HTMLText renders real HTML and CSS via SVG foreignObject for rich markup. SplitText and SplitBitmapText expose per-character containers for animation on short or long strings. All classes use options-object constructors; v7 positional string and style forms are removed. tagStyles on Text and HTMLText enable inline colored tags when configured. BitmapFont.install pre-generates atlases before BitmapText creation, and MSDF fonts stay sharp at any scale in custom builds. The skill documents update cost comparisons, CJK and emoji guidance, and common mistakes such as updating Text.text every frame instead of BitmapText or adding children to text leaf nodes.
- Five text classes: Text, BitmapText, HTMLText, SplitText, SplitBitmapText.
- Options-object constructors replace v7 positional args.
- BitmapText for per-frame score and timer updates.
- tagStyles for inline colored markup on Text and HTMLText.
- Update cost table guides Text versus BitmapText choices.
Pixijs Scene Text by the numbers
- 3,004 all-time installs (skills.sh)
- +214 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #168 of 2,277 Frontend Development skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
pixijs-scene-text capabilities & compatibility
- Capabilities
- text class for high quality static labels · bitmaptext atlas updates without canvas redraw · htmltext rich markup via svg foreignobject · splittext and splitbitmaptext per character anim · tagstyles inline colored tag parsing · bitmapfont.install and msdf font guidance
- Use cases
- frontend · ui design
- Pricing
- Free
What pixijs-scene-text says it does
All text classes use options-object constructors
npx skills add https://github.com/pixijs/pixijs-skills --skill pixijs-scene-textAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3k |
|---|---|
| repo stars | ★ 293 |
| Security audit | 2 / 3 scanners passed |
| Last updated | June 4, 2026 |
| Repository | pixijs/pixijs-skills ↗ |
Which PixiJS v8 text class should I use for styled labels, per-frame scores, or per-character animation?
Choose and configure PixiJS v8 text classes for labels, scores, HTML markup, and per-character animation.
Who is it for?
Game and canvas frontend developers rendering labels, scores, or animated text in PixiJS v8.
Skip if: Skip for non-Pixi text stacks, backend APIs, or scene graph basics without text needs.
When should I use this skill?
User asks about PixiJS Text, BitmapText, HTMLText, SplitText, tagStyles, or text update performance.
What you get
Correct text class, constructor options, and update pattern for the target label or animation.
- BitmapText HUD components
- optimized ticker-driven labels
Files
PixiJS has five text-rendering classes that cover different trade-offs between styling, performance, and animation. Text renders to a canvas for full CSS-style fidelity. BitmapText reads from a pre-generated atlas for cheap updates. HTMLText renders an HTML fragment via SVG <foreignObject> for rich markup. SplitText and SplitBitmapText wrap the first two classes and expose per-character, per-word, and per-line containers for animation.
Assumes familiarity with pixijs-scene-core-concepts. All text classes are leaf nodes; they cannot have children. Wrap multiple text instances in a Container to group them.
Quick Start
const text = new Text({
text: "Hello PixiJS",
style: {
fontFamily: "Arial",
fontSize: 36,
fill: 0xffffff,
stroke: { color: 0x4a1850, width: 5 },
dropShadow: { color: 0x000000, blur: 4, distance: 6 },
},
});
text.anchor.set(0.5);
text.x = app.screen.width / 2;
text.y = 40;
app.stage.addChild(text);All text classes use options-object constructors; positional (string, style) from v7 is not supported.
Related skills: pixijs-scene-core-concepts (leaves, transforms), pixijs-assets (font loading), pixijs-performance (BitmapText tradeoffs), pixijs-color (FillInput for fill/stroke), pixijs-scene-graphics (gradients and patterns reused via FillInput).
Variants
| Variant | Use when | Trade-offs | Reference |
|---|---|---|---|
Text | High-quality static or infrequent-update labels | Expensive to update (canvas re-draw + GPU upload) | references/text.md |
BitmapText | Scores, timers, gameplay labels, anything that changes every frame | Limited styling; fixed glyph atlas; requires MSDF for crisp scaling | references/bitmap-text.md |
HTMLText | Rich formatted text, mixed styles, real HTML tags | Async rendering (one frame delay); similar update cost to Text | references/html-text.md |
SplitText | Per-character animation with rich styling | Each char is a full Text; expensive for long strings | references/split-text.md |
SplitBitmapText | Per-character animation on long strings or dynamic content | Inherits BitmapText limitations (glyph atlas, no MSDF-free crispness) | references/split-bitmap-text.md |
When to use what
- "I need a styled static label" →
Text. Use for titles, menus, dialog, error messages. Seereferences/text.md. - "I need a score or timer that updates every frame" →
BitmapText. Updates only reposition quads; no canvas re-draw. Seereferences/bitmap-text.md. - "I need mixed formatting with `<b>`, `<i>`, `<br>`" →
HTMLText. Real HTML/CSS rendering via SVG. Seereferences/html-text.md. - "I need inline colored tags like `<red>Warning:</red>`" →
TextorHTMLTextwithtagStyles. Both support it. - "I need to animate each character individually" →
SplitTextfor short strings,SplitBitmapTextfor long strings or many instances. Seereferences/split-text.md/references/split-bitmap-text.md. - "I need CJK / Arabic / emoji-heavy text" →
TextorHTMLText.BitmapTextfails because the glyph set is too large for a single atlas. - "I need a custom font" → Load via
Assets.load({ src: 'font.woff2', data: { family: 'MyFont' } })first, then setstyle.fontFamily: 'MyFont'. Works forTextandHTMLText.
Update cost comparison
| Update trigger | Text | BitmapText | HTMLText | SplitText | SplitBitmapText |
|---|---|---|---|---|---|
Changing .text | High | Very low | High | Very high (N text re-renders) | Low (N quad repositions) |
Changing .style | High | Medium | High | Very high | Medium |
Moving (.x, .y) | Free | Free | Free | Free | Free |
| Rotating / scaling | Free | Free | Free | Free | Free |
"Free" = normal Container transform cost. "High" = new canvas draw + GPU upload. "Very low" = quad reposition only. Update strings that change per-frame only on BitmapText or SplitBitmapText.
Quick concepts
- Options-object constructors. Every v8 text class uses
new Text({ text, style, ... }). The v7(string, style)form is removed. - `tagStyles`.
TextandHTMLTextsupport per-tag styling viastyle.tagStyles. Tags are only parsed whentagStyleshas entries; otherwise<is treated literally. - `BitmapFont.install`. Pre-generates an atlas before you create any
BitmapText. Without install, the firstBitmapTextwith a newfontFamilygenerates the atlas lazily. - MSDF fonts. Multi-channel Signed Distance Field fonts stay sharp at any size. Generate with external tools (e.g., msdf-bmfont), load via
Assets.load('font.fnt'). Requiresimport 'pixi.js/text-bitmap'in custom builds.
Common Mistakes
[HIGH] Updating Text.text every frame
Wrong:
app.ticker.add(() => {
scoreText.text = `Score: ${score}`;
});Correct:
const scoreText = new BitmapText({ text: "Score: 0", style });
app.ticker.add(() => {
scoreText.text = `Score: ${score}`;
});Every Text update re-rasterizes the whole string. Use BitmapText for any value that changes per-frame.
[HIGH] Positional constructor args
Wrong:
const text = new Text("Hello", { fontSize: 24 });Correct:
const text = new Text({ text: "Hello", style: { fontSize: 24 } });v8 removed the (string, style) form. All text classes use options objects.
[HIGH] Not importing pixi.js/text-bitmap in custom builds
Under skipExtensionImports: true or aggressive tree-shaking, Assets.load('font.fnt') silently returns raw data unless you add import 'pixi.js/text-bitmap'. The standard import { ... } from 'pixi.js' bundle includes the extension.
[MEDIUM] Adding children to a text instance
Every text class sets allowChildren = false. Wrap in a Container to group text with other content.
API Reference
BitmapText
Text rendered from a pre-generated texture atlas of glyphs. Updating the text string only repositions quads; no canvas re-render, no GPU upload per change. Use BitmapText for scores, timers, gameplay labels, and any text whose content changes frequently. Trade-off: limited styling, fixed glyph set, pixel-perfect only at the font's native size (unless you use MSDF).
Quick Start
const score = new BitmapText({
text: "Score: 0",
style: {
fontFamily: "Arial",
fontSize: 32,
fill: 0xffffff,
},
});
app.stage.addChild(score);
app.ticker.add(() => {
score.text = `Score: ${Math.floor(performance.now() / 100)}`;
});When you pass a system font family without calling BitmapFont.install, the text-bitmap system generates a dynamic bitmap font on first use.
Construction
const minimal = new BitmapText({ text: "Score: 0" });
const styled = new BitmapText({
text: "Hello",
style: { fontFamily: "GameFont", fontSize: 48, fill: 0xff0000 },
anchor: 0.5,
roundPixels: true,
});BitmapTextOptions
BitmapText's constructor accepts the base TextOptions directly (no additional bitmap-specific fields). Fields match references/text.md with two caveats: style is still TextStyle \| TextStyleOptions (the same class Text uses, but many style fields are ignored by the bitmap pipeline), and resolution is managed by the underlying BitmapFont at install time rather than per-instance.
| Option | Type | Default | Description |
|---|---|---|---|
text | TextString | '' | Text content. Same as Text. |
style | `TextStyle \ | TextStyleOptions` | new TextStyle() with fill = 0xffffff |
anchor | `PointData \ | number` | 0 |
resolution | `number \ | null` | null |
roundPixels | boolean | false | Same as Text. |
All Container options (position, scale, tint, label, filters, zIndex, etc.) are also valid here — see skills/pixijs-scene-core-concepts/references/constructor-options.md.
BitmapTextdoes NOT accepttextureStyleorautoGenerateMipmaps; those are specific to canvasTextandHTMLText. The atlas texture style is controlled when installing the font viaBitmapFont.install({ textureStyle }).
Core Patterns
Dynamic fonts (system font -> runtime atlas)
const dynamic = new BitmapText({
text: "Hello",
style: { fontFamily: "Arial", fontSize: 32, fill: 0xff1010 },
});The first BitmapText with a given fontFamily + fontSize generates an atlas lazily. Subsequent BitmapText instances reuse it. The system also scales an existing close-match size rather than re-generating.
Pre-installed fonts
import { BitmapFont } from "pixi.js";
BitmapFont.install({
name: "GameFont",
style: {
fontFamily: "Arial",
fontSize: 48,
fill: 0xffffff,
stroke: { color: "#000000", width: 2 },
},
});
const title = new BitmapText({
text: "Level 1",
style: { fontFamily: "GameFont", fontSize: 48, fill: 0x00ff00 },
});BitmapFont.install pre-generates an atlas so the first BitmapText render has no setup cost. Useful for known fixed styles in a game.
Install options
| Option | Purpose |
|---|---|
chars | Character set to pre-render. Accepts a string, nested ranges, or a preset: BitmapFontManager.ALPHA, BitmapFontManager.NUMERIC, BitmapFontManager.ALPHANUMERIC, BitmapFontManager.ASCII. Essential for non-ASCII, CJK, or restricted charsets. |
resolution | Texture atlas resolution. Default 1. Use window.devicePixelRatio for HiDPI displays. |
padding | Glyph padding inside the atlas. Default 4. Raise to avoid bleeding at large scales. |
skipKerning | Skip kerning metadata to save memory and install time. Default false. |
textureStyle | TextureStyle/TextureStyleOptions override for the generated atlas (for example { scaleMode: 'nearest' } for pixel fonts). |
dynamicFill | Allow runtime tinting via BitmapText.tint. Requires the font style.fill to be white, no stroke, no drop shadow, or a drop shadow with color 0x000000 (black). The idiomatic way to color bitmap text without generating a new atlas per color. |
import { BitmapFont, BitmapFontManager } from "pixi.js";
BitmapFont.install({
name: "UIFont",
chars: BitmapFontManager.ALPHANUMERIC,
resolution: window.devicePixelRatio,
padding: 8,
skipKerning: true,
textureStyle: { scaleMode: "nearest" },
dynamicFill: true,
style: { fontFamily: "Arial", fontSize: 32, fill: 0xffffff },
});
const hp = new BitmapText({
text: "100",
style: { fontFamily: "UIFont", fill: "red" },
});
const mp = new BitmapText({
text: "50",
style: { fontFamily: "UIFont", fill: "blue" },
});Loaded bitmap fonts (FNT / XML)
import "pixi.js/text-bitmap";
import { Assets, BitmapText } from "pixi.js";
await Assets.load("fonts/arcade.fnt");
const arcade = new BitmapText({
text: "HIGH SCORE",
style: { fontFamily: "arcade", fontSize: 36 },
});Load .fnt or .xml files (AngelCode BMFont format) via Assets. Generate them from a .ttf / .otf with AssetPack. The side-effect import 'pixi.js/text-bitmap' registers the loader; required for custom builds that set skipExtensionImports: true.
MSDF / SDF fonts for crisp scaling
Multi-channel Signed Distance Field (MSDF) fonts stay sharp at any size. Generate them with AssetPack (Pixi's own asset pipeline, which takes a .ttf or .otf and emits the .fnt + atlas) or msdf-bmfont. Load them via Assets; they're detected automatically when the FNT file declares a distanceField section.
await Assets.load("fonts/msdf-hero.fnt");
const heading = new BitmapText({
text: "Title",
style: { fontFamily: "msdf-hero", fontSize: 120 },
});
heading.scale.set(2);MSDF fonts trade CPU rendering time for a custom fragment shader, but remain crisp when scaled up or down.
Word wrap
const paragraph = new BitmapText({
text: "A long wrapped paragraph of bitmap text",
style: {
fontFamily: "Arial",
fontSize: 24,
wordWrap: true,
wordWrapWidth: 300,
lineHeight: 30,
},
});Standard TextStyle wrapping properties work. Line-height and alignment (align: 'center' | 'left' | 'right') apply to wrapped output.
Updating content
score.text = `Score: ${value}`;Updates reposition glyph quads only. No canvas re-draw, no GPU upload. This is why BitmapText is the right choice for every-frame text updates.
Common Mistakes
[HIGH] Not importing pixi.js/text-bitmap in custom builds
Wrong (custom build with skipExtensionImports: true):
import { Assets } from "pixi.js";
await Assets.load("font.fnt");Correct:
import "pixi.js/text-bitmap";
import { Assets } from "pixi.js";
await Assets.load("font.fnt");Without the side-effect import, .fnt and .xml files aren't recognized by the asset loader; the call silently succeeds but returns raw data instead of a BitmapFont.
[MEDIUM] Setting resolution on BitmapText
Wrong:
text.resolution = 2;BitmapText ignores resolution and logs a warning. The effective resolution is baked into the BitmapFont at install time. To get higher resolution, install the font with a larger fontSize and scale the text down.
[MEDIUM] Missing characters silently dropped
If the font atlas doesn't contain a glyph (e.g., a rare Unicode character), the glyph is silently skipped with no visible error. Text may appear incomplete. For unknown or user-generated content, fall back to canvas Text or HTMLText.
[HIGH] Using BitmapText for CJK or emoji-heavy content
CJK (Chinese/Japanese/Korean), Arabic, and emoji-heavy strings need thousands of glyphs. A bitmap atlas containing all of them exceeds GPU texture-size limits. Use Text or HTMLText for text with unpredictable or very large character sets.
API Reference
HTMLText
Text rendered via an SVG <foreignObject> wrapping an HTML fragment. This gives you the full HTML/CSS box model for typography; real <b>, <i>, <br>, <div>, line-breaks, nested styles, emoji; rasterized to a texture. Use HTMLText for rich formatting, mixed content, inline custom tags, or markup that the canvas Text class can't express.
Quick Start
const rich = new HTMLText({
text: "<b>Bold</b> and <i>italic</i> text",
style: {
fontFamily: "Arial",
fontSize: 24,
fill: 0x333333,
wordWrap: true,
wordWrapWidth: 400,
},
});
app.stage.addChild(rich);HTMLText is a leaf. It uses HTMLTextStyle, which is TextStyle minus leading, textBaseline, trim, and filters (those four are unsupported by the SVG rendering path). Rendering is asynchronous; the text may not appear on the same frame it's created.
Construction
const minimal = new HTMLText({ text: "<b>Hello</b>" });
const styled = new HTMLText({
text: "<i>Styled</i>",
style: { fontSize: 24, fill: 0xffffff },
anchor: 0.5,
resolution: 2,
autoGenerateMipmaps: true,
textureStyle: { scaleMode: "linear" },
});HTMLTextOptions
HTMLText extends the base TextOptions (typed with HTMLTextStyle / HTMLTextStyleOptions as its style) and adds HTML-specific fields. Inherited text, style, anchor, resolution, and roundPixels behave as documented in references/text.md — only the HTMLText-specific additions are listed here.
| Option | Type | Default | Description |
|---|---|---|---|
style | `HTMLTextStyle \ | HTMLTextStyleOptions` | new HTMLTextStyle() |
textureStyle | `TextureStyle \ | TextureStyleOptions` | undefined |
autoGenerateMipmaps | boolean | TextureSource.defaultOptions.autoGenerateMipmaps | Generate mipmaps for the text texture; improves quality when scaled down. |
All base text options (text, anchor, resolution, roundPixels) are inherited from TextOptions — see references/text.md.
All Container options (position, scale, tint, label, filters, zIndex, etc.) are also valid here — see skills/pixijs-scene-core-concepts/references/constructor-options.md.
BothtextureStyleandautoGenerateMipmapsare also exposed as runtime instance properties, but mutating them after construction requires callinghtmlText.onViewUpdate()to trigger a re-render.
Core Patterns
Custom tags via tagStyles
const message = new HTMLText({
text: "<warning>Low power</warning> <custom>Press any key</custom>",
style: {
fontFamily: "Arial",
fontSize: 28,
fill: 0xffffff,
tagStyles: {
warning: { fill: 0xff3333, fontWeight: "bold" },
custom: { fill: 0x66ccff, fontStyle: "italic" },
},
},
});tagStyles maps custom (or standard) HTML tag names to style overrides. Inherit is automatic; a nested <warning> inside <custom> inherits the outer style. Standard tags like <b>, <i>, <u>, <br> work as expected.
Raw CSS overrides
const styled = new HTMLText({
text: "Underlined shadowed text",
style: { fontSize: 24, fill: 0xffffff },
});
styled.style.addOverride("text-decoration: underline");
styled.style.addOverride("text-shadow: 2px 2px 4px rgba(0,0,0,0.5)");For CSS properties without a TextStyle equivalent, use addOverride to inject raw CSS. Useful for text-decoration, text-transform, letter-spacing beyond what TextStyle exposes, and any other CSS property supported inside SVG <foreignObject>.
Word wrap
const wrapped = new HTMLText({
text: "A long paragraph of HTML text that should wrap automatically",
style: {
fontFamily: "Arial",
fontSize: 20,
fill: 0xffffff,
wordWrap: true,
wordWrapWidth: 300,
align: "center",
},
});Word wrap is handled by the browser's SVG layout, so it supports everything CSS wrapping supports; including hyphenation, justification, and RTL scripts when the font and the rendered CSS support them.
Resolution and mipmaps
const crisp = new HTMLText({
text: "Retina crisp",
style: { fontSize: 32, fill: 0xffffff },
resolution: 2,
autoGenerateMipmaps: true,
});Same pattern as canvas Text: resolution controls the rasterized texture density; autoGenerateMipmaps improves quality when drawn smaller than native.
Async rendering
const htmlText = new HTMLText({
text: "Initial content",
style: { fontSize: 24, fill: 0xffffff },
});
htmlText.visible = false;
app.stage.addChild(htmlText);
app.ticker.addOnce(() => {
htmlText.visible = true;
});HTMLText renders to an SVG blob, then a texture. The texture is available one frame after creation. If you need the text ready before showing it, add it while hidden and reveal it on the next tick.
Common Mistakes
[HIGH] Updating HTMLText content every frame
Wrong:
app.ticker.add(() => {
htmlText.text = `Score: ${score}`;
});Correct:
const bitmap = new BitmapText({ text: "Score: 0", style });
app.ticker.add(() => {
bitmap.text = `Score: ${score}`;
});Each HTMLText.text assignment re-renders the SVG, rasterizes it, and uploads to the GPU. At 60fps this is far too expensive. Use BitmapText for any text that changes per-frame.
[HIGH] Missing CORS headers on fonts
If the HTML references a web font loaded from a different origin without CORS headers, the SVG <foreignObject> is tainted and the rasterization fails (or falls back to a default font). Host fonts on the same origin or include Access-Control-Allow-Origin.
[MEDIUM] Expecting HTMLText frame on creation
Wrong:
const text = new HTMLText({ text: "Hello", style });
text.x = (app.screen.width - text.width) / 2; // text.width is 0 hereCorrect:
const text = new HTMLText({ text: "Hello", style });
app.ticker.addOnce(() => {
text.x = (app.screen.width - text.width) / 2;
});HTMLText measurement happens asynchronously. Defer layout calculations to the next frame, or use canvas Text when you need immediate metrics.
API Reference
SplitBitmapText (experimental)
The bitmap counterpart to SplitText. Wraps a BitmapText and exposes lines, words, and chars as independently-animatable containers. Use SplitBitmapText when you want per-character animation on long strings, many simultaneous instances, or text that changes frequently; scenarios where SplitText's per-char canvas rasterization would be too expensive.
Quick Start
import { BitmapFont, SplitBitmapText } from "pixi.js";
BitmapFont.install({
name: "GameFont",
style: { fontFamily: "Arial", fontSize: 48 },
});
const split = new SplitBitmapText({
text: "Fast Animate",
style: { fontFamily: "GameFont", fontSize: 48 },
charAnchor: { x: 0.5, y: 1 },
});
app.stage.addChild(split);
split.chars.forEach((char, i) => {
char.onRender = () => {
const t = performance.now() / 200 + i;
char.y = Math.sin(t) * 5;
};
});The API mirrors SplitText exactly; the only difference is the underlying text engine. Install a bitmap font (or load one from a .fnt file) before creating the instance.
Construction
const minimal = new SplitBitmapText({
text: "Hello World",
style: { fontFamily: "GameFont", fontSize: 32 },
});
const full = new SplitBitmapText({
text: "Fast\nPer-char",
style: { fontFamily: "GameFont", fontSize: 48, fill: 0xffffff },
autoSplit: true,
lineAnchor: 0.5,
wordAnchor: { x: 0, y: 0.5 },
charAnchor: { x: 0.5, y: 1 },
x: 100,
y: 200,
});SplitBitmapTextOptions
SplitBitmapText uses the same option shape as SplitText: all splitting fields are inherited from AbstractSplitOptions, and the only concrete difference is that style drives a BitmapText render instead of a Text render. Like SplitText, it is a Container, not a ViewContainer; the top-level options do NOT include anchor, resolution, roundPixels, textureStyle, or autoGenerateMipmaps.
| Option | Type | Default | Description |
|---|---|---|---|
text | string | — | Text content to render and segment. Required. |
style | `TextStyle \ | Partial<TextStyleOptions>` | — |
autoSplit | boolean | true | Automatically re-split when text or style changes; set false to batch updates and call split.split() manually. |
lineAnchor | `number \ | PointData` | 0 |
wordAnchor | `number \ | PointData` | 0 |
charAnchor | `number \ | PointData` | 0 |
All Container options (position, scale, tint, label, filters, zIndex, etc.) are also valid here — see skills/pixijs-scene-core-concepts/references/constructor-options.md.
SplitBitmapText.defaultOptionsoverrides the defaults globally for every newSplitBitmapTextinstance.SplitBitmapText.from(existingBitmapText, options?)builds an instance by cloning the source's text and style —tagStyleson the source are discarded (bitmap text does not support them) and a warning is logged.
Core Patterns
Segment access
split.lines.forEach((line) => {
line.alpha = 0.9;
});
split.words.forEach((word) => {
word.scale.set(1.1);
});
split.chars.forEach((char) => {
char.rotation = 0.05;
});Each lines[i], words[i], chars[i] is a Container wrapping a BitmapText instance for that segment. BitmapText glyph quads are cheap, so iterating and transforming hundreds of characters stays performant.
Transform origins
const split = new SplitBitmapText({
text: "Wave motion",
style: { fontFamily: "GameFont", fontSize: 48 },
lineAnchor: 0.5,
wordAnchor: { x: 0, y: 0.5 },
charAnchor: { x: 0.5, y: 1 },
});Same normalized 0–1 anchors as SplitText: line, word, and char containers each get a separate transform origin. Set once at construction; the anchors apply to every future re-split.
Stagger animation
split.chars.forEach((char, i) => {
char.alpha = 0;
});
let elapsed = 0;
app.ticker.add((ticker) => {
elapsed += ticker.deltaMS;
split.chars.forEach((char, i) => {
if (elapsed > i * 50) char.alpha = Math.min(char.alpha + 0.05, 1);
});
});Reveal characters one at a time. Because each char is a cheap BitmapText, this scales to long strings without dropping frames.
Constructing from an existing BitmapText
const label = new BitmapText({
text: "Press start",
style: { fontFamily: "GameFont", fontSize: 32 },
});
const animatable = SplitBitmapText.from(label);SplitBitmapText.from(existingBitmapText) copies content and style, then splits. Useful for attaching per-character animation to a text that was already laid out elsewhere.
Updating content
split.text = "New string";autoSplit (default true) rebuilds the segment arrays on every text/style change. Per-segment animations attached via onRender persist across re-splits as long as you re-apply them in the update step.
Common Mistakes
[HIGH] Using SplitBitmapText without a BitmapFont
Wrong:
const split = new SplitBitmapText({
text: "Hello",
style: { fontFamily: "Arial", fontSize: 48 },
});The above works; dynamic bitmap fonts auto-generate; but for known game fonts prefer pre-installing:
Correct:
BitmapFont.install({
name: "GameFont",
style: { fontFamily: "Arial", fontSize: 48 },
});
const split = new SplitBitmapText({
text: "Hello",
style: { fontFamily: "GameFont", fontSize: 48 },
});Pre-installation avoids first-render latency and gives you control over the atlas.
[MEDIUM] Expecting CJK or emoji support
SplitBitmapText inherits BitmapText's limitations: the glyph atlas must contain every character, and very large character sets exceed GPU texture size. For per-character animation on CJK or emoji-heavy text, use SplitText (which accepts the performance cost) or pre-generate a targeted atlas covering only the characters you'll use.
[MEDIUM] Missing glyphs silently dropped
If a character isn't in the font atlas, its segment is skipped and no error is thrown. Array indices may not line up one-to-one with source character positions if glyphs are missing. Always test your font against the full string content.
API Reference
SplitText
A container that splits a canvas Text render into independently-animatable lines, words, and chars; each exposed as its own Text instance. Use SplitText for per-character intro animations, staggered reveals, or any effect where you need to transform each glyph, word, or line separately.
Quick Start
const split = new SplitText({
text: "Animate Me",
style: { fontSize: 48, fill: 0xffffff },
charAnchor: { x: 0.5, y: 1 },
});
app.stage.addChild(split);
split.chars.forEach((char, i) => {
char.alpha = 0;
char.y = -40;
const delay = i * 80;
setTimeout(() => {
app.ticker.add(() => {
char.alpha = Math.min(char.alpha + 0.05, 1);
char.y += (0 - char.y) * 0.1;
});
}, delay);
});SplitText is new in v8; API shape may still evolve. It wraps Text internally, so the same TextStyle options apply. Every character is a full Text instance; use sparingly and prefer SplitBitmapText for long strings or many animated instances.
Construction
const minimal = new SplitText({
text: "Hello World",
style: { fontSize: 32, fill: 0xffffff },
});
const full = new SplitText({
text: "Animate\nEvery Character",
style: { fontSize: 48, fill: "white", stroke: { color: "black", width: 2 } },
autoSplit: true,
lineAnchor: 0.5,
wordAnchor: { x: 0, y: 0.5 },
charAnchor: { x: 0.5, y: 1 },
x: 100,
y: 200,
alpha: 0.8,
});SplitTextOptions
SplitText is a Container (not a ViewContainer), so unlike Text it does NOT take anchor, resolution, roundPixels, textureStyle, or autoGenerateMipmaps at the top level. Those belong on the internal per-character Text instances. The splitting behavior is controlled by the fields below.
| Option | Type | Default | Description |
|---|---|---|---|
text | string | — | Text content to render and segment. Required. |
style | `TextStyle \ | Partial<TextStyleOptions>` | — |
autoSplit | boolean | true | Automatically re-split when text or style changes; set false to batch updates and call split.split() manually. |
lineAnchor | `number \ | PointData` | 0 |
wordAnchor | `number \ | PointData` | 0 |
charAnchor | `number \ | PointData` | 0 |
All Container options (position, scale, tint, label, filters, zIndex, etc.) are also valid here — see skills/pixijs-scene-core-concepts/references/constructor-options.md.
SplitText.defaultOptionsoverrides the defaults globally for every newSplitTextinstance. The built-in defaults are the values above.
Core Patterns
Segment access
split.lines.forEach((line) => {
line.alpha = 0.8;
});
split.words.forEach((word) => {
word.rotation = 0.1;
});
split.chars.forEach((char) => {
char.scale.set(1.2);
});lines and words are Container[]; chars is Text[] directly (or BitmapText[] for SplitBitmapText). All three arrays are refreshed whenever the text or style changes (when autoSplit is true).
Transform origins
const split = new SplitText({
text: "Wave",
style: { fontSize: 64, fill: 0xffffff },
lineAnchor: 0.5,
wordAnchor: { x: 0, y: 0.5 },
charAnchor: { x: 0.5, y: 1 },
});lineAnchor: transform origin for each line container (0–1 normalized).wordAnchor: transform origin for each word container.charAnchor: transform origin for each character container.
Setting these once at construction time ensures rotations and scales pivot around the intended point (center, bottom, etc.).
Animating per character
split.chars.forEach((char, i) => {
char.onRender = () => {
const t = performance.now() / 200 + i;
char.y = Math.sin(t) * 5;
};
});Using each character's onRender hook avoids one global ticker callback. Each char updates itself every render pass.
Auto-split on change
split.text = "New text";With autoSplit = true (default), reassigning text or style re-splits and rebuilds the segment arrays. Set autoSplit = false to batch multiple changes before calling split.split() manually (see pixijs-scene-text source).
Constructing from an existing Text
const plain = new Text({ text: "Convert me", style: { fontSize: 32 } });
const converted = SplitText.from(plain);SplitText.from(existingText) copies content and style, then splits. Useful when you already have a Text instance and want to apply per-character animation retroactively.
Common Mistakes
[HIGH] Many SplitText instances with long strings
Each character is a full Text instance with its own canvas rasterization and GPU texture. A 40-character SplitText creates 40 text renders at construction time. For long strings, use SplitBitmapText; it wraps BitmapText instead, so each character reuses the glyph atlas.
[MEDIUM] Modifying chars array directly
Wrong:
split.chars.push(extraChar);Correct:
split.text = split.text + "extra";chars, words, and lines are managed arrays. Pushing to them is ignored and will be overwritten on the next auto-split. Always update via the text property and let the class rebuild segments.
[MEDIUM] Expecting segments before the first render
When autoSplit is true, splitting happens lazily on first property read or render. If you need segments immediately after construction, access chars (or call split.split() manually) once before iterating.
API Reference
Text (Canvas Text)
The primary text renderer in PixiJS v8. Rasterizes strings to an off-screen canvas via the native Canvas API, then uploads the result as a GPU texture. Use Text for high-quality typography, styled labels, UI text, and anything where visual fidelity matters more than update speed. For frequently-changing numeric displays (scores, timers), prefer BitmapText.
Quick Start
const text = new Text({
text: "Hello PixiJS",
style: {
fontFamily: "Arial",
fontSize: 36,
fill: 0xffffff,
stroke: { color: "#4a1850", width: 5 },
dropShadow: {
color: "#000000",
blur: 4,
distance: 6,
angle: Math.PI / 6,
},
},
anchor: 0.5,
x: 400,
y: 40,
});
app.stage.addChild(text);Text is a leaf (allowChildren = false). It uses an options-object constructor; positional (string, style) arguments from v7 are no longer supported.
Construction
const minimal = new Text({ text: "Hello" });
const styled = new Text({
text: "Styled Text",
style: { fontSize: 24, fill: 0xff1010 },
anchor: 0.5,
resolution: 2,
roundPixels: true,
});
const crisp = new Text({
text: "Crisp Text",
style: { fontSize: 32 },
textureStyle: { scaleMode: "nearest" },
autoGenerateMipmaps: true,
});CanvasTextOptions
These are the options accepted by new Text({ ... }). The concrete Text constructor takes CanvasTextOptions, which extends the base TextOptions with the canvas-specific textureStyle and autoGenerateMipmaps fields.
| Option | Type | Default | Description |
|---|---|---|---|
text | TextString (`string \ | number \ | { toString(): string }`) |
style | `TextStyle \ | TextStyleOptions` | new TextStyle() |
anchor | `PointData \ | number` | 0 |
resolution | number | null (auto) | Pixel density of the rasterized texture; null follows the renderer's resolution (setting to null at runtime enables auto-resolution; the interface type is number). |
roundPixels | boolean | false | Snap rendered x/y to whole pixels to avoid sub-pixel anti-aliasing. |
textureStyle | `TextureStyle \ | TextureStyleOptions` | undefined |
autoGenerateMipmaps | boolean | TextureSource.defaultOptions.autoGenerateMipmaps | Generate mipmaps for the text texture; improves quality when scaled down. |
All Container options (position, scale, tint, label, filters, zIndex, etc.) are also valid here — see skills/pixijs-scene-core-concepts/references/constructor-options.md.
The style field is a rich nested type; the rest of this document covers its key properties.
Core Patterns
TextStyle properties
const styled = new Text({
text: "Styled",
style: {
fontFamily: "Arial",
fontSize: 32,
fontWeight: "bold",
fontStyle: "italic",
fill: 0xff1010,
stroke: { color: "#4a1850", width: 5 },
dropShadow: {
color: "#000000",
blur: 4,
distance: 6,
angle: Math.PI / 6,
alpha: 0.8,
},
align: "center",
wordWrap: true,
wordWrapWidth: 300,
lineHeight: 45,
letterSpacing: 2,
padding: 4,
},
});Key TextStyle properties:
fontFamily,fontSize,fontWeight,fontStylefill: color, gradient, or pattern (sameFillInputasGraphics)stroke:{ color, width }objectdropShadow:{ color, blur, distance, angle, alpha }wordWrap,wordWrapWidthbreakWords: allow breaking mid-word when wrapping. RequireswordWrap: truewhiteSpace:'normal' | 'pre' | 'pre-line'whitespace handling for multi-line stringsalign:'left','center','right','justify'; only affects multi-line texttextBaseline:'alphabetic' | 'top' | 'hanging' | 'middle' | 'ideographic' | 'bottom'lineHeight,letterSpacingleading: additional line spacing in pixels on top oflineHeighttrim: boolean; crop transparent padding after rasterization (expensive, use only when needed)padding: extra space around the rendered texture; increase when a stroke or shadow gets clippedfilters: array of Pixi filters applied to the generated text texture at bake time, cheaper than filters on theTextnode for static stringstagStyles: per-tag inline style overrides
Tagged text
const alert = new Text({
text: "<red>Warning:</red> system <b>overloaded</b>",
style: {
fontSize: 24,
fill: 0xffffff,
tagStyles: {
red: { fill: 0xff0000 },
b: { fontWeight: "bold" },
},
},
});Tags are parsed only when tagStyles has entries; without entries, < is treated literally. Nested tags inherit from outer tags via an internal style stack.
Font loading
await Assets.load({
src: "my-font.woff2",
data: { family: "MyFont" },
});
const text = new Text({
text: "Custom Font",
style: { fontFamily: "MyFont", fontSize: 36, fill: 0xffffff },
});Supported formats: woff2 (preferred), woff, ttf, otf. The data object is forwarded to FontFace; valid fields are family, display, style, weights (string array, e.g., ['normal', 'bold']), stretch, unicodeRange, featureSettings, variant.
await Assets.load({
src: "titan-one.woff",
data: { family: "Titan One", weights: ["normal", "bold"] },
});Resolution and mipmaps
const sharp = new Text({
text: "Crisp on retina",
style: { fontSize: 36, fill: 0xffffff },
resolution: 2,
autoGenerateMipmaps: true,
});resolution(default: renderer's resolution): pixel density of the underlying texture. Higher values produce sharper text on high-DPI displays.autoGenerateMipmaps: improves quality when the text is drawn smaller than its native size.
Dynamic content
app.ticker.add(() => {
const next = `Score: ${score}`;
if (scoreText.text !== next) {
scoreText.text = next;
}
});Guard text updates with an equality check when using Text for live values. Every assignment triggers a canvas re-render and GPU upload.
Gradient and pattern fills
import { FillGradient } from "pixi.js";
const gradient = new FillGradient({
end: { x: 0, y: 1 },
colorStops: [
{ color: 0xff0000, offset: 0 },
{ color: 0x0000ff, offset: 1 },
],
});
const title = new Text({
text: "Gradient",
style: { fontSize: 64, fill: gradient },
});For a texture fill, pass a FillPattern. Its constructor takes an options object; the legacy positional form new FillPattern(texture, repetition) is also accepted.
import { Assets, FillPattern } from "pixi.js";
const pattern = new FillPattern({
texture: await Assets.load("bricks.png"),
repetition: "repeat",
});
const label = new Text({
text: "PixiJS",
style: { fontSize: 64, fill: pattern },
});textureSpace controls tiling and defaults to 'global': tiles repeat continuously so adjacent shapes share one grid. Pass textureSpace: 'local' to fit a single tile to each shape's bounds. A FillPattern works for stroke too, e.g. style: { stroke: { fill: pattern, width: 10 } }.
fill accepts any FillInput that Graphics accepts; gradients, patterns, solid colors, and arrays of stops.
Common Mistakes
[HIGH] Updating Text content every frame
Wrong:
app.ticker.add(() => {
scoreText.text = `Score: ${score}`;
});Correct:
const scoreText = new BitmapText({ text: "Score: 0", style });
app.ticker.add(() => {
scoreText.text = `Score: ${score}`;
});Every Text update re-rasterizes the full string and uploads a new texture. At 60fps this burns frame budget. Use BitmapText for values that change per-frame, or at minimum guard with an equality check against the previous string.
[HIGH] Using positional constructor args
Wrong:
const text = new Text("Hello", { fontSize: 24 });Correct:
const text = new Text({ text: "Hello", style: { fontSize: 24 } });v8 Text uses an options object. The v7 (string, style) signature is not supported.
[MEDIUM] Stroke or shadow getting clipped at edges
Wrong:
const text = new Text({
text: "Hello",
style: { stroke: { color: "red", width: 10 } },
});Correct:
const text = new Text({
text: "Hello",
style: { stroke: { color: "red", width: 10 }, padding: 10 },
});The underlying canvas is sized from the text metrics; heavy strokes or drop-shadows render past those bounds and get clipped. Add padding to the style to enlarge the texture.
API Reference
Related skills
How it compares
Use pixijs-scene-text for per-frame HUD numbers; use standard PixiJS Text when rich styling or infrequent updates matter more than upload cost.
FAQ
Why avoid updating Text every frame?
Each Text update re-rasterizes the string; use BitmapText for per-frame values.
How create text in v8?
Use new Text({ text, style }) options objects; positional constructors were removed.
When use HTMLText?
When you need real HTML tags like bold, italic, or line breaks with CSS styling.
Is Pixijs Scene Text safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.