Syntactic Trees in Forest
approvedby Lucianski
Renders LaTeX forest-style syntactic trees for linguistics inline. - This plugin has not been manually reviewed by Obsidian staff.
Syntactic Trees in Forest
Render LaTeX forest-style syntactic trees inline in Obsidian notes. Node labels go through Obsidian's MathJax pipeline, so primes (X$'$), superscripts (X$^0$), subscripts and arbitrary inline math just work.
The plugin focuses on the subset of forest that linguists actually use for syntactic trees: bracket syntax, per-node options, the for tree propagator, edge labels, triangle (roof) phrases, and \draw-style movement arrows between named nodes — with TikZ-faithful to[…] semantics.
Usage
Trees live in fenced forest code blocks:
```forest
[CP
[DP, name=who [who$_i$]]
[C$'$
[C [did]]
[TP
[DP [Mary]]
[T$'$
[T]
[vP
[DP [$t$]]
[v$'$
[v]
[VP
[V [see]]
[DP, name=obj [$t_i$]]]]]]]]]
\draw[->] (obj) to[out=south, in=south] (who);
```
The plugin processes the source as forest-package syntax and emits an inline SVG. Labels are MathJax-rendered, so anything between $…$ typesets properly.
Bracket syntax
Every node sits in [ … ]. Children are nested directly:
[Parent [Child1] [Child2 [Grandchild]]]
To put a literal [, ], ,, or = into a label, brace-protect that chunk: [{a, b}].
Per-node options
A comma after the node content introduces a comma-separated option list:
[VP, draw, name=verb, tier=word]
| Option | Effect |
|---|---|
name=<id> | Names the node so movement arrows can target it |
tier=<tier> | Aligns all nodes sharing a tier name to the same horizontal line |
roof / triangle | Draws a triangle from the parent down to this node (phrasal shorthand) |
draw | Outlines the node with a rectangle |
circle | Outlines the node with a circle |
phantom | Reserves layout space but draws nothing — useful for alignment |
parent anchor=south|center | Where the edge leaving this node downward attaches |
child anchor=north|center | Where the edge entering this node from above attaches |
edge label={...} | Italic label on the edge between this node and its parent |
Inheritance: for tree and for descendants
Apply options to a node and all its descendants:
[CP, for tree={parent anchor=south, child anchor=north}
[DP] [C$'$ [C] [TP]]]
for descendants={...} does the same but excludes the node itself.
Math in labels
Anything between $…$ in a node label is rendered with MathJax:
| You type | You get |
|---|---|
X$'$ | X′ |
X$^0$ | X⁰ |
t$_i$ | tᵢ |
$\overline{X}$ | X̄ |
Sizing trees and nodes
Forest's TeX-dimension size options can sit on any node — root or interior — and apply locally:
| Option | Meaning |
|---|---|
s sep=X | Sibling separation among this node's children |
l sep=X | Level separation between this node and its children |
s sep+=X | Add to the global sibling separation |
l sep+=X | Add to the global level separation |
inner sep=X | Padding inside each node label |
inner xsep=X | Horizontal-only padding |
inner ysep=X | Vertical-only padding |
font size=X | Label font size |
X is a TeX dimension: pt, pc, bp, cm, mm, em, ex, in, px, or a bare number (= pixels). 1 pt ≈ 1.333 px, 1 cm ≈ 37.8 px at 96 dpi.
Local overrides on inner nodes only affect that node's children:
```forest
[vP ← root spacing default
[DP [Mary]]
[v$'$, s sep=50pt, l sep=40pt ← v's subtree breathes out, rest stays tight
[v]
[VP
[V [saw]]
[DP [John]]]]]
```
To apply to a whole subtree, use the propagator:
```forest
[CP
[DP]
[C$'$
[C]
[TP, for tree={l sep=40pt, s sep=50pt}
[DP] [T$'$ [T] [vP [DP] [v$'$ [v] [VP]]]]]]]
```
Movement arrows
After the tree, you can add \draw commands between nodes:
[CP
[DP, name=who [who$_i$]]
[C$'$
[C [did, name = c]]
[TP
[DP, name=subj [Mary$_j$]]
[T$'$
[T [did, name = t]]
[vP
[DP, name=vsubj [$t_j$]]
[v$'$
[V + v [see, name = v]]
[VP
[V [see, name = V]]
[DP, name=obj [$t_i$]]]]]]]]]
\draw[->] (obj) to[out=west, in=west] (who);
\draw[->] (vsubj) to[out=west, in=east] (subj);
\draw[->] (t) to[out=west, in=w] (c);
\draw[->] (V) to[out=west, in=w] (v);
Other forms the parser recognises:
\draw[->] (a) to (b);
\draw[->, dashed] (a) to[bend left=45] (b);
\draw[->, dotted] (a) to node{move} (b);
\draw[->, solid] (a) -- (b);
Recognised within each \draw:
| Token | Meaning |
|---|---|
\draw[-> …] | Style block. Recognises dashed, dotted, solid (default is dashed) |
(name) | Reference a node by its name= option or by its content text |
to[…] | Curved arrow with option block (see below) |
to | Default: drape below the tree |
-- | Straight line (TikZ syntax) |
node{label} | Italic label placed near the arrow's apex |
Direction control: to[out=A, in=B]
Per TikZ semantics, out=A and in=B set both the attachment point on the node boundary AND the tangent direction there. The attachment is the intersection of the ray from the node's centre at angle A with the node's bounding box. So out=south attaches at the south boundary and the curve leaves heading south.
You can use compass names or polar angles (0°=east, 90°=north, 180°=west, 270°=south):
\draw[->] (a) to[out=south, in=south] (b);
\draw[->] (a) to[out=south west, in=south] (b);
\draw[->] (a) to[out=east, in=west] (b);
\draw[->] (a) to[out=270, in=180] (b);
\draw[->] (a) to[out=south east, in=north west, looseness=1.4] (b);
Compass-name → angle: east=0°, north east=45°, north=90°, north west=135°, west=180°, south west=225°, south=270°, south east=315°.
Whitespace inside compound names is tolerated. An optional looseness=N (default 1) scales how strongly the tangent is enforced — higher values exaggerate the curve.
Bend modifiers
bend left=N and bend right=N rotate the tangent angles. Combined with out/in, they add N° CCW to out and subtract N° from in (for bend left), bowing the curve further to that side of the path direction:
\draw[->] (a) to[out=south, in=east] (b); ← baseline
\draw[->] (a) to[out=south, in=east, bend left=20] (b); ← bows more left
\draw[->] (a) to[out=south, in=east, bend right=20] (b); ← bows more right
Used alone (without out/in), bend left=N / bend right=N produce a quadratic curve bowing to that side of the direct line.
Anchor shorthand: (name.south)
For simple cases you can also pin an endpoint to a named anchor directly: (name.south), (name.east), (name.north west), etc. This is shorthand that pins the attachment point and uses the corresponding outward tangent direction. The full TikZ list is supported:
north, south, east, west, north east, north west, south east, south west, center.
When you give neither an anchor nor out=/in=, the renderer auto-picks the side of each node facing the other endpoint.
Arrowheads
Drawn geometrically as filled triangles oriented along the curve's tangent at the endpoint — no dependence on SVG <marker> definitions, which makes them robust across themes and embedded-SVG quirks. Style mimics TikZ's default > (slim, slightly concave back).
Settings
A settings tab exposes the global visual defaults: level separation, sibling separation, subtree separation, label font size, edge stroke width, and a toggle to outline every node by default. Per-tree options (above) override these locally.
Styling
The default look is aligned with TikzJax-rendered TikZ output (Computer- Modern-style serif text, thin crisp lines, refined arrow tips). Customize further with a CSS snippet targeting any of:
| Selector | What it controls |
|---|---|
.forest-svg | The root SVG. Set font-family, color, font-size |
.forest-label-wrap | Wrapper around each node label |
.forest-edges | Group containing all tree edges |
.forest-edge | Each individual tree edge (line or roof) |
.forest-roof | The triangle of a roof/triangle node |
.forest-arrows | Group containing all movement arrows |
.forest-movement | Each arrow's curve (the line, not the head) |
.forest-arrowhead | The filled triangle at the arrow tip |
.forest-edge-label | Italic label next to a tree edge |
.forest-movement-label | Italic label near an arrow's apex |
.forest-node-shape | The bounding box drawn around draw/circle nodes |
All strokes and fills resolve through currentColor, set from color: var(--text-normal) on the SVG. So one rule recolors everything:
.forest-svg { color: #003366; font-family: "EB Garamond", serif; }
Dark mode is automatic because --text-normal flips with the theme.
Installation
- Download
main.js,manifest.json,styles.cssfrom the release. - Drop them into
<your-vault>/.obsidian/plugins/obsidian-forest/. - Reload plugins in Obsidian settings, then enable Forest Syntactic Trees.
Alternatively, to build from source:
npm install
npm run build
What this plugin is not
It is not a complete port of the forest LaTeX package. forest is a TikZ/PGF DSL with conditionals, dynamic trees, aggregate functions, custom handlers, externalization, etc. — none of which is sensible to reimplement in JavaScript. This plugin covers the slice syntacticians reach for daily: bracket trees, primes and bars in labels, triangles, named nodes, movement arrows, and TikZ-style direction control via out=/in= and bend.
For plugin developers
Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.