API Reference
Complete reference for all JavaScript, TypeScript, and Rust APIs exported by
wordcloud-wasm. The engine computes geometry only — all rendering stays in caller code.
Main API wordcloud-wasm #
Promise-based functions that hide the WASM initialisation dance. Import from the package root.
import { layout, treemap, ready, LayoutMode } from "wordcloud-wasm";
ready
Ensures the WASM module is initialized. Idempotent — the first call starts initialization; every subsequent call awaits the same cached promise. You rarely need to call this directly; layout() and treemap() call it automatically.
| Parameter | Type | Description |
|---|---|---|
| input | unknown (optional) | Custom WASM source forwarded to the generated init(). Accepts a URL string, Response, ArrayBuffer, or WebAssembly.Module. Omit in browser environments — the .wasm file is fetched automatically. |
Returns: Promise<unknown> — resolves when the module is ready.
// Pre-warm the module during page load. await ready();
.wasm file — especially with a static export — pass an explicit URL instead of relying on the default same-directory fetch. The simplest zero-config fix is pointing at the published package on a CDN, pinned to your installed version:
import { version } from "wordcloud-wasm/package.json"; await ready(`https://unpkg.com/wordcloud-wasm@${version}/pkg/wordcloud_layout_bg.wasm`);
See the README's Using with a bundler section for CDN vs. self-hosted trade-offs and a Webpack asset/resource config for self-hosting.
layout
Lays out weighted words and resolves to their computed placements. Words are placed in decreasing weight order; words that do not fit are silently omitted from the output.
| Parameter | Type | Description |
|---|---|---|
| items | Item[] | Array of weighted words. Must be a non-empty array. |
| options | LayoutOptions (optional) | Canvas dimensions, font range, strategy mode, padding, and render font. See LayoutOptions. |
Returns: Promise<Placement[]> — x/y are the bounding-box center; render with textAlign:"center" and textBaseline:"middle".
Throws: TypeError if items is not an array; RangeError for an unknown mode string; an Error if options.font cannot be fetched or parsed.
const items = [ { text: "WebAssembly", weight: 100 }, { text: "Rust", weight: 80 }, { text: "JavaScript", weight: 60 }, ]; const placements = await layout(items, { mode: "pretty", width: 800, height: 600, minFontSize: 14, maxFontSize: 80, padding: 4, }); // → [{ text, x, y, rotation, fontSize, baselineOffset }, ...] const ctx = canvas.getContext("2d"); for (const { text, x, y, rotation, fontSize } of placements) { ctx.save(); ctx.translate(x, y); if (rotation) ctx.rotate(rotation * Math.PI / 180); ctx.font = `${fontSize}px sans-serif`; ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.fillText(text, 0, 0); ctx.restore(); }
Matching the layout to your render font. The engine has no font of its own, so tell layout which one you draw with and it measures each word in it via canvas measureText — the same code path the browser will use to paint the text, which means kerning, ligatures, font fallback and shaping all come along for free.
const placements = await layout(items, { width: 800, height: 600, font: "Inter, sans-serif", // defaults to "sans-serif" fontWeight: 600, // optional }); ctx.textAlign = "center"; ctx.textBaseline = "alphabetic"; for (const p of placements) { ctx.font = `600 ${p.fontSize}px Inter, sans-serif`; ctx.fillText(p.text, p.x, p.y + p.baselineOffset); }
Without a matching measurement the engine falls back to a built-in approximation of a generic sans-serif. Where your real font is wider than that guess, the drawn glyphs overrun the space reserved for them; since placements are center-anchored the excess splits across both ends, appearing as slight overlap at a word's first and last letters.
Measurements are cached per font, so repeated layouts over the same words cost nothing. Pass metrics: null to skip measuring, or your own TextMetrics to supply it from elsewhere.
measureText
Measures words with a canvas, in the font they will be rendered in. layout() calls this for you — reach for it directly only when the layout runs somewhere the render font is not available.
The main case is a Web Worker: a worker's font set is not the document's, so a font the page loaded at runtime would measure there as whatever fallback the worker resolves to. createLayoutWorker() already handles this by measuring on the main thread before posting.
| Parameter | Type | Description |
|---|---|---|
| texts | Array<string | Item> | Words to measure, in the order they will be handed to layout(). |
| options | FontOptions (optional) | Which font to measure in. Defaults to sans-serif. |
Returns: TextMetrics | null — null where no canvas is available to measure with, or where the font string could not be parsed.
import { measureText } from "wordcloud-wasm"; import { createLayoutWorker } from "wordcloud-wasm/layout-worker"; const metrics = measureText(items, { font: "Inter, sans-serif" }); const placements = await createLayoutWorker().layout(items, { width: 800, height: 600, metrics, });
treemap
Lays out weighted items as a squarified treemap — axis-aligned rectangles whose areas are proportional to weight, tiling the canvas with no gaps or overlaps.
| Parameter | Type | Description |
|---|---|---|
| items | Item[] | Array of weighted items. |
| options | TreemapOptions (optional) | Canvas dimensions. See TreemapOptions. |
Returns: Promise<TreemapRect[]> — x/y are the top-left corner of each tile.
Throws: TypeError if items is not an array.
const rects = await treemap([ { text: "Alpha", weight: 40 }, { text: "Beta", weight: 30 }, { text: "Gamma", weight: 20 }, ], { width: 800, height: 600 }); // → [{ text, value, x, y, width, height }, ...] for (const { text, x, y, width, height } of rects) { ctx.strokeRect(x, y, width, height); ctx.fillText(text, x + 4, y + 16); }
Types #
TypeScript interfaces, type aliases, and constants exported from wordcloud-wasm.
Item
A single weighted input word. The engine's Input Model.
interface Item { text: string; // the word or label weight: number; // relative importance — drives font size and placement order }
Placement
A placed word returned by layout(). Coordinates are the center of the word's bounding box.
interface Placement { text: string; // the word x: number; // bounding-box center X (px) y: number; // bounding-box center Y (px) rotation: number; // 0 or 90 (degrees) fontSize: number; // computed font size (px) baselineOffset: number; // distance from y down to the baseline (px) }
ctx.translate(x, y); ctx.rotate(rotation * Math.PI / 180);baselineOffset is measured by whichever rasterizer sized the word. Drawing with textBaseline:"alphabetic" at y + baselineOffset puts the baseline exactly where the engine reserved space, instead of depending on how a given browser defines "middle". Rendering with "middle" stays close enough for most fonts, so this field is purely additive.
TreemapRect
A treemap tile returned by treemap(). Origin is top-left.
interface TreemapRect { text: string; // the item label value: number; // the item's original weight x: number; // top-left X (px) y: number; // top-left Y (px) width: number; height: number; }
ctx.fillRect(x, y, width, height) — top-left origin, unlike Placement which is centered.LayoutOptions
| Field | Type | Default | Description |
|---|---|---|---|
| mode | LayoutModeName | "pretty" | Layout strategy. See Mode Comparison. |
| width | number | 1024 | Canvas width in pixels. |
| height | number | 768 | Canvas height in pixels. |
| minFontSize | number | 12 | Font size (px) assigned to the lowest-weight word. |
| maxFontSize | number | 96 | Font size (px) assigned to the highest-weight word. |
| padding | number | 2 | Gap (px) added around each word's footprint to prevent crowding. |
| font | string | "sans-serif" | CSS font-family words will be rendered in. Words are measured in it so their footprints match what gets drawn. |
| fontWeight | string | number | — | CSS font-weight, if not normal. |
| fontStyle | string | — | CSS font-style, if not normal. |
| metrics | TextMetrics | null | — | Measurements to use instead of measuring here. null skips measuring entirely. |
| wasm | unknown | — | Custom WASM source forwarded to ready(). |
TreemapOptions
| Field | Type | Default | Description |
|---|---|---|---|
| width | number | 1024 | Canvas width in pixels. |
| height | number | 768 | Canvas height in pixels. |
| wasm | unknown | — | Custom WASM source forwarded to ready(). |
FontOptions
Which font to measure in. These are assembled into a CSS font shorthand and set on a canvas, so anything a canvas accepts works — including family stacks with fallbacks ("Inter, Helvetica, sans-serif").
| Field | Type | Default | Description |
|---|---|---|---|
| font | string | "sans-serif" | CSS font-family. |
| fontWeight | string | number | — | CSS font-weight, if not normal. |
| fontStyle | string | — | CSS font-style, if not normal. |
TextMetrics
Text dimensions measured in the render font, as returned by measureText().
interface TextMetrics { ascent: number; // above the baseline, as a fraction of the font size descent: number; // below the baseline, as a fraction of the font size widths: number[]; // footprint width per word, parallel to the items }
Everything is a fraction of the font size rather than a pixel count, so a single measurement pass serves every size the layout assigns.
widths is positional: entry i describes item i. A short list is safe — items past its end fall back to the built-in approximation rather than erroring.LayoutModeName
String alias accepted by LayoutOptions.mode. Case-insensitive. See Mode Comparison for guidance.
LayoutMode
The raw WASM enum, re-exported for advanced callers that work directly with the low-level bindings. Prefer the string mode option in LayoutOptions for normal use.
const LayoutMode: { readonly Pretty: 0; readonly Balanced: 1; readonly Fast: 2; };
Web Worker Helper wordcloud-wasm/layout-worker #
Runs the layout engine in a dedicated Web Worker so the main thread is never blocked by heavy layout work.
import { createLayoutWorker } from "wordcloud-wasm/layout-worker";
createLayoutWorker
Creates a LayoutWorker backed by a Web Worker. The worker is spawned immediately; call layoutWorker.terminate() when it is no longer needed.
| Parameter | Type | Description |
|---|---|---|
| opts | LayoutWorkerOptions (optional) | Configuration. See LayoutWorkerOptions. |
Returns: LayoutWorker
const layoutWorker = createLayoutWorker(); const placements = await layoutWorker.layout(items, { mode: "balanced", width: 1200, height: 800, }); layoutWorker.terminate(); // when done
LayoutWorkerOptions
| Field | Type | Description |
|---|---|---|
| worker | Worker (optional) | Bring your own Worker instance. If omitted, a module worker is spawned automatically from worker.js next to layout-worker.js. Useful for custom bundler setups or when controlling the worker URL. |
LayoutWorker
| Member | Type | Description |
|---|---|---|
| worker | Worker | The underlying Worker instance, exposed for advanced use (e.g. monitoring onerror). |
| layout() | (items, options?) => Promise<Placement[]> | Same signature and semantics as the main-thread layout(), but executes inside the worker. Supports multiple concurrent calls — each call is tracked by a unique ID. |
| terminate() | () => void | Terminates the worker immediately. All in-flight layout() calls are rejected. |
Request:
{ type: "layout", id, items, options }Success:
{ type: "result", id, placements }Error:
{ type: "error", id, message }
Multi-threading wordcloud-wasm/threads #
Optional Rayon-based thread pool for the threaded WASM build. The default build is single-threaded and does not require this module. Everything degrades gracefully — if threading is unavailable, initThreads() returns 0.
Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp headers. (3) await initThreads() once before the first layout() call.
import { threadsSupported, initThreads } from "wordcloud-wasm/threads";
threadsSupported
Returns true when the runtime can host WASM threads — SharedArrayBuffer is available and (in a browser) crossOriginIsolated is true.
initThreads
Starts the WASM Rayon thread pool. Safe to call unconditionally — returns 0 when the threaded build is not loaded, SharedArrayBuffer is unavailable, or cross-origin isolation is missing.
| Parameter | Type | Default | Description |
|---|---|---|---|
| threads | number (optional) | hardwareConcurrency | Desired number of worker threads. |
Returns: Promise<number> — the number of threads started, or 0 if threading is unavailable.
const n = await initThreads(); console.log(n > 0 ? `Using ${n} threads` : "Single-threaded mode"); // layout() now uses the thread pool automatically. const placements = await layout(items, { mode: "fast" });
Low-level WASM API pkg/wordcloud_layout.js #
The raw wasm-bindgen generated bindings. Use the main JS API instead for typical use cases. This layer is documented for advanced scenarios: custom bundler integration, direct WASM instantiation, or SSR environments.
import init, { layout_words, layout_treemap, LayoutMode, ping, version } from "wordcloud-wasm/pkg";
init
Asynchronously instantiates the WASM module. Must be called and awaited before any other WASM function. Accepts a URL, Response, BufferSource, or a pre-compiled WebAssembly.Module.
initSync
Synchronously instantiates the WASM module from bytes or a pre-compiled WebAssembly.Module. Use only when async initialization is not possible.
layout_words
Raw WASM binding for word cloud layout. Deserializes the JS input array and config object, runs the selected strategy, and returns a JS array of placement objects.
| Parameter | Description |
|---|---|
| input | JS array of { text, weight } objects. |
| mode | LayoutMode.Pretty, LayoutMode.Balanced, or LayoutMode.Fast. |
| config | JS object with { width?, height?, minFontSize?, maxFontSize?, padding?, metrics? }. null/undefined yields all defaults. |
Returns: JS array of { text, x, y, rotation, fontSize, baselineOffset }. Throws a JS exception on malformed input — never panics.
Pass config.metrics — { ascent, descent, widths }, exactly the shape measureText() returns — to size words from measurements taken in the render font. widths is parallel to input. Without it, words fall back to the built-in metrics approximation.
const metrics = measureText(items, { font: "Inter, sans-serif" }); const placements = layout_words(items, LayoutMode.Pretty, { ...cfg, metrics });
layout_treemap
Raw WASM binding for treemap layout. Only width and height are read from config; font-size fields are ignored.
Returns: JS array of { text, value, x, y, width, height }.
ping
Liveness check. Returns a human-readable greeting such as "wordcloud_layout v0.3.0 ready". Useful for verifying the WASM module loaded correctly.
version
Returns the crate version string, e.g. "0.3.0".
Rust Crate API #
For consumers building Rust applications or extending the engine with new layout strategies. All public types and traits are re-exported from the crate root (crate/src/lib.rs).
LayoutEngine
The core abstraction. A layout strategy is any type that implements this trait. The three word-cloud modes (Pretty, Balanced, Fast) and the treemap are all separate impl LayoutEngine — they share no code with each other, only the underlying infrastructure (OccupancyBitmap, SpatialGrid, WordMask).
pub trait LayoutEngine { type Item; // input element (e.g. a weighted word) type Config; // canvas / tuning parameters type Output; // computed placement handed to the renderer fn layout(&self, items: &[Self::Item], config: &Self::Config) -> Vec<Self::Output>; }
Custom strategy example:
use wordcloud_layout::{LayoutEngine, Item, LayoutConfig, Placement}; struct StackAtCenter; impl LayoutEngine for StackAtCenter { type Item = Item; type Config = LayoutConfig; type Output = Placement; fn layout(&self, items: &[Item], cfg: &LayoutConfig) -> Vec<Placement> { let (cx, cy) = (cfg.width / 2.0, cfg.height / 2.0); items.iter().map(|it| Placement { text: it.text.clone(), x: cx, y: cy, rotation: 0, font_size: 12.0, baseline_offset: 3.6, }).collect() } }
Structs
Item
pub struct Item { pub text: String, pub weight: f32, }
Weighted text input. Implements Serialize + Deserialize (serde). Matches the JS { text, weight } input model.
Placement
pub struct Placement { pub text: String, pub x: f32, pub y: f32, pub rotation: u8, // 0 or 90 pub font_size: f32, // distance (px, downward) from `y` to the text baseline pub baseline_offset: f32, }
Public, JSON-serializable output. Serialized with camelCase keys (fontSize, baselineOffset) to match the JS output model.
LayoutConfig
pub struct LayoutConfig { pub width: f32, // default: 1024 pub height: f32, // default: 768 pub min_font_size: f32, // default: 12 pub max_font_size: f32, // default: 96 pub padding: f32, // default: 2 pub metrics: Option<TextMetrics>, // default: None }
Deserializes from camelCase JS objects (minFontSize, maxFontSize). Implements Default. metrics carries text measured in the caller's own render font; when present, the layout functions size words from it instead of the built-in approximation.
OccupancyBitmap
Canvas-sized bit array tracking occupied pixels. Used by layout strategies for pixel-perfect collision detection.
| Method | Signature | Description |
|---|---|---|
| new | (width: u32, height: u32) → Self | Allocates a blank bitmap. |
| collides | (&self, mask: &WordMask, dx: i32, dy: i32) → bool | Tests if placing mask at offset (dx, dy) overlaps any occupied pixel. |
| occupy | (&mut self, mask: &WordMask, dx: i32, dy: i32) | Marks all pixels covered by mask at offset (dx, dy) as occupied. |
WordMask
Per-word pixel mask. Passed to OccupancyBitmap::collides and occupy.
| Method | Signature | Description |
|---|---|---|
| new | (width: u32, height: u32) → Self | Empty mask. |
| from_pixels | (pixels: &[u8], width: u32, height: u32) → Self | Build from a raw pixel buffer. |
| get / set | (x: u32, y: u32) → bool / (val: bool) | Read/write individual pixels. |
| count_ones | () → u32 | Number of set pixels. |
SpatialGrid
Uniform-grid broad-phase spatial index. Used by the Balanced strategy to accelerate neighbor lookups before expensive per-pixel collision checks.
| Method | Description |
|---|---|
| new(w, h, cell_size) | Create a grid for a canvas of w × h pixels. |
| recommended_cell_size(w, h) | Compute a sensible cell size for the given canvas. |
| insert(id, x, y, w, h) | Register word id at bounding box (x, y, w, h). |
| neighbors(x, y, w, h) | Return word IDs in cells overlapping (x, y, w, h). |
| clear() | Reset the grid for reuse. |
Rasterizer
pub trait Rasterizer: Sync { fn measure(&self, text: &str, font_size: f32) -> (u32, u32); fn baseline_offset(&self, font_size: f32) -> f32; fn rasterize( &self, text: &str, font_size: f32, rotation: Rotation, padding: u32, ) -> WordMask; }
Produces the collision footprint for a word. Sync because mask generation is the layout's main parallelization target — implementations are shared across Rayon workers. Two implementations ship: MetricsRasterizer and MeasuredRasterizer. LayoutConfig::metrics picks between them automatically.
Both produce the same shape of mask — a run of filled boxes, whitespace left empty. Deliberately not glyph contours: contours would let the packer slide one word's ascender into the gap between another word's letters, and the empty space inside a box is much of what keeps a cloud legible.
MetricsRasterizer
The fallback Rasterizer. Each character contributes a filled box sized from a per-character advance-width table, so m/w measure wider than i/l without shipping real font metrics. Needs no font asset and builds anywhere, but it is a guess at whatever font the renderer uses — where it undershoots, drawn glyphs overrun their reserved space and words graze each other at the edges.
MeasuredRasterizer
impl MeasuredRasterizer { pub fn new<'a>( metrics: &TextMetrics, texts: impl IntoIterator<Item = &'a str>, ) -> Self; }
Boxes sized from measurements the caller took in its own render font. In a browser those come from canvas measureText, which the JS wrapper handles automatically; from Rust, from whatever text stack you already draw with.
The built-in advance table still decides how a word's total width is divided between characters, so whitespace keeps its share and phrases retain their inter-word gaps. Only the overall width is authoritative. baseline_offset reports (ascent - descent) / 2.
use wordcloud_layout::{layout_pretty, LayoutConfig, TextMetrics}; let config = LayoutConfig { metrics: Some(TextMetrics { ascent: 0.905, descent: 0.212, widths }), ..LayoutConfig::default() }; let placements = layout_pretty(&items, &config);
MetricsRasterizer, so partial measurements degrade rather than fail.TextMetrics
pub struct TextMetrics { pub ascent: f32, pub descent: f32, pub widths: Vec<f32>, }
Text dimensions measured by the caller, as fractions of the font size so one measurement pass serves every size. widths is parallel to the items slice handed to the layout; entries past its end fall back to the approximation. Carried on LayoutConfig::metrics, and serialized as { ascent, descent, widths } across the WASM boundary.
Enums
Rotation
pub enum Rotation { None, Ninety } // Rotation::from_degrees(deg: u8) → Self
Word rotation passed to the Rasterizer. None = 0°, Ninety = 90°.
WeightDist
pub enum WeightDist { Uniform, Linear, Zipf }
Weight distribution selector for benchmark data generation. Available in crate::datasets.
Free Functions
layout_pretty / layout_balanced / layout_fast
pub fn layout_pretty (items: &[Item], cfg: &LayoutConfig) -> Vec<Placement>; pub fn layout_balanced(items: &[Item], cfg: &LayoutConfig) -> Vec<Placement>; pub fn layout_fast (items: &[Item], cfg: &LayoutConfig) -> Vec<Placement>;
Run a specific strategy directly, bypassing the dispatch enum. Words are measured from cfg.metrics when it is present, and with the built-in MetricsRasterizer otherwise.
layout_pretty_with / layout_balanced_with / layout_fast_with
pub fn layout_pretty_with( items: &[Item], cfg: &LayoutConfig, rasterizer: &dyn Rasterizer, ) -> Vec<Placement>;
The same three strategies, measuring words with an explicit Rasterizer. Only needed when you want to bypass LayoutConfig::metrics and hand in a rasterizer directly. The WithRasterizer { mode, rasterizer } wrapper offers the same thing through the LayoutEngine trait.
rasterize_word
pub fn rasterize_word( text: &str, font_size: f32, rotation: u8, padding: u32, ) -> WordMask;
Rasterize a single word into a WordMask using the built-in MetricsRasterizer.
datasets::generate / datasets::config_for
pub fn generate(n: usize, dist: WeightDist, seed: Option<u64>) -> Vec<Item>; pub fn config_for(n: usize) -> LayoutConfig;
Deterministic benchmark helpers. generate produces synthetic items; config_for returns a canvas configuration appropriate for n words.
Error Handling #
Both layout() and treemap() validate their inputs before entering WASM and throw synchronously on bad arguments:
| Error | Condition |
|---|---|
TypeError: items must be an array |
items argument is not an array. |
| RangeError: unknown layout mode | options.mode is not "pretty", "balanced", or "fast". |
| Exception from WASM | Malformed individual items (e.g. missing text field). The WASM module never panics on user input — all errors become JS exceptions. |
Since the functions return Promise, WASM errors arrive as rejected promises. Always wrap in try/catch:
try { const placements = await layout(items, options); } catch (err) { console.error("Layout failed:", err.message); }
Layout Modes — Comparison #
| Mode | String | Visual Quality | Speed | Best for |
|---|---|---|---|---|
| Pretty | "pretty" |
★★★ | Medium | Presentations, final renders, small–medium word counts |
| Balanced | "balanced" |
★★★ | Slower | Large word counts (1000+), maximum density |
| Fast | "fast" |
★★ | ★★★ | Live previews, real-time updates, very large datasets |
Archimedean spiral placement
Words spiral outward from the canvas center, largest first. Uses pixel-perfect OccupancyBitmap collision detection for tight, organic-looking clouds. Best quality for typical word counts.
Spiral + spatial grid
Same spiral as Pretty, augmented with a SpatialGrid broad-phase filter that rejects distant candidates without per-pixel checks. Handles 1000+ words efficiently while maintaining high visual quality.
Shelf packing
Arranges words in horizontal rows by decreasing font size. Less organic-looking but dramatically faster — ~120× faster than d3-cloud at 100 words, ~180× at 5000 words. Ideal for live previews or very large datasets.
mode at any time without changing any other code.