Probability trees n×p, growing left-to-right (the standard reading direction for probability trees). This module is built on CeTZ (@preview/cetz:0.5.2). The sources are available on GitHub.
Features
- Probability trees n×p, growing left-to-right.
- Local overrides for every node and probability (
sn/sp). - Global and local text styles (weight, italic, small caps, highlight, custom function…).
- Probability labels above, below or on top of the edge (
above,below,on,hybrid), optionally sloped along the edge. - In
onmode, the edge is cut under the label, with transparency preserved. - Improved error messages for malformed tree data.
- Exact node positions exposed for precise custom labels or annotations (
extracallback). - Quick list-based syntax (
proba-tree-short) to write the tree with Typst lists instead of nested tuples.
Table of contents
- Installation
- Quick start
- Data structure
- Quick tree syntax (
proba-tree-short) - API
- Node positions (
extra) - Text styles
- Probability positions
- Default tree
- Error handling
- Dependencies
- License
- Authors
Installation
#import "@preview/probabilitree:0.1.3": proba-tree, sn, sp, proba-tree-short
If the package is installed locally:
#import "@local/probabilitree:0.1.3": proba-tree, sn, sp, proba-tree-short
Quick start
#proba-tree(data: (
$Omega$,
(sn($A$, style: (fill: green)), $p$, ($B$, $q$), ($overline(B)$, $1-q$)),
($overline(A)$, $1-p$, ($B$, $q$), ($overline(B)$, $1-q$)),
))
data structure
Each node is an array (label, proba, ..children):
label: node content (e.g.$A$,[A],"A") or a local settingsn(...).proba: probability shown on the branch (e.g.$p$,$1-p$) or a local settingsp(...)...children: zero or more child nodes, with the same structure.
The root has no probability: $Omega$ is just the label.
#proba-tree(data: (
$Omega$,
(sn($A$, style: (fill: green)), sp($p$, style: (fill: red, weight: "bold")), ($B$, $q$)),
($overline(A)$, $1-p$, ($B$, $q$)),
))
Quick tree syntax (proba-tree-short)
Writing nested tuples can get verbose. proba-tree-short lets you write the same tree with Typst lists — - for a node, + for the probability of the branch leading to it, alternating and nested:
#proba-tree-short[
- $Omega$
+ $p$
- $A$
+ $q$
- $B$
+ $1-q$
- $overline(B)$
+ $1-p$
- $overline(A)$
+ $q$
- $B$
+ $1-q$
- $overline(B)$
]
All the proba-tree options (h, v, proba-position, node-style, extra, …) remain available — only the construction of data changes. Per-node fine styles (sn / sp) are not expressible in this syntax: use proba-tree directly if you need them.
proba-tree-short-data returns the data array alone, so you can reuse it in proba-tree:
#let data = proba-tree-short-data[
- $Omega$
+ $p$
- $A$
]
#proba-tree(data: data)
API
proba-tree(...)
| Parameter | Type | Default | Description |
|---|---|---|---|
h |
float |
2.0 |
Horizontal stretch of the edges. |
v |
float |
0.8 |
Vertical spacing between branches. |
proba-position |
string |
hybrid |
Probability placement: above, below, on or hybrid. |
proba-distance |
float |
0.3 |
Label distance from the edge. |
proba-sloped |
bool |
false |
Align probabilities along the edge (“sloped”). |
proba-padding |
length |
3pt |
In on mode, gap between the cut line and the probability bbox. |
proba-style |
dictionary |
(size: 80%) |
Global style for probabilities (size 80% is always kept as a base). |
node-style |
dictionary |
none |
Global style for node labels. |
first-child-top |
bool |
true |
Put the first listed child on top. |
node-padding |
float |
0.3 |
Gap left around each letter (the edge is not drawn there). |
data |
array |
default Ω tree | The tree to draw. |
extra |
function |
none |
Callback (pos, draw) => ... drawn in the same canvas, receiving the exact position of every node. |
proba-tree-short(..options, markup) — quick list-based syntax
Writes a tree with Typst lists instead of nested tuples (- for a node, + for the probability of the branch leading to it, alternating and nested). All the proba-tree options are forwarded; only data is built from the markup.
#proba-tree-short[
- $Omega$
+ $p$
- $A$
]
sp(content, ...) — local probability settings
Returns a dictionary usable as a probability in data. Any omitted parameter falls back to the global settings.
| Parameter | Type | Default | Description |
|---|---|---|---|
content |
content |
— | The probability to display (e.g. $p$, 1-p). |
position |
string |
auto |
above, below, on or hybrid. |
sloped |
bool |
auto |
Align the probability along the edge (“sloped”). |
distance |
number |
auto |
Label distance from the edge. |
style |
dictionary |
auto |
Local style merged with proba-style. |
#proba-tree(data: (
$Omega$,
($A$, sp($p$, style: (fill: red, weight: "bold")), ($B$, $q$)),
($overline(A)$, $1-p$),
))
sn(content, ...) — local node settings
| Parameter | Type | Default | Description |
|---|---|---|---|
content |
content |
— | The node text (e.g. $A$, [A], "A"). |
style |
dictionary |
auto |
Local style merged with node-style. |
#proba-tree(data: (
$Omega$,
(sn($A$, style: (fill: green, weight: "bold")), $p$, ($B$, $q$)),
($overline(A)$, $1-p$),
))
Node positions (extra)
proba-tree exposes the exact canvas position of every node, so you can overlay labels, annotations or lines that align perfectly with the tree — without importing CeTZ yourself.
#proba-tree(
data: (
$Omega$,
($F$, $$, ($F$, $$, ($F$, $$), ($P$, $$)), ($P$, $$, ($F$, $$), ($P$, $$))),
($P$, $$, ($F$, $$, ($F$, $$), ($P$, $$)), ($P$, $$, ($F$, $$), ($P$, $$))),
),
extra: (pos, draw) => {
let issues = ("FFF", "FFP", "FPF", "FPP", "PFF", "PFP", "PPF", "PPP")
for i in range(0, 8) {
let p = pos.at("N4" + str(i + 1))
draw.content((p.at(0) + 0.35, p.at(1)), anchor: "west", [$space arrow.long.r space #issues.at(i)$])
}
},
)
The callback receives two arguments:
pos: a dictionary keyedN<level><index>(1-indexed) →(x, y)in canvas units.N11is the root,N41the 1st leaf of a 4-level tree, etc. Indices follow the visual top-to-bottom order, whatever the value offirst-child-top.draw: the CeTZdrawnamespace, usable directly (draw.content,draw.line, …).
Text styles
Styles apply globally (proba-style / node-style) or locally (sp(style:) / sn(style:)). Local keys override global ones; the others are preserved.
Accepted keys:
- All
#textparameters:size,fill,weight,style(italic),background,font,features, … smallcaps(bool): small caps (via the OpenTypesmcpfeature).highlight(color): highlighting (background box) — works with text and equations.function(content -> content): custom function applied last.
#proba-tree(
proba-style: (fill: blue),
node-style: (size: 11pt, fill: blue),
data: (
$Omega$,
($A$, $p$, ($B$, sp($q$, style: (highlight: yellow)))),
($overline(A)$, $1-p$),
),
)
Probability positions
Constants: above, below, on, hybrid.
above/below: above / below the edge.on: the probability sits on the edge, which is cut under the label (no colored background, transparency preserved). The gap between the cut line and the probability is set withproba-padding.hybrid(default):aboveif the branch goes up,belowif it goes down.
#proba-tree(proba-position: "on", proba-padding: 5pt)
Default tree
If data is omitted, the following tree is drawn: root Ω, with A/Ā (probabilities p/1-p), each leading to B/B̄ (q/1-q).
Error handling
If a non-root node is missing a probability (i.e., the tuple is malformed), the package now provides a clear error message:
proba-tree: non-root node without probability — expected format is (label, proba, ..children). Received: ...
This replaces the generic Typst index error, making debugging easier.
Dependencies
- CeTZ
@preview/cetz:0.5.2
License
Distributed under the MIT License.
Copyright (c) 2026 Mikaël MAUNIER and DeepSeek
Authors
- Mikaël MAUNIER
- DeepSeek
- Claude