Skip to content

Rendering

The rendering layer draws a TreeBuffers onto a WebGPU canvas. The shipping entry point is the PhyloScene controller, most easily driven through mountPhyloScene, which manages the render loop and resize observer for you.

mountPhyloScene(canvas, opts) constructs a PhyloScene, starts a requestAnimationFrame loop (rendering only when a frame is needed), installs a DPR-aware ResizeObserver, and returns an imperative PhyloChartHandle. The caller owns the GPU device — the handle never destroys it.

import { AnnotationTable, parseNewick, mountPhyloScene } from "insomni-phylo";
const annotations = new AnnotationTable();
const tree = parseNewick("((A:0.1,B:0.2):0.3,C:0.4);", { annotations });
const handle = mountPhyloScene(canvas, {
device, // caller-owned GPUDevice
tree,
annotations,
interactive: true, // pan/zoom (default true)
showNavigator: true,
style: { branchStyle: "curve", showTips: true },
onResize: (w, h) => console.log("css size", w, h),
});
handle.setStyle({ layout: "circular" });
const hit = handle.pickAt(120, 80); // PhyloHit | null
handle.focusNode(hit?.nodeIdx ?? 0);
handle.destroy(); // cancels rAF + observer; idempotent

MountPhyloSceneOptions highlights: device (required), tree, annotations, style (merged onto DEFAULT_PHYLO_STYLE), interactive, showNavigator, partialRedraw (damage-tracked redraw, default on), onError, onResize, onLayoutProgress.

PhyloChartHandle surface:

MemberPurpose
tree / annotations / style / rendererRead-only introspection.
setTree(tree) / setAnnotations(table) / setStyle(patch)Update data + style (style is rAF-coalesced).
setHover(hit) / setSelectionHit(hit) / setSearchMatches(ids)Interaction state.
pickAt(x, y) / searchNodes(query)Queries (canvas px / by name).
setInteractive(active)Toggle pan/zoom.
focusNode(nodeIdx)Select + center the camera on a node.
resize(cssW, cssH) / destroy()Lifecycle.

setStyle takes a Partial<PhyloStyle> merged onto DEFAULT_PHYLO_STYLE. PhyloStyle is broad; the most-used fields:

FieldNotes
layout: PhyloLayout"rect" (default), "circular", or "unrooted".
coordOptionsPolar knobs for "circular" (startAngle, openAngle, innerRadius).
branchStyle: BranchStyle"step" (default), "curve", "rounded-step", "slanted".
strokeWidth, curveTension, cornerRadiusPxBranch geometry.
treeOrder"none" | "increasing" | "decreasing" — ladderize applied at setTree.
treeTransform: TreeTransform"proportional" / "cladogram" / "equal".
reverseAxis, alignTipLabelsMirror time axis; gutter-align tip labels.
showAxis, showLabels, showTipsLayer toggles.
tipSize, tipColor, labelColor, background, foregroundColors / sizes.
tipLabelAttribute, tipLabelSigDigitsLabel source + formatting.
branchColorScheme, branchThicknessScheme, tipColorScheme, tipSizeSchemeData-driven channels ("fixed" / "continuous" / "discrete").
nodeShapesInternal-node shape layer ({ shown, colorScheme, sizeScheme }).

Data-driven schemes (ColorScheme, ThicknessScheme, SizeScheme) map an annotation column through a palette; see the Tree model for the catalog that feeds them. Built-in palettes (PALETTES, CATEGORY10, SET2, DISCRETE_PALETTES, hsbRamp) and scale builders (sequentialScale, categoricalScale, buildBranchColors, buildNodeColors, …) are also exported from the package root.

If you run your own loop, construct PhyloScene yourself:

import { PhyloScene } from "insomni-phylo";
const scene = new PhyloScene({ device, canvas, showNavigator: true });
scene.setTree(tree);
scene.setAnnotations(annotations);
scene.setStyle({ branchStyle: "step" });
function frame(t: number) {
if (scene.needsFrame()) scene.render(t / 1000);
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);

Key methods: setTree, setAnnotations, setStyle, render(time), needsFrame(), resize(w, h) (device px), pickAt(cssX, cssY), searchNodes(query), setHover, setSelectionByNode, setSearchMatches, centerOnNode, setInteractive, destroy. PhyloSceneOptions exposes xScale, padX, rowSpacingFallback, showNavigator, partialRedraw, onFrameTiming, and onLayoutProgress. DEFAULT_PHYLO_STYLE is exported for seeding your own partials.

Below the scene sits a set of pure functions that pack geometry into an insomni Layer you supply. The scene composes them; you can call them directly when building a custom renderer. Each takes a Layer plus an options object:

FunctionDraws
renderPhyloTileBranch tiles (the baked tree skeleton).
populatePhyloLabels / populatePhyloBranchLabels / populatePhyloInternalLabelsTip / branch / internal labels.
populatePhyloTipsTip dots.
populatePhyloNodesInternal-node shapes.
populatePhyloNodeBarsPer-node HPD/range bars.
populatePhyloHilights!hilight clade backgrounds.
bakePhyloAxisThe scale axis.
populatePhyloHoverHover emphasis overlay.
buildCladeStripsClade strip specs.
hitTestPhylo / hitTestUnrootedPointer hit-testing → PhyloHit.

Label helpers resolveLabelText, formatAnnotationValue, and formatSigDigits (plus the LABEL_SOURCE_NAMES / LABEL_SOURCE_BRANCH_LENGTHS / LABEL_SOURCE_NODE_AGES constants) handle the text resolution shared across label kinds. Coordinate projection is provided by cartesianCoord() / polarCoord() (type PhyloCoord).