API reference
Every public export, generated from the TypeScript source when the site builds.
Film and stage
Aspecttype
type Aspect = "16:9" | "9:16" | "1:1" | "4:3" | "21:9"
Captioninterface
t0: numbert1: numbertext: string
Chapterinterface
title: stringtime: number
Clipinterface
t0: numbert1: numberlabels: string[]
CompiledFilmclass
A film ready to sample. frame(t) is pure: seek to any time, in any order.
new CompiledFilm(stage
width: numberheight: numberfps: numberduration: numbertitle: stringdescription: stringlook: Lookbloom: numberbackground: string | nullposterTime: number | undefinedThe author's chosen poster time, when set.bleed: booleanchapters: readonly Chapter[]clips: readonly Clip[]captions: readonly Caption[]stage: StageframeCount: numberpreloadImages(): Promise<void>Preload static and keyed images, including those not visible in the first frame.frame(time: number): FrameThe draw list at a time in seconds. May throw Suspend if a drawing needs a new asset.svg(time: number, options?: { transparent?: boolean | undefined; scale?: number | undefined; }): stringStandalone SVG for a time.
compileFilmfunction
Run the build function, waiting for fonts and math when it asks for them, and return a seekable film.
function compileFilm(definition: FilmDefinition, overrides?: CompileOverrides): Promise<CompiledFilm>
CompileOverridesinterface
imageNetwork?: "allow" | "deny" | undefinedlook?: Look | "midnight" | "paper" | "blueprint" | "chalk" | "neon" | undefinedfps?: number | undefinedbloom?: number | undefinedbackground?: string | null | undefined
filmfunction
Define a film: a function that authors a timeline on a stage.
function film(build: (stage: Stage) => void | Promise<void>): FilmDefinition function film(options: FilmOptions, build: (stage: Stage) => void | Promise<void>): FilmDefinition
FilmDefinitioninterface
kind: "vizmatic.film"options: FilmOptionsbuild: (stage: Stage) => void | Promise<void>compile: (overrides?: CompileOverrides | undefined) => Promise<CompiledFilm>Compile with the engine that authored this film. The clock is module state, so a tool with its own bundled engine must call this and not its own compileFilm.
FilmOptionsinterface
imageNetwork?: "allow" | "deny" | undefinedLegacy URL images are allowed by default. Prepared image assets never use the network.bleed?: boolean | undefinedLines and shapes run past the frame edges on purpose, as a warped grid does. Text is still checked.title?: string | undefineddescription?: string | undefinedaspect?: Aspect | undefinedLogical stage size. 16:9 is 1920x1080. Everything is authored in these units.width?: number | undefinedheight?: number | undefinedfps?: number | undefinedlook?: Look | "midnight" | "paper" | "blueprint" | "chalk" | "neon" | undefinedfont?: FontFamily | undefinedThe font of every text of the film that names none: atext()with nofont, and each label of an object that got nofont. A text or an object that names a font keeps it, and so do the code and the values that an object sets in mono. Without it, such a text is sans.background?: string | null | undefinedbloom?: number | undefinedLight that spills from bright marks. Defaults to the look's value.poster?: number | undefinedTime in seconds of the frame to use as a poster. Defaults to the richest frame.
isFilmfunction
function isFilm(value: unknown): value is FilmDefinition
isSuspendfunction
True for a Suspend thrown by any copy of the engine.
function isSuspend(error: unknown): error is Suspend
PlayOptionsinterface
duration?: number | undefinedease?: Ease | undefinedlabel?: string | undefinedName shown in timelines.
Stageclass
The timeline a film is authored on. All coordinates are centered: (0, 0) is mid-frame and y points down.
new Stage(options
root: Nodecamera: NodeMove, zoom, and rotate the view with to(s.camera, ...).sxis the zoom.width: numberheight: numberfps: numberlook: Lookfont: FontFamily | nullThe font that the film names for its texts, or null when it names none. A text with no font is then sans.chapters: Chapter[]clips: Clip[]captions: Caption[]updaters: Updater[]lights: Light[]The lights thatspotlightadded.settleLightsturns them into thedimof each node, in time order.arrivals: WeakMap<Node, number>Three records for the lights, of facts that the timeline does not hold. Only a film with a light reads them, so they change nothing in a film with none.arrivalsis the time at which a verb brought a node to the stage: a node that arrives at the very time of a light enters with it, and at time 0 the timeline cannot tell it from a node ofs.add.marksis the node that a mark of a verb belongs to, such as the line ofcircumscribeand its target: the mark is as bright as that node.morphsis each morph with its nodes: a light must not dim a node while it changes its shape.marks: Map<Node, Node>morphs: MorphRecord[]backdrop: RgbaThe color that a dim node goes toward: the background of the film, or of its look when the film has none.time: numberright: numberHalf of the frame in each direction.left: numbertop: numberbottom: numberadd<T extends Node>(first: T, ...rest: Node[]): TPut nodes on stage now. Returns the first, so setup reads as one line.mountAt(node: Node, time: number): voidPut a node on stage so it first shows attime, which may differ from the cursor.remove(...nodes: Node[]): voidplay(...items: (Anim | PlayOptions)[]): StageRun animations together and advance the timeline past them.wait(seconds?: number): StageHold on the current picture.at(time: number, build: () => void): StageAuthor something at another time, then return to where you were.chapter(title: string): StageMark a chapter for the player, presentation steps, and the timeline.caption(text: string, duration?: number): StageShow a caption for a span of time. Captions also export as a VTT sidecar.value(initial?: number): ValueA number that animates, for driving dynamic drawings.update<T extends Node>(node: T, fn: (node: T, time: number) => void, window?: { from?: number | undefined; to?: number | undefined; }): TRe-run a function each frame while the node shows. It can set any property on the node.duration: number
Suspendclass
Thrown while an asset loads. The compiler waits and runs the build function again.
new Suspend(promise
isVizmaticSuspend: trueLets a second copy of the engine recognize this without instanceof.message: stringWhat a tool prints when this leaves the compiler: a text at the top of a film file throws it when the file loads, and a second copy of the engine throws one that this copy does not know.promise: Promise<unknown>toString(): string
Animations
alongfunction
Follow a path, or the shape of a node, from start to end.
function along(node: Node, track: Node | Path, options?: AnimOptions & { orient?: boolean | undefined; }): AnimAniminterface
A piece of motion. schedule writes timeline keys for a window of time.
label: stringduration: numberease?: Ease | undefinedschedule(ctx: AnimContext): void
AnimContextinterface
t0: numbert1: numberease: Easestage: Stage
AnimOptionsinterface
duration?: number | undefinedease?: Ease | undefinedlag?: number | undefinedFraction of one item's time between the starts of consecutive items.
appearfunction
Show at this moment, with no transition.
function appear(target: Target): Anim
applyMatrixfunction
Multiply a node's geometry by a 2x2 matrix around a stage point.
function applyMatrix(target: Target, matrix: readonly [readonly [number, number], readonly [number, number]], options?: AnimOptions & { origin?: Vec | undefined; yUp?: boolean | undefined; }): Animcallfunction
Run a plain function at a moment on the timeline.
function call(fn: () => void): Anim
circumscribefunction
Trace a line around a node for emphasis, then let it run off.
function circumscribe(target: Node, options?: AnimOptions & { color?: ColorInput | undefined; pad?: number | undefined; radius?: number | undefined; width?: number | undefined; }): Animdelayfunction
function delay(anim: Anim, seconds: number): Anim
drawfunction
Trace strokes in order. Filled shapes outline first, then fill.
function draw(target: Target, options?: AnimOptions): Anim
erasefunction
The reverse of draw.
function erase(target: Target, options?: AnimOptions): Anim
fadefunction
function fade(node: Node, opacity: number, options?: AnimOptions): Anim
fadeInfunction
function fadeIn(target: Target, options?: FadeOptions): Anim
FadeOptionsinterface
shift?: Vec | undefinedStart offset for fades in, or travel for fades out.scale?: number | undefinedStart scale for fades in, or end scale for fades out.duration?: number | undefinedease?: Ease | undefinedlag?: number | undefinedFraction of one item's time between the starts of consecutive items.
fadeOutfunction
function fadeOut(target: Target, options?: FadeOptions): Anim
flashfunction
Radial burst at a point, for a moment of arrival.
function flash(point: Node | Vec, options?: AnimOptions & { color?: ColorInput | undefined; radius?: number | undefined; rays?: number | undefined; }): Animgrowfunction
Scale up from nothing. Pass a stage point to grow from it, such as an arrow's tail.
function grow(target: Target, options?: AnimOptions & { from?: Vec | undefined; }): Animindicatefunction
Swell and tint for a moment, then return.
function indicate(target: Target, options?: AnimOptions & { color?: ColorInput | undefined; scale?: number | undefined; }): Animlaggedfunction
Start each piece after a fraction of the previous one has played.
function lagged(anims: readonly Anim[], options?: { lag?: number | undefined; duration?: number | undefined; }): Animmorphfunction
Turn from into to, whatever their shapes. Afterwards to is on stage and from is hidden, so later code always refers to the node it can see.
function morph(from: Target, to: Target, options?: AnimOptions & { lag?: number | undefined; }): AnimmorphMatchingfunction
Morph equations and labels by meaning. Glyphs that exist on both sides travel to their new places. The rest fade out or in where they stand.
function morphMatching(from: Node, to: Node, options?: AnimOptions & { key?: ((leaf: Node) => unknown) | undefined; fadeShift?: Vec | undefined; }): AnimmoveTofunction
function moveTo(node: Node, target: Node | Vec, options?: AnimOptions): Anim
parfunction
Run together. Each piece keeps its own length unless the whole is rescaled.
function par(...anims: readonly Anim[]): Anim
pulsefunction
A short lit segment that travels along a line: a packet on an edge, a signal on a wire. The source line stays in place underneath.
function pulse(line: Node, options?: AnimOptions & { color?: ColorInput | undefined; length?: number | undefined; width?: number | undefined; glow?: number | undefined; }): Animrecolorfunction
function recolor(node: Node, color: ColorInput, options?: AnimOptions): Anim
retimefunction
Change an animation's length or easing without changing what it does.
function retime(anim: Anim, options: AnimOptions): Anim
ripplefunction
Expanding ring that fades out, for a pulse from a point.
function ripple(point: Node | Vec, options?: AnimOptions & { color?: ColorInput | undefined; radius?: number | undefined; width?: number | undefined; }): Animrotatefunction
function rotate(node: Node, radians: number, options?: AnimOptions & { about?: Vec | undefined; }): AnimscaleByfunction
function scaleBy(node: Node, factor: number | Vec, options?: AnimOptions & { about?: Vec | undefined; }): Animseqfunction
Run one after another.
function seq(...anims: readonly Anim[]): Anim
shakefunction
Shake gently to draw the eye.
function shake(node: Node, options?: AnimOptions & { angle?: number | undefined; cycles?: number | undefined; }): Animshiftfunction
function shift(node: Node, dx: number, dy: number, options?: AnimOptions): Anim
Targettype
type Target = Node | readonly Node[]
tofunction
Animate any change. Make the change on the node inside the callback, and the engine interpolates from the old state to the new one, including geometry. s.play(to(dot, (d) => d.moveTo(axes.p(2, 4)).color('yellow').scale(1.5)))
function to<T extends Node>(node: T, change: (node: T) => unknown, options?: AnimOptions & { restore?: boolean | undefined; }): AnimValueclass
A number on the timeline. Read .value inside dynamic and update to drive anything.
new Value(initial
track: Track<number>value: numberset(value: number): ValueSet at the current point on the timeline.to(value: number, options?: AnimOptions): Anim
vanishfunction
Hide at this moment, with no transition.
function vanish(target: Target): Anim
warpfunction
Deform every shape under a node through a function of stage position. This is how a grid bends under a matrix or a complex map.
function warp(target: Target, f: (x: number, y: number) => Vec, options?: AnimOptions & { subdivisions?: number | undefined; }): Animwritefunction
Handwrite text and equations: each glyph is outlined, then filled.
function write(target: Target, options?: AnimOptions): Anim
Nodes
BlendModetype
type BlendMode = "normal" | "screen" | "multiply" | "overlay"
dynamicfunction
A node that rebuilds its contents on every frame from timeline values.
function dynamic(build: (time: number) => Node | readonly Node[] | null): Node
Edgetype
type Edge = "left" | "right" | "top" | "bottom"
groupfunction
A group. Children keep their own transforms inside it.
function group(...children: Node[]): Node
Nodeclass
Every visible thing on stage. A node with a path draws. A node with children groups. Both can carry a transform, and every property can change over time. Setter methods chain and apply at the current point on the timeline, so the same calls set up a node before it appears and change it afterwards.
new Node(path?
id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Nodetrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Nodedrop(child: Node): Nodeall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): NodeA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): NodeSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): NodemoveTo(target: Node | Vec): NodeMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): NodeMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): NodeScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): NodeRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): NodeScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): NodePlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): NodeLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): NodeLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Nodestroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Nodecolor(color: ColorInput): NodeRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Nodeglow(radius: number, color?: ColorInput | undefined): NodeSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): NodezIndex(value: number): Nodepin(): NodePin to the screen so the camera does not move this node.hide(): Nodeshow(): Nodenamed(label: string): NodelabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
NodePropsinterface
x: numbery: numberrotation: numbersx: numbersy: numberpx: numberpy: numberopacity: numberdim: numberThe part of its color that a node keeps while a light is on something else: at 1 the node has its own look, and at 0 its paints are the background color of the film.spotlightowns it, so a film that fades a node and a light that dims it do not write the same value. It does not change the alpha, so a dim node still hides what is behind it. It does not add up along a branch: a node draws with the leastdimof itself and its ancestors.visible: booleanz: numberpath: Path | nullfill: Paint | nullfillOpacity: numberstroke: Paint | nullstrokeOpacity: numberstrokeWidth: numbercap: "round" | "butt" | "square"join: "round" | "miter" | "bevel"dash: readonly number[] | nulldashOffset: numbertrimFrom: numbertrimTo: numbertrimEach: booleanglow: numberglowColor: Rgba | nullblend: BlendModefixed: booleanIgnore the camera. Use it for titles, captions, and other screen-space marks.image: StageImage | null
PropNametype
type PropName = keyof NodeProps
Sidetype
type Side = Vec | Edge | "up" | "down"
StageImageinterface
src: stringEmbedded raster, local filename or legacy URL, resolved before the film is rendered.width: numberheight: numbercrop?: Readonly<ImageCrop> | undefinedasset?: ImageAsset | undefined
Shapes
arcfunction
function arc(radius: number, from: number, to: number, style?: ShapeStyle): Node
arrowfunction
A shaft and a clean tip as one group. The shaft ends at the tip's base, so a thick line never pokes through the point.
function arrow(from: Vec, to: Vec, style?: ArrowStyle): Node
ArrowStyleinterface
tip?: number | undefinedTip length in pixels.tipRatio?: number | undefinedTip width relative to its length.bend?: number | undefinedCurve the shaft: positive bends left of the direction of travel.pad?: number | undefinedSpace left at each end, so arrows stop short of what they point at.both?: boolean | undefinedfill?: ColorInput | null | undefinedstroke?: ColorInput | null | undefinedwidth?: number | undefinedopacity?: number | undefinedglow?: number | undefineddash?: readonly number[] | undefinedz?: number | undefined
bracefunction
Curly brace beside a span of two points, pointing out to one side.
function brace(from: Vec, to: Vec, style?: ShapeStyle & { depth?: number | undefined; }): Nodecirclefunction
function circle(radius: number, style?: ShapeStyle): Node
curvefunction
A smooth curve through points. Non-finite points leave gaps.
function curve(points: readonly Vec[], style?: ShapeStyle & { closed?: boolean | undefined; }): Nodedotfunction
A filled point.
function dot(radius?: number, color?: ColorInput, style?: ShapeStyle): Node
ellipsefunction
function ellipse(rx: number, ry: number, style?: ShapeStyle): Node
imagefunction
A raster picture, such as a logo or a photo, centered on its origin. src is a URL or a data URL. A data URL keeps a film in one file and renders the same in every tool. It fades, moves, and scales like any node. Give width, height, or both.
function image(src: string | ImageAsset, options?: ImageOptions): Node
ImageOptionsinterface
width?: number | undefinedWidth on the stage. With only one of width and height, the other keeps the picture's proportions.height?: number | undefinedopacity?: number | undefinedfit?: "contain" | "cover" | undefinedcrop?: ImageCrop | undefinedA region in intrinsic image pixels, before fitting the destination box.
linefunction
function line(a: Vec, b: Vec, style?: ShapeStyle): Node
parametricfunction
function parametric(f: (t: number) => Vec, from: number, to: number, style?: ShapeStyle & { samples?: number | undefined; }): Nodepolygonfunction
function polygon(points: readonly Vec[], style?: ShapeStyle): Node
polylinefunction
function polyline(points: readonly Vec[], style?: ShapeStyle & { closed?: boolean | undefined; }): Noderectfunction
function rect(width: number, height: number, style?: ShapeStyle & { radius?: number | undefined; }): NoderegularPolygonfunction
function regularPolygon(sides: number, radius: number, style?: ShapeStyle): Node
shapefunction
Wrap any path as a stroked shape in the default ink.
function shape(path: Path, style?: ShapeStyle): Node
ShapeStyleinterface
fill?: ColorInput | null | undefinedstroke?: ColorInput | null | undefinedwidth?: number | undefinedopacity?: number | undefinedglow?: number | undefineddash?: readonly number[] | undefinedz?: number | undefined
svgPathfunction
function svgPath(d: string, style?: ShapeStyle): Node
Images
ImageAssetinterface
Encoded bytes and intrinsic dimensions, validated before a film uses them.
id?: string | undefinedsrc: stringmediaType: "image/png" | "image/jpeg" | "image/gif"width: numberheight: numbersha256: string
ImageAssetErrorclass
new ImageAssetError(message
ImageAssetOptionsinterface
id?: string | undefinedmediaType?: string | undefinedsha256?: string | undefinedbaseDir?: string | undefinedResolve a local filename relative to this directory in Node.width?: number | undefinedExpected intrinsic dimensions, such as those declared in an asset manifest.height?: number | undefined
ImageCropinterface
x: numbery: numberwidth: numberheight: number
ImageFitOptionsinterface
width?: number | undefinedheight?: number | undefinedfit?: "contain" | "cover" | undefinedcrop?: ImageCrop | undefinedA region in intrinsic image pixels, before fitting the destination box.
resolveImageAssetfunction
Load once before authoring. No network request or file read occurs while painting.
function resolveImageAsset(source: string | Uint8Array<ArrayBufferLike>, options?: ImageAssetOptions): Promise<ImageAsset>
Objects
angleMarkfunction
An angle at a vertex, between the rays toward two points.
function angleMark(vertex: Vec, from: Vec, to: Vec, options?: AngleOptions): Node
AngleOptionsinterface
radius?: number | undefinedcolor?: ColorInput | undefinedwidth?: number | undefinedlabel?: string | undefinedlabelSize?: number | undefinedfillOpacity?: number | undefinedSoft wedge under the arc.reflex?: boolean | undefinedTake the long way around.
applyComplexfunction
Deform a complex plane through f(re, im). Labels stay readable by fading separately, so this moves only the grid.
function applyComplex(plane: NumberPlane, f: (re: number, im: number) => Vec, options?: AnimOptions & { subdivisions?: number | undefined; }): AnimAreaOptionsinterface
from?: Reactive | undefinedto?: Reactive | undefinedfill?: ColorInput | undefinedopacity?: number | undefinedsamples?: number | undefined
areaUnderfunction
Shaded region between a curve and the x axis. Pass functions for bounds that move.
function areaUnder(axes: Axes, f: (x: number) => number, options?: AreaOptions): Node
arrayCellsfunction
function arrayCells(values: readonly (string | number)[], options?: ArrayCellsOptions): ArrayCells
ArrayCellsclass
A row of boxes. swap(i, j) lifts two items over each other, and .cell(i) follows them.
new ArrayCells(values
slots: Node[]indexLabels: Node[]items: Node[]The item at each index, updated as swaps are authored.cell(index: number): NodeThe item currently at an index.slotAt(index: number): VecLocal stage position of a slot.swap(i: number, j: number, options?: AnimOptions & { lift?: number | undefined; }): AnimExchange two items along opposite arcs.mark(index: number, color: ColorInput, options?: AnimOptions): AnimRecolor an item.enter(options?: AnimOptions): AnimCells appear from left to right.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): ArrayCellstrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): ArrayCellsdrop(child: Node): ArrayCellsall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): ArrayCellsA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): ArrayCellsSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): ArrayCellsmoveTo(target: Node | Vec): ArrayCellsMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): ArrayCellsMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): ArrayCellsScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): ArrayCellsRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): ArrayCellsScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): ArrayCellsPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): ArrayCellsLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): ArrayCellsLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): ArrayCellsstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): ArrayCellscolor(color: ColorInput): ArrayCellsRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): ArrayCellsglow(radius: number, color?: ColorInput | undefined): ArrayCellsSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): ArrayCellszIndex(value: number): ArrayCellspin(): ArrayCellsPin to the screen so the camera does not move this node.hide(): ArrayCellsshow(): ArrayCellsnamed(label: string): ArrayCellslabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
ArrayCellsOptionsinterface
cell?: number | undefinedgap?: number | undefinedsize?: number | undefinedcolor?: ColorInput | undefinedindices?: boolean | undefinedindexSize?: number | undefinedfont?: FontFamily | undefinedThe font of the values. Defaults to mono. The indexes are in mono.
attentionMatrixfunction
Attention weights as a heatmap, with source tokens across the top and targets down the side. It is a Heatmap, so .cell(i, j) and .row(i) work.
function attentionMatrix(rows: readonly string[], columns: readonly string[], weights: readonly (readonly number[])[], options?: AttentionMatrixOptions): Heatmap
AttentionMatrixOptionsinterface
cell?: number | undefinedcolors?: readonly ColorInput[] | undefinedvalues?: boolean | undefinedlabelSize?: number | undefinedgap?: number | undefinedfont?: FontFamily | undefinedThe font of the token labels. Defaults to the font of the film.
axesfunction
function axes(options?: AxesOptions): Axes
Axesclass
Coordinate axes. .p(x, y) converts math coordinates to stage points in this group's local space, and .plot(f) draws a function on them.
new Axes(options?
xRange: readonly [number, number]yRange: readonly [number, number]plotWidth: numberplotHeight: numberlines: NodeLines and ticks, for drawing in on their own.grid: Nodenumbers: Nodep(x: number, y: number): [number, number]Math coordinates to this group's local stage coordinates.world(x: number, y: number): [number, number]Math coordinates to stage coordinates, following this group's transform.plotPath(f: (x: number) => number, options?: { from?: number | undefined; to?: number | undefined; samples?: number | undefined; }): PathGeometry of a function over a range, clipped to the visible y range.plot(f: (x: number) => number, style?: ShapeStyle & { from?: number | undefined; to?: number | undefined; samples?: number | undefined; }): NodeDraw a function on the axes. The curve becomes a child, so it moves with them.dotAt(x: number, y: number, color?: ColorInput, radius?: number): NodeA point on the axes.guides(x: number, y: number, style?: ShapeStyle): NodeDashed guides from a point down to both axes.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Axestrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Axesdrop(child: Node): Axesall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): AxesA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): AxesSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): AxesmoveTo(target: Node | Vec): AxesMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): AxesMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): AxesScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): AxesRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): AxesScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): AxesPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): AxesLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): AxesLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Axesstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Axescolor(color: ColorInput): AxesRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Axesglow(radius: number, color?: ColorInput | undefined): AxesSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): AxeszIndex(value: number): Axespin(): AxesPin to the screen so the camera does not move this node.hide(): Axesshow(): Axesnamed(label: string): AxeslabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
AxesOptionsinterface
x?: readonly [number, number] | undefinedMath range on the x axis.y?: readonly [number, number] | undefinedwidth?: number | undefinedOn-screen size in stage pixels.height?: number | undefinedxStep?: number | undefinedyStep?: number | undefinedcolor?: ColorInput | undefinedlabels?: boolean | undefinedNumber labels on ticks.labelSize?: number | undefinedgrid?: boolean | undefinedFaint background grid.xLabel?: string | undefinedyLabel?: string | undefinedtips?: boolean | undefined
barChartfunction
function barChart(data: readonly BarDatum[], options?: BarChartOptions): BarChart
BarChartclass
Bars with direct labels and a single hairline baseline. enter() grows them from zero and update(values) animates to new data, with readouts that count.
new BarChart(data
bars: Node[]labels: Node[]readouts: Node[]baseline: Nodevalues: Value[]bar(i: number): NodelabelsOf(part: Node): readonly Node[]The name and the readout of a bar belong to it, so a light on the bar keeps them bright.enter(options?: AnimOptions): AnimBaseline and labels appear, then bars rise one after another while readouts count up.update(values: readonly number[], options?: AnimOptions): AnimAnimate to new values. The scale follows unless it was fixed at creation.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): BarCharttrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): BarChartdrop(child: Node): BarChartall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): BarChartA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): BarChartSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): BarChartmoveTo(target: Node | Vec): BarChartMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): BarChartMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): BarChartScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): BarChartRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): BarChartScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): BarChartPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): BarChartLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): BarChartLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): BarChartstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): BarChartcolor(color: ColorInput): BarChartRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): BarChartglow(radius: number, color?: ColorInput | undefined): BarChartSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): BarChartzIndex(value: number): BarChartpin(): BarChartPin to the screen so the camera does not move this node.hide(): BarChartshow(): BarChartnamed(label: string): BarChart
BarChartOptionsinterface
gap?: number | undefinedFraction of each band left empty between bars.max?: number | undefinedFixes the top of the scale. Defaults to the largest value.min?: number | undefinedshowValues?: boolean | undefinedformat?: ((value: number) => string) | undefinedhorizontal?: boolean | undefinedradius?: number | undefinedwidth?: number | undefinedheight?: number | undefinedlabelSize?: number | undefinedText size for direct labels and ticks.color?: ColorInput | undefinedfont?: FontFamily | undefinedThe font of the labels and the ticks. Defaults to the font of the film. A value that the chart prints is in mono.
BarDatuminterface
label: stringvalue: numbercolor?: ColorInput | undefined
bitFieldfunction
A word split into named fields: bitField([{ bits: 10, label: 'directory' }, { bits: 10, label: 'table', color: 'orange' }, { bits: 12, label: 'offset' }], { width: 1400 }).
function bitField(parts: readonly BitFieldPart[], options?: BitFieldOptions): BitField
BitFieldclass
A word split into named fields: a row of boxes, one per field, with the bit numbers over them. It shows a virtual address as its table indexes, the flags of a register, or the layout of a structure by byte offset. The first field is at the left. With wrap, the fields fill rows from the top. .at() places the middle of a full row, and of all rows from the top to the bottom. A field shows its label inside its box. A field with a value shows the value inside and the label under the box, and setValue() changes the value in place. field('PTE') is the node of one field, to light it, and p('PTE') is a point on its box.
new BitField(parts
fields: Node[]One node per field, which holds its box, its texts and its numbers. The first field is at the left.boxes: Node[]labels: (Node | null)[]The name of each field, or null for a field that has none.values: (Node | null)[]The content that each field shows now, or null for a field that has none.setValue()replaces the node.numbers: Node[]The numbers over the boxes, row by row, from the left to the right. Each one is also a part of its field.total: numberThe sum of the bits of all fields.field(which: string | number): NodeThe node of a field, by its index or by its label. Index 0 is the first field, at the left of the first row.p(which: string | number, edge?: "left" | "right" | "top" | "bottom"): [number, number]The middle of an edge of the box of a field, in the coordinates of the object: the point where a line from the field starts. Defaults to the bottom edge. The left and the right edge are for the field at an end of a row. The point does not follow the object: read it again after the object moves.range(which: string | number): [number, number]The high and the low bit of a field. Withnumbers: 'offsets', its first and its last offset. Without numbers, the bits count as withnumbers: 'bits'.enter(options?: AnimOptions): AnimThe fields arrive one after another, each with its texts and its numbers: from the left, and row by row.setValue(which: string | number, value: string, options?: AnimOptions): AnimThe value of a field changes in place, such as a flag that goes from '0' to '1'. The text in the box fades out, then the new text fades in, so no moment shows two texts. It works on a field that was built with avalue, because the layout does not change. The new text follows the rule of the first one: it shrinks to fit, and a text that cannot fit is an error.valuesthen holds the new text.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): BitFieldtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): BitFielddrop(child: Node): BitFieldall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): BitFieldA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): BitFieldSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): BitFieldmoveTo(target: Node | Vec): BitFieldMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): BitFieldMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): BitFieldScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): BitFieldRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): BitFieldScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): BitFieldPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): BitFieldLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): BitFieldLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): BitFieldstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): BitFieldcolor(color: ColorInput): BitFieldRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): BitFieldglow(radius: number, color?: ColorInput | undefined): BitFieldSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): BitFieldzIndex(value: number): BitFieldpin(): BitFieldPin to the screen so the camera does not move this node.hide(): BitFieldshow(): BitFieldnamed(label: string): BitFieldlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
BitFieldOptionsinterface
width?: number | undefinedThe width of one row: of all fields together, or ofwrapbits. Defaults to 1200.height?: number | undefinedThe height of the boxes. Defaults to 2.6 ofsize.scale?: "proportional" | "even" | undefined'proportional' gives each field a width for its bits. 'even' gives every field of a row the same width. Defaults to proportional.minWidth?: number | undefinedThe least width of a field, so that a field of one bit can hold a name. The wider fields share the rest. Defaults to 0.wrap?: number | undefinedHow many bits (or bytes) one row holds. The fields then fill rows from the top, for the layout of a structure. A field that would cross the end of a row is an error. Without it, all fields are in one row.rowGap?: number | undefinedThe room between two rows. Defaults to the room that the numbers and the labels under the boxes need, and a little more.numbers?: false | "bits" | "offsets" | undefinedWhat to write over the boxes. 'bits' numbers the bits from the right, with the highest bit at the left, such as 63, 48, 47, 39. 'offsets' counts up from 0 at the left, for the bytes of a structure. False writes nothing. Defaults to 'bits'.size?: number | undefinedText size of the labels and values. The numbers are smaller. Defaults to 26.color?: ColorInput | undefinedThe color of a field that has none of its own. Defaults to blue.font?: FontFamily | undefinedThe font of the labels. The values and the numbers are in mono.
BitFieldPartinterface
bits: numberHow many bits (or bytes) the field has. A whole number, 1 or more.label?: string | undefinedThe name of the field, on one line.value?: string | undefinedThe content of the field, such as '0x1fe' or '1', on one line. Drawn in mono inside the box. The label then goes under the box. An empty text is a value too: an empty box thatsetValue()can fill.color?: ColorInput | undefinedDefaults to thecolorof the object.unused?: boolean | undefinedA field that carries nothing, such as reserved bits: a faint box with a hatch.
boundaryfunction
An open line between two sides that counts what crosses it: boundary({ label: 'system call line', above: 'user space', below: 'kernel', tally: ['system call', 'system calls'] }). .at(x, y) places its middle.
function boundary(options?: BoundaryOptions): Boundary
Boundaryclass
An open line between two sides, such as user space and the kernel, that counts what crosses it. cross() sends a mark through the line: the line lights there, a tick stays, and the tally goes up. addCrossings() runs the tally up with no mark, for a number that is too large to draw, and fills the line with ticks. clear() starts the next round of a comparison. .at() places the middle of the line. The names are at the left end and the tally is at the right end, and x() gives the places between them. What changes with time is kept in lists that a change replaces and never edits, and in the nodes. So a copy of the line, made alone or with the group or the zone that holds it, has its own tally and its own ticks.
new Boundary(options?
line: NodelabelNode: Node | nullThe name of the line, or null when it has none.sides: readonly [Node | null, Node | null]The names of the side over the line and of the side under it. Each is null when it is not given.tallyNode: Node | nullThe number and the word of the tally, as one node, or null when the line shows no tally.noteNode: Node | nullThe note under the right end of the line that says what one tick stands for, such as "each tick: about 40". It is null while one tick is one crossing.ticks: readonly Node[]The ticks that are on the line after the changes that are scheduled so far, in the order that they arrive.x(part: number): numberA place along the line forcross(), in the coordinates of the line: 0 is the first place after the names and 1 is the last place before the tally, with room for the widest tally of the line.count: numberThe tally after the changes that are scheduled so far.enter(options?: AnimOptions): AnimThe line draws from the left, then the names, the tally and the ticks arrive.cross(x: number, options?: BoundaryCrossOptions): BoundaryCrossingA mark travels through the line atx, a place in the coordinates of the line: useline.x(part). Where the mark passes, the line lights for a moment and a tick stays. The tally goes up when the mark is on the line the first time.addCrossings(count: number, options?: AnimOptions): AnimThe tally runs up bycountwith no mark, for a number that is too large to draw, such asline.addCrossings(10000). The number on screen runs through whole values and ends exact. The line fills with ticks from the left while the number runs: one tick for each crossing, or for eachtickScalecrossings. When the line is full, a note under it says what one tick stands for.clear(options?: AnimOptions): AnimThe ticks and the note fade and the tally goes back to 0, for the next round of a comparison.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Boundarytrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Boundarydrop(child: Node): Boundaryall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): BoundaryA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): BoundarySet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): BoundarymoveTo(target: Node | Vec): BoundaryMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): BoundaryMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): BoundaryScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): BoundaryRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): BoundaryScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): BoundaryPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): BoundaryLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): BoundaryLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Boundarystroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Boundarycolor(color: ColorInput): BoundaryRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Boundaryglow(radius: number, color?: ColorInput | undefined): BoundarySoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): BoundaryzIndex(value: number): Boundarypin(): BoundaryPin to the screen so the camera does not move this node.hide(): Boundaryshow(): Boundarynamed(label: string): BoundarylabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
BoundaryCrossinginterface
A crossing that s.play() can run. It also gives its two parts, for a film that reads their places.
mark: NodeThe mark that travels.labelNode: Node | nullThe text of the crossing, or null when it has none.label: stringduration: numberease?: Ease | undefinedschedule(ctx: AnimContext): void
BoundaryCrossOptionsinterface
direction?: "up" | "down" | "round" | undefined'down' goes from over the line to under it, 'up' the reverse, and 'round' goes down and comes back. Defaults to down.reach?: number | undefinedHow far from the line the mark starts and ends. Defaults to 2.7 of the text size of the line, as a whole number: 70 at size 26.color?: ColorInput | undefinedThe color of the mark and of its tick. Without it, the mark is ink and the tick has the color of the line.label?: string | undefinedA short text beside the place where the mark starts. It fades with the mark.counts?: number | undefinedHow much the tally goes up. A whole number, 0 or more. Defaults to 1.duration?: number | undefinedease?: Ease | undefinedlag?: number | undefinedFraction of one item's time between the starts of consecutive items.
BoundaryOptionsinterface
width?: number | undefinedThe length of the line. Defaults to 1600.label?: string | undefinedThe name of the line, over its left end, such as 'system call line'.above?: string | undefinedThe name of the side over the line, at the left end, such as 'user space'.below?: string | undefinedThe name of the side under the line, at the left end, such as 'kernel'.color?: ColorInput | undefinedThe color of the line, its label and its ticks. Defaults to yellow.weight?: number | undefinedThe width of the stroke. Defaults to 3. A wall that is harder to cross can take 6.dash?: boolean | undefinedA dashed line, for a boundary that is a rule and not a wall.tally?: string | boolean | readonly [one: string, many: string] | undefinedShow a tally of the crossings.trueshows the number alone. A string is the word after the number. A pair is the word for one and the word for more, such as['system call', 'system calls'].tallySide?: "over" | "end" | undefinedWhere the tally is. 'end' puts it over the right end of the line, next to the line, andx()keeps crossings clear of it. 'over' puts it on a row of its own, over the place where a mark starts, so that crossings have the line up to its right end. Defaults to 'end'.tallyDigits?: number | undefinedThe digits that the tally has room for. A count with more digits is an error. Defaults to 6.tallySize?: number | undefinedText size of the tally. Defaults tosize.tallyColor?: ColorInput | undefinedThe color of the number of the tally. Its word stays dim. Defaults to ink.tickScale?: number | undefinedHow many crossings one tick stands for. Give the lines that a film compares one value, so that their ticks compare as their counts do. Without it, one tick is one crossing until the line is full.size?: number | undefinedText size. Defaults to 26.font?: FontFamily | undefined
braceLabelfunction
A brace along one side of a node or nodes, with a label at its point.
function braceLabel(target: Node | readonly Node[], label: string, options?: BraceLabelOptions): Node
BraceLabelOptionsinterface
side?: "left" | "right" | "top" | "bottom" | undefinedgap?: number | undefinedsize?: number | undefinedcolor?: ColorInput | undefinedplain?: boolean | undefinedSet for plain text labels. Labels are math by default.font?: FontFamily | undefinedThe font of a plain label. Defaults to the font of the film.depth?: number | undefined
calloutfunction
function callout(target: Node | Vec, body: string, options?: CalloutOptions): Callout
Calloutclass
A note with a leader line back to what it describes. The leader stops at the text block's edge, so it never runs through the words.
new Callout(target
leader: Nodemarker: Nodebody: NodetitleNode: Node | nullenter(options?: AnimOptions): AnimThe marker lands, the leader runs out, and the text settles at the end of it.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Callouttrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Calloutdrop(child: Node): Calloutall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): CalloutA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): CalloutSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): CalloutmoveTo(target: Node | Vec): CalloutMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): CalloutMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): CalloutScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): CalloutRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): CalloutScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): CalloutPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): CalloutLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): CalloutLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Calloutstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Calloutcolor(color: ColorInput): CalloutRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Calloutglow(radius: number, color?: ColorInput | undefined): CalloutSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): CalloutzIndex(value: number): Calloutpin(): CalloutPin to the screen so the camera does not move this node.hide(): Calloutshow(): Calloutnamed(label: string): CalloutlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
CalloutOptionsinterface
title?: string | undefinedside?: Edge | undefinedWhich way the text sits from its target.distance?: number | undefinedDistance from the target to the text, in stage pixels.offset?: number | undefinedSlide the text along the other axis.width?: number | undefinedWrap width for the body.size?: number | undefinedcolor?: ColorInput | undefinedbodyColor?: ColorInput | undefinedplate?: boolean | undefinedPlate behind the text, for placing a note over busy marks.font?: FontFamily | undefinedThe font of the title and the body. Defaults to the font of the film.
captionLinesfunction
Split spoken text into caption lines: by sentence, then by length.
function captionLines(text: string, length?: number): string[]
cardfunction
A box with a title, a picture, and a caption, such as card({ title: 'Logs', figure: pictogram('logs'), caption: 'one line per event' }).
function card(options?: CardOptions): Card
Cardclass
A box with a title at the top, a picture in the middle, and a caption at the bottom. The title and the caption sit at fixed distances from the border, and the picture is centered in the space between them. Cards with one width, one height, and one captionLines share one layout whatever they hold.
new Card(options?
frame: Nodetitle: Node | nullfigure: Node | nullcaption: Node | nullfigureBox: BoundsThe space that the picture may use, in the coordinates of the card, where (0, 0) is its middle.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Cardtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Carddrop(child: Node): Cardall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): CardA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): CardSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): CardmoveTo(target: Node | Vec): CardMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): CardMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): CardScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): CardRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): CardScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): CardPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): CardLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): CardLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Cardstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Cardcolor(color: ColorInput): CardRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Cardglow(radius: number, color?: ColorInput | undefined): CardSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): CardzIndex(value: number): Cardpin(): CardPin to the screen so the camera does not move this node.hide(): Cardshow(): Cardnamed(label: string): CardlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
CardOptionsinterface
width?: number | undefinedOuter width, in pixels. Defaults to 420.height?: number | undefinedOuter height, in pixels. Without it, the card is as tall as its content. Give one height to the cards of a row.title?: string | undefinedThe name at the top. A name that is too wide is set smaller, down to 0.6 oftitleSize.figure?: Node | undefinedThe picture in the middle. It must not be on the stage or in a group yet. A picture that is too large is scaled down to fit.caption?: string | undefinedThe text at the bottom. It wraps to the width of the card.captionLines?: number | undefinedThe number of caption lines to keep room for. Set it to the longest caption of a row, so that a card with a short caption has the same layout as the others. Defaults to the lines of this caption.color?: ColorInput | undefinedThe color of the border and the title. Defaults to ink.captionColor?: ColorInput | undefinedThe color of the caption. Defaults to ink.titleSize?: number | undefinedFont size of the title. Defaults to 40.captionSize?: number | undefinedFont size of the caption. Defaults to 0.72 oftitleSize.padding?: number | undefinedSpace between the border and the content. Defaults to 0.7 oftitleSize.fill?: ColorInput | null | undefinedThe fill of the card. Pass null for none. Defaults to the surface color.radius?: number | undefinedborderWidth?: number | undefinedWidth of the border. Defaults to 3.font?: FontFamily | undefined
ChartOptionsinterface
width?: number | undefinedheight?: number | undefinedlabelSize?: number | undefinedText size for direct labels and ticks.color?: ColorInput | undefinedfont?: FontFamily | undefinedThe font of the labels and the ticks. Defaults to the font of the film. A value that the chart prints is in mono.
checkFilmfunction
Inspect a film for layout defects: marks off the canvas, text against an edge of the frame, text on text, strokes through text, and characters that no font can draw. It looks where the film comes to rest, because objects may cross each other while moving. With motion, it also looks at every frame. Runs anywhere, with no rendering.
function checkFilm(film: FilmDefinition | CompiledFilm, options?: FilmCheckOptions): Promise<FilmCheckReport>
CODE_LANGUAGESconst
The languages that tokenize and codeBlock color. An unknown name falls back to TypeScript.
const CODE_LANGUAGES: readonly string[]
codeBlockfunction
function codeBlock(source: string, options?: CodeBlockOptions): CodeBlock
CodeBlockclass
Syntax-colored code as real glyphs. It can be written on, focused line by line, and morphed token by token into a new version with morphCode.
new CodeBlock(source
lines: Node[]tokens: CodeToken[]chars: Node[]panelNode: Node | nullbody: Nodesource: stringline(index: number): NodeOne line of code, as a node. Lines count from 0.find(fragment: string, line?: number | undefined): Node[]Glyphs of the code that matches. A fragment that is one token, such aslo, matches every such token. Any other fragment, such aslo + hi, matches as text inside a line, and spaces do not count. Pass a line, counted from 0, to search that line only.focus(lines: number | readonly number[], options?: AnimOptions & { dim?: number | undefined; color?: ColorInput | undefined; }): AnimDim everything except these lines, and lay a soft bar behind them.unfocus(options?: AnimOptions): AnimRestore all lines and remove the focus bar.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): CodeBlocktrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): CodeBlockdrop(child: Node): CodeBlockall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): CodeBlockA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): CodeBlockSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): CodeBlockmoveTo(target: Node | Vec): CodeBlockMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): CodeBlockMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): CodeBlockScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): CodeBlockRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): CodeBlockScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): CodeBlockPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): CodeBlockLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): CodeBlockLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): CodeBlockstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): CodeBlockcolor(color: ColorInput): CodeBlockRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): CodeBlockglow(radius: number, color?: ColorInput | undefined): CodeBlockSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): CodeBlockzIndex(value: number): CodeBlockpin(): CodeBlockPin to the screen so the camera does not move this node.hide(): CodeBlockshow(): CodeBlocknamed(label: string): CodeBlocklabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
CodeBlockOptionsinterface
language?: string | undefinedOne ofCODE_LANGUAGES, or an alias such aspy,tla+, orsmt2. Defaults to TypeScript.size?: number | undefinedleading?: number | undefinedLine height as a multiple of size.lineNumbers?: boolean | undefinedpanel?: boolean | undefinedDraw a quiet panel behind the code.padding?: number | undefinedminWidth?: number | undefinedMinimum panel width, so versions of a snippet can share one frame.colors?: Partial<Record<"string" | "number" | "function" | "keyword" | "comment" | "type" | "punctuation" | "plain", ColorInput>> | undefined
CodeTokeninterface
text: stringkind: TokenKindline: numberglyphs: Node[]
complexconst
Small complex arithmetic on [re, im] pairs.
const complex: { mul: (a: Vec, b: Vec) => Vec; div: (a: Vec, b: Vec) => Vec; exp: (a: Vec) => Vec; log: (a: Vec) => Vec; pow: (a: Vec, n: number) => Vec; sin: (a: Vec) => Vec; cos: (a: Vec) => Vec; inv: (a: Vec) => Vec; }complexPlanefunction
function complexPlane(options?: ComplexPlaneOptions): ComplexPlane
ComplexPlaneclass
new ComplexPlane(options?
numbers: Nodez(re: number, im: number): [number, number]Complex number to local stage coordinates.unit: numbermajor: Nodeminor: Nodeaxes: Nodep(x: number, y: number): [number, number]Math coordinates to stage coordinates. y points up on the plane.vector(x: number, y: number, style?: ShapeStyle): NodeA vector drawn from the origin, as an arrow.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): ComplexPlanetrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): ComplexPlanedrop(child: Node): ComplexPlaneall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): ComplexPlaneA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): ComplexPlaneSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): ComplexPlanemoveTo(target: Node | Vec): ComplexPlaneMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): ComplexPlaneMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): ComplexPlaneScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): ComplexPlaneRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): ComplexPlaneScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): ComplexPlanePlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): ComplexPlaneLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): ComplexPlaneLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): ComplexPlanestroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): ComplexPlanecolor(color: ColorInput): ComplexPlaneRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): ComplexPlaneglow(radius: number, color?: ColorInput | undefined): ComplexPlaneSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): ComplexPlanezIndex(value: number): ComplexPlanepin(): ComplexPlanePin to the screen so the camera does not move this node.hide(): ComplexPlaneshow(): ComplexPlanenamed(label: string): ComplexPlanelabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
ComplexPlaneOptionsinterface
labelRange?: number | undefinedLabel real and imaginary integers out to this distance from the origin.labelSize?: number | undefinedunit?: number | undefinedStage pixels per math unit.x?: readonly [number, number] | undefinedExtent in math units. Defaults to overscan past the frame edges, for warps that pull in.y?: readonly [number, number] | undefinedcolor?: ColorInput | undefinedminor?: number | undefinedMinor grid lines per unit.axisColor?: ColorInput | undefinedwidth?: number | undefined
connectorfunction
function connector(from: ConnectorEnd, to: ConnectorEnd, options?: ConnectorOptions): Connector
Connectorclass
A line between two nodes that is routed fresh on every frame. Move a node and the line follows. draw(connector) traces it, and .pulse() sends a signal.
new Connector(from
routeNow(): PathGeometry from end to end at this moment, in stage coordinates.pulse(options?: AnimOptions & { color?: ColorInput | undefined; length?: number | undefined; width?: number | undefined; glow?: number | undefined; }): AnimSend a signal along the line. Pulses can overlap, and they follow a moving route.pointNow(fraction?: number): [number, number]Point at a fraction along the current route, for placing notes beside the line.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Connectortrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Connectordrop(child: Node): Connectorall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): ConnectorA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): ConnectorSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): ConnectormoveTo(target: Node | Vec): ConnectorMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): ConnectorMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): ConnectorScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): ConnectorRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): ConnectorScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): ConnectorPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): ConnectorLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): ConnectorLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Connectorstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Connectorcolor(color: ColorInput): ConnectorRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Connectorglow(radius: number, color?: ColorInput | undefined): ConnectorSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): ConnectorzIndex(value: number): Connectorpin(): ConnectorPin to the screen so the camera does not move this node.hide(): Connectorshow(): Connectornamed(label: string): ConnectorlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
ConnectorEndtype
type ConnectorEnd = Node | Vec
ConnectorOptionsinterface
from?: Edge | undefinedSide to leave from and arrive at. Chosen automatically when omitted.to?: Edge | undefinedroute?: "curve" | "ortho" | "straight" | undefinedcolor?: ColorInput | undefinedwidth?: number | undefineddash?: readonly number[] | undefinedtip?: boolean | undefinedDraw an arrowhead at the end.tipSize?: number | undefinedlabel?: string | undefinedLabel on the line, on a plate so the line never crosses its text.font?: FontFamily | undefinedThe font of the label. Defaults to the font that the film names, and to mono in a film that names none.labelSize?: number | undefinedlabelColor?: ColorInput | undefinedlabelBackground?: ColorInput | undefinedlabelAt?: number | undefinedWhere the label sits along the line.gap?: number | undefinedSpace between the line and the box it touches.fromOffset?: number | undefinedSlide each end along its side, from -1 to 1. Use it to separate parallel lines.toOffset?: number | undefinedcurvature?: number | undefinedradius?: number | undefinedflow?: number | undefinedDashes travel along the line at this many pixels per second.
counterfunction
function counter(initial?: number, options?: CounterOptions): Counter
Counterclass
A number readout. Animate it with countUp.
new Counter(initial?
value: Valueid: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Countertrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Counterdrop(child: Node): Counterall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): CounterA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): CounterSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): CountermoveTo(target: Node | Vec): CounterMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): CounterMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): CounterScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): CounterRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): CounterScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): CounterPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): CounterLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): CounterLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Counterstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Countercolor(color: ColorInput): CounterRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Counterglow(radius: number, color?: ColorInput | undefined): CounterSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): CounterzIndex(value: number): Counterpin(): CounterPin to the screen so the camera does not move this node.hide(): Countershow(): Counternamed(label: string): CounterlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
CounterOptionsinterface
digits?: number | undefinedsize?: number | undefinedcolor?: ColorInput | undefinedfont?: FontFamily | undefinedDefaults to mono, in every film: a number that changes keeps its width in mono.weight?: 400 | 600 | 700 | undefinedprefix?: string | undefinedsuffix?: string | undefinedgrouped?: boolean | undefinedThousands separators.
countUpfunction
Count a Counter or Value to a target.
function countUp(target: Value | Counter, to: number, options?: AnimOptions): Anim
crossThroughfunction
One mark through several lines, from the first to the last: crossThrough([calls, wall], calls.x(0.5), { direction: 'round' }). Each line lights, keeps a tick, and counts.
function crossThrough(lines: readonly Boundary[], x: number, options?: BoundaryCrossOptions): BoundaryCrossing
dashedGuidesfunction
Dashed drop lines from a point on the axes. Pass functions to follow a moving point.
function dashedGuides(axes: Axes, x: Reactive, y: Reactive, style?: ShapeStyle): Node
DateEventinterface
at: DateInputWhen the event was. With scale'order', it can also be a version, such as'5.1'.label?: string | number | undefinedThe name of the event, on one line. A number is written as its text. An event with no label is a mark only, with no stem.note?: string | number | undefinedA second line under the label, in dim. A number is written as its text.color?: ColorInput | undefinedThe color of the mark, the stem and the label. Defaults to ink.side?: "over" | "under" | undefinedWhere the label goes. Without it, the labels go over the line and under it by turns, in date order.mark?: DateMark | undefinedThe mark on the line: a dot, a star for an event of another kind, or a ring for an event with no sure date. Defaults to a dot.
DateEventNodeclass
One event of a DateLine object. It is hidden until the cursor reaches it.
new DateEventNode(mark
covers: Node[]The ticks of the line that are in the clear room of the mark. A tick leaves while the event shows, by any way that the film shows it.mark: NodeThe mark of the event: a dot, a star, or a ring.stem: Node | nullThe line to the label, or null for an event with no label.text: TextNode | nullThe label of the event, or null when the event has none.note: TextNode | nullThe line under the label, or null when the event has none.halo: NodeThe ring around the mark of the event thatlight()lit. It has no opacity when the event is not lit.set<K extends PropName>(prop: K, value: NodeProps[K]): DateEventNodetrack<K extends PropName>(prop: K): Track<NodeProps[K]>id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): DateEventNodedrop(child: Node): DateEventNodeall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): DateEventNodeA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): DateEventNodeSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): DateEventNodemoveTo(target: Node | Vec): DateEventNodeMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): DateEventNodeMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): DateEventNodeScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): DateEventNodeRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): DateEventNodeScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): DateEventNodePlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): DateEventNodeLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): DateEventNodeLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): DateEventNodestroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): DateEventNodecolor(color: ColorInput): DateEventNodeRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): DateEventNodeglow(radius: number, color?: ColorInput | undefined): DateEventNodeSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): DateEventNodezIndex(value: number): DateEventNodepin(): DateEventNodePin to the screen so the camera does not move this node.hide(): DateEventNodeshow(): DateEventNodenamed(label: string): DateEventNodelabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
DateInputtype
A date: 'YYYY', 'YYYY-MM' or 'YYYY-MM-DD', or a number of years such as 2020 or 2023.25.
type DateInput = string | number
dateLinefunction
A line of time with events at their dates: dateLine([{ at: '2025-06-10', label: 'the newsletter' }, { at: '2026-03-24', label: 'the report', note: 'names the system' }], { width: 1600, from: 2020, to: 2027 }).
function dateLine(events: readonly DateEvent[], options?: DateLineOptions): DateLine
DateLineclass
A line of time with events at their dates: a mark on the line for each event, and a stem to its label. The object places the labels over the line and under it, at more than one height, so that no two of them meet. An event with no label is a mark only. A mark that would touch another one goes to a row over the line. advance(date) moves a cursor to a date and stops there, and each event appears when the cursor passes it. .at() places the middle of the line. The bounds of the object hold every label from the start: read extent.
new DateLine(events
events: DateEventNode[]One node per event, in the order given.axis: NodeThe line, its ticks and the texts under them, and the words "not to scale" of an'order'line.cursor: NodeA small pointer under the line, at the date that the film has reached. It moves in a room of its own, under the marks and over the texts under the line, so it touches nothing. The firstadvanceshows it.domain: readonly [number, number]The two ends of the line as numbers of years. With scale'order', the ends are counted in events: -0.5 and the number of dates less 0.5.unit: DateUnit | nullWhat a tick of the line is. Null with scale'order', where a tick is an event.extent: { readonly left: number; readonly right: number; readonly top: number; readonly bottom: number; }The room of the line with all its events, in the coordinates of the object: how far the labels and the rows of marks go over the line and under it, and how far a text passes each end. The bounds of the object are this room from the start, also while the events are hidden.date: numberThe place of the cursor after the changes that are scheduled so far, as a number of years. It starts at the first end of the line. With scale'order', it is counted in events: 0 is the first date.lit: number | nullThe index of the event that has the accent color, after the changes that are scheduled so far. Null when no event is lit.x(date: DateInput): numberThe x of a date, in the coordinates of the object. The middle of the line is at 0. With scale'order', the line has a place for the dates of its events only.p(index: number): [number, number]The place of the mark of an event, in the coordinates of the object, by its index in the list.enter(options?: AnimOptions): AnimThe line draws from its first end, and its ticks and their texts arrive as it passes them. No event shows yet.advance(to: DateInput, options?: AnimOptions): AnimThe cursor moves from its date toto, and stops. Each event that it passes appears: its mark, then its stem, then its label. All of it is over when the animation ends: when the last event needs time after the cursor reaches it, the cursor stops early.showAll(options?: AnimOptions): AnimThe same asadvanceto the last end of the line. It takes 3 seconds when the film gives no duration.light(index: number | null, options?: AnimOptions): AnimOne event takes the look of the lit event: the accent color on its mark, its stem and its label, and a ring around its mark. The event that had this look takes its own color back.light(null)leaves no event lit. An event that the cursor has not reached stays hidden, and it is lit when it appears.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): DateLinetrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): DateLinedrop(child: Node): DateLineall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): DateLineA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): DateLineSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): DateLinemoveTo(target: Node | Vec): DateLineMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): DateLineMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): DateLineScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): DateLineRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): DateLineScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): DateLinePlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): DateLineLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): DateLineLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): DateLinestroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): DateLinecolor(color: ColorInput): DateLineRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): DateLineglow(radius: number, color?: ColorInput | undefined): DateLineSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): DateLinezIndex(value: number): DateLinepin(): DateLinePin to the screen so the camera does not move this node.hide(): DateLineshow(): DateLinenamed(label: string): DateLinelabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
DateLineOptionsinterface
width?: number | undefinedThe length of the line. Every label is inside it. A text under a tick at an end passes it by half of its width. Defaults to 1600.from?: DateInput | undefinedThe dates at the two ends of the line, for scale'time', such asfrom: '2025-06'. They default to the whole years, months or days around the events, by the unit of the ticks.to?: DateInput | undefinedscale?: "time" | "order" | undefined'time'puts each event at its date.'order'puts the events at equal steps in date order, for events that are crowded in time: each one has its date under the line as the film wrote it, and the line says "not to scale" at its end. Defaults to time.unit?: DateUnit | undefinedWhat a tick is, for scale'time': the start of a year, of a month, or of a day. Without it, the span of the line decides: days up to 45 days, months under 2 years, and years from 2 years.size?: number | undefinedText size of the labels. The notes and the texts under the line are a little smaller. Defaults to 26.levels?: number | undefinedHow many heights the labels may take at each side of the line. The object uses as few as the labels need. Defaults to 3.rows?: number | undefinedThe most rows of marks: the line, and the rows over it. A mark that would touch an earlier mark goes one row up, and the object has as many rows as the marks need, up to this number. Defaults to 10.cursor?: boolean | undefinedDraw the cursor. With false, no cursor is ever drawn, and the events still appear at their dates. On by default.accent?: ColorInput | undefinedThe color of the event thatlight()lights. Defaults to orange.font?: FontFamily | undefined
DateMarktype
The mark of an event on the line.
type DateMark = "dot" | "ring" | "star"
DateUnittype
What a tick of the line is: the start of a year, of a month, or of a day.
type DateUnit = "year" | "month" | "day"
decimalfunction
A number readout that follows a function of the timeline, drawn fresh each frame.
function decimal(read: () => number, options?: DecimalOptions): Node
DecimalOptionsinterface
digits?: number | undefinedsize?: number | undefinedcolor?: ColorInput | undefinedfont?: FontFamily | undefinedDefaults to mono, in every film: a number that changes keeps its width in mono.prefix?: string | undefinedsuffix?: string | undefined
donutfunction
function donut(data: readonly PieDatum[], options?: PieOptions): PieChart
dotChartfunction
Dots on a shared axis, one row per item, with a line for each interval: dotChart([{ label: 'blocks', value: 0.89, low: 0.87, high: 0.91 }]).
function dotChart(data: readonly DotDatum[], options?: DotChartOptions): DotChart
DotChartclass
One row per item: its label, a dot at its value on a shared axis, and a line for its interval. It shows many scores with their uncertainty in less room than bars, and its axis does not have to start at zero. The rows keep the order of the data, so sort the data to rank it. enter() brings the rows in one by one. scale: 'log' gives each power of ten the same room, for values that differ by many powers of ten.
new DotChart(data
rows: Node[]One node per item, which holds its label, its interval and its dot.labels: Node[]dots: Node[]intervals: (Node | null)[]The interval of each item, or null for an item that has none.axis: NodeThe axis line, the grid, the ticks and their numbers, and the axis label.domain: readonly [number, number]The two ends of the axis, as values.labelsOf(part: Node): readonly Node[]The label, the dot and the interval of one row belong together, so a light on one of them keeps the row bright.p(value: number, row: number): [number, number]The place of a value in a row, in the coordinates of the chart. Row 0 is the first item. On a log axis, the value must be more than 0.enter(options?: AnimOptions): AnimThe axis fades in, then the rows arrive one after another, from the first to the last.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): DotCharttrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): DotChartdrop(child: Node): DotChartall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): DotChartA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): DotChartSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): DotChartmoveTo(target: Node | Vec): DotChartMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): DotChartMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): DotChartScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): DotChartRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): DotChartScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): DotChartPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): DotChartLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): DotChartLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): DotChartstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): DotChartcolor(color: ColorInput): DotChartRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): DotChartglow(radius: number, color?: ColorInput | undefined): DotChartSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): DotChartzIndex(value: number): DotChartpin(): DotChartPin to the screen so the camera does not move this node.hide(): DotChartshow(): DotChartnamed(label: string): DotChart
DotChartOptionsinterface
width?: number | undefinedThe length of the axis. The labels are outside it, at the left. Defaults to 900.height?: number | undefinedThe height of all rows together. Defaults to 1.65 oflabelSizefor each row.labelSize?: number | undefinedText size of the labels and the ticks. Defaults to 20.color?: ColorInput | undefinedThe color of the axis. Defaults to faint.dotColor?: ColorInput | undefinedThe color of a dot that has none of its own. Defaults to blue.domain?: readonly [number, number] | undefinedThe values at the two ends of the axis. Defaults to a round range that holds every value and interval. On a log axis, the default is the powers of ten around them.ticks?: number | readonly number[] | undefinedAbout how many ticks to draw, or the values to mark. Defaults to about 5. On a log axis, the default is the powers of ten in the domain, and every second or third one when there are more than 8.format?: ((value: number) => string) | undefinedHow to write a tick. On a log axis, the default is a short form: 1, 10, 100, 1k, 1M, and 0.01.scale?: "linear" | "log" | undefined'log'gives each power of ten the same room, for values that differ by more than about a hundred times. Every value, interval end, domain end and tick must then be more than 0. Say on screen that the axis is logarithmic, for example inxLabel. Defaults to linear.xLabel?: string | undefinedA line under the axis that says what the value is and which way is better.grid?: boolean | undefinedDraw a faint vertical line at each tick. On by default.font?: FontFamily | undefined
DotDatuminterface
label: stringvalue: numberlow?: number | undefinedThe two ends of an interval around the value, such as a 95% confidence interval. Give both or none.high?: number | undefinedcolor?: ColorInput | undefinedDefaults to thedotColorof the chart.hollow?: boolean | undefinedDraw a ring in place of a filled dot, to tell a second group apart.
epicyclesfunction
function epicycles(terms: readonly FourierTerm[], options: EpicyclesOptions): Epicycles
Epicyclesclass
Circles on circles: the tip of the chain draws the original shape.
new Epicycles(terms
terms: readonly FourierTerm[]outline(): PathThe reconstructed outline as a path, for morphing the drawing into something else.point(u: number): [number, number]id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Epicyclestrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Epicyclesdrop(child: Node): Epicyclesall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): EpicyclesA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): EpicyclesSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): EpicyclesmoveTo(target: Node | Vec): EpicyclesMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): EpicyclesMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): EpicyclesScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): EpicyclesRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): EpicyclesScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): EpicyclesPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): EpicyclesLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): EpicyclesLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Epicyclesstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Epicyclescolor(color: ColorInput): EpicyclesRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Epicyclesglow(radius: number, color?: ColorInput | undefined): EpicyclesSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): EpicycleszIndex(value: number): Epicyclespin(): EpicyclesPin to the screen so the camera does not move this node.hide(): Epicyclesshow(): Epicyclesnamed(label: string): EpicycleslabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
EpicyclesOptionsinterface
u: () => numberProgress as a function of the timeline. 1 means one full trip.circles?: number | undefinedMost circles to draw. The sum still uses every term.circleColor?: ColorInput | undefinedarmColor?: ColorInput | undefinedtraceColor?: ColorInput | undefinedtraceWidth?: number | undefinedglow?: number | undefinedtail?: number | undefinedShare of a trip to keep lit. Use 1 for the whole drawing.samples?: number | undefined
eventLogfunction
A list of records that grows, with states: eventLog([{ id: 1, name: 'WorkflowExecutionStarted' }, { ids: [2, 3, 4], name: 'workflow task' }], { title: 'event history', visible: 12 }), then log.append({ id: 5, name: 'ActivityTaskScheduled', detail: 'chargeCard', state: 'new' }).
function eventLog(rows?: readonly LogRow[], options?: EventLogOptions): EventLog
EventLogclass
A list of records with an identity, such as the event history of a workflow, the steps of an agent, or the life of a sandbox. A row has an id, a name, one short detail, and a state that sets its color and its mark. append() adds rows at the bottom, mark() changes the state of rows, fold() puts rows that follow each other into one line, focus() moves one outline to a row, and scrollTo() moves the window to a row. The window shows visible lines. When the list has more, the oldest rows leave at the top, and the first line says how many are above. The list changes its rows one change at a time: two changes of the rows whose times overlap are an error. .at() places the middle of the panel. The panel has its full height from the start.
new EventLog(rows?
frame: NodeThe panel: its fill and its border.rules: Node[]The line under the title and the line over the footer.title: Node | nullfooter: Node | nullThe line under the list that follows the count, or null.earlier: NodeThe first line of a window with rows above it: the number of those records.later: NodeThe last line of a window with rows under it: the number of those records.outline: NodeWhatfocus()moves: the outline around a row, or the band behind it.pitch: numberThe distance from one row to the next. A code block beside the list can take it:leading: log.pitch / size.rows: LogRowNode[]The nodes of the lines in the window, from the top, after the changes that are scheduled so far. A fold is one node.count: numberThe number of records that show, after the changes that are scheduled so far. A fold counts each record that it holds. A row that waits forshow()does not count.ids: Id[]The ids of the records, in order, after the changes that are scheduled so far.row(id: Id): LogRowNodeThe node of the line that shows a record, after the changes that are scheduled so far. For a record in a fold, that is the line of the fold. A row that is out of the window is an error:scrollTo()brings it in.p(id: Id, side?: "left" | "right"): [number, number]The point where a connector to a row ends: on the left or the right border of the panel, at the middle height of the row, in the coordinates of the list. It is the place after the changes that are scheduled so far, and it does not follow the row. For a fold, any of its ids gives the point. Defaults to the left.find(test: (row: LogRow) => boolean): Id[]The ids of the rows that a function picks, in the order of the list, after the changes that are scheduled so far: one id for a row, and each id of a fold. Somark(find(test), state)changes the same records asmark(test, state). The function gets a row as the list shows it: with itsid, itsids, itsstateand itsfamily. A fold is one row there. A row that waits forshow()hasshown: false.clone(): EventLogA copy with its own rows and its own bookkeeping, so a change of the copy does not change the list.enter(options?: AnimOptions): AnimThe panel, the title and the footer arrive, and then the rows of the window.append(rows: LogRow | readonly LogRow[], options?: AnimOptions): AnimRows arrive at the bottom, one after another. In a full window, the oldest rows leave at the top as the new ones arrive, and all rows move as one. The first line then says how many records are above.lag: 0brings the rows of one call together, as a group. A window thatscrollTo()moved goes back to the end first. An id that the list has is an error.show(): EventLog show(which: Id | readonly Id[] | ((row: LogRow) => boolean), options?: AnimOptions | undefined): AnimRows that had their place and did not show arrive there, one after another, as the rows ofappendarrive.whichis an id, a list of ids, or a function of a row, asmarktakes. No row moves, and the window does not move: a row that is out of it changes the count of its side. The footer and the two counts count the rows that show. A row that shows already does not change. With no argument, the list itself shows, as any node does.mark(which: Id | readonly Id[] | ((row: LogRow) => boolean), state: string, options?: AnimOptions): AnimRows change state: the color of the name cross-fades, and so does the mark at the left.whichis an id, a list of ids, or a function of a row, which the list calls when the change is scheduled. So a second change in one sequence sees the states that the first one left. An id names one record. In a closed fold, the record takes the state, and the fold shows the look of the record that was marked last. Afterunfold(), each record has its own state. The function gets a row asfind()gives it. A fold is one row there, and a fold that it picks takes the state with all its records. A row that already has the state does not change.lagmakes the rows change one after another, in the order of the list.fold(ids: readonly Id[], label?: string | LogRowFields | undefined, options?: AnimOptions): AnimRows that follow each other become one line: a fold, with its first id, its last id, and a name, such as "2 to 4 workflow task". The label is the name, or the fields of the row of the fold: a name, a detail, a time, a state, a family. Without a state, the fold has the state of its first row. With no label, a fold thatunfold()opened closes again as it was. The fold counts as one line of the window, and as its records incount. The ids can be in any order. Rows that do not follow each other, and a row that is in a fold, are errors.unfold(firstId: Id, options?: AnimOptions): AnimA fold opens into its rows.firstIdis the first id of the fold.fold()with the same ids closes it again.scrollTo(id: Id, options?: AnimOptions): AnimThe window moves to a row that is out of it, and the row shows: at the top of the window for a row that was above, and at its end for a row that was under. The first line says how many records are above the window, and the last line says how many are under it. A row that is in the window moves nothing, so such a call can play with any change.append()brings the window back to the end.focus(id: Id | null, options?: AnimOptions): AnimThe outline goes to a row, and stays there until the nextfocus.focus(null)takes it away. The outline is with its row at every frame: it moves with a row that scrolls, arrives with a row that arrives, and leaves with a row that leaves the window. On its way to another row it fades out at the first and comes in at the second, so it does not cross the text of the rows between the two. Each call takes the place of the outline and how much of it shows back from its time on, whatever the film did to the nodeoutlinebefore. Aleadingthat is too small for the outline is an error here.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): EventLogtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): EventLogdrop(child: Node): EventLogall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): EventLogSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): EventLogmoveTo(target: Node | Vec): EventLogMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): EventLogMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): EventLogScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): EventLogRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): EventLogScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): EventLogPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): EventLogLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): EventLogLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): EventLogstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): EventLogcolor(color: ColorInput): EventLogRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): EventLogglow(radius: number, color?: ColorInput | undefined): EventLogSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): EventLogzIndex(value: number): EventLogpin(): EventLogPin to the screen so the camera does not move this node.hide(): EventLognamed(label: string): EventLoglabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
EventLogOptionsinterface
width?: number | undefinedThe width of the list. Defaults to 640.visible?: number | undefinedHow many lines show at once. More rows scroll: the oldest leave at the top, and the first line then says how many are above. Defaults to 10.size?: number | undefinedText size. Defaults to 24.leading?: number | undefinedThe height of a row, as a part ofsize. Defaults to the height that the outline of the focus needs, about 1.9. A list withfocusLook: 'band', or with no focus, can take less, such as 1.5.states?: Record<string, LogState> | undefinedThe states and their looks. Defaults to LOG_STATES.title?: string | undefinedA title over the list, such as 'event history'.footer?: ((count: number) => string) | undefinedA line under the list that follows the count of the rows that show, such as (count) =>${count} events.titleSize?: number | undefinedThe text size of the title, the footer and the counts of the rows out of the window. Defaults to 0.85 ofsize.nameWidth?: number | undefinedThe room of the names. A longer name is cut with an ellipsis. Defaults to the longest name of the first rows, and to 60% of the width at most. The details have the room that is left. The name of a row with no detail may go on to the end of the details.idWidth?: number | undefinedThe room of the ids. A longer id is an error. Defaults to the longest id of the first rows, and to two digits at least.timeWidth?: number | undefinedThe room of the times. A longer time is an error. Defaults to the longest time of the first rows. Set it for a list that gets its first time later.ellipsis?: boolean | undefinedWith false, a name or a detail that does not fit is an error, and not a text with an ellipsis. Defaults to true.focusLook?: "outline" | "band" | undefinedWhatfocus()draws: an outline around the row, or a band behind it. Defaults to outline.font?: FontFamily | undefinedThe font of the title, the footer and the details. Defaults to the font of the film. The ids, the names and the times are in mono.detailFont?: FontFamily | undefinedThe font of the details, for a detail that is data, such as the output of a run. Defaults tofont.
FieldFntype
type FieldFn = (x: number, y: number, t: number) => Vec
FilmCheckOptionsinterface
times?: readonly number[] | undefinedInspect exactly these times, in seconds, in place of the automatic sampling.samples?: number | undefinedAdd this many evenly spaced samples to the moments at rest.motion?: boolean | undefinedAlso inspect every frame. A problem that appears only while something moves is a warning, because the check cannot tell a collision from one picture that replaces another. A stretch of 0.2 seconds or more with nothing visible is reported asfilm.blank. Text that leaves before a viewer can read it is reported asfilm.text_brief.readingSpeed?: number | undefinedFor a motion scan, the characters that a viewer reads in one second. Text that leaves before a reader at this speed gets to its end is reported asfilm.text_brief. Defaults to 20, the fastest speed that subtitle guides allow for an adult viewer. 0 turns the rule off.stepped?: boolean | undefinedThe film plays one chapter at a time, as a deck does: the player waits for the viewer on the first frame of each chapter after the start. Text that is up at such a stop has all the time that the viewer gives it, sofilm.text_briefdoes not report it.contentTop?: number | undefinedFor a motion scan, the canvas y where the content starts. Marks above it, such as a heading that stays up, do not count as content when the scan looks for blank frames.textMargin?: number | undefinedThe least distance between text and an edge of the frame, in canvas pixels. Text that is nearer is reported asfilm.text_edge. Defaults to 2% of the shorter side of the frame. 0 turns the rule off.imageNetwork?: "allow" | "deny" | undefinedlook?: Look | "midnight" | "paper" | "blueprint" | "chalk" | "neon" | undefinedfps?: number | undefinedbloom?: number | undefinedbackground?: string | null | undefined
FilmCheckReportinterface
ok: booleanwidth: numberheight: numberduration: numbersampled: number[]The moments at rest that were inspected, in seconds.frames?: number | undefinedThe number of frames that a motion scan inspected.diagnostics: FilmDiagnostic[]summary: { errors: number; warnings: number; }
FilmDiagnosticinterface
code: FilmDiagnosticCodeseverity: "error" | "warning"message: stringtime: numberFirst sampled time with this problem, in seconds.until: numberLast sampled time with this problem, in seconds.box: BoundsWhere to look, in canvas pixels with the origin at the top left.stage: { x: number; y: number; }The center of the box in authoring coordinates, with (0, 0) at mid-frame.text?: string | undefinedThe text involved, when there is any.other?: string | undefinedThe second text involved in an overlap.motion?: { spans: number; seconds: number; } | undefinedSet when a motion scan finds the problem only while something moves.timeanduntilthen bound the longest stretch.spanscounts the separate stretches, andsecondsis their total length. Forfilm.text_brief,timeanduntilbound the first stretch, or a whole row of texts that follow each other.spanscounts the stretches or the texts, andsecondsis the time that they were readable.
FilmDiagnosticCodetype
type FilmDiagnosticCode = "film.off_canvas" | "film.text_edge" | "film.text_overlap" | "film.text_crossed" | "film.text_brief" | "film.missing_glyph" | "film.blank" | "film.image_asset"
fourierfunction
Discrete Fourier transform of a closed outline, largest terms first. Sample the points evenly along the outline, for example with samplePath.
function fourier(points: readonly Vec[], options?: { terms?: number | undefined; }): FourierTerm[]fourierPointfunction
Value of a Fourier series at u, where u from 0 to 1 is one trip around the shape.
function fourierPoint(terms: readonly FourierTerm[], u: number): [number, number]
FourierTerminterface
freq: numberWhole turns per trip around the shape.amp: numberphase: number
fromComponentfunction
Render any Vizmatic TSX component and return it as animatable vector nodes, so every existing diagram and chart can be written on, faded, and morphed. It runs in Node only, because it needs the layout engine: const flow = await fromComponent(<Flow c={c} stages={stages} />, { width: 900, height: 300 }) s.play(write(flow))
function fromComponent(element: ReactNode, options: FromComponentOptions): Promise<SvgNode>
FromComponentOptionsinterface
width: numberLayout size of the component, in pixels.height: numbertheme?: ThemeMode | undefinedcanvas?: RenderBackground | undefinedCanvas behind the component. Transparent by default, so it sits on the film.scale?: number | undefinedScale on stage. Layout stays atwidthandheight, so dense text stays small.center?: boolean | undefinedCenter the picture on the node's origin. On by default.background?: boolean | undefinedKeep a full-canvas background rectangle. Films have their own, so it is dropped by default.splitGlyphs?: boolean | undefinedSplit filled multi-glyph outlines into one node per glyph, so text writes letter by letter.font?: FontFamily | undefinedThe font of the text elements. Defaults to the font that the film names. Text that the SVG sets in a monospace family is code, and it is then mono. With no font from this option or from the film, every text is sans, as it was before films had a font: the importer does not read which family the SVG names.
fromSvgfunction
Turn SVG markup into stage nodes. Each shape becomes a leaf whose origin is its center, so it can be drawn, faded, scaled, and morphed like anything else. Filters and masks are skipped, because shapes draw without them.
function fromSvg(source: string, options?: FromSvgOptions): SvgNode
FromSvgOptionsinterface
width?: number | undefinedFit into this box, keeping proportions. Defaults to the SVG's own size.height?: number | undefinedcenter?: boolean | undefinedCenter the picture on the node's origin. On by default.background?: boolean | undefinedKeep a full-canvas background rectangle. Films have their own, so it is dropped by default.splitGlyphs?: boolean | undefinedSplit filled multi-glyph outlines into one node per glyph, so text writes letter by letter.font?: FontFamily | undefinedThe font of the text elements. Defaults to the font that the film names. Text that the SVG sets in a monospace family is code, and it is then mono. With no font from this option or from the film, every text is sans, as it was before films had a font: the importer does not read which family the SVG names.
graphfunction
function graph(nodes: readonly GraphNodeSpec[], edges: readonly GraphEdgeSpec[], options?: GraphOptions): Graph
Graphclass
Nodes and edges with a stable layout. node(id) and edge(from, to) return ordinary nodes, so every verb works on them, and visit and pulseEdge tell an algorithm's story.
new Graph(nodes
nodes: Map<string, GraphNode>edges: GraphEdge[]edgeLayer: NodenodeLayer: Nodenode(id: string): GraphNodeedge(from: string, to: string): GraphEdgeenter(options?: AnimOptions): AnimNodes pop in by order, and each edge draws as its source is shown.pulseEdge(from: string, to: string, options?: AnimOptions & { color?: ColorInput | undefined; length?: number | undefined; }): AnimA lit signal travels along an edge.visit(id: string, options?: AnimOptions & { color?: ColorInput | undefined; }): AnimMark a node as reached: its color changes and it swells for a moment.moveNode(id: string, to: Vec, options?: AnimOptions): AnimMove a node. Its edges follow, re-routed as straight lines.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Graphtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Graphdrop(child: Node): Graphall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): GraphA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): GraphSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): GraphmoveTo(target: Node | Vec): GraphMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): GraphMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): GraphScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): GraphRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): GraphScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): GraphPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): GraphLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): GraphLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Graphstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Graphcolor(color: ColorInput): GraphRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Graphglow(radius: number, color?: ColorInput | undefined): GraphSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): GraphzIndex(value: number): Graphpin(): GraphPin to the screen so the camera does not move this node.hide(): Graphshow(): Graphnamed(label: string): GraphlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
GraphEdgeclass
new GraphEdge(path?
shaft: Nodetip: Node | nullcaption: Node | nullfrom: stringto: stringtipSize: numberid: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): GraphEdgetrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): GraphEdgedrop(child: Node): GraphEdgeall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): GraphEdgeA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): GraphEdgeSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): GraphEdgemoveTo(target: Node | Vec): GraphEdgeMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): GraphEdgeMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): GraphEdgeScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): GraphEdgeRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): GraphEdgeScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): GraphEdgePlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): GraphEdgeLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): GraphEdgeLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): GraphEdgestroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): GraphEdgecolor(color: ColorInput): GraphEdgeRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): GraphEdgeglow(radius: number, color?: ColorInput | undefined): GraphEdgeSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): GraphEdgezIndex(value: number): GraphEdgepin(): GraphEdgePin to the screen so the camera does not move this node.hide(): GraphEdgeshow(): GraphEdgenamed(label: string): GraphEdgelabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
GraphEdgeSpecinterface
from: stringto: stringlabel?: string | undefinedcolor?: ColorInput | undefinedwidth?: number | undefineddash?: readonly number[] | undefineddirected?: boolean | undefined
GraphNodeclass
new GraphNode(path?
shape: Nodecaption: Node | nullhalfWidth: numberhalfHeight: numberround: booleanedgePoint(fx: number, fy: number, tx: number, ty: number, pad?: number): VecPoint on this node's outline facing a stage point, in graph space.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): GraphNodetrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): GraphNodedrop(child: Node): GraphNodeall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): GraphNodeA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): GraphNodeSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): GraphNodemoveTo(target: Node | Vec): GraphNodeMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): GraphNodeMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): GraphNodeScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): GraphNodeRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): GraphNodeScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): GraphNodePlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): GraphNodeLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): GraphNodeLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): GraphNodestroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): GraphNodecolor(color: ColorInput): GraphNodeRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): GraphNodeglow(radius: number, color?: ColorInput | undefined): GraphNodeSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): GraphNodezIndex(value: number): GraphNodepin(): GraphNodePin to the screen so the camera does not move this node.hide(): GraphNodeshow(): GraphNodenamed(label: string): GraphNodelabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
GraphNodeSpecinterface
id: stringlabel?: string | undefinedcolor?: ColorInput | undefinedradius?: number | undefinedCircle radius.width?: number | undefinedBox size. A node with a width draws as a rounded box.height?: number | undefinedat?: Vec | undefinedManual position, used by layout: 'manual'.math?: boolean | undefinedTypeset the label as math.
GraphOptionsinterface
layout?: "dagre" | "force" | "circle" | "manual" | undefineddirection?: "LR" | "RL" | "TB" | "BT" | undefinedwidth?: number | undefinedBounding size for force and circle layouts.height?: number | undefinednodeGap?: number | undefinedrankGap?: number | undefinedradius?: number | undefinedlabelSize?: number | undefinededgeLabelSize?: number | undefineddirected?: boolean | undefinednodeColor?: ColorInput | undefinededgeColor?: ColorInput | undefinedseed?: number | undefinedfillOpacity?: number | undefinedFill nodes softly with their color instead of leaving them hollow.font?: FontFamily | undefinedThe font of the node labels. Defaults to the font of the film.edgeFont?: FontFamily | undefinedThe font of the edge labels. Defaults to the font that the film names, and to mono in a film that names none.
GridLegendOptionsinterface
size?: number | undefinedText size of the names. The sample cells are a little smaller. Defaults to 26.gap?: number | undefinedRoom between a name and the next sample cell. Defaults to 1.3 ofsize.font?: FontFamily | undefined
GridStateinterface
color: ColorInputfill?: number | undefinedOpacity of the fill, 0 to 1. 0 draws the outline only. Defaults to 0.7.dash?: boolean | undefinedA dashed outline, for a thing that is not there.
heatmapfunction
function heatmap(values: readonly (readonly number[])[], options?: HeatmapOptions): Heatmap
Heatmapclass
new Heatmap(values
cells: Node[][]rowLabels: Node[]columnLabels: Node[]numbers: Node[][]rowCount: numbercolCount: numbercolorFor(value: number): Rgbacell(row: number, column: number): Noderow(index: number): Node[]col(index: number): Node[]enter(options?: AnimOptions): AnimCells fade in along the diagonal.update(values: readonly (readonly number[])[], options?: AnimOptions): AnimRecolor every cell to new values.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Heatmaptrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Heatmapdrop(child: Node): Heatmapall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): HeatmapA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): HeatmapSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): HeatmapmoveTo(target: Node | Vec): HeatmapMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): HeatmapMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): HeatmapScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): HeatmapRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): HeatmapScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): HeatmapPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): HeatmapLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): HeatmapLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Heatmapstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Heatmapcolor(color: ColorInput): HeatmapRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Heatmapglow(radius: number, color?: ColorInput | undefined): HeatmapSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): HeatmapzIndex(value: number): Heatmappin(): HeatmapPin to the screen so the camera does not move this node.hide(): Heatmapshow(): Heatmapnamed(label: string): HeatmaplabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
HeatmapOptionsinterface
cell?: number | undefinedCell edge in stage pixels.cellWidth?: number | undefinedcellHeight?: number | undefinedgap?: number | undefinedcolors?: readonly ColorInput[] | undefineddomain?: readonly [number, number] | undefinedValue range for the color ramp.values?: boolean | undefinedPrint values in cells.format?: ((value: number) => string) | undefinedvalueSize?: number | undefinedrowLabels?: readonly string[] | undefinedcolumnLabels?: readonly string[] | undefinedlabelSize?: number | undefinedcolumnLabelAngle?: number | undefinedTilt column labels so long names fit, in radians.radius?: number | undefinedfont?: FontFamily | undefinedThe font of the row and the column labels. Defaults to the font of the film. The values in the cells are in mono.
HighlightOptionsinterface
color?: ColorInput | undefinedopacity?: number | undefinedpad?: number | undefinedExtra room around the target.radius?: number | undefinedfrom?: "left" | "right" | undefinedKeep the mark afterwards. By default it stays until you fade it.duration?: number | undefinedease?: Ease | undefinedlag?: number | undefinedFraction of one item's time between the starts of consecutive items.
highlightSweepfunction
A marker stroke sweeps across a target and stays behind it. .mark is the node, so it can be faded out later.
function highlightSweep(target: Node | readonly Node[], options?: HighlightOptions): Anim & { mark: Node; }histogramfunction
function histogram(samples: readonly number[], options?: HistogramOptions): Histogram
Histogramclass
A distribution that can fill up over time. fillUp() drops the samples in, in order, so bars rise exactly as the data accumulates.
new Histogram(samples
shown: Valuebins: numberrange: readonly [number, number]chartWidth: numberchartHeight: numberaxis: Nodetotal: numberpeak: numberp(value: number, count: number): [number, number]Local stage point for a value and a count.fillUp(options?: AnimOptions & { count?: number | undefined; }): AnimSamples arrive in order, from none tocount(all of them by default).curve(f: (x: number) => number, options?: { color?: ColorInput | undefined; width?: number | undefined; samples?: number | undefined; }): NodeOverlay a curve, such as the fitted density, on the same axes. f returns a count.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Histogramtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Histogramdrop(child: Node): Histogramall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): HistogramA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): HistogramSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): HistogrammoveTo(target: Node | Vec): HistogramMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): HistogramMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): HistogramScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): HistogramRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): HistogramScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): HistogramPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): HistogramLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): HistogramLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Histogramstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Histogramcolor(color: ColorInput): HistogramRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Histogramglow(radius: number, color?: ColorInput | undefined): HistogramSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): HistogramzIndex(value: number): Histogrampin(): HistogramPin to the screen so the camera does not move this node.hide(): Histogramshow(): Histogramnamed(label: string): HistogramlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
HistogramOptionsinterface
bins?: number | undefinedrange?: readonly [number, number] | undefinedmax?: number | undefinedFixes the top of the scale, for a stable axis while samples arrive.gap?: number | undefinedticks?: number | undefinedNumber of x tick labels.colors?: readonly ColorInput[] | undefinedwidth?: number | undefinedheight?: number | undefinedlabelSize?: number | undefinedText size for direct labels and ticks.color?: ColorInput | undefinedfont?: FontFamily | undefinedThe font of the labels and the ticks. Defaults to the font of the film. A value that the chart prints is in mono.
infoBoxfunction
function infoBox(options: InfoBoxOptions): InfoBox
InfoBoxclass
A compact node for system stories: a title, a status, and rows of labeled metrics at reading size. It packs information like an engineering figure, not a slide. For a talk, raise size and the whole box scales with it.
new InfoBox(options
frame: NodeaccentBar: Node | nulltitleNode: NodesubtitleNode: Node | nullbadgeNode: Node | nullrowLabels: Node[]rowValues: (Node | null)[]meters: Node[]tagNodes: Node[]boxWidth: numberboxHeight: numberport(side: Edge, at?: number): [number, number]Stage point on one side of the box.atmoves along the side, from -1 to 1.enter(options?: AnimOptions): AnimThe frame draws, then the contents settle in, top to bottom.setValue(row: number, value: string, options?: AnimOptions & { tone?: ColorInput | undefined; }): AnimChange a row's value in place. Matching digits slide, and the others cross-fade.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): InfoBoxtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): InfoBoxdrop(child: Node): InfoBoxall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): InfoBoxA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): InfoBoxSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): InfoBoxmoveTo(target: Node | Vec): InfoBoxMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): InfoBoxMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): InfoBoxScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): InfoBoxRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): InfoBoxScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): InfoBoxPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): InfoBoxLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): InfoBoxLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): InfoBoxstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): InfoBoxcolor(color: ColorInput): InfoBoxRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): InfoBoxglow(radius: number, color?: ColorInput | undefined): InfoBoxSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): InfoBoxzIndex(value: number): InfoBoxpin(): InfoBoxPin to the screen so the camera does not move this node.hide(): InfoBoxshow(): InfoBoxnamed(label: string): InfoBoxlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
InfoBoxOptionsinterface
title: stringsubtitle?: string | undefinedbadge?: string | undefinedShort status tag at the top right, such as "p99" or "degraded".badgeTone?: ColorInput | undefinedrows?: readonly InfoRow[] | undefinedtags?: readonly string[] | undefinedSmall chips along the bottom.width?: number | undefinedWidth in stage pixels. Grows to fit the content when omitted.size?: number | undefinedBase text size. Everything scales from it. Raise it for a presentation.accent?: ColorInput | undefinedColor of the left edge.fill?: ColorInput | undefinedborder?: ColorInput | undefinedradius?: number | undefinedfont?: FontFamily | undefinedThe font of the title and the row labels. Defaults to the font of the film. The subtitle, the badge, the values and the tags are in mono.
InfoRowinterface
label: stringvalue?: string | undefinedtone?: ColorInput | undefinedColor for the value.meter?: number | undefinedThin meter under the row, from 0 to 1.spark?: readonly number[] | undefinedSmall trend drawn at the end of the row.
labeledDotfunction
function labeledDot(label: string, options?: LabeledDotOptions): LabeledDot
LabeledDotclass
A point with a label. The node's origin stays on the point.
new LabeledDot(label
mark: Nodecaption: Nodeid: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): LabeledDottrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): LabeledDotdrop(child: Node): LabeledDotall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): LabeledDotA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): LabeledDotSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): LabeledDotmoveTo(target: Node | Vec): LabeledDotMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): LabeledDotMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): LabeledDotScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): LabeledDotRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): LabeledDotScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): LabeledDotPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): LabeledDotLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): LabeledDotLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): LabeledDotstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): LabeledDotcolor(color: ColorInput): LabeledDotRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): LabeledDotglow(radius: number, color?: ColorInput | undefined): LabeledDotSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): LabeledDotzIndex(value: number): LabeledDotpin(): LabeledDotPin to the screen so the camera does not move this node.hide(): LabeledDotshow(): LabeledDotnamed(label: string): LabeledDotlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
LabeledDotOptionsinterface
color?: ColorInput | undefinedradius?: number | undefinedside?: Side | undefinedgap?: number | undefinedsize?: number | undefinedplain?: boolean | undefinedfont?: FontFamily | undefinedThe font of a plain label. Defaults to the font of the film.labelColor?: ColorInput | undefined
Laneinterface
name: stringcolor?: ColorInput | undefinedThe color of the spans and the marks of the lane. Defaults to blue.
LaneBoundaryinterface
A line along the time area between two lanes, such as the line between user space and the kernel.
between: readonly [LaneRef, LaneRef]The two lanes that the boundary is between. They are neighbors.label?: string | undefinedText after the right end of the line, outside the time area.color?: ColorInput | undefinedDefaults to yellow.count?: boolean | undefinedShow the number of handoffs that crossed the boundary so far, after the label, and a tick on the line for each one.
LaneEventtype
type LaneEvent = { lane: LaneRef; start: number; end: number; label?: string | undefined; labelSide?: LaneLabelSide | undefined; color?: ColorInput | undefined; open?: boolean | undefined; idle?: boolean | undefined; } | { from: LaneRef; to: LaneRef; at: number; label?: string | undefined; labelSide?: LaneLabelSide | undefined; color?: ColorInput | undefined; dashed?: boolean | undefined; } | { lane: LaneRef; at: number; label?: string | undefined; labelSide?: LaneLabelSide | undefined; color?: ColorInput | undefined; }LaneEventNodeclass
One event of a Lanes object. It is hidden until the cursor reaches it.
new LaneEventNode(kind: "mark" | "span" | "handoff", shape
kind: "mark" | "span" | "handoff"shape: NodeThe bar of a span, the arrow of a handoff, or the dot of a mark.text: Node | nullThe label of the event, or null when it has none.tip: Node | nullThe arrow head of an open span that reaches the last end of the domain.edge: Node | nullThe dashed outline of an idle span.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): LaneEventNodetrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): LaneEventNodedrop(child: Node): LaneEventNodeall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): LaneEventNodeA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): LaneEventNodeSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): LaneEventNodemoveTo(target: Node | Vec): LaneEventNodeMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): LaneEventNodeMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): LaneEventNodeScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): LaneEventNodeRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): LaneEventNodeScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): LaneEventNodePlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): LaneEventNodeLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): LaneEventNodeLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): LaneEventNodestroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): LaneEventNodecolor(color: ColorInput): LaneEventNodeRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): LaneEventNodeglow(radius: number, color?: ColorInput | undefined): LaneEventNodeSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): LaneEventNodezIndex(value: number): LaneEventNodepin(): LaneEventNodePin to the screen so the camera does not move this node.hide(): LaneEventNodeshow(): LaneEventNodenamed(label: string): LaneEventNodelabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
LaneLabelSidetype
Where the label of an event goes, when the film must choose. For a span: over the bar, under it, or beside it at its left or its right end. For a handoff: at the left or the right of its arrow, and over or under the boundary that the arrow crosses first. For a mark: over the dot, under it, or beside it.
type LaneLabelSide = "left" | "right" | "over" | "under"
LaneReftype
The name of a lane, or its index.
type LaneRef = string | number
lanesfunction
Lanes over time, one per actor, with spans, handoffs and marks: lanes(['guest', 'KVM'], [{ lane: 'guest', start: 0, end: 30, label: 'runs' }, { from: 'guest', to: 'KVM', at: 30, label: 'VM exit' }, { lane: 'KVM', start: 30, end: 42 }]).
function lanes(names: readonly (string | Lane)[], events: readonly LaneEvent[], options?: LanesOptions): Lanes
Lanesclass
Who runs when: one lane per actor, a bar for each span of work, an arrow for each handoff between two lanes, and a dot for each instant. Time runs from left to right. advance(to) moves a cursor to a time and stops there, and what the cursor passes appears, so a narration can step through the sequence. With ticks, the object also draws a time axis, for a chart of durations. With boundaries, it draws a line between two lanes and counts the handoffs that cross it. .at() places the middle of the time area. The names of the lanes are outside it, at the left.
new Lanes(lanes
names: Node[]The name of each lane, at the left of the time area.rails: Node[]A faint line along each lane.events: LaneEventNode[]One node per event, in the order given.spans: LaneEventNode[]The events by kind, in the order given.handoffs: LaneEventNode[]marks: LaneEventNode[]boundaries: Node[]One node per boundary: its line, its label, and its counter.counters: (Node | null)[]The number at the right end of each boundary that counts its crossings, or null for a boundary that does not.axis: Node | nullThe axis line, the ticks and their numbers, and the axis label. Null withoutticks.cursor: NodeA thin vertical line over all lanes, at the time of the object. The firstadvanceshows it.domain: readonly [number, number]The times at the two ends of the time area.labelsOf(part: Node): readonly Node[]The label of an event belongs to its bar, its arrow, or its dot, so a light on that shape keeps the label bright.time: numberThe time of the cursor after the changes that are scheduled so far. It starts at the first end of the domain.x(time: number): numberThe x of a time, in the coordinates of the object. The middle of the domain is at 0.y(lane: LaneRef): numberThe y of the middle of a lane, in the coordinates of the object. The first lane and the last one are at the same distance from 0.crossings(boundary: string | number): numberHow many handoffs crossed a boundary up to the time of the cursor, after the changes that are scheduled so far. Name the boundary by its index or by its label.enter(options?: AnimOptions): AnimThe names, the lines of the lanes, the boundaries and the axis arrive, lane by lane. No event shows yet.advance(to: number, options?: AnimOptions): AnimThe cursor moves from its time toto, and stops. What it passes appears. A span grows with the cursor. A handoff draws its arrow and a mark shows its dot when the cursor reaches them. A label arrives when its event is complete, and a label inside a bar arrives when the bar starts to grow. All of it is over when the animation ends: when the last event needs time after the cursor reaches it, the cursor stops early.showAll(options?: AnimOptions): AnimThe same asadvanceto the last end of the domain. It takes 4 seconds when the film gives no duration.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Lanestrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Lanesdrop(child: Node): Lanesall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): LanesA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): LanesSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): LanesmoveTo(target: Node | Vec): LanesMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): LanesMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): LanesScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): LanesRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): LanesScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): LanesPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): LanesLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): LanesLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Lanesstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Lanescolor(color: ColorInput): LanesRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Lanesglow(radius: number, color?: ColorInput | undefined): LanesSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): LaneszIndex(value: number): Lanespin(): LanesPin to the screen so the camera does not move this node.hide(): Lanesshow(): Lanesnamed(label: string): Lanes
LanesOptionsinterface
width?: number | undefinedThe width of the time area. The lane names are outside it, at the left. Defaults to 1400.barHeight?: number | undefinedThe height of the bar of a span. Defaults to 44.pitch?: number | undefinedFrom the middle of one lane to the middle of the next. Defaults to a value with room for the label of a handoff between two bars. Two lanes with a boundary between them are further apart, by 1.25 of the text size.domain?: readonly [number, number] | undefinedThe times at the two ends. Defaults to the range of the events.ticks?: number | readonly number[] | undefinedTicks under the lanes: a count, or the values. Without it, no axis is drawn, for a sequence that is not to scale.format?: ((time: number) => string) | undefinedHow to write a tick.axisLabel?: string | undefinedA line under the axis that names the unit. Withoutticks, the axis is a line with this label and no ticks.boundaries?: readonly LaneBoundary[] | undefinedLines between lanes that are neighbors. A handoff crosses each boundary that is between its two lanes.cursor?: boolean | undefinedDraw the cursor. With false, no cursor is ever drawn, and the events still appear at their times. On by default.backing?: ColorInput | null | undefinedThe color of the patches behind the labels, the bars and the arrows of dashes. They hide the cursor and the lines of the lanes there. Defaults to the background of the look. On a panel of another color, give that color. Pass null for no patches, on a background with no single color.size?: number | undefinedText size. Defaults to 26.font?: FontFamily | undefined
legendfunction
A small key. Prefer direct labels. Use this when a figure holds too many marks to label.
function legend(items: readonly LegendItem[], options?: LegendOptions): Node
LegendIteminterface
label: stringcolor?: ColorInput | undefinedkind?: "dash" | "line" | "swatch" | "dot" | "ring" | undefined
LegendOptionsinterface
direction?: "row" | "column" | undefinedsize?: number | undefinedgap?: number | undefinedfont?: FontFamily | undefinedThe font of the labels. Defaults to the font of the film.
lineChartfunction
function lineChart(series: readonly LineSeries[], options?: LineChartOptions): LineChart
LineChartclass
new LineChart(series
lines: Node[]areas: Node[]names: Node[]axis: Nodep(x: number, y: number): [number, number]series(name: string): Nodeenter(options?: AnimOptions): AnimAxes fade in, lines draw left to right, and their labels arrive as each line lands.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): LineCharttrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): LineChartdrop(child: Node): LineChartall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): LineChartA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): LineChartSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): LineChartmoveTo(target: Node | Vec): LineChartMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): LineChartMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): LineChartScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): LineChartRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): LineChartScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): LineChartPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): LineChartLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): LineChartLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): LineChartstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): LineChartcolor(color: ColorInput): LineChartRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): LineChartglow(radius: number, color?: ColorInput | undefined): LineChartSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): LineChartzIndex(value: number): LineChartpin(): LineChartPin to the screen so the camera does not move this node.hide(): LineChartshow(): LineChartnamed(label: string): LineChartlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
LineChartOptionsinterface
x?: readonly [number, number] | undefinedy?: readonly [number, number] | undefinedxLabel?: string | undefinedyLabel?: string | undefinedxFormat?: ((x: number) => string) | undefinedyFormat?: ((y: number) => string) | undefinedxTicks?: number | undefinedyTicks?: number | undefinedwidth?: number | undefinedheight?: number | undefinedlabelSize?: number | undefinedText size for direct labels and ticks.color?: ColorInput | undefinedfont?: FontFamily | undefinedThe font of the labels and the ticks. Defaults to the font of the film. A value that the chart prints is in mono.
LineSeriesinterface
name?: string | undefinedvalues?: readonly number[] | undefinedY values at x = 0, 1, 2, ...points?: readonly Vec[] | undefinedcolor?: ColorInput | undefinedwidth?: number | undefineddash?: readonly number[] | undefinedarea?: number | undefinedSoft fill under the line.smooth?: boolean | undefined
LoadedFontinterface
unitsPerEm: numberascent: numberdescent: numberlineGap: numberglyphFor(code: number): numberadvance(glyph: number): numberkern(left: number, right: number): numberoutline(glyph: number): GlyphOutline | null
LOG_STATESconst
States for a history that is written and then replayed. Each has its own mark, so color is not the only sign.
const LOG_STATES: { stored: { color: string; mark: "dot"; }; new: { color: string; mark: "dot"; }; answered: { color: string; mark: "check"; }; waiting: { color: string; mark: "ring"; }; failed: { color: string; mark: "cross"; }; }LogRowtype
One row of the list. A row has an id: an event number, a step number. Or it has ids, and stands for several records that follow each other, such as the three events of one task. Such a row is a fold. It shows its first and its last id, as "2 to 4", and unfold() opens it into its rows. An id is in the list once.
type LogRow = LogRowFields & ({ id: Id; ids?: readonly Id[] | undefined; } | { id?: Id | undefined; ids: readonly Id[]; rows?: readonly LogRow[] | undefined; })LogRowFieldsinterface
What a row shows and carries, apart from its identity.
name: stringThe name of the record, in mono:ActivityTaskCompleted.detail?: string | number | undefinedOne short detail, dim:"ch_1001",attempt 4. A number is taken as its text.time?: string | number | undefinedA time at the right end of the row, dim and in mono:+6.01 s. The list has this column when one of its first rows has a time.state?: string | undefinedDefaults to the first state ofstates. A row of a fold defaults to the state of the fold.family?: string | undefinedA word that the list does not draw. A film reads it infind()andmark(), to pick the rows of one kind.shown?: boolean | undefinedWith false, the row has its place in the list from the start and does not show.show()brings it in. Defaults to true.
LogRowNodeclass
One line of an EventLog: a row, or a fold.
new LogRowNode(mark
content: NodeThe holder of the parts.show()brings it in, for a row that had its place and did not show.mark: NodeThe mark at the left, in the color and the shape of the state. A fold in a state with a dot has three small dots.idText: NodeThe id. For a fold, its first id.range: Node | nullFor a fold, the word "to" and its last id. Null for a row.name: NodeThe name, in the color of the state.detail: Node | nullThe detail, or null for a row that has none.time: Node | nullThe time, or null for a row that has none.parts: Node[]The parts that the line has, from the left: the mark, the id, the range of a fold, the name, the detail, the time.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): LogRowNodetrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): LogRowNodedrop(child: Node): LogRowNodeall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): LogRowNodeA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): LogRowNodeSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): LogRowNodemoveTo(target: Node | Vec): LogRowNodeMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): LogRowNodeMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): LogRowNodeScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): LogRowNodeRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): LogRowNodeScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): LogRowNodePlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): LogRowNodeLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): LogRowNodeLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): LogRowNodestroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): LogRowNodecolor(color: ColorInput): LogRowNodeRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): LogRowNodeglow(radius: number, color?: ColorInput | undefined): LogRowNodeSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): LogRowNodezIndex(value: number): LogRowNodepin(): LogRowNodePin to the screen so the camera does not move this node.hide(): LogRowNodeshow(): LogRowNodenamed(label: string): LogRowNodelabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
LogStateinterface
color: ColorInputdim?: boolean | undefinedDraw the row dim, for a record that is old or not reached.mark?: "dot" | "ring" | "check" | "cross" | undefinedThe shape of the mark at the left of the row, so that a viewer can tell two states with no color. Defaults to dot.
matrixfunction
function matrix(rows: readonly (readonly (string | number)[])[], options?: MatrixOptions): MatrixNode
MatrixNodeclass
A typeset matrix with real TeX brackets. Each entry is its own node.
new MatrixNode(rows
cells: TexNode[][]entries: Nodebrackets: NoderowCount: numbercolCount: numbercell(row: number, column: number): TexNoderow(index: number): TexNode[]col(index: number): TexNode[]setCell(row: number, column: number, value: string | number, options?: AnimOptions): AnimReplace one entry, morphing matching glyphs. Later calls to.cellreturn the new one.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): MatrixNodetrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): MatrixNodedrop(child: Node): MatrixNodeall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): MatrixNodeA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): MatrixNodeSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): MatrixNodemoveTo(target: Node | Vec): MatrixNodeMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): MatrixNodeMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): MatrixNodeScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): MatrixNodeRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): MatrixNodeScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): MatrixNodePlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): MatrixNodeLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): MatrixNodeLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): MatrixNodestroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): MatrixNodecolor(color: ColorInput): MatrixNodeRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): MatrixNodeglow(radius: number, color?: ColorInput | undefined): MatrixNodeSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): MatrixNodezIndex(value: number): MatrixNodepin(): MatrixNodePin to the screen so the camera does not move this node.hide(): MatrixNodeshow(): MatrixNodenamed(label: string): MatrixNodelabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
MatrixOptionsinterface
size?: number | undefinedcolor?: ColorInput | undefinedgapX?: number | undefinedSpace between columns and rows, as multiples of size.gapY?: number | undefinedbracket?: "[" | "(" | "|" | undefinedbracketColor?: ColorInput | undefined
morphCodefunction
Morph one version of code into another. Tokens are matched in order, so unchanged code slides to its new place and only real edits fade.
function morphCode(from: CodeBlock, to: CodeBlock, options?: AnimOptions): Anim
narrationfunction
Lay a measured narration on the stage. Each scene gets a clock with phrase cues. Captions and chapters are added to the film, so the MP4 carries them.
function narration(stage: Stage, timing: NarrationTiming, options?: NarrationOptions): Narration
Narrationclass
The timeline of a whole narration.
new Narration(stage
scenes: readonly SceneClock[]duration: numberThe end of the last scene, in seconds. Hold the film at least this long, or the voice is cut.lead: numbergap: numberscene(id: string): SceneClockThe clock of one scene.placements(): { scene: string; beat: number; start: number; end: number; }[]Where every beat's recording starts, in order, for the script that mixes the sound.
NarrationBeatinterface
One take of the voice: a sentence or a short paragraph.
say: stringThe text that the voice speaks. Captions use it.duration: numberMeasured length of the recording, in seconds.hold?: number | undefinedSilence after the beat, in seconds, for the viewer to think.words?: readonly NarrationWord[] | undefinedWord times, from the speech engine or from a transcription. Cues need them.
NarrationOptionsinterface
lead?: number | undefinedSeconds before the first word, for a logo or an opening picture. Defaults to 0.6.gap?: number | undefinedSeconds of silence between scenes, for the picture to change. Defaults to 0.4.captions?: boolean | undefinedWrite a caption for each spoken line. On by default.captionLength?: number | undefinedLongest caption line, in characters. Defaults to 74.chapters?: boolean | undefinedStart a chapter at each scene that has a title. On by default.
NarrationSceneinterface
id: stringtitle?: string | undefinedChapter title. A scene without one does not start a chapter.beats: readonly NarrationBeat[]
NarrationTiminginterface
scenes: readonly NarrationScene[]lead?: number | undefinedSeconds before the first word.NarrationOptions.leadoverrides it.gap?: number | undefinedSeconds of silence between scenes.NarrationOptions.gapoverrides it.
NarrationWordinterface
One spoken word, timed in seconds from the start of its beat.
text: stringstart: numberend: number
neuralNetfunction
function neuralNet(layers: readonly number[], options?: NeuralNetOptions): NeuralNet
NeuralNetclass
Layers of neurons joined by hairline weights. forward() runs a signal through it.
new NeuralNet(layers
neurons: Node[][]links: Node[][][]Edges between layer i and layer i + 1, by [from][to].edgeLayer: NodeneuronLayer: Nodecaptions: Node[]layer(index: number): Node[]enter(options?: AnimOptions): AnimNeurons appear layer by layer, then the weights draw between them.activate(layer: number, values: readonly number[], options?: AnimOptions & { color?: ColorInput | undefined; }): AnimSet how strongly each neuron in a layer fires, from 0 to 1.forward(options?: AnimOptions & { activations?: readonly (readonly number[])[] | undefined; color?: ColorInput | undefined; pulses?: number | undefined; }): AnimRun a signal from the first layer to the last. Pass activations per layer, or let a seeded generator pick plausible ones. The strongest paths light up.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): NeuralNettrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): NeuralNetdrop(child: Node): NeuralNetall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): NeuralNetA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): NeuralNetSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): NeuralNetmoveTo(target: Node | Vec): NeuralNetMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): NeuralNetMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): NeuralNetScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): NeuralNetRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): NeuralNetScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): NeuralNetPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): NeuralNetLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): NeuralNetLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): NeuralNetstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): NeuralNetcolor(color: ColorInput): NeuralNetRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): NeuralNetglow(radius: number, color?: ColorInput | undefined): NeuralNetSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): NeuralNetzIndex(value: number): NeuralNetpin(): NeuralNetPin to the screen so the camera does not move this node.hide(): NeuralNetshow(): NeuralNetnamed(label: string): NeuralNetlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
NeuralNetOptionsinterface
width?: number | undefinedheight?: number | undefinedradius?: number | undefinedcollapse?: number | undefinedShow at most this many neurons per layer, with an ellipsis for the rest.colors?: readonly ColorInput[] | undefinededgeColor?: ColorInput | undefinededgeOpacity?: number | undefinedlayerLabels?: readonly string[] | undefinedlabelSize?: number | undefinedseed?: number | undefinedSeed for the default activations.font?: FontFamily | undefinedThe font of the layer labels. Defaults to the font of the film.
numberLinefunction
function numberLine(options?: NumberLineOptions): NumberLine
NumberLineclass
new NumberLine(options?
range: readonly [number, number]length: numberline: Nodeticks: Nodenumbers: Nodep(value: number): [number, number]Value to local stage coordinates.world(value: number): [number, number]marker(value: Reactive, options?: { color?: ColorInput | undefined; label?: string | undefined; radius?: number | undefined; labelSize?: number | undefined; }): NodeA point on the line. Pass a function to follow a Value over time.interval(from: number, to: number, options?: { color?: ColorInput | undefined; width?: number | undefined; offset?: number | undefined; openLeft?: boolean | undefined; openRight?: boolean | undefined; }): NodeA highlighted span, with open or closed endpoints.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): NumberLinetrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): NumberLinedrop(child: Node): NumberLineall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): NumberLineA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): NumberLineSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): NumberLinemoveTo(target: Node | Vec): NumberLineMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): NumberLineMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): NumberLineScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): NumberLineRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): NumberLineScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): NumberLinePlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): NumberLineLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): NumberLineLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): NumberLinestroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): NumberLinecolor(color: ColorInput): NumberLineRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): NumberLineglow(radius: number, color?: ColorInput | undefined): NumberLineSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): NumberLinezIndex(value: number): NumberLinepin(): NumberLinePin to the screen so the camera does not move this node.hide(): NumberLineshow(): NumberLinenamed(label: string): NumberLinelabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
NumberLineOptionsinterface
range?: readonly [number, number] | undefinedlength?: number | undefinedOn-screen length in stage pixels.step?: number | undefinedlabels?: boolean | undefinedlabelSize?: number | undefinedcolor?: ColorInput | undefinedtips?: boolean | undefinedwidth?: number | undefined
NumberPlaneclass
The classic infinite grid. Each line is a dense path, so it bends smoothly under applyMatrix and under warp with a nonlinear function.
new NumberPlane(options?
unit: numbermajor: Nodeminor: Nodeaxes: Nodep(x: number, y: number): [number, number]Math coordinates to stage coordinates. y points up on the plane.vector(x: number, y: number, style?: ShapeStyle): NodeA vector drawn from the origin, as an arrow.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): NumberPlanetrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): NumberPlanedrop(child: Node): NumberPlaneall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): NumberPlaneA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): NumberPlaneSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): NumberPlanemoveTo(target: Node | Vec): NumberPlaneMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): NumberPlaneMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): NumberPlaneScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): NumberPlaneRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): NumberPlaneScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): NumberPlanePlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): NumberPlaneLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): NumberPlaneLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): NumberPlanestroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): NumberPlanecolor(color: ColorInput): NumberPlaneRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): NumberPlaneglow(radius: number, color?: ColorInput | undefined): NumberPlaneSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): NumberPlanezIndex(value: number): NumberPlanepin(): NumberPlanePin to the screen so the camera does not move this node.hide(): NumberPlaneshow(): NumberPlanenamed(label: string): NumberPlanelabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
PAGE_STATESconst
States for pages of memory.
const PAGE_STATES: { absent: { color: string; fill: number; dash: true; }; zero: { color: string; fill: number; }; present: { color: string; fill: number; }; dirty: { color: string; fill: number; }; shared: { color: string; fill: number; }; copied: { color: string; fill: number; }; file: { color: string; fill: number; }; swapped: { color: string; fill: number; }; }pictogramfunction
A small picture of a concept, such as pictogram('graph', { size: 120, color: 'dim', accent: 'teal' }).
function pictogram(name: "warning" | "ring" | "series" | "chart" | "forecast" | "threshold" | "anomaly" | "logs" | "traces" | "bars" | "table" | "matrix" | "event" | "alert" | "graph" | "dag" | "tree" | "layers" | "pipeline" | "branch" | "service" | "database" | "queue" | "cloud" | "chip" | "vm" | "process" | "kernel" | "memory" | "disk" | "nic" | "network" | "snapshot" | "clone" | "jail" | "wall" | "key" | "stopwatch" | "bolt" | "proxy" | "certificate" | "sleep" | "shard" | "core" | "timer" | "firmware" | "irqchip" | "bus" | "drive" | "floppy" | "serial" | "keyboard" | "server" | "laptop" | "thread" | "interrupt" | "syscall" | "page" | "pagetable" | "pin" | "worker" | "workflow" | "activity" | "signal" | "query" | "update" | "issue" | "pullRequest" | "repository" | "checklist" | "approval" | "model" | "tokens" | "vector" | "agent" | "spark" | "dashboard" | "terminal" | "container" | "experiment" | "target" | "loop" | "eye" | "message" | "shield" | "browser" | "desktop" | "recording" | "creditCard" | "warehouse" | "truck" | "microphone" | "forum" | "paywall" | "document" | "code" | "gear" | "clock" | "user" | "search" | "filter" | "lock" | "deploy" | "check" | "cross" | "question", options?: PictogramOptions): Pictogram
Pictogramclass
A small picture of a concept. .at(x, y) places the middle of its grid, so pictures of one size line up.
new Pictogram(name: "warning" | "ring" | "series" | "chart" | "forecast" | "threshold" | "anomaly" | "logs" | "traces" | "bars" | "table" | "matrix" | "event" | "alert" | "graph" | "dag" | "tree" | "layers" | "pipeline" | "branch" | "service" | "database" | "queue" | "cloud" | "chip" | "vm" | "process" | "kernel" | "memory" | "disk" | "nic" | "network" | "snapshot" | "clone" | "jail" | "wall" | "key" | "stopwatch" | "bolt" | "proxy" | "certificate" | "sleep" | "shard" | "core" | "timer" | "firmware" | "irqchip" | "bus" | "drive" | "floppy" | "serial" | "keyboard" | "server" | "laptop" | "thread" | "interrupt" | "syscall" | "page" | "pagetable" | "pin" | "worker" | "workflow" | "activity" | "signal" | "query" | "update" | "issue" | "pullRequest" | "repository" | "checklist" | "approval" | "model" | "tokens" | "vector" | "agent" | "spark" | "dashboard" | "terminal" | "container" | "experiment" | "target" | "loop" | "eye" | "message" | "shield" | "browser" | "desktop" | "recording" | "creditCard" | "warehouse" | "truck" | "microphone" | "forum" | "paywall" | "document" | "code" | "gear" | "clock" | "user" | "search" | "filter" | "lock" | "deploy" | "check" | "cross" | "question", options?
size: numberThe side of the square that holds the picture, in pixels.name: "warning" | "ring" | "series" | "chart" | "forecast" | "threshold" | "anomaly" | "logs" | "traces" | "bars" | "table" | "matrix" | "event" | "alert" | "graph" | "dag" | "tree" | "layers" | "pipeline" | "branch" | "service" | "database" | "queue" | "cloud" | "chip" | "vm" | "process" | "kernel" | "memory" | "disk" | "nic" | "network" | "snapshot" | "clone" | "jail" | "wall" | "key" | "stopwatch" | "bolt" | "proxy" | "certificate" | "sleep" | "shard" | "core" | "timer" | "firmware" | "irqchip" | "bus" | "drive" | "floppy" | "serial" | "keyboard" | "server" | "laptop" | "thread" | "interrupt" | "syscall" | "page" | "pagetable" | "pin" | "worker" | "workflow" | "activity" | "signal" | "query" | "update" | "issue" | "pullRequest" | "repository" | "checklist" | "approval" | "model" | "tokens" | "vector" | "agent" | "spark" | "dashboard" | "terminal" | "container" | "experiment" | "target" | "loop" | "eye" | "message" | "shield" | "browser" | "desktop" | "recording" | "creditCard" | "warehouse" | "truck" | "microphone" | "forum" | "paywall" | "document" | "code" | "gear" | "clock" | "user" | "search" | "filter" | "lock" | "deploy" | "check" | "cross" | "question"id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Pictogramtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Pictogramdrop(child: Node): Pictogramall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): PictogramA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): PictogramSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): PictogrammoveTo(target: Node | Vec): PictogramMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): PictogramMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): PictogramScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): PictogramRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): PictogramScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): PictogramPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): PictogramLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): PictogramLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Pictogramstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Pictogramcolor(color: ColorInput): PictogramRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Pictogramglow(radius: number, color?: ColorInput | undefined): PictogramSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): PictogramzIndex(value: number): Pictogrampin(): PictogramPin to the screen so the camera does not move this node.hide(): Pictogramshow(): Pictogramnamed(label: string): PictogramlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
PictogramNametype
type PictogramName = "warning" | "ring" | "series" | "chart" | "forecast" | "threshold" | "anomaly" | "logs" | "traces" | "bars" | "table" | "matrix" | "event" | "alert" | "graph" | "dag" | "tree" | "layers" | "pipeline" | "branch" | "service" | "database" | "queue" | "cloud" | "chip" | "vm" | "process" | "kernel" | "memory" | "disk" | "nic" | "network" | "snapshot" | "clone" | "jail" | "wall" | "key" | "stopwatch" | "bolt" | "proxy" | "certificate" | "sleep" | "shard" | "core" | "timer" | "firmware" | "irqchip" | "bus" | "drive" | "floppy" | "serial" | "keyboard" | "server" | "laptop" | "thread" | "interrupt" | "syscall" | "page" | "pagetable" | "pin" | "worker" | "workflow" | "activity" | "signal" | "query" | "update" | "issue" | "pullRequest" | "repository" | "checklist" | "approval" | "model" | "tokens" | "vector" | "agent" | "spark" | "dashboard" | "terminal" | "container" | "experiment" | "target" | "loop" | "eye" | "message" | "shield" | "browser" | "desktop" | "recording" | "creditCard" | "warehouse" | "truck" | "microphone" | "forum" | "paywall" | "document" | "code" | "gear" | "clock" | "user" | "search" | "filter" | "lock" | "deploy" | "check" | "cross" | "question"
PictogramOptionsinterface
size?: number | undefinedThe side of the square that holds the picture, in pixels. Defaults to 96.color?: ColorInput | undefinedThe color of the lines. Defaults to ink.accent?: ColorInput | undefinedThe color of the part that carries the meaning, such as the forecast inforecast. A picture with one part, such asseriesorcheck, is all in this color. Defaults tocolor.weight?: number | undefinedStroke width, in units of the 24-unit grid. Defaults to 1.6.
PICTOGRAMSconst
Every pictogram name, in the order of the catalog.
const PICTOGRAMS: readonly ("warning" | "ring" | "series" | "chart" | "forecast" | "threshold" | "anomaly" | "logs" | "traces" | "bars" | "table" | "matrix" | "event" | "alert" | "graph" | "dag" | "tree" | "layers" | "pipeline" | "branch" | "service" | "database" | "queue" | "cloud" | "chip" | "vm" | "process" | "kernel" | "memory" | "disk" | "nic" | "network" | "snapshot" | "clone" | "jail" | "wall" | "key" | "stopwatch" | "bolt" | "proxy" | "certificate" | "sleep" | "shard" | "core" | "timer" | "firmware" | "irqchip" | "bus" | "drive" | "floppy" | "serial" | "keyboard" | "server" | "laptop" | "thread" | "interrupt" | "syscall" | "page" | "pagetable" | "pin" | "worker" | "workflow" | "activity" | "signal" | "query" | "update" | "issue" | "pullRequest" | "repository" | "checklist" | "approval" | "model" | "tokens" | "vector" | "agent" | "spark" | "dashboard" | "terminal" | "container" | "experiment" | "target" | "loop" | "eye" | "message" | "shield" | "browser" | "desktop" | "recording" | "creditCard" | "warehouse" | "truck" | "microphone" | "forum" | "paywall" | "document" | "code" | "gear" | "clock" | "user" | "search" | "filter" | "lock" | "deploy" | "check" | "cross" | "question")[]piefunction
function pie(data: readonly PieDatum[], options?: PieOptions): PieChart
PieChartclass
Share of a whole, labeled directly. enter() sweeps around, and update() re-divides it.
new PieChart(data
wedges: Node[]labels: Node[]leaders: Node[]centerText: Node | nullenter(options?: AnimOptions): AnimWedges sweep open around the circle, then the labels arrive.update(values: readonly number[], options?: AnimOptions): AnimRe-divide the circle. Geometry animates, and labels follow their wedges.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): PieCharttrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): PieChartdrop(child: Node): PieChartall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): PieChartA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): PieChartSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): PieChartmoveTo(target: Node | Vec): PieChartMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): PieChartMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): PieChartScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): PieChartRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): PieChartScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): PieChartPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): PieChartLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): PieChartLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): PieChartstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): PieChartcolor(color: ColorInput): PieChartRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): PieChartglow(radius: number, color?: ColorInput | undefined): PieChartSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): PieChartzIndex(value: number): PieChartpin(): PieChartPin to the screen so the camera does not move this node.hide(): PieChartshow(): PieChartnamed(label: string): PieChartlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
PieDatuminterface
label: stringvalue: numbercolor?: ColorInput | undefined
PieOptionsinterface
radius?: number | undefinedhole?: number | undefinedInner radius as a fraction of the outer radius. 0 makes a pie.gap?: number | undefinedAngular gap between wedges, in radians.labelSize?: number | undefinedpercent?: boolean | undefinedShow each share as a percentage beside its label.labels?: boolean | undefinedcenterLabel?: string | undefinedcenterSize?: number | undefinedfont?: FontFamily | undefinedThe font of the labels. Defaults to the font of the film. The center label is in the display serif.
PlacedBeatinterface
Where one beat falls on the film's timeline, in seconds.
start: numberThe first moment of the recording.end: numberThe last moment of the recording.until: numberThe end of the silence after it.text: stringwords: readonly NarrationWord[]
planefunction
function plane(options?: PlaneOptions): NumberPlane
PlaneOptionsinterface
unit?: number | undefinedStage pixels per math unit.x?: readonly [number, number] | undefinedExtent in math units. Defaults to overscan past the frame edges, for warps that pull in.y?: readonly [number, number] | undefinedcolor?: ColorInput | undefinedminor?: number | undefinedMinor grid lines per unit.axisColor?: ColorInput | undefinedwidth?: number | undefined
quotefunction
The exact words of a source, with who said them and when: quote('the agent loop lives in Temporal', { by: 'Cursor', when: '2026-06-02', width: 900, color: 'orange' }).
function quote(words: string, options?: QuoteOptions): Quote
Quoteclass
The exact words of a source between quotation marks, with a rule at their left, and one line under them that says who said them, when, and where. .at(x, y) places the top end of the rule, so quotes of different lengths start at one corner. The object is as high as its words need: read quote.height.
new Quote(words
words: TextNodeThe words with their quotation marks, as one text. The film check reads it as one sentence.source: TextNode | nullThe line under the words: the speaker, the date, and the place. Null when the film gives none of them.rule: NodeThe line at the left, from the top of the words to the bottom of the source line.baseline: numberThe y of the base line of the first line of the words, in the coordinates of the object. A label beside the quote can stand on it.find(part: string): Node[]The glyphs of a part of the words, to color them or to move them. The part is as the film wrote the words. Spaces do not count, and the part may run over the end of a line. Places at the borders of words come first:find('not')gives the word "not" at each of its places, and not the letters in "nothing". A part that is only inside longer words gives those places. A part that is not in the words gives an empty list.reading: stringWhat a viewer reads: the words and the source line, as one text. A quote stays up forreadingSeconds(said.reading). The film check gives the words and the source line each its own time, which is less.lineEnd(line?: number): numberThe x of the end of the ink of a line of the words, in the coordinates of the object. Line 0 is the first one.enter(options?: AnimOptions): AnimThe rule grows down from the top, the words arrive line by line, and then the source line.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Quotetrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Quotedrop(child: Node): Quoteall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): QuoteA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): QuoteSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): QuotemoveTo(target: Node | Vec): QuoteMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): QuoteMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): QuoteScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): QuoteRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): QuoteScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): QuotePlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): QuoteLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): QuoteLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Quotestroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Quotecolor(color: ColorInput): QuoteRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Quoteglow(radius: number, color?: ColorInput | undefined): QuoteSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): QuotezIndex(value: number): Quotepin(): QuotePin to the screen so the camera does not move this node.hide(): Quoteshow(): Quotenamed(label: string): QuotelabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
QuoteOptionsinterface
by?: string | undefinedWho said the words: a person, a company, or a document.when?: string | undefinedWhen, written as the film wants it on screen, such as'2026-06-02'.where?: string | undefinedWhere: the title of the paper, the page, or the talk.width?: number | undefinedThe width of the object, from the left edge of the rule to the place where the words wrap. Nothing of the object passes it. Defaults to 900.size?: number | undefinedFont size of the words. Defaults to 32.color?: ColorInput | undefinedThe color of the rule and of the marked parts. Defaults to blue.mark?: string | readonly string[] | undefinedParts of the words to set inmarkColor, such as the phrase that the voice says. A part starts and ends at the borders of words:'not'is the word "not", and not the first letters of "nothing". A part that is in the words more than once is marked at all its places. A part with no such place is an error.markColor?: ColorInput | undefinedThe color of the marked parts. Defaults tocolor.cut?: boolean | undefinedThe film left words out. Write[...]in the words at each such place: the object adds none. Withcut, each[...]is in dim, so the viewer sees that it is not a word of the source.italic?: boolean | undefinedSet the words in italic. On by default.sourceSize?: number | undefinedFont size of the source line. Defaults to 0.78 ofsize, and to 24 at least whensizeis 24 or more.leading?: number | undefinedLine height of the words, as a multiple ofsize. Defaults to 1.3.font?: FontFamily | undefinedThe font of the words and of the source line. Defaults to text-serif.
quotePairfunction
Two quotes side by side with a word between them: quotePair({ words: 'it is safe', by: 'A' }, { words: 'it is not', by: 'B' }, { width: 1700, versus: 'but' }).
function quotePair(first: QuoteSpec, second: QuoteSpec, options?: QuotePairOptions): QuotePair
QuotePairclass
Two quotes side by side, for two sources that disagree. They have one width and one top line, and an optional word stands between them. .at(x, y) places the top end of the rule of the first quote, and nothing of the pair is over that top line.
new QuotePair(first
quotes: readonly [Quote, Quote]The two quotes, the left one first.versus: TextNode | nullThe word between the two quotes, or null.enter(options?: AnimOptions): AnimThe first quote arrives, then the word, then the second quote.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): QuotePairtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): QuotePairdrop(child: Node): QuotePairall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): QuotePairA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): QuotePairSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): QuotePairmoveTo(target: Node | Vec): QuotePairMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): QuotePairMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): QuotePairScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): QuotePairRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): QuotePairScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): QuotePairPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): QuotePairLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): QuotePairLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): QuotePairstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): QuotePaircolor(color: ColorInput): QuotePairRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): QuotePairglow(radius: number, color?: ColorInput | undefined): QuotePairSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): QuotePairzIndex(value: number): QuotePairpin(): QuotePairPin to the screen so the camera does not move this node.hide(): QuotePairshow(): QuotePairnamed(label: string): QuotePairlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
QuotePairOptionsinterface
width?: number | undefinedThe width of the pair, from the rule of the first quote to the place where the words of the second one wrap. Defaults to 1700.gap?: number | undefinedThe room between the two quotes. Defaults to 3 times the text size, or the larger one of two sizes.versus?: string | undefinedA word between the two quotes, such as'but'or'against'. It has the same room at both sides: from the end of the first line of the first quote, and to the rule of the second quote.font?: FontFamily | undefinedThe font of the words and of the source line. Defaults to text-serif.size?: number | undefinedFont size of the words. Defaults to 32.color?: ColorInput | undefinedThe color of the rule and of the marked parts. Defaults to blue.markColor?: ColorInput | undefinedThe color of the marked parts. Defaults tocolor.italic?: boolean | undefinedSet the words in italic. On by default.sourceSize?: number | undefinedFont size of the source line. Defaults to 0.78 ofsize, and to 24 at least whensizeis 24 or more.leading?: number | undefinedLine height of the words, as a multiple ofsize. Defaults to 1.3.
QuoteSpecinterface
One quote of a pair: its words and its own options. The pair gives it its width.
words: stringfont?: FontFamily | undefinedThe font of the words and of the source line. Defaults to text-serif.by?: string | undefinedWho said the words: a person, a company, or a document.when?: string | undefinedWhen, written as the film wants it on screen, such as'2026-06-02'.where?: string | undefinedWhere: the title of the paper, the page, or the talk.size?: number | undefinedFont size of the words. Defaults to 32.color?: ColorInput | undefinedThe color of the rule and of the marked parts. Defaults to blue.mark?: string | readonly string[] | undefinedParts of the words to set inmarkColor, such as the phrase that the voice says. A part starts and ends at the borders of words:'not'is the word "not", and not the first letters of "nothing". A part that is in the words more than once is marked at all its places. A part with no such place is an error.markColor?: ColorInput | undefinedThe color of the marked parts. Defaults tocolor.cut?: boolean | undefinedThe film left words out. Write[...]in the words at each such place: the object adds none. Withcut, each[...]is in dim, so the viewer sees that it is not a word of the source.italic?: boolean | undefinedSet the words in italic. On by default.sourceSize?: number | undefinedFont size of the source line. Defaults to 0.78 ofsize, and to 24 at least whensizeis 24 or more.leading?: number | undefinedLine height of the words, as a multiple ofsize. Defaults to 1.3.
readingSecondsfunction
The seconds that a viewer needs to read a plain text on screen: 20 characters in one second, and half a second at least. The motion scan of checkFilm uses the same limit for film.text_brief. Use it for the time that a text stays up, such as s.wait(readingSeconds('one forecast drifts, and the law breaks')). For a formula, the scan counts the signs that it draws, which are fewer than the characters of its source.
function readingSeconds(text: string, speed?: number): number
riemannfunction
Rectangles under a curve. Morph one set into a finer one to show the integral appear.
function riemann(axes: Axes, f: (x: number) => number, options?: RiemannOptions): Node
RiemannOptionsinterface
from?: number | undefinedto?: number | undefinedn?: number | undefinedrule?: "left" | "right" | "mid" | undefinedWhere each rectangle samples the function.colors?: readonly ColorInput[] | undefinedopacity?: number | undefinedstroke?: ColorInput | undefinedstrokeWidth?: number | undefined
rightAnglefunction
The small square that marks a right angle.
function rightAngle(vertex: Vec, a: Vec, b: Vec, options?: { size?: number | undefined; color?: ColorInput | undefined; width?: number | undefined; }): NoderingBufferfunction
A ring buffer with a head and a tail: ringBuffer(8, { label: 'submission queue', color: 'blue' }). push() writes at the tail, and pop() reads at the head.
function ringBuffer(slots: number, options?: RingBufferOptions): RingBuffer
RingBufferclass
A ring buffer: a band of slots, the entries in them, and two pointers. The producer writes an entry at the tail with push(), and the consumer reads the entry at the head with pop(). The two indexes only move forward, and the slot of an index is the index modulo the number of slots. It is the picture of the two rings of io_uring, of a virtqueue, and of any bounded queue. enter() draws the band, then brings the pointers and the caption. .at() places the center of the ring. The bounds of the ring hold the outside name at every slot, so a zone or a card around a ring has room for it.
new RingBuffer(slots
slots: Node[]The outline of each slot. Slot 0 is at the top, and the slots follow clockwise.entries: Node[]The entry of each slot: its fill, its outline, and its label. Its opacity is 0 while the slot is empty.numbers: Node[]The number beside each slot. The list is empty withoutnumbers.headPointer: Node | nullThe arrow head and the name of each pointer, or null for a pointer whose name is null. Fade, scale, or recolor a pointer as one node. Its place is its tip, and the ring moves it: do not move it.tailPointer: Node | nullwriteMarker: Node | nullThe dim arrow head at the place where the producer writes. Null withouttailFollows: 'publish'.caption: Node | nullThe text under the ring, or null for a ring with nolabel.size: numberThe number of slots.radius: numberThe outer radius of the band, and the radial size of a slot.thickness: numberhead: numberThe running index of the consumer: the number of entries that were read. Its slot ishead % size.tail: numberThe running index of the producer: the number of entries that were written. Its slot istail % size.length: numberThe number of entries in the ring:tail - head. It is 0 for an empty ring, andsizefor a full ring.pending: numberThe number of entries that are written and not published.p(slot: number): [number, number]The middle of a slot in the coordinates of the ring, so that a film can fly an entry in from another place. The slot wraps:p(size)isp(0). A fraction gives a place between two slots.clone(): RingBufferA copy with its own entries and pointers, at rest in the state that the ring has now.push(entry?: RingBufferEntry, options?: AnimOptions): AnimThe producer writes at the tail. The slot at the tail fills, then the tail pointer moves one slot on. Withcounts, the number beside the name changes when the pointer arrives. Apendingentry is a dashed outline with no fill untilpublish(). WithtailFollows: 'publish', the tail pointer stays for a pending entry, and the mark of the writer moves.publish(options?: AnimOptions): AnimThe producer publishes what it wrote: every pending entry fills. With alag, the entries fill one after another, from the oldest. WithtailFollows: 'publish', the tail pointer moves over them.pop(options?: AnimOptions): AnimThe consumer reads at the head. The entry at the head empties, then the head pointer moves one slot on.enter(options?: AnimOptions): AnimThe band draws slot by slot, from slot 0. Then the pointers and the caption arrive.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): RingBuffertrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): RingBufferdrop(child: Node): RingBufferall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): RingBufferSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): RingBuffermoveTo(target: Node | Vec): RingBufferMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): RingBufferMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): RingBufferScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): RingBufferRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): RingBufferScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): RingBufferPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): RingBufferLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): RingBufferLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): RingBufferstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): RingBuffercolor(color: ColorInput): RingBufferRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): RingBufferglow(radius: number, color?: ColorInput | undefined): RingBufferSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): RingBufferzIndex(value: number): RingBufferpin(): RingBufferPin to the screen so the camera does not move this node.hide(): RingBuffershow(): RingBuffernamed(label: string): RingBufferlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
RingBufferEntryinterface
color?: ColorInput | undefinedDefaults to thecolorof the ring.label?: string | undefinedA short text in the slot, such as 'A' or 'read'.pending?: boolean | undefinedThe entry is written and not yet published: a dashed outline with no fill.publish()fills it.
RingBufferOptionsinterface
radius?: number | undefinedOuter radius. Defaults to 170.thickness?: number | undefinedRadial size of a slot. Defaults to 0.32 ofradius.color?: ColorInput | undefinedThe outline of the slots, the caption, and the color of an entry that has none. Defaults to blue.label?: string | undefinedA caption under the ring, such as 'submission queue'.captionGap?: number | undefinedThe distance from the band to the top of the capital letters of the caption. The default is the room of an arrow head and a name of 5 em under the band, and of the numbers when the ring hasnumbers. It follows fromradius,sizeandnumbers, and from no other option. So two rings that share these three have their captions on one line. Give two rings that differ onecaptionGap.size?: number | undefinedText size of the caption and the pointer names. Defaults to 26.labelSize?: number | undefinedText size of the label of an entry. A label that is too wide for its slot is set smaller, down to 0.6 of this size. Defaults to 0.28 ofthickness, and tosizeat the most. With the default thickness, a word of four letters such as 'read' then keeps the size, so short labels have one size.head?: string | null | undefinedThe name of the head pointer. Null draws no head pointer. Defaults to 'head'.tail?: string | null | undefinedThe name of the tail pointer. Null draws no tail pointer. Defaults to 'tail'.headColor?: ColorInput | undefinedThe color of the arrow head and the name of the head pointer. Defaults to thecolorof the ring.tailColor?: ColorInput | undefinedThe color of the arrow head and the name of the tail pointer. Defaults to thecolorof the ring.outside?: "head" | "tail" | undefinedWhich pointer is outside the ring. The other one is inside. Defaults to 'head'.tailFollows?: "push" | "publish" | undefinedWhen the tail pointer moves. With 'push', it moves at each push, also for a pending entry: it is the index that the producer keeps. With 'publish', it stays behind the pending entries untilpublish(): it is the index that the consumer reads. A dim arrow head with no name then shows where the producer writes. Defaults to 'push'.counts?: boolean | undefinedShow the running index beside each pointer name, such as 'tail 5'.numbers?: boolean | undefinedWrite the number of each slot beside it.font?: FontFamily | undefined
ringThroughfunction
A closed outline shrunk to a parametric function.
function ringThrough(f: (t: number) => Vec, samples?: number): Path
samplePathfunction
Evenly spaced points along a path, by arc length.
function samplePath(path: Path, count: number): Vec[]
scatterfunction
function scatter(points: readonly ScatterPoint[], options?: ScatterOptions): Scatter
Scatterclass
new Scatter(points
dots: Node[]axis: NodegroupLabels: Node[]p(x: number, y: number): [number, number]enter(options?: AnimOptions): AnimAxes draw, then points land from left to right.fit(f: (x: number) => number, options?: { color?: ColorInput | undefined; width?: number | undefined; samples?: number | undefined; }): NodeA straight or curved fit through the data, drawn on the same axes.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Scattertrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Scatterdrop(child: Node): Scatterall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): ScatterA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): ScatterSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): ScattermoveTo(target: Node | Vec): ScatterMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): ScatterMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): ScatterScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): ScatterRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): ScatterScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): ScatterPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): ScatterLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): ScatterLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Scatterstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Scattercolor(color: ColorInput): ScatterRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Scatterglow(radius: number, color?: ColorInput | undefined): ScatterSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): ScatterzIndex(value: number): Scatterpin(): ScatterPin to the screen so the camera does not move this node.hide(): Scattershow(): Scatternamed(label: string): ScatterlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
ScatterOptionsinterface
x?: readonly [number, number] | undefinedy?: readonly [number, number] | undefinedradius?: number | undefinedxLabel?: string | undefinedyLabel?: string | undefinedgroupLabels?: boolean | undefinedLabel each group near its center, instead of using a legend.width?: number | undefinedheight?: number | undefinedlabelSize?: number | undefinedText size for direct labels and ticks.color?: ColorInput | undefinedfont?: FontFamily | undefinedThe font of the labels and the ticks. Defaults to the font of the film. A value that the chart prints is in mono.
ScatterPointinterface
x: numbery: numbercolor?: ColorInput | undefinedradius?: number | undefinedgroup?: string | undefinedPoints with the same group share a color and one direct label.
SceneClockclass
The clock of one scene. Every time is in film seconds.
new SceneClock(stage
id: stringstart: numberend: numberbeats: readonly PlacedBeat[]cue(phrase: string, beat?: number | undefined): { start: number; end: number; }When the voice says a phrase. Pass a beat index to search one beat only. Throws if the phrase is not spoken.on(phrase: string, build: () => void, offset?: number, beat?: number | undefined): SceneClockBuild when the voice starts a phrase.offsetmoves it, in seconds.after(phrase: string, build: () => void, offset?: number, beat?: number | undefined): SceneClockBuild when the voice finishes a phrase.onBeat(index: number, build: () => void, offset?: number): SceneClockBuild when a beat starts.at(seconds: number, build: () => void): SceneClockBuild at a time counted from the start of the scene.clear(targets: readonly (Node | Value)[], lead?: number, duration?: number): SceneClockFade nodes out just before the scene ends, so the next picture starts on a clean frame. A value goes to zero in the same time, for adynamicdrawing that takes its opacity from one.
secantLinefunction
The line through two points on a curve. Pass functions to shrink h on screen.
function secantLine(axes: Axes, f: (x: number) => number, a: Reactive, b: Reactive, style?: SecantOptions): Node
SecantOptionsinterface
extend?: number | undefinedExtra length past each point, in stage pixels.points?: boolean | undefinedlegs?: boolean | undefinedDraw the run and rise legs of the slope triangle.legColor?: ColorInput | undefinedfill?: ColorInput | null | undefinedstroke?: ColorInput | null | undefinedwidth?: number | undefinedopacity?: number | undefinedglow?: number | undefineddash?: readonly number[] | undefinedz?: number | undefined
setAssetBasefunction
Tell the browser build where fonts live. The URL must end with a slash.
function setAssetBase(url: string): void
sparklinefunction
A word-sized line chart, with no axes.
function sparkline(values: readonly number[], options?: SparklineOptions): Node
SparklineOptionsinterface
width?: number | undefinedheight?: number | undefinedcolor?: ColorInput | undefinedweight?: number | undefineddotted?: boolean | undefinedMark the latest value.area?: number | undefined
spinfunction
Steady rotation for as long as it plays: radians per second, around the center or a point.
function spin(node: Node, rate: number, options?: AnimOptions & { about?: Vec | undefined; }): Animspotlightfunction
Put the light on one node or more: s.play(spotlight(row3)). The rest of the stage dims, and the targets keep their look. The light stays on. A second spotlight moves it, and spotlight(null) turns it off. The rest is every node that shows before the light starts and that is not a target, not in a target, and not an ancestor of a target. An ancestor keeps its look, and its other children dim. Pinned nodes and the nodes in keep stay bright as the targets do. A node that comes to the stage with the light, or later, is not dimmed. A dim node keeps its opacity. Its colors go toward the background of the film, so it still hides what is behind it, and a fade that the film makes while the light is on is still there when the light goes off. The verb only adds a light to the list of the stage. What is dim at a time comes from that list in time order, when the film is complete. So a film can name its lights, and bring its nodes, in any order.
function spotlight(target: Node | readonly Node[] | null, options?: SpotlightOptions): Anim
SpotlightOptionsinterface
dim?: number | undefinedThe part of its color that the rest keeps, from 0 to 1. At 0 the rest has the color of the background. Defaults to 0.3 on a dark look and to 0.4 on a light look, which leave dim text at the same contrast.within?: Node | undefinedDim only inside this node, such as one chart or one group. The rest of the stage does not change.keep?: Node | readonly Node[] | undefinedOne node or more that stay bright with the targets, such as a heading, a source line, or a backdrop.duration?: number | undefinedease?: Ease | undefinedlag?: number | undefinedFraction of one item's time between the starts of consecutive items.
stateGridfunction
A grid of cells in named states: stateGrid({ columns: 16, rows: 8, initial: 'absent' }), then grid.change([3, 4, 5], 'present').
function stateGrid(options: StateGridOptions): StateGrid
StateGridclass
A grid of cells, each in a named state: pages of memory, blocks of a disk, the bits of a bitmap, the hosts of a fleet. The state sets the look of the cell. change() moves cells to another state as a sweep, and count(), stateOf() and indexes() read the states, so a number beside the grid can follow it. enter() brings the cells in row by row, and legend() makes the key. .at() places the middle of the grid.
new StateGrid(options
cells: Node[]One rounded square per cell, row by row. The cell of a row and a column has the indexrow * columns + column.columns: numberrows: numbersize: numberThe number of cells.p(index: number): [number, number]The middle of a cell, in the coordinates of the grid.stateOf(index: number): stringThe state of a cell, after the changes that are scheduled so far.count(state: string): numberThe number of cells in a state, after the changes that are scheduled so far.indexes(state: string): number[]The cells in a state, by index, after the changes that are scheduled so far.enter(options?: AnimOptions): AnimThe cells arrive row by row, from the top.change(which: number | readonly number[] | ((index: number, state: string) => boolean), state: string, options?: AnimOptions): AnimCells go to another state.whichis an index, a list of indexes, or a function of the index and the state of a cell, which the grid calls when the change is scheduled. So a second change in one sequence sees the states that the first one left. The cells change in index order, one after another, and the whole change takesduration. A cell that already has the state does not change and takes no time.legend(items?: readonly (string | readonly [state: string, label: string])[] | undefined, options?: GridLegendOptions): NodeA key for the states, in one row: a sample cell and a name for each. It is a new node, and not a child of the grid..at(x, y)places the left end of the row, andyis the middle line of the cells and the names.itemslists the states to show, each as a name or as a[state, label]pair. Withoutitems, the key shows the states that the grid has now, in the order ofstates.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): StateGridtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): StateGriddrop(child: Node): StateGridall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): StateGridA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): StateGridSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): StateGridmoveTo(target: Node | Vec): StateGridMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): StateGridMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): StateGridScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): StateGridRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): StateGridScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): StateGridPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): StateGridLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): StateGridLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): StateGridstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): StateGridcolor(color: ColorInput): StateGridRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): StateGridglow(radius: number, color?: ColorInput | undefined): StateGridSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): StateGridzIndex(value: number): StateGridpin(): StateGridPin to the screen so the camera does not move this node.hide(): StateGridshow(): StateGridnamed(label: string): StateGridlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
StateGridOptionsinterface
columns: numberrows: numbercell?: number | undefinedSide of one cell. Defaults to 28.gap?: number | undefinedRoom between two cells. Defaults to 0.18 ofcell, at least 3.states?: Record<string, GridState> | undefinedThe states and their looks. Defaults to PAGE_STATES.initial?: string | readonly string[] | ((index: number) => string) | undefinedThe state of each cell at the start: one name, one name per cell, or a function of the index. Defaults to the first state.
Stepinterface
label: stringThe name of the step, on one line.note?: string | undefinedA short text under the box, such as the price of the step. It has one line, or two with a line break.
StepEdgetype
type StepEdge = "left" | "right" | "top" | "bottom"
stepsfunction
A row of named steps with one of them lit: steps(['batching', 'SQPOLL', 'register', 'I/O polling'], { width: 1800, numbered: true, current: 0 }). strip.go(1) moves the light.
function steps(list: readonly (string | Step)[], options?: StepsOptions): Steps
Stepsclass
A row of named steps with one of them lit: the chapters of an argument, the stages of a process, or a list of options that the film goes through one by one. The steps before the lit one are dim, and the steps after it are weaker, and still readable. go(index) moves the light. .at() places the middle of the boxes. The notes are outside them: under the row, or between the boxes of a column.
new Steps(steps
items: Node[]One node per step, which holds its box, its label and its note.boxes: Node[]labels: TextNode[]notes: (TextNode | null)[]The note of each step, or null for a step that has none.current: number | nullThe index of the lit step after the changes that are scheduled so far, or null when no step is lit.stateOf(index: number): StepStateThe state of a step after the changes that are scheduled so far. With no lit step, every step is'done': dim, and readable.p(index: number, edge?: StepEdge | undefined): [number, number]A point on an edge of the box of a step, in the coordinates of the object, for a connector to the picture of the step. Without an edge: the bottom in a row, and the right side in a column. The bottom point of a step that has a note is under the note, with clear room under the tails of its letters, so a line from it does not touch the note.enter(options?: AnimOptions): AnimThe steps arrive one after another, from the first to the last, each one with the look of its state.go(index: number | null, options?: AnimOptions): AnimThe light moves to another step, or goes off with null. Each step whose state changes takes its new look: the lit one, a done one before it, or one that is ahead after it. A strip that is not on the stage comes to it at the start of the move, in the state before the move.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Stepstrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Stepsdrop(child: Node): Stepsall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): StepsA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): StepsSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): StepsmoveTo(target: Node | Vec): StepsMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): StepsMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): StepsScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): StepsRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): StepsScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): StepsPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): StepsLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): StepsLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Stepsstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Stepscolor(color: ColorInput): StepsRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Stepsglow(radius: number, color?: ColorInput | undefined): StepsSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): StepszIndex(value: number): Stepspin(): StepsPin to the screen so the camera does not move this node.hide(): Stepsshow(): Stepsnamed(label: string): StepslabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
StepsOptionsinterface
width?: number | undefinedIn a row: the width of the whole row. In a column: the width of each box. Defaults to 1600 for a row and to 360 for a column.height?: number | undefinedThe height of one box. Defaults to 2.2 ofsize. A height with no room for a letter with a tail is an error.gap?: number | undefinedThe room between two boxes. Defaults to 0.6 ofsize.size?: number | undefinedText size of the labels. A label that is too wide for its box makes all labels smaller, down to 0.7 of this size. Defaults to 28.noteSize?: number | undefinedText size of the notes. Defaults to 0.9 ofsize.numbered?: boolean | undefinedWrite the number of each step before its label: "1 batching", "2 SQPOLL".current?: number | null | undefinedThe index of the lit step, or null for none. Defaults to null.direction?: "row" | "column" | undefined'row'puts the boxes side by side.'column'puts them one under the other, with the labels at the left. Defaults to row.color?: ColorInput | undefinedThe color of the border of the lit step. Defaults to ink.font?: FontFamily | undefined
StepStatetype
Where a step is: the lit one, one before it, or one after it.
type StepState = "current" | "done" | "ahead"
streamLinesfunction
function streamLines(f: FieldFn, options?: StreamLinesOptions): StreamLines
StreamLinesclass
Flow made visible: lines are integrated once, then light runs along them. Drawing is batched by color and brightness, so hundreds of lines stay live.
new StreamLines(f
lineCount: numberid: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): StreamLinestrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): StreamLinesdrop(child: Node): StreamLinesall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): StreamLinesA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): StreamLinesSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): StreamLinesmoveTo(target: Node | Vec): StreamLinesMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): StreamLinesMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): StreamLinesScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): StreamLinesRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): StreamLinesScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): StreamLinesPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): StreamLinesLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): StreamLinesLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): StreamLinesstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): StreamLinescolor(color: ColorInput): StreamLinesRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): StreamLinesglow(radius: number, color?: ColorInput | undefined): StreamLinesSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): StreamLineszIndex(value: number): StreamLinespin(): StreamLinesPin to the screen so the camera does not move this node.hide(): StreamLinesshow(): StreamLinesnamed(label: string): StreamLineslabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
StreamLinesOptionsinterface
x?: readonly [number, number] | undefinedy?: readonly [number, number] | undefinedunit?: number | undefinedspacing?: number | undefinedMath units between seed points. Smaller is denser.dt?: number | undefinedIntegration step and number of steps for each line.steps?: number | undefinedcolors?: readonly ColorInput[] | undefinedmax?: number | undefinedwidth?: number | undefinedspeed?: number | undefinedPasses per second along a full line.tail?: number | undefinedLit fraction of each line.seed?: number | undefinedtrace?: number | undefinedShow the whole lines faintly underneath the moving light.
surroundingBoxfunction
A rounded box around a node or nodes, measured now.
function surroundingBox(target: Node | readonly Node[], options?: SurroundOptions): Node
SurroundOptionsinterface
pad?: number | undefinedradius?: number | undefinedcolor?: ColorInput | undefinedwidth?: number | undefinedfill?: ColorInput | undefinedfillOpacity?: number | undefinedglow?: number | undefined
SvgNodeclass
An imported picture. It is a regular node tree, so every verb works on it.
new SvgNode(path?
byId: Map<string, Node>pictureWidth: numberpictureHeight: numberfind(id: string): NodeFind an element by its SVG id.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): SvgNodetrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): SvgNodedrop(child: Node): SvgNodeall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): SvgNodeA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): SvgNodeSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): SvgNodemoveTo(target: Node | Vec): SvgNodeMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): SvgNodeMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): SvgNodeScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): SvgNodeRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): SvgNodeScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): SvgNodePlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): SvgNodeLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): SvgNodeLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): SvgNodestroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): SvgNodecolor(color: ColorInput): SvgNodeRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): SvgNodeglow(radius: number, color?: ColorInput | undefined): SvgNodeSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): SvgNodezIndex(value: number): SvgNodepin(): SvgNodePin to the screen so the camera does not move this node.hide(): SvgNodeshow(): SvgNodenamed(label: string): SvgNodelabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
swapfunction
Two nodes trade places along opposite arcs.
function swap(a: Node, b: Node, options?: AnimOptions & { lift?: number | undefined; }): Animtagfunction
A word in a pill with a note, such as tag('MEASURED', { note: 'the study of June 2026', color: 'green' }).at(-912, -490).
function tag(word: string, options?: TagOptions): Tag
Tagclass
A word in a pill, with a note beside it: the kind of claim that is on the screen, and where it comes from. .at(x, y) places the left end of the pill, so tags with words of different lengths start at one x.
new Tag(word
pill: Nodeword: Nodenote: Node | nullcolor(color: ColorInput): TagRecolor the border of the pill and the word. The fill of the pill and the note keep their colors.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Tagtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Tagdrop(child: Node): Tagall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): TagA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): TagSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): TagmoveTo(target: Node | Vec): TagMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): TagMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): TagScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): TagRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): TagScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): TagPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): TagLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): TagLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Tagstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Tagopacity(value: number): Tagglow(radius: number, color?: ColorInput | undefined): TagSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): TagzIndex(value: number): Tagpin(): TagPin to the screen so the camera does not move this node.hide(): Tagshow(): Tagnamed(label: string): TaglabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
TagOptionsinterface
note?: string | undefinedPlain text beside the pill, such as the source of a claim and its date. It stays on one line.color?: ColorInput | undefinedThe color of the pill and its word. Defaults to dim.noteColor?: ColorInput | undefinedThe color of the note. Defaults to dim.size?: number | undefinedFont size of the word and the note. Defaults to 26.font?: FontFamily | undefinedThe font of the word. Defaults to mono.noteFont?: FontFamily | undefinedThe font of the note. Defaults to the font oftext().fill?: ColorInput | null | undefinedThe fill of the pill. Pass null for none. Defaults to the surface color.
TagStateinterface
at: numberWhen this state starts, in seconds of the film.word: stringnote?: string | undefinedPlain text beside the pill, such as the source of a claim and its date. It stays on one line.color?: ColorInput | undefinedThe color of the pill and its word. Defaults to dim.noteColor?: ColorInput | undefinedThe color of the note. Defaults to dim.size?: number | undefinedFont size of the word and the note. Defaults to 26.font?: FontFamily | undefinedThe font of the word. Defaults to mono.noteFont?: FontFamily | undefinedThe font of the note. Defaults to the font oftext().fill?: ColorInput | null | undefinedThe fill of the pill. Pass null for none. Defaults to the surface color.
tagTrackfunction
A tag that changes with time, at one place. It takes the states in order. One state is on screen at a time: a state fades out before the next one fades in. States that start out of order, or less than the two fades apart, are an error, so no moment shows two tags. Place it as any node: tagTrack(states, { until }).at(-912, -490) is the top left of a 1920 by 1080 frame. It is pinned, so a move or a zoom of the camera does not move it.
function tagTrack(states: readonly TagState[], options: TagTrackOptions): Node
TagTrackOptionsinterface
until: numberWhen the last state leaves, in seconds of the film.fadeIn?: number | undefinedSeconds to fade a state in and out. Defaults to 0.25 and 0.18.fadeOut?: number | undefinednote?: string | undefinedPlain text beside the pill, such as the source of a claim and its date. It stays on one line.color?: ColorInput | undefinedThe color of the pill and its word. Defaults to dim.noteColor?: ColorInput | undefinedThe color of the note. Defaults to dim.size?: number | undefinedFont size of the word and the note. Defaults to 26.font?: FontFamily | undefinedThe font of the word. Defaults to mono.noteFont?: FontFamily | undefinedThe font of the note. Defaults to the font oftext().fill?: ColorInput | null | undefinedThe fill of the pill. Pass null for none. Defaults to the surface color.
tangentLinefunction
The tangent at x, as a line on the axes. Pass a function of time to make it slide.
function tangentLine(axes: Axes, f: (x: number) => number, x: Reactive, style?: TangentOptions): Node
TangentOptionsinterface
length?: number | undefinedLength of the drawn segment, in stage pixels.point?: boolean | undefinedMark the point of tangency.fill?: ColorInput | null | undefinedstroke?: ColorInput | null | undefinedwidth?: number | undefinedopacity?: number | undefinedglow?: number | undefineddash?: readonly number[] | undefinedz?: number | undefined
terminalfunction
The output of a program in a panel, with the exact characters: terminal([{ text: './one-byte', kind: 'command' }, 'minor faults: 1'], { title: 'runs/01-one-byte.txt', note: 'Linux 7.0, arm64, 2026-11-08' }).
function terminal(lines: readonly (string | TerminalLine)[], options?: TerminalOptions): Terminal
Terminalclass
The output of a real program, as a terminal shows it: a panel with a bar that says where and when it ran, a command after its prompt, and the lines that the program printed, with the exact characters. It shows all its lines, unless it starts with shown: false. enter() brings the empty panel, type() types a command, and print() brings the next block of lines, up to the next command. find() selects one number, to color it or to point at it. .at() places the middle of the panel.
new Terminal(lines
frame: NodeThe panel: its fill and its border.bar: NodeThe band at the top, behind the title and the note.title: Node | nullnote: Node | nulllines: Node[]One node per line. The node of a command holds its prompt.prompts: (Node | null)[]The prompt of each command line, or null for another line.find(text: string, occurrence?: number): NodeOne part of the text of a line, as one node: to color a number, toindicateit, or to point a callout at it. It matches the exact characters, spaces included, in the commands, the output and the notes, from the first line to the last.occurrencecounts the matches from 0.codeBlock.findis different: it returns the glyphs of every match as a list, spaces do not count there, and its second argument is a line.p(line: number, side?: "left" | "right"): [number, number]The start or the end of a line, in the coordinates of the panel, at the middle height of its letters. The start of a command is the start of its prompt. Defaults to the start.clone(): TerminalA copy with its own record of the lines that are shown.enter(options?: AnimOptions): AnimThe panel and the bar arrive, with no line.type(),print()andshowLines()bring the lines. A line that one of them shows in the sameplaystays, whatever the order of the two.type(line: number, options?: AnimOptions): AnimA command arrives character by character, at a steady rate, after its prompt. Lines count from 0. A command that is already shown is an error: start the terminal withshown: false, or playenter().print(options?: AnimOptions & { from?: number | undefined; to?: number | undefined; }): AnimLines arrive one after another, in order, each at once, as a terminal prints them. Withoutfromandto, it prints the next block: from the first line that is not yet shown, to the line before the next command that is not yet shown. A command at the start of the block arrives whole. Sotype()thenprint()types a command and prints its output,print()alone shows the command and its output, and a secondprint()goes on with the next block. With a range, such as{ from: 1, to: 3 }for lines 1, 2 and 3, it prints every line of the range that is not yet shown.showLines(options?: AnimOptions): AnimEvery line that is not yet shown fades in, all together: for a film that does not want the typing.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Terminaltrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Terminaldrop(child: Node): Terminalall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): TerminalSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): TerminalmoveTo(target: Node | Vec): TerminalMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): TerminalMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): TerminalScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): TerminalRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): TerminalScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): TerminalPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): TerminalLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): TerminalLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Terminalstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Terminalcolor(color: ColorInput): TerminalRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Terminalglow(radius: number, color?: ColorInput | undefined): TerminalSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): TerminalzIndex(value: number): Terminalpin(): TerminalPin to the screen so the camera does not move this node.hide(): Terminalshow(): Terminalnamed(label: string): TerminallabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
TerminalLineinterface
text: stringkind?: "command" | "output" | "note" | undefined'command' draws the prompt before the text. 'output' is what the program printed. 'note' is a dim line that the film adds, such as '[...]' for lines that it left out. Defaults to output.color?: ColorInput | undefinedThe color of the text. Defaults to ink, and to dim for a note.
TerminalOptionsinterface
title?: string | undefinedText in the bar at the top, at the left: a file path or a host name.note?: string | undefinedDim text in the bar, at the right: where and when it ran, such as 'Linux 7.0, arm64, 2026-11-08'.columns?: number | undefinedCharacters in one line. Defaults to the longest line, or to more when the title and the note need more room. A longer line is an error, or is cut with an ellipsis whenclipis true. The spaces at the end of a line do not count: they are not drawn.clip?: boolean | undefinedshown?: boolean | undefinedStart with every line on screen. Defaults to true, for a picture that does not move. Give false to a terminal that a film types and prints, so that its panel starts empty wherever the film adds it.size?: number | undefinedText size. Defaults to 26.prompt?: string | undefinedDrawn before a command. Defaults to '$ '.color?: ColorInput | undefinedThe border and the bar. Defaults to grid.
tokenizefunction
A small tokenizer for film code. It colors the common cases and never blocks on odd input.
function tokenize(source: string, language?: string): Omit<CodeToken, "glyphs">[]
TokenKindtype
type TokenKind = "string" | "number" | "function" | "keyword" | "comment" | "type" | "punctuation" | "plain" | "space"
TokenRowclass
A sequence of token chips, as in a tokenizer's output. Small type by default.
new TokenRow(items
chips: Node[]token(index: number): Nodeenter(options?: AnimOptions): AnimChips arrive from left to right.arcs(from: number, weights: readonly number[], options?: { color?: ColorInput | undefined; maxWidth?: number | undefined; lift?: number | undefined; }): NodeCurved links from one token to others, brighter for stronger weights. Add the result to the stage after this row is in place.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): TokenRowtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): TokenRowdrop(child: Node): TokenRowall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): TokenRowA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): TokenRowSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): TokenRowmoveTo(target: Node | Vec): TokenRowMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): TokenRowMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): TokenRowScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): TokenRowRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): TokenRowScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): TokenRowPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): TokenRowLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): TokenRowLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): TokenRowstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): TokenRowcolor(color: ColorInput): TokenRowRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): TokenRowglow(radius: number, color?: ColorInput | undefined): TokenRowSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): TokenRowzIndex(value: number): TokenRowpin(): TokenRowPin to the screen so the camera does not move this node.hide(): TokenRowshow(): TokenRownamed(label: string): TokenRowlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
tokensfunction
function tokens(items: readonly (string | TokenSpec)[], options?: TokensOptions): TokenRow
TokensOptionsinterface
size?: number | undefinedmaxWidth?: number | undefinedWrap to this width.gap?: number | undefinedcolor?: ColorInput | undefinedcolors?: readonly ColorInput[] | undefinedRamp of colors across the row.font?: FontFamily | undefinedThe font of the tokens. Defaults to mono. The line under a chip is in mono.subSize?: number | undefinedradius?: number | undefined
TokenSpecinterface
text: stringsub?: string | undefinedSmall line under the chip, such as an id or a probability.color?: ColorInput | undefined
tracedfunction
The path a moving point has taken. It is computed by re-reading the point at earlier times, so it scrubs correctly in both directions. The point function should read Values or tracked node properties, not update overlays.
function traced(point: () => Vec, from: number, to: number, options?: TracedOptions): Node
TracedOptionsinterface
color?: ColorInput | undefinedwidth?: number | undefinedtail?: number | undefinedSeconds of history to keep. Older parts of the trail are gone.fade?: number | undefinedBrightness at the old end, from 0 to 1. Use 1 for a solid trail.rate?: number | undefinedSamples per second along the trail.glow?: number | undefined
treefunction
function tree(root: TreeSpec, options?: TreeOptions): TreeGraph
TreeGraphclass
A tidy tree: parents center over children, and subtrees never overlap.
new TreeGraph(root
depths: Map<string, number>enter(options?: AnimOptions): AnimGrow from the root, one level at a time.nodes: Map<string, GraphNode>edges: GraphEdge[]edgeLayer: NodenodeLayer: Nodenode(id: string): GraphNodeedge(from: string, to: string): GraphEdgepulseEdge(from: string, to: string, options?: AnimOptions & { color?: ColorInput | undefined; length?: number | undefined; }): AnimA lit signal travels along an edge.visit(id: string, options?: AnimOptions & { color?: ColorInput | undefined; }): AnimMark a node as reached: its color changes and it swells for a moment.moveNode(id: string, to: Vec, options?: AnimOptions): AnimMove a node. Its edges follow, re-routed as straight lines.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): TreeGraphtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): TreeGraphdrop(child: Node): TreeGraphall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): TreeGraphA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): TreeGraphSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): TreeGraphmoveTo(target: Node | Vec): TreeGraphMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): TreeGraphMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): TreeGraphScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): TreeGraphRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): TreeGraphScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): TreeGraphPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): TreeGraphLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): TreeGraphLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): TreeGraphstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): TreeGraphcolor(color: ColorInput): TreeGraphRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): TreeGraphglow(radius: number, color?: ColorInput | undefined): TreeGraphSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): TreeGraphzIndex(value: number): TreeGraphpin(): TreeGraphPin to the screen so the camera does not move this node.hide(): TreeGraphshow(): TreeGraphnamed(label: string): TreeGraphlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
TreeOptionsinterface
levelGap?: number | undefinedsiblingGap?: number | undefineddirection?: "right" | "down" | undefinedfillOpacity?: number | undefinedFill nodes softly with their color instead of leaving them hollow.font?: FontFamily | undefinedThe font of the node labels. Defaults to the font of the film.radius?: number | undefinedlabelSize?: number | undefinednodeColor?: ColorInput | undefinededgeColor?: ColorInput | undefined
TreeSpecinterface
id?: string | undefinedlabel?: string | undefinedcolor?: ColorInput | undefinedchildren?: readonly TreeSpec[] | undefinedmath?: boolean | undefined
typewriterfunction
Characters appear one at a time, at a steady rate, with an optional cursor.
function typewriter(target: Target, options?: TypewriterOptions): Anim
TypewriterOptionsinterface
cursor?: boolean | undefinedShow a block cursor at the writing position.cursorColor?: ColorInput | undefinedduration?: number | undefinedease?: Ease | undefinedlag?: number | undefinedFraction of one item's time between the starts of consecutive items.
UnitArrangementinterface
rows?: number | undefinedMarks in one column. The chart fills column by column, so the width of a group shows its size.columns?: number | undefinedMarks in one row. The chart fills row by row. Giverowsorcolumns, not both.split?: boolean | undefinedDraw each group as its own block, with its label over it. Without it, the groups follow each other in one grid.mixed?: boolean | undefinedPut the marks of all groups at mixed places in one grid, to show a few among many.seed?: number | undefinedA whole number. The same seed gives the same mixed places. Defaults to 1.
unitChartfunction
One mark for each unit, in groups: unitChart([{ count: 262 }, { count: 39, color: 'green', label: '39 better' }], { rows: 11, split: true }).
function unitChart(groups: readonly UnitGroup[], options?: UnitChartOptions): UnitChart
UnitChartclass
One mark for each unit: a count that the viewer can see, not only read. Groups share one grid, or stand as blocks side by side, or sit at mixed places to show a few among many. enter() brings the marks in line by line, paint() gives the groups their colors, and rearrange() moves the marks to another arrangement. .at() places the middle of the grid, or of the row of blocks. The labels are over the blocks.
new UnitChart(groups
marks: Node[]Every mark, group after group.labels: (Node | null)[]The label of each group, or null for a group that has none. A label is part of the chart from the first time that the groups are split. Before that it is not drawn, and not a child of the chart.total: numberThe number of marks.groups: Node[][]The marks of each group, in the order of the groups.p(index: number): [number, number]The place of a mark in the chart, by its index inmarks, in the arrangement that the chart has now.clone(): UnitChartA copy with its own marks and labels, in the arrangement that the chart has now.enter(options?: AnimOptions): AnimThe marks arrive line by line, in the order that the chart fills. The label of a block arrives with its first line.paint(options?: AnimOptions): AnimEach mark and each label takes the color of its group. Use it on a chart that startsplain.rearrange(to: UnitArrangement, options?: AnimOptions): AnimThe marks move to another arrangement, such as{ split: true }to gather mixed marks into blocks. The new arrangement keeps the rows or columns and the seed that it does not name.splitandmixedturn each other off, androwsreplacescolumns. Labels leave before the move and arrive after it. Each group moves over the whole time, so a small group does not wait for a large one.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): UnitCharttrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): UnitChartdrop(child: Node): UnitChartall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): UnitChartSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): UnitChartmoveTo(target: Node | Vec): UnitChartMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): UnitChartMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): UnitChartScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): UnitChartRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): UnitChartScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): UnitChartPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): UnitChartLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): UnitChartLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): UnitChartstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): UnitChartcolor(color: ColorInput): UnitChartRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): UnitChartglow(radius: number, color?: ColorInput | undefined): UnitChartSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): UnitChartzIndex(value: number): UnitChartpin(): UnitChartPin to the screen so the camera does not move this node.hide(): UnitChartshow(): UnitChartnamed(label: string): UnitChartlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
UnitChartOptionsinterface
cell?: number | undefinedSide or diameter of one mark. Defaults to 16.gap?: number | undefinedRoom between two marks. Defaults to 0.6 ofcell.blockGap?: number | undefinedRoom between two blocks while the groups are split. Defaults to 2.5 marks with their gaps.shape?: "square" | "dot" | undefinedDefaults to dot.plain?: boolean | undefinedStart with every mark and label inplainColor.chart.paint()then gives each group its color.plainColor?: ColorInput | undefinedThe color beforepaint(), for a chart that startsplain. Defaults to dim.labelSize?: number | undefinedText size of the labels. Defaults to 28.font?: FontFamily | undefinedrows?: number | undefinedMarks in one column. The chart fills column by column, so the width of a group shows its size.columns?: number | undefinedMarks in one row. The chart fills row by row. Giverowsorcolumns, not both.split?: boolean | undefinedDraw each group as its own block, with its label over it. Without it, the groups follow each other in one grid.mixed?: boolean | undefinedPut the marks of all groups at mixed places in one grid, to show a few among many.seed?: number | undefinedA whole number. The same seed gives the same mixed places. Defaults to 1.
UnitGroupinterface
count: numberHow many marks the group has. A whole number, 0 or more.color?: ColorInput | undefinedDefaults to dim.label?: string | undefinedText over the block of the group. It shows only while the groups are split.
vectorFieldfunction
function vectorField(f: FieldFn, options?: VectorFieldOptions): VectorField
VectorFieldclass
A grid of arrows that shows a field's direction and strength. y points up in field space.
new VectorField(f
unit: numberarrows: Node[]f: FieldFnid: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): VectorFieldtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): VectorFielddrop(child: Node): VectorFieldall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): VectorFieldA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): VectorFieldSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): VectorFieldmoveTo(target: Node | Vec): VectorFieldMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): VectorFieldMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): VectorFieldScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): VectorFieldRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): VectorFieldScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): VectorFieldPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): VectorFieldLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): VectorFieldLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): VectorFieldstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): VectorFieldcolor(color: ColorInput): VectorFieldRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): VectorFieldglow(radius: number, color?: ColorInput | undefined): VectorFieldSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): VectorFieldzIndex(value: number): VectorFieldpin(): VectorFieldPin to the screen so the camera does not move this node.hide(): VectorFieldshow(): VectorFieldnamed(label: string): VectorFieldlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
VectorFieldOptionsinterface
x?: readonly [number, number] | undefinedy?: readonly [number, number] | undefinedstep?: number | undefinedMath units between arrows.unit?: number | undefinedStage pixels per math unit.maxLength?: number | undefinedLongest arrow, in stage pixels.max?: number | undefinedMagnitude that reaches the last color.colors?: readonly ColorInput[] | undefinedwidth?: number | undefinedThickness of the shaft.animate?: boolean | undefinedRecompute from f(x, y, t) on every frame.
VideoCodectype
type VideoCodec = "h264" | "vp9"
wipefunction
Reveal marks in the order a moving edge reaches them. Each mark fades up as the edge passes, so a dense diagram opens like a sweep across the page.
function wipe(target: Target, options?: WipeOptions): Anim
WipeOptionsinterface
from?: "left" | "right" | "top" | "bottom" | undefinedEdge the reveal starts from.soft?: number | undefinedShare of the time each mark takes to fade up as the edge passes.travel?: number | undefinedSlide each mark this far as it arrives.duration?: number | undefinedease?: Ease | undefinedlag?: number | undefinedFraction of one item's time between the starts of consecutive items.
zonefunction
A named border around nodes, such as zone([vmm, guest], { title: 'jail', dash: true }). .at(x, y) places its middle.
function zone(content: Node | readonly Node[], options?: ZoneOptions): Zone
Zoneclass
A border around a group of nodes, with a name on it: a host, a network, a sandbox, a boundary of trust. The zone measures its content when it is built and takes the content as its children, so the zone and what it holds move, fade, and scale as one node. A zone can hold other zones.
new Zone(content
frame: NodeThe border. With a title it is an open line, sodraw(zone.frame)traces it from one side of the title to the other.backdrop: Node | nullThe fill inside the border, or null when the zone has none.title: Node | nullThe title on the border, or null when the zone has none.content: readonly Node[]The nodes inside the border, in the order given.box: BoundsThe middle line of the border, in the coordinates of the zone, where (0, 0) is the middle of the border.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Zonetrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Zonedrop(child: Node): Zoneall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): ZoneA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): ZoneSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): ZonemoveTo(target: Node | Vec): ZoneMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): ZoneMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): ZoneScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): ZoneRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): ZoneScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): ZonePlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): ZoneLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): ZoneLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Zonestroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Zonecolor(color: ColorInput): ZoneRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Zoneglow(radius: number, color?: ColorInput | undefined): ZoneSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): ZonezIndex(value: number): Zonepin(): ZonePin to the screen so the camera does not move this node.hide(): Zoneshow(): Zonenamed(label: string): ZonelabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
ZoneOptionsinterface
title?: string | undefinedThe name of the zone. It sits in a gap of the top border.titleAlign?: "left" | "right" | "center" | undefinedWhere the title sits along the top border. Defaults to left.titleSize?: number | undefinedFont size of the title. Defaults to 26.padding?: number | undefinedSpace between the content and the border. Defaults to 32.color?: ColorInput | undefinedThe color of the border and the title. Defaults to dim.dash?: boolean | readonly number[] | undefinedA dashed border, for a boundary that is a rule and not a wall. Pass true for the default pattern.fill?: ColorInput | null | undefinedThe fill inside the border. Defaults to none. Its top edge runs behind the title, so keep it faint.fillOpacity?: number | undefinedOpacity of the fill. Defaults to 0.12, so that the content stays the strongest thing in the zone.radius?: number | undefinedborderWidth?: number | undefinedWidth of the border. Defaults to 2.5.font?: FontFamily | undefinedminWidth?: number | undefinedGive the zone this size when the content is smaller, so that zones of a row have one size.minHeight?: number | undefined
3D
axes3dfunction
function axes3d(options?: Axes3DOptions): Axes3D
Axes3Dclass
Three axes with ticks and tips.
new Axes3D(options?
emit(view: View3D): voidlabelsNow(view: View3D): Node[]Screen-facing labels, drawn on top.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Axes3Dtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Axes3Ddrop(child: Node): Axes3Dall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): Axes3DA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): Axes3DSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): Axes3DmoveTo(target: Node | Vec): Axes3DMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): Axes3DMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): Axes3DScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): Axes3DRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): Axes3DScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): Axes3DPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): Axes3DLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): Axes3DLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Axes3Dstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Axes3Dcolor(color: ColorInput): Axes3DRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Axes3Dglow(radius: number, color?: ColorInput | undefined): Axes3DSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): Axes3DzIndex(value: number): Axes3Dpin(): Axes3DPin to the screen so the camera does not move this node.hide(): Axes3Dshow(): Axes3Dnamed(label: string): Axes3DlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
Axes3DOptionsinterface
x?: readonly [number, number] | undefinedy?: readonly [number, number] | undefinedz?: readonly [number, number] | undefinedstep?: number | undefinedcolor?: ColorInput | undefinedwidth?: number | undefinedlabels?: boolean | readonly [string, string, string] | undefinedLabels at the axis tips. Pass false to leave them off.labelSize?: number | undefinednumbers?: boolean | undefinedfont?: FontFamily | undefinedThe font of the numbers. Defaults to the font of the film. The names of the axes are math.ticks?: boolean | undefinedbias?: number | undefinedPull toward the camera, so axes lying in a surface do not break into dashes.onTop?: boolean | undefined
curve3dfunction
function curve3d(f: (t: number) => Vec3, from: number, to: number, style?: Line3DStyle & { samples?: number | undefined; }): Curve3DCurve3Dclass
A curve in space, cut into pieces so it passes correctly in front of and behind other marks.
new Curve3D(f: (t
emit(view: View3D): voidlabelsNow(_view: View3D): Node[]Screen-facing labels, drawn on top.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Curve3Dtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Curve3Ddrop(child: Node): Curve3Dall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): Curve3DA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): Curve3DSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): Curve3DmoveTo(target: Node | Vec): Curve3DMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): Curve3DMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): Curve3DScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): Curve3DRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): Curve3DScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): Curve3DPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): Curve3DLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): Curve3DLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Curve3Dstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Curve3Dcolor(color: ColorInput): Curve3DRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Curve3Dglow(radius: number, color?: ColorInput | undefined): Curve3DSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): Curve3DzIndex(value: number): Curve3Dpin(): Curve3DPin to the screen so the camera does not move this node.hide(): Curve3Dshow(): Curve3Dnamed(label: string): Curve3DlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
gridPlane3dfunction
function gridPlane3d(options?: GridPlaneOptions): GridPlane3D
GridPlane3Dclass
A hairline grid on a horizontal plane, to ground a scene.
new GridPlane3D(options?
emit(view: View3D): voidlabelsNow(_view: View3D): Node[]Screen-facing labels, drawn on top.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): GridPlane3Dtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): GridPlane3Ddrop(child: Node): GridPlane3Dall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): GridPlane3DA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): GridPlane3DSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): GridPlane3DmoveTo(target: Node | Vec): GridPlane3DMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): GridPlane3DMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): GridPlane3DScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): GridPlane3DRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): GridPlane3DScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): GridPlane3DPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): GridPlane3DLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): GridPlane3DLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): GridPlane3Dstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): GridPlane3Dcolor(color: ColorInput): GridPlane3DRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): GridPlane3Dglow(radius: number, color?: ColorInput | undefined): GridPlane3DSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): GridPlane3DzIndex(value: number): GridPlane3Dpin(): GridPlane3DPin to the screen so the camera does not move this node.hide(): GridPlane3Dshow(): GridPlane3Dnamed(label: string): GridPlane3DlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
GridPlaneOptionsinterface
x?: readonly [number, number] | undefinedy?: readonly [number, number] | undefinedz?: number | undefinedstep?: number | undefinedcolor?: ColorInput | undefinedwidth?: number | undefinedopacity?: number | undefined
label3dfunction
function label3d(content: string | Node, position: Reactive3, options?: Label3DOptions): Label3D
Label3Dclass
A label pinned to a point in space. It always faces the camera.
new Label3D(content
emit(): voidlabelsNow(view: View3D): Node[]Screen-facing labels, drawn on top.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Label3Dtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Label3Ddrop(child: Node): Label3Dall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): Label3DA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): Label3DSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): Label3DmoveTo(target: Node | Vec): Label3DMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): Label3DMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): Label3DScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): Label3DRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): Label3DScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): Label3DPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): Label3DLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): Label3DLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Label3Dstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Label3Dcolor(color: ColorInput): Label3DRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Label3Dglow(radius: number, color?: ColorInput | undefined): Label3DSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): Label3DzIndex(value: number): Label3Dpin(): Label3DPin to the screen so the camera does not move this node.hide(): Label3Dshow(): Label3Dnamed(label: string): Label3DlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
Label3DOptionsinterface
size?: number | undefinedcolor?: ColorInput | undefinedmath?: boolean | undefinedA label is math by default. Pass false for plain text.font?: FontFamily | undefinedThe font of a plain label. Defaults to the font of the film.offset?: Vec | undefined
line3dfunction
function line3d(a: Reactive3, b: Reactive3, style?: Line3DStyle & { pieces?: number | undefined; }): Line3DLine3Dclass
A straight line in space. Ends can be functions, so it can follow moving points.
new Line3D(a
emit(view: View3D): voidlabelsNow(_view: View3D): Node[]Screen-facing labels, drawn on top.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Line3Dtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Line3Ddrop(child: Node): Line3Dall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): Line3DA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): Line3DSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): Line3DmoveTo(target: Node | Vec): Line3DMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): Line3DMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): Line3DScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): Line3DRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): Line3DScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): Line3DPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): Line3DLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): Line3DLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Line3Dstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Line3Dcolor(color: ColorInput): Line3DRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Line3Dglow(radius: number, color?: ColorInput | undefined): Line3DSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): Line3DzIndex(value: number): Line3Dpin(): Line3DPin to the screen so the camera does not move this node.hide(): Line3Dshow(): Line3Dnamed(label: string): Line3DlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
Line3DStyleinterface
color?: ColorInput | undefinedcolors?: readonly ColorInput[] | undefinedColor ramp along the line.width?: number | undefinedopacity?: number | undefinedglow?: number | undefinedonTop?: boolean | undefinedDraw over everything else in the scene.bias?: number | undefinedNudge toward the camera so a line lying on a surface stays visible.
Object3Dclass
A thing in a 3D scene. It is a regular node with an empty 2D path, so verbs work on it: fadeIn sets opacity, and draw animates its reveal through trimTo.
new Object3D()
emit(view: View3D, time: number): voidlabelsNow(_view: View3D): Node[]Screen-facing labels, drawn on top.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Object3Dtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Object3Ddrop(child: Node): Object3Dall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): Object3DA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): Object3DSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): Object3DmoveTo(target: Node | Vec): Object3DMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): Object3DMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): Object3DScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): Object3DRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): Object3DScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): Object3DPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): Object3DLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): Object3DLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Object3Dstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Object3Dcolor(color: ColorInput): Object3DRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Object3Dglow(radius: number, color?: ColorInput | undefined): Object3DSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): Object3DzIndex(value: number): Object3Dpin(): Object3DPin to the screen so the camera does not move this node.hide(): Object3Dshow(): Object3Dnamed(label: string): Object3DlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
ParametricFntype
type ParametricFn = (u: number, v: number, t: number) => Vec3
parametricSurfacefunction
A surface over any two parameters.
function parametricSurface(fn: ParametricFn, u: readonly [number, number], v: readonly [number, number], options?: SurfaceOptions): Surface3D
point3dfunction
One point in space, as a convenience over points3d.
function point3d(point: Reactive3, style?: Points3DStyle): Points3D
points3dfunction
function points3d(points: readonly Vec3[] | (() => readonly Vec3[]), style?: Points3DStyle): Points3D
Points3Dclass
Points in space. Pass a function to move them. They shrink with distance and sort with everything else.
new Points3D(source
emit(view: View3D): voidlabelsNow(_view: View3D): Node[]Screen-facing labels, drawn on top.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Points3Dtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Points3Ddrop(child: Node): Points3Dall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): Points3DA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): Points3DSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): Points3DmoveTo(target: Node | Vec): Points3DMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): Points3DMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): Points3DScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): Points3DRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): Points3DScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): Points3DPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): Points3DLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): Points3DLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Points3Dstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Points3Dcolor(color: ColorInput): Points3DRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Points3Dglow(radius: number, color?: ColorInput | undefined): Points3DSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): Points3DzIndex(value: number): Points3Dpin(): Points3DPin to the screen so the camera does not move this node.hide(): Points3Dshow(): Points3Dnamed(label: string): Points3DlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
Points3DStyleinterface
color?: ColorInput | undefinedcolorAt?: ((index: number, point: Vec3) => ColorInput) | undefinedPer-point color.radius?: number | undefinedRadius in stage pixels at the focus.opacity?: number | undefinedonTop?: boolean | undefinedbias?: number | undefinedNudge toward the camera so a point resting on a surface is not cut by it.halo?: number | undefinedSoft ring around each point.
scene3dfunction
function scene3d(options?: Scene3DOptions): Scene3D
Scene3Dclass
new Scene3D(options?
phi: Valuetheta: Valuedistance: Valueunit: ValuefocusX: ValuefocusY: ValuefocusZ: Valueorbit(target: { phi?: number | undefined; theta?: number | undefined; distance?: number | undefined; unit?: number | undefined; focus?: Vec3 | undefined; }, options?: AnimOptions): AnimMove the camera over time. Any subset of phi, theta, distance, unit, and focus.spin(rate?: number): Scene3DStart a slow ambient rotation here on the timeline, in radians per second.stopSpin(): Scene3DstagePoint(point: Vec3): [number, number]Stage point of a world position at this moment, for placing 2D notes.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Scene3Dtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Scene3Ddrop(child: Node): Scene3Dall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): Scene3DA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): Scene3DSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): Scene3DmoveTo(target: Node | Vec): Scene3DMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): Scene3DMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): Scene3DScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): Scene3DRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): Scene3DScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): Scene3DPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): Scene3DLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): Scene3DLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Scene3Dstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Scene3Dcolor(color: ColorInput): Scene3DRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Scene3Dglow(radius: number, color?: ColorInput | undefined): Scene3DSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): Scene3DzIndex(value: number): Scene3Dpin(): Scene3DPin to the screen so the camera does not move this node.hide(): Scene3Dshow(): Scene3Dnamed(label: string): Scene3DlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
Scene3DOptionsinterface
phi?: number | undefinedAngle down from straight overhead, in radians. 0 looks straight down.theta?: number | undefinedRotation around the vertical axis, in radians.distance?: number | undefinedCamera distance in math units. Smaller exaggerates perspective.unit?: number | undefinedStage pixels per math unit.focus?: Vec3 | undefinedperspective?: boolean | undefinedSet to false for an orthographic view.light?: Vec3 | undefinedDirection toward the light, in world space.ambient?: number | undefinedShare of light that reaches every face, from 0 to 1.shine?: number | undefinedSize of the bright highlight. 0 turns it off.fog?: number | undefinedHow much distant marks sink toward the background, from 0 to 1.fogColor?: ColorInput | undefinedbrightness?: number | undefinedMultiplies lit surface color. Defaults to 0.8 on dark looks, where large areas also bloom.
spherefunction
function sphere(radius?: number, options?: SurfaceOptions & { center?: Vec3 | undefined; }): Surface3Dsurfacefunction
A height field z = f(x, y). f also receives time when animate is on.
function surface(f: (x: number, y: number, t: number) => number, options?: SurfaceOptions & { x?: readonly [number, number] | undefined; y?: readonly [number, number] | undefined; }): Surface3DSurface3Dclass
A shaded mesh. Faces sort with everything else in the scene, so objects can intersect.
new Surface3D(fn
emit(view: View3D, time: number): voidlabelsNow(_view: View3D): Node[]Screen-facing labels, drawn on top.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Surface3Dtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Surface3Ddrop(child: Node): Surface3Dall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): Surface3DA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): Surface3DSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): Surface3DmoveTo(target: Node | Vec): Surface3DMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): Surface3DMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): Surface3DScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): Surface3DRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): Surface3DScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): Surface3DPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): Surface3DLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): Surface3DLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Surface3Dstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Surface3Dcolor(color: ColorInput): Surface3DRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Surface3Dglow(radius: number, color?: ColorInput | undefined): Surface3DSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): Surface3DzIndex(value: number): Surface3Dpin(): Surface3DPin to the screen so the camera does not move this node.hide(): Surface3Dshow(): Surface3Dnamed(label: string): Surface3DlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
SurfaceOptionsinterface
resolution?: number | readonly [number, number] | undefinedGrid cells in each direction. A number applies to both.color?: ColorInput | undefinedOne color for the whole surface.colors?: readonly ColorInput[] | undefinedColor ramp by height.checker?: readonly [ColorInput, ColorInput] | undefinedTwo colors in a checker pattern.colorAt?: ((x: number, y: number, z: number, u: number, v: number) => Rgba) | undefinedAny color per face.opacity?: number | undefinedmesh?: false | ColorInput | undefinedThin lines along the cells.meshOpacity?: number | undefinedmeshWidth?: number | undefinedshaded?: boolean | undefinedSet to false for flat, unlit color.cull?: boolean | undefinedSkip faces pointing away. Good for closed shapes such as spheres.animate?: boolean | undefinedRecompute the shape on every frame from time.outwardFrom?: Vec3 | undefinedPoint normals away from this point. Closed shapes need it socullkeeps the near side.
trail3dfunction
function trail3d(point: () => Vec3, from: number, to: number, style?: Line3DStyle & { tail?: number | undefined; rate?: number | undefined; fade?: number | undefined; }): Trail3DTrail3Dclass
The path a moving point has taken, with a fading tail. Scrubs correctly in both directions.
new Trail3D(point: () => Vec3, from
emit(view: View3D, time: number): voidlabelsNow(_view: View3D): Node[]Screen-facing labels, drawn on top.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Trail3Dtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Trail3Ddrop(child: Node): Trail3Dall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): Trail3DA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): Trail3DSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): Trail3DmoveTo(target: Node | Vec): Trail3DMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): Trail3DMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): Trail3DScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): Trail3DRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): Trail3DScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): Trail3DPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): Trail3DLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): Trail3DLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Trail3Dstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Trail3Dcolor(color: ColorInput): Trail3DRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Trail3Dglow(radius: number, color?: ColorInput | undefined): Trail3DSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): Trail3DzIndex(value: number): Trail3Dpin(): Trail3DPin to the screen so the camera does not move this node.hide(): Trail3Dshow(): Trail3Dnamed(label: string): Trail3DlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
Vec3type
type Vec3 = readonly [number, number, number]
vector3dfunction
function vector3d(from: Reactive3, to: Reactive3, style?: Line3DStyle & { tip?: number | undefined; }): Vector3DVector3Dclass
An arrow in space. The head always faces the camera, so it reads from any angle.
new Vector3D(from
emit(view: View3D): voidlabelsNow(_view: View3D): Node[]Screen-facing labels, drawn on top.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): Vector3Dtrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): Vector3Ddrop(child: Node): Vector3Dall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): Vector3DA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): Vector3DSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): Vector3DmoveTo(target: Node | Vec): Vector3DMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): Vector3DMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): Vector3DScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): Vector3DRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): Vector3DScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): Vector3DPlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): Vector3DLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): Vector3DLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): Vector3Dstroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): Vector3Dcolor(color: ColorInput): Vector3DRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): Vector3Dglow(radius: number, color?: ColorInput | undefined): Vector3DSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): Vector3DzIndex(value: number): Vector3Dpin(): Vector3DPin to the screen so the camera does not move this node.hide(): Vector3Dshow(): Vector3Dnamed(label: string): Vector3DlabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
View3Dclass
Everything an object needs to project itself for one frame.
new View3D()
ops: PathOp[]depths: number[]rx: numberry: numberux: numberuy: numberuz: numberdx: numberdy: numberdz: numberfx: numberfy: numberfz: numberdistance: numberunit: numberperspective: booleanmatrix: Matalpha: numberscale: numberlight: Vec3half: Vec3ambient: numbershine: numberfog: numberfogColor: RgbadepthRange: numberbrightness: numberpx: numberpy: numberpz: numberps: numberproject(x: number, y: number, z: number): booleanProject a world point. Returns false when it falls behind the camera.screen(x: number, y: number, z: number): [number, number] | nullLocal stage point before the scene's own transform, for placing labels.fogged(r: number, g: number, b: number, depth: number): RgbaMix a color toward the background for marks further from the camera.push(op: PathOp, depth: number): void
Text and math
FontFamilytype
type FontFamily = "sans" | "serif" | "text-serif" | "mono" | "noto"
fontForfunction
Get a font now, or suspend the current compile until it arrives.
function fontFor(family: BundledFamily, weight?: FontWeight, italic?: boolean): LoadedFont
FontWeighttype
type FontWeight = 400 | 600 | 700
glyphPathfunction
Glyph outline scaled to a pixel size, with y pointing down and the origin on the baseline.
function glyphPath(font: LoadedFont, glyph: number, size: number): Path
parseFontfunction
function parseFont(bytes: ArrayBuffer): LoadedFont
texfunction
Typeset TeX with real math glyphs. tex.part('x^2') selects a term for color or morphing.
function tex(source: string, options?: TexOptions): TexNode
TexNodeclass
Typeset equation as glyph nodes. Ordinary characters keep their identity for morphs.
new TexNode(path?
glyphs: Node[]source: stringpart(fragment: string): Node[]Glyphs matching a TeX fragment, such as eq.part('b^2'). Use it to color or morph a term.parts(fragment: string): Node[][]Every occurrence of a fragment, in order.id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): TexNodetrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): TexNodedrop(child: Node): TexNodeall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): TexNodeA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): TexNodeSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): TexNodemoveTo(target: Node | Vec): TexNodeMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): TexNodeMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): TexNodeScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): TexNodeRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): TexNodeScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): TexNodePlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): TexNodeLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): TexNodeLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): TexNodestroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): TexNodecolor(color: ColorInput): TexNodeRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): TexNodeglow(radius: number, color?: ColorInput | undefined): TexNodeSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): TexNodezIndex(value: number): TexNodepin(): TexNodePin to the screen so the camera does not move this node.hide(): TexNodeshow(): TexNodenamed(label: string): TexNodelabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
TexOptionsinterface
size?: number | undefinedcolor?: ColorInput | undefineddisplay?: boolean | undefined
textfunction
function text(content: string, options?: TextOptions): TextNode
TextNodeclass
A line or block of text as real vector glyphs. Each glyph is its own child, so text can be handwritten, staggered, or morphed. .chars lists them in order.
new TextNode(path?
chars: Node[]source: stringfont: FontFamilyThe font of the text: the font that it names, or the font of the film.text()records it when it makes the node.find(fragment: string): Node[]Glyph nodes whose characters match, such as every "x".id: numberparent: Node | nullchildren: Node[]data: Record<string, unknown>Free-form metadata, such as the source character of a glyph.label: stringLabel shown in timelines and diagnostics.base: NodePropstracks: Map<keyof NodeProps, Track<unknown>> | nulloverlay: Map<string, unknown> | nulloverlayFrame: numbermounted: booleanOn stage: writes become timeline keys instead of initial state.draw: ((time: number) => Node | readonly Node[] | null) | nullRebuilt on every frame from timeline values.rawOps: ((matrix: Mat, alpha: number, ops: DrawOp[]) => void) | nullEmits draw operations directly, for objects with thousands of marks such as 3D meshes.get<K extends PropName>(prop: K): NodeProps[K]set<K extends PropName>(prop: K, value: NodeProps[K]): TextNodetrack<K extends PropName>(prop: K): Track<NodeProps[K]>tween<K extends PropName>(prop: K, to: NodeProps[K], t0: number, t1: number, ease: Ease | null): voidRecord a change from the current value totoover [t0, t1].add(...nodes: Node[]): TextNodedrop(child: Node): TextNodeall(): Node[]Self and all descendants, parents first.leaves(): Node[]Descendants that draw something. A leaf group returns itself if it draws.clone(): TextNodeA static copy of the current state, off stage. An object that keeps its parts in fields, such as the glyphs of a text or the frame of a card, gets fields that point at the copies of those parts.localMatrix(): MatworldMatrix(): MattoWorld(x: number, y: number): [number, number]bounds(): BoundsStage-space bounds of this node and its descendants.center: [number, number]top: [number, number]bottom: [number, number]left: [number, number]right: [number, number]width: numberheight: numberat(x: number | Vec, y?: number | undefined): TextNodeSet the local position of the origin.shift(dx: number | Vec, dy?: number | undefined): TextNodemoveTo(target: Node | Vec): TextNodeMove so the center of the bounds lands on a stage point or on another node's center.pivotAt(point: Vec): TextNodeMove the origin to a stage point without changing how anything looks.scale(factor: number | Vec, about?: Vec | undefined): TextNodeScale around the center, or around a given stage point.rotate(radians: number, about?: Vec | undefined): TextNodeRotate around the center, or around a given stage point.fitTo(size: { width?: number | undefined; height?: number | undefined; }): TextNodeScale to an exact on-screen width or height, keeping proportions.nextTo(target: Node | Vec, side?: Side, gap?: number): TextNodePlace beside another node or stage point, with a gap measured between bounds.alignTo(target: Node | Vec, edge: Edge): TextNodeLine up one edge with another node's same edge.arrange(side?: Side, gap?: number): TextNodeLay out children in a row or column, keeping the group's center.fill(color: ColorInput | null, opacity?: number | undefined): TextNodestroke(color: ColorInput | null, width?: number | undefined, opacity?: number | undefined): TextNodecolor(color: ColorInput): TextNodeRecolor this node and its descendants, keeping whichever paints exist.opacity(value: number): TextNodeglow(radius: number, color?: ColorInput | undefined): TextNodeSoft light around the shape. The radius is in stage pixels.dash(pattern: readonly number[] | null, offset?: number): TextNodezIndex(value: number): TextNodepin(): TextNodePin to the screen so the camera does not move this node.hide(): TextNodeshow(): TextNodenamed(label: string): TextNodelabelsOf(_part: Node): readonly Node[]The labels that belong to one part of this object, such as the name and the readout of a bar. A light on the part keeps them bright. An object that has such labels says so here.
TextOptionsinterface
size?: number | undefinedfont?: FontFamily | undefinedDefaults to thefontof the film, and to sans in a film that has none.weight?: FontWeight | undefineditalic?: boolean | undefinedcolor?: ColorInput | undefinedalign?: "left" | "right" | "center" | undefinedLine alignment inside the block.anchor?: "left" | "right" | "center" | undefinedThe point of the block that.at(x, y)places: its left edge, its middle, or its right edge. The default is the middle. The height of the point does not change, so labels with different anchors on oneyshare a baseline. The block also scales and rotates about it.maxWidth?: number | undefinedWrap to this width, in stage pixels.leading?: number | undefinedLine height as a multiple of size.tracking?: number | undefinedExtra space between letters, as a fraction of size.uppercase?: boolean | undefined
Easing
bounceconst
const bounce: Ease
cubicBezierfunction
CSS-style cubic-bezier timing function.
function cubicBezier(x1: number, y1: number, x2: number, y2: number): Ease
duringfunction
Restrict an ease to a window of the animation.
function during(from: number, to: number, inner?: Ease): Ease
Easetype
Rate functions map animation progress in [0, 1] to eased progress.
type Ease = (t: number) => number
easeInCubicconst
const easeInCubic: Ease
easeInExpoconst
const easeInExpo: Ease
easeInOutCubicconst
const easeInOutCubic: Ease
easeInOutQuintconst
const easeInOutQuint: Ease
easeInQuadconst
const easeInQuad: Ease
easeOutCubicconst
const easeOutCubic: Ease
easeOutExpoconst
const easeOutExpo: Ease
easeOutQuadconst
const easeOutQuad: Ease
easeOutQuintconst
const easeOutQuint: Ease
linearconst
const linear: Ease
lingeringfunction
Holds at 1 for the last part of the time.
function lingering(hold?: number, inner?: Ease): Ease
notQuiteTherefunction
Stops short of the target, for an unfinished gesture.
function notQuiteThere(amount?: number, inner?: Ease): Ease
overshootfunction
Overshoot the target once, then settle.
function overshoot(amount?: number): Ease
runningStartfunction
Backs up briefly, then runs through to the target.
function runningStart(pull?: number): Ease
rushFromconst
const rushFrom: Ease
rushIntoconst
const rushInto: Ease
slowIntoconst
const slowInto: Ease
smoothconst
const smooth: Ease
smoothByfunction
The house default. A sigmoid with a calm start and a calm landing.
function smoothBy(inflection: number): Ease
smootherconst
Fifth-order smoothstep, for long camera moves.
const smoother: Ease
springfunction
Physically based settle. Exactly 1 at t=1 so holds never drift.
function spring(options?: { bounce?: number | undefined; frequency?: number | undefined; }): EasethereAndBackconst
Goes to 1 and returns to 0. Ends where it started, for emphasis.
const thereAndBack: Ease
thereAndBackWithPausefunction
function thereAndBackWithPause(pause?: number): Ease
wigglefunction
Oscillates around 0 and ends at 0.
function wiggle(cycles?: number): Ease
Color and looks
blueprintconst
Drafting-table navy with cool cyan linework.
const blueprint: Look
chalkconst
Near-black green with warm chalk, for classroom-style lessons.
const chalk: Look
colorAlongfunction
Color that travels along a stroke, such as a graph that warms from left to right.
function colorAlong(...colors: readonly ColorInput[]): AlongGradient
ColorInputtype
type ColorInput = string | Rgba | Gradient
cssfunction
function css(color: Rgba, alpha?: number): string
Gradienttype
type Gradient = LinearGradient | RadialGradient | AlongGradient
linearGradientfunction
function linearGradient(from: readonly [number, number], to: readonly [number, number], colors: readonly ColorInput[] | readonly (readonly [number, ColorInput])[]): LinearGradient
Lookinterface
A look is a complete art direction for a film: background, ink, a small set of named pigments, and how much light the image gives off. Authors write color: 'yellow', so one film renders correctly in every look.
name: stringdark: booleanbackground: stringbloom: numberFull-frame bloom in the light-emitting looks. 0 turns it off.colors: Readonly<Record<LookToken, string>>
LookNametype
type LookName = "midnight" | "paper" | "blueprint" | "chalk" | "neon"
LOOKSconst
const LOOKS: { readonly midnight: Look; readonly paper: Look; readonly blueprint: Look; readonly chalk: Look; readonly neon: Look; }LookTokentype
type LookToken = "dim" | "bg" | "surface" | "ink" | "faint" | "grid" | "gridStrong" | "blue" | "teal" | "green" | "yellow" | "gold" | "orange" | "red" | "pink" | "purple"
midnightconst
Deep neutral black with luminous pigments. The default.
const midnight: Look
mixColorfunction
Mix two colors in OKLab, which keeps blends bright and even in tone.
function mixColor(a: Rgba, b: Rgba, t: number): Rgba
neonconst
Saturated light on pure black, for trailers and hero loops.
const neon: Look
Painttype
type Paint = Rgba | Gradient
paperconst
Warm paper and printed ink, for articles and print.
const paper: Look
parseColorfunction
function parseColor(input: string): Rgba
radialGradientfunction
function radialGradient(center: readonly [number, number], radius: number, colors: readonly ColorInput[]): RadialGradient
rampfunction
Evenly spaced colors between stops, for coloring a list of items.
function ramp(count: number, ...colors: readonly ColorInput[]): Rgba[]
resolveLookfunction
function resolveLook(look: Look | "midnight" | "paper" | "blueprint" | "chalk" | "neon" | undefined): Look
Rgbatype
r, g, b from 0 to 255 and alpha from 0 to 1.
type Rgba = readonly [number, number, number, number]
toPaintfunction
function toPaint(input: ColorInput): Paint
Geometry
applyfunction
function apply(m: Mat, x: number, y: number): [number, number]
arcPathfunction
Elliptical or circular arc, with one cubic per 45 degrees at most.
function arcPath(cx: number, cy: number, rx: number, ry: number, from: number, to: number): Path
Boundsinterface
x: numbery: numberwidth: numberheight: number
circlePathfunction
function circlePath(cx: number, cy: number, r: number): Path
composefunction
Translate * Rotate * Scale * Translate(-pivot).
function compose(x: number, y: number, rotation: number, sx: number, sy: number, px: number, py: number): Mat
concatfunction
function concat(...paths: readonly Path[]): Path
curveThroughfunction
Hermite fit through samples. Non-finite samples split the curve into separate rings, so poles such as tan(x) leave gaps instead of drawing vertical strokes.
function curveThrough(points: readonly Vec[]): Path
EMPTY_BOUNDSconst
const EMPTY_BOUNDS: Bounds
IDENTITYconst
const IDENTITY: Mat
invertfunction
function invert(m: Mat): Mat
linePathfunction
function linePath(a: Vec, b: Vec): Path
Mattype
2x3 affine matrix: x' = a*x + c*y + e, y' = b*x + d*y + f.
type Mat = readonly [number, number, number, number, number, number]
matScalefunction
Mean scale of a matrix, used to scale stroke widths with the geometry.
function matScale(m: Mat): number
mulfunction
function mul(m: Mat, n: Mat): Mat
parametricPathfunction
Smooth open curve through samples of a parametric function.
function parametricPath(f: (t: number) => Vec, from: number, to: number, samples?: number): Path
parseSvgPathfunction
Parse SVG path data, including arcs and relative commands, into cubic rings.
function parseSvgPath(d: string): Path
Pathtype
type Path = readonly Ring[]
pathBoundsfunction
function pathBounds(path: Path): Bounds | null
pathLengthfunction
function pathLength(path: Path): number
pathToDfunction
Serialize a path to compact SVG path data.
function pathToD(path: Path): string
pointAtfunction
Point at an arc-length fraction of a path, in the path's own coordinates.
function pointAt(path: Path, fraction: number): [number, number]
polyPathfunction
function polyPath(points: readonly Vec[], closed?: boolean): Path
rectPathfunction
function rectPath(x: number, y: number, width: number, height: number, radius?: number): Path
reverseRingfunction
function reverseRing(r: Ring): Ring
ringfunction
function ring(points: ArrayLike<number>, closed?: boolean): Ring
Ringinterface
p: Float64Array<ArrayBufferLike>closed: booleanstraight?: boolean | undefinedEvery segment is a straight line, so painters can use cheap line commands.
ringCenterfunction
function ringCenter(r: Ring): [number, number]
ringLengthfunction
function ringLength(r: Ring): number
segmentCountfunction
function segmentCount(r: Ring): number
signedAreafunction
Shoelace area over the anchors. The sign gives the winding direction.
function signedArea(r: Ring): number
smoothPathfunction
A smooth cubic spline through every point (Catmull-Rom, centripetal-ish tension).
function smoothPath(points: readonly Vec[], closed?: boolean, tension?: number): Path
subdivideEachfunction
Split every segment of a ring into n equal parameter pieces. Geometry is unchanged.
function subdivideEach(r: Ring, n: number): Ring
tangentAtfunction
Unit tangent at an arc-length fraction, in the path's own coordinates.
function tangentAt(path: Path, fraction: number): [number, number]
transformPathfunction
function transformPath(path: Path, m: Mat): Path
trimPathfunction
Keep a fraction of a path. With each, every ring draws at once, which suits glyphs. Without it, rings draw one after another along the total length.
function trimPath(path: Path, from: number, to: number, each?: boolean): Path
trimRingfunction
Keep the part of a ring between two arc-length fractions.
function trimRing(r: Ring, from: number, to: number): Ring | null
unionBoundsfunction
function unionBounds(a: Bounds | null, b: Bounds | null): Bounds | null
Vectype
Vector geometry for the stage engine. Every shape is a list of rings. A ring is a run of cubic Bezier segments in a flat array: [x0, y0, c1x, c1y, c2x, c2y, x1, y1, c1x, ...]. One representation for lines, arcs, glyphs, and imported SVG makes any shape morph into any other. Rings and paths are immutable by convention. Length tables and bounds are cached per ring, so sampling the same geometry on many frames stays cheap.
type Vec = readonly [number, number]
warpPathfunction
Deform a path through any point function. Segments split first, so curved maps stay smooth.
function warpPath(path: Path, f: (x: number, y: number) => Vec, subdivisions?: number): Path
Drawing
buildOpsfunction
The draw list of a tree. backdrop is the color that a dim node goes toward, and marks gives the node that each mark of a verb is as bright as. They have an effect only in a film with a light, where spotlight gives nodes a dim under 1.
function buildOps(root: Node, camera: CameraState, width: number, height: number, backdrop?: Rgba, marks?: ReadonlyMap<Node, Node> | null): DrawOp[]
Canvas2Dtype
type Canvas2D = CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D
DrawOptype
type DrawOp = PathOp | ImageOp
Frameinterface
width: numberheight: numberbackground: string | nullbloom: numberops: readonly DrawOp[]dark: boolean
frameInkBoundsfunction
Pixel-space bounds of a frame's ink, used by checks for off-canvas marks.
function frameInkBounds(frame: Frame): { x: number; y: number; width: number; height: number; } | nullframeToSvgfunction
Serialize a frame to standalone SVG. Text is already outlines, so no fonts are needed.
function frameToSvg(frame: Frame, options?: { scale?: number | undefined; transparent?: boolean | undefined; }): stringImageOpinterface
kind: "image"image: StageImagematrix: Matalpha: numbernode?: Node | undefineddim?: number | undefinedA dim picture: each pixel keeps this part of its color, and the rest of it isbackdrop. The alpha of each pixel stays, so the clear parts of the picture stay clear.backdrop?: Rgba | undefined
paintFramefunction
Paint a frame to a 2D context at a device scale. Same result as the SVG backend.
function paintFrame(ctx: Canvas2D, frame: Frame, scale?: number, images?: ImageLookup | undefined): void
PathOpinterface
kind: "path"path: PathstrokePath: PathStroke geometry when it differs from the fill, such as during a partial draw.fill: Paint | nullfillAlpha: numberstroke: Paint | nullstrokeAlpha: numberstrokeWidth: numbercap: "round" | "butt" | "square"join: "round" | "miter" | "bevel"dash: readonly number[] | nulldashOffset: numberglow: numberglowColor: Rgba | nullblend: BlendModenode?: Node | undefinedThe node that produced this mark, for checks and tools.