Skip to content

Repository files navigation

ZeroText

Zero-allocation text layout engine for the web. Sub-microsecond cached layouts, 5KB core.

npm versionbundle sizelicenseCI

Demo

Live Demo — interactive pipeline visualization with real ported algorithms, benchmark runner, and 62 browser tests.

Why ZeroText?

Canvas-based text layout allocates on every frame. DOM-based layout triggers reflow. ZeroText does neither.

  • Zero allocation -- arena pooling and pre-allocated typed arrays eliminate GC pauses entirely
  • O(1) glyph lookup -- perfect hash table with ASCII fast path (codepoints 0-127 cached without hashing)
  • O(log n) line breaking -- prefix-sum binary search over a DFA-driven break table (UAX#14)
  • Sub-microsecond hot path -- LRU cache keyed by numeric FNV-1a hashes (no string allocation on lookup)
  • WASM SIMD + WebGPU -- optional acceleration paths for batch workloads

Benchmarks

MetricTargetActual
Bundle size<8 KB~5-6 KB
Initial layout (cold)<8 us5.6 us
Hot layout (cached)<1 us0.1 us (100 ns)
GC pauses00

Run locally: npx tsx benchmarks/features.ts

Features

#FeatureStatusAlgorithm
1Line breakingDoneUAX#14 DFA + prefix-sum binary search
2Glyph lookupDonePerfect hash table, O(1) + ASCII cache
3Arena memoryDonePool allocator, generational buffers
4LRU cacheDoneDoubly-linked list + Map, FNV-1a keys
5Bidi/RTLDoneUAX#9 stack-based embedding levels
6LigaturesDoneFlat trie substitution (fi, fl, ff, ffi, ffl)
7KerningDonePair-wise FNV-1a hash table
8HyphenationDoneLiang algorithm + soft-hyphen insertion
9AlignmentDoneLeft, center, right, justify
10TruncationDoneEnd, middle, start with ellipsis
11DecorationDoneSoA bitflags (underline, strikethrough, overline)
12Vertical writingDonevertical-rl, vertical-lr coordinate transform
13Hit testingDoneBinary search over lines and spans

Installation

npm install @zerotext/core

Usage

Basic layout

import{createEngine}from"@zerotext/core";importtype{GlyphEntry}from"@zerotext/core";constglyphs: GlyphEntry[]=[{codepoint: 72,width: 8},// H{codepoint: 101,width: 6},// e{codepoint: 108,width: 3},// l{codepoint: 111,width: 7},// o// ...];constengine=createEngine({});constprepared=engine.prepare("Hello world",glyphs);constlayout=engine.update(prepared,200);// 200px container widthconsole.log(layout.lines.length,layout.height);

Full pipeline (bidi + ligatures + alignment)

import{createEngine,createGlyphTable}from"@zerotext/core";constengine=createEngine({enableBidi: true,enableLigatures: true,enableHyphenation: true,textAlign: 1,// CenterwritingMode: 0,// Horizontaltruncate: 0,// NonemaxLines: 0,// Unlimited});constglyphTable=createGlyphTable(glyphs);consttext=newUint32Array([/* codepoints */]);constresult=engine.layoutFull({
glyphTable,
text,width: 300,lineHeight: 20,});

Hit testing

constcaret=engine.hitTest(mouseX,mouseY,layout);console.log(caret.offset,caret.lineIndex);constrects=engine.getSelectionRects({start: 0,end: 10},layout,);

React hook

import{ZeroTextProvider,useZeroText}from"@zerotext/react";importtype{GlyphEntry}from"@zerotext/core";functionApp(){return(<ZeroTextProviderconfig={{enableLigatures: true}}><TextBlocktext="Hello world"/></ZeroTextProvider>);}functionTextBlock({ text }: {text: string}){constglyphs: GlyphEntry[]=[/* glyph entries */];const{ ref, layout, isReady }=useZeroText(text,{glyphEntries: glyphs,lineHeight: 20,});return<divref={ref}>{isReady&&<span>{layout!.lines.length} lines</span>}</div>;}

Packages

PackageDescription
@zerotext/coreLayout engine, arena, cache, all text features
@zerotext/compilerAhead-of-time font subset + ZTB binary format
@zerotext/flowText wrapping around obstacles
@zerotext/reactReact bindings (useZeroText, ZeroTextProvider)
@zerotext/vueVue composable
@zerotext/svelteSvelte action
@zerotext/wasmWASM SIMD acceleration (Rust)
@zerotext/webgpuWebGPU compute shader batch layout

Build plugins: @zerotext/vite, @zerotext/webpack, @zerotext/rollup

Architecture

Text input
|
v
Arena (pre-allocated typed arrays, zero GC)
|
v
PrefixSum (cumulative glyph widths)
|
v
DFA (UAX#14 line break classification)
|
v
BinarySearch (O(log n) break point selection)
|
v
Cache (LRU, FNV-1a numeric keys, O(1) hot path)
|
v
LayoutResult (lines, spans, positions)

Optional pipeline stages (bidi, ligatures, hyphenation, alignment, truncation, decoration, vertical transform) are applied in engine.layoutFull().

Contributing

See CONTRIBUTING.md for setup, testing, and coding guidelines.

License

Apache-2.0. See LICENSE.

About

Zero-allocation text layout engine for the web. Prefix-sum binary search line breaking, perfect hash glyph lookup, arena-pooled memory — sub-microsecond hot layouts with zero GC pressure.

Topics

Resources

Contributing

Stars

9 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages