funky-pixi-text
v1.0.0
Published
A PIXI.js addon text field class using MSDF font rendering
Maintainers
Readme
funky-pixi-text
A high-quality, GPU-accelerated MSDF (Multi-channel Signed Distance Field) text rendering library for PIXI.js v7. It provides crisp text rendering at any scale, advanced visual effects (gradients, drop shadows, strokes, 3D extrusion, texture fills), full layout control (word wrap, kerning, alignment, letter spacing), and an adaptive performance system that scales from ultra-low-end mobile devices to high-end desktops.
Table of Contents
- Features
- Installation
- Quick Start
- Generating MSDF Fonts
- Loading Fonts
- The
TextClass - TextStyle Reference
- Usage Examples
- Performance System
- Coming Soon
- Architecture
- Development
- License
Features
- MSDF rendering — crisp glyphs at any zoom level from a single texture atlas
- Single draw call per text instance — all glyphs batched into one PIXI mesh
- Layout engine — multi-line, word wrap, kerning, letter spacing, leading, line-height, alignment (
left/center/right/justify) - Visual effects — solid color, linear gradient, radial (
circular) gradient, stroke, drop shadow (with blur), 3D extrusion, texture fill with blend modes - Auto-scale to fit within a
maxWidth/maxHeightbox - Adaptive performance — six built-in presets (
ultra_low→ultra), automatic GPU detection, FPS / memory / thermal / battery aware runtime monitor - Custom font loading via a PIXI
Assetsextension (FontLoader) - Familiar API —
TextextendsPIXI.Container; properties update viasetStyle(),setColor(), etc.
Installation
npm install funky-pixi-text pixi.jsPIXI.js v7 (
^7.2.4) is a peer dependency.
Quick Start
import { Application, Assets } from "pixi.js";
import { Text, FontLoader } from "funky-pixi-text";
const app = new Application({ backgroundColor: 0x222222, resizeTo: window });
document.body.appendChild(app.view as HTMLCanvasElement);
// Load an MSDF font (atlas .png + bmfont .json)
await Assets.load({
src: "fonts/Roboto-Black.json",
loadParser: FontLoader.name,
});
const label = new Text("Hello MSDF!", {
fontName: "Roboto-Black",
fontSize: 96,
color: 0xffffff,
align: "center",
anchorX: 0.5,
anchorY: 0.5,
});
label.position.set(app.screen.width / 2, app.screen.height / 2);
app.stage.addChild(label);Generating MSDF Fonts
This library consumes BMFont JSON atlases generated by msdf-bmfont-xml or compatible tools.
npm i -g msdf-bmfont-xml
msdf-bmfont -f json -o Roboto-Black --font-size 64 ./Roboto-Black.ttfThis produces two files (the loader picks both up automatically):
Roboto-Black.json— glyph metrics, kerning, atlas layoutRoboto-Black.png— the MSDF atlas texture
Place them side-by-side and load the .json through PIXI Assets.
Loading Fonts
The package exports a PIXI Asset load parser, registered automatically as a PIXI extension when you import the library.
Loading by URL (texture resolved automatically)
import { Assets } from "pixi.js";
import { FontLoader } from "funky-pixi-text";
await Assets.load({
src: "fonts/Roboto-Black.json",
loadParser: FontLoader.name, // "MSDFFontLoaderExtension"
});
// Reference in Text() by the file base name
new Text("Hi", { fontName: "Roboto-Black" });Loading multiple fonts
await Assets.load([
{ src: "fonts/Roboto-Black.json", loadParser: FontLoader.name },
{ src: "fonts/Tourney.json", loadParser: FontLoader.name },
{ src: "fonts/Meddon-Regular.json", loadParser: FontLoader.name },
]);Overriding the texture URL or font name
Pass data to override defaults:
await Assets.load({
src: "fonts/MyFont.json",
loadParser: FontLoader.name,
data: {
fontName: "MyCustomKey", // key used by Text({ fontName })
textureUrl: "fonts/[email protected]", // override atlas path
},
});The Text Class
Text extends PIXI.Container and emits a single batched PIXI.Mesh containing all glyphs.
Constructor
new Text(text: string, style: TextStyle)Both arguments are required. The font referenced by style.fontName must already be loaded via Assets.
Instance methods
| Method | Description |
|---|---|
| setText(text: string): void | Replace the displayed string and rebuild the layout. |
| setStyle(partial: Partial<TextStyle>): void | Update one or more style properties. Performs a uniform-only fast-path when possible; otherwise rebuilds geometry. |
| setColor(color: number \| TextStyleGradient): void | Shortcut to change fill color or gradient (uniform-only update when shape unchanged). |
| setSmoothing(value: number): void | Adjust edge AA smoothing (0.05 – 5.0). |
| setKerning(enabled: boolean, scale?: number): void | Toggle kerning and optionally scale the kerning amount. |
| getTextBounds(): PIXI.Rectangle | Returns the laid-out bounding box including padding for shadow / extrude / smoothing. |
| debugKerning(): void | Logs all kerning pairs present in the font. |
| destroy(options?): void | Frees geometry, material, shader, and removes the instance from internal tracking. |
Static methods
| Method | Description |
|---|---|
| Text.enableAutoConfigUpdate(ticker?: PIXI.Ticker): () => void | Begin watching the global performance config and rebuild all live Text instances when it changes. Returns a cleanup function. |
| Text.disableAutoConfigUpdate(): void | Stop the global watcher. |
TextStyle Reference
type TextStyle = {
fontName: string; // REQUIRED — key used when font was loaded
fontSize?: number; // default 16 (logical pixels)
color?: number | TextStyleGradient; // 0xRRGGBB or gradient object; default 0xffffff
// Layout
align?: "left" | "center" | "right" | "justify"; // default "left"
anchorX?: number; // 0..1 (0 = left, 0.5 = center, 1 = right); default 0
anchorY?: number; // 0..1 (0 = top, 0.5 = middle, 1 = bottom); default 0
wordWrap?: boolean; // default false
maxWidth?: number; // pixels, used with wordWrap and/or autoScale
maxHeight?: number; // pixels, used with autoScale
autoScale?: boolean; // shrink fontSize to fit maxWidth/maxHeight; default false
leading?: number; // extra px added to line height
lineHeight?: number; // override line height in pixels (0 = use font default)
letterSpacing?: number; // px added between characters
kerning?: boolean; // default true
kerningScale?: number; // multiplier for kerning amount; default 1.0
// Effects
stroke?: TextStyleStroke | null;
dropShadow?: TextStyleDropShadow | null;
extrude?: TextStyleExtrude | null;
fillTexture?: TextStyleTextureFill | null;
// Rendering
smoothing?: number; // edge AA half-width (0.05..5.0); default 0.02
scale?: number; // visual multiplier; default 1.0
msdfSign?: number; // 1 or -1; flip signed distance (rare)
// Performance
performanceMode?: "auto" | "high-quality" | "balanced" | "mobile-optimized";
performanceConfig?: PerformanceConfig; // see below
};Gradient
type TextStyleGradient = {
type: "linear" | "circular";
colors: number[]; // 0xRRGGBB array (up to maxGradientStops)
alphas?: number[]; // matching per-stop alphas (0..1)
stops?: number[]; // matching positions (0..1)
angle?: number; // degrees, linear only
center?: { x: number; y: number }; // circular only, 0..1 in glyph space
radius?: number; // circular only, 0..1; <=0 = auto
};Stroke
type TextStyleStroke = {
color: number; // 0xRRGGBB
width: number; // 0..10 (px in MSDF distance units)
smoothing?: number; // 0..10, default 1.0
};Drop Shadow
type TextStyleDropShadow = {
color: number; // 0xRRGGBB
angle: number; // degrees
distance: number; // px
blur: number; // 0..30 (clamped by performance config)
alpha?: number; // 0..1, default 1
};Extrude (3D)
type TextStyleExtrude = {
depth?: number; // px, default 16
steps?: number; // raymarch steps (clamped by performance config), default 24
angle?: number; // degrees, direction of extrusion, default 45
sideColor?: number; // 0xRRGGBB; defaults to a darker face color
};Texture Fill
type TextStyleTextureFill = {
url?: string; // image URL (loaded via Assets if not cached)
assetId?: string; // OR an existing Assets alias
uvScale?: [number, number]; // default [1,1]
uvOffset?: [number, number]; // default [0,0]
rotation?: number; // radians, default 0
mix?: number; // 0..1, default 1
colorAlpha?: number; // 0..1, default 1
textureAlpha?: number; // 0..1, default 1
blendMode?: TextureBlendMode; // "normal" | "multiply" | "screen" |
// "overlay" | "softLight" | "hardLight" |
// "colorDodge" | "colorBurn" |
// "vividLight" | "linearLight" |
// "luminosity" | "hue"
};Usage Examples
Basic Text
const t = new Text("PIXI MSDF", {
fontName: "Roboto-Black",
fontSize: 64,
color: 0xff8800,
anchorX: 0.5,
anchorY: 0.5,
});Word Wrap and Auto-Scaling
// Fixed-width paragraph that wraps
const paragraph = new Text(
"The quick brown fox jumps over the lazy dog.",
{
fontName: "Roboto-Black",
fontSize: 32,
wordWrap: true,
maxWidth: 400,
align: "justify",
lineHeight: 40,
},
);
// Shrink to fit a fixed box (no wrap)
const fit = new Text("LongHeadline", {
fontName: "Roboto-Black",
fontSize: 200,
autoScale: true,
maxWidth: 300,
maxHeight: 80,
});
// Combine: wrap AND shrink so the wrapped block also fits a height
const fitAndWrap = new Text("A multi-line block that must fit.", {
fontName: "Roboto-Black",
fontSize: 80,
wordWrap: true,
autoScale: true,
maxWidth: 300,
maxHeight: 200,
});Linear & Radial Gradients
// Linear gradient (top → bottom)
new Text("GRADIENT", {
fontName: "Tourney",
fontSize: 96,
color: {
type: "linear",
angle: 90,
colors: [0xff0066, 0xffff00, 0x00ccff],
stops: [0.0, 0.5, 1.0],
},
});
// Radial / circular gradient
new Text("SUN", {
fontName: "Tourney",
fontSize: 96,
color: {
type: "circular",
colors: [0xffffff, 0xffaa00, 0xff0000],
center: { x: 0.5, y: 0.5 },
radius: 0.7,
},
});Stroke / Outline
new Text("Outlined", {
fontName: "Roboto-Black",
fontSize: 72,
color: 0xffffff,
stroke: { color: 0x000000, width: 3, smoothing: 1 },
});Drop Shadow
new Text("Shadowed", {
fontName: "Roboto-Black",
fontSize: 72,
color: 0xffffff,
dropShadow: {
color: 0x000000,
angle: 45,
distance: 6,
blur: 8,
alpha: 0.6,
},
});3D Extrusion
new Text("3D!", {
fontName: "Tourney",
fontSize: 160,
color: 0xffcc00,
extrude: {
depth: 24,
steps: 16,
angle: 60,
sideColor: 0x885500,
},
});Texture Fill
import { Assets } from "pixi.js";
await Assets.load({ alias: "wood", src: "/images/wood.jpg" });
new Text("WOODEN", {
fontName: "Roboto-Black",
fontSize: 120,
color: 0xffffff,
fillTexture: {
assetId: "wood",
uvScale: [2, 2],
rotation: 0.1,
mix: 1,
blendMode: "overlay",
},
});Updating Text and Style at Runtime
Text is constructed with (text, style). Change the displayed string with setText(), and edit visual properties with setStyle() (which uses a uniform-only fast-path without rebuilding the layout when possible):
label.setText("New string");
label.setColor(0x00ff88);
label.setSmoothing(0.05);
label.setKerning(true, 1.2);
label.setStyle({
fontSize: 80,
align: "center",
dropShadow: { color: 0x000000, angle: 90, distance: 4, blur: 6 },
});
// Bounds (includes shadow/stroke/extrude padding)
const bounds = label.getTextBounds();
console.log(bounds.width, bounds.height);
// Clean up
label.destroy();Performance System
The library ships with a tunable performance pipeline. Every Text instance compiles a shader variant based on a PerformanceConfig. Lower-tier configs disable features (shadow blur, gradients, extrusion, texture fill) and reduce supersampling / loop iterations to keep frame rates high on weak GPUs.
Presets
Built-in preset names:
type PerformancePresetName =
| "ultra_low" // no shadow blur / no gradients / no extrusion
| "mobile_low"
| "mobile_mid"
| "mobile_high"
| "desktop"
| "ultra"; // 8x supersampling, max blur, max stepsimport {
setDefaultPreset,
getPreset,
getDefaultPerformanceConfig,
} from "funky-pixi-text";
setDefaultPreset("desktop");
const cfg = getDefaultPerformanceConfig();Manual Configuration
Override the global config or pass a per-instance config:
import {
setGlobalPerformanceConfig,
mergePerformanceConfig,
getPreset,
Text,
} from "funky-pixi-text";
// Globally
setGlobalPerformanceConfig(
mergePerformanceConfig(getPreset("mobile_mid"), {
maxShadowBlurRadius: 6,
enableExtrusion: false,
}),
);
// Or per instance
new Text("Fast", {
fontName: "Roboto-Black",
fontSize: 48,
performanceConfig: getPreset("mobile_low"),
});You can also load presets from JSON at startup:
import { loadPresetsFromUrl } from "funky-pixi-text";
await loadPresetsFromUrl("/performance-presets.json");Auto-Detection
import {
detectPerformancePreset,
detectPerformancePresetAsync,
detectWebGLCapabilities,
} from "funky-pixi-text";
const preset = detectPerformancePreset(); // sync, heuristic
const detected = await detectPerformancePresetAsync(); // async, includes GPU benchmark
const caps = detectWebGLCapabilities();Runtime Adaptive Monitoring
PerformanceMonitor watches FPS, memory pressure, thermal state, battery state, and GPU memory pressure, then steps presets up or down at runtime. Combine with Text.enableAutoConfigUpdate so every live text instance reacts to the changes.
import { PerformanceMonitor, Text } from "funky-pixi-text";
import { Ticker } from "pixi.js";
Text.enableAutoConfigUpdate();
const monitor = new PerformanceMonitor({
targetFPS: 60,
autoAdjust: true,
autoUpdateGlobalConfig: true,
checkMemoryPressure: true,
checkThermalState: true,
checkBatteryState: true,
respectReducedMotion: true,
onPerformanceChange: (preset, fps) => {
console.log(`Switched to '${preset}' at ${fps.toFixed(1)} fps`);
},
});
Ticker.shared.add(() => monitor.update());Coming Soon
We're actively working on the next generation of tooling and platform support:
- Text Style Editor — a standalone application for designing text styles visually. It will:
- Export ready-to-use JSON style files you can drop straight into
Text. - Generate MSDF (Multi-channel Signed Distance Field) font atlases directly from
.ttfand.otffont files — no separate CLI tooling required. - Let you configure and live-test performance configuration presets so you can dial in the right quality/performance balance before shipping.
- Export ready-to-use JSON style files you can drop straight into
- PIXI.js v8 support — a version of the library targeting PIXI.js v8 is in development.
Stay tuned!
Architecture
┌──────────────────────────┐
│ Text │ PIXI.Container facade
└────────────┬─────────────┘
│
┌──────────────┼─────────────────────────┐
▼ ▼ ▼
getLayout() ShaderAssembler PerformanceConfig
(layout/) (renderer/multipass (renderer/)
+ passes + shaders)
│ │
▼ ▼
LayoutData PIXI.Shader + uniforms
└───────────┬──┘
▼
PIXI.Mesh (one draw call)src/Text.ts– facadeTextclass extendingPIXI.Container. Owns text + style state, computes auto-scale, builds geometry, drives uniform updates.src/layout/– pure layout engine (getLayout) that converts characters into per-glyph positions, lines, kerning, wrap, alignment. PlusFontCacheandKerninghelpers.src/renderer/– multi-pass shader assembler. Composable passes (BaseTextPass,StrokePass,GradientPass,TextureFillPass,ExtrudePass,DropShadowPass) are assembled byShaderAssemblerinto a single shader program tuned by the activePerformanceConfig.src/fontLoader/FontLoader.ts– PIXIAssetsload parser that pulls in the BMFont JSON plus its companion PNG atlas and caches aLoadedFont(texture + metrics).src/utils/– default style construction, path helpers, and small utilities.
Development
# Install dependencies
npm install
# Build the library (type declarations + CJS/ESM bundles into dist/)
npm run build
# Run the test suite
npm testLicense
This software is distributed under a custom proprietary license — see
license.txt. Use and redistribution of the unmodified
package are permitted for personal and commercial purposes; modification,
derivative works, and reverse engineering are prohibited.
