Lesson 6.4Lesson 6.4 · Scripting Rhino & Grasshopper
Data Trees & Grasshopper Logic
The branching data structure at the heart of Grasshopper - how it differs from a Python list, and when code beats components for handling it
The data tree is the idea that makes Grasshopper powerful - and the one that makes newcomers cry. It is simpler than it looks.
Ask any Grasshopper user what confused them longest and most will say the same two words: data trees. Components quietly reshape your data into branching structures, and when two branches do not line up, results go wrong in baffling ways. It feels like magic gone hostile.
It is not magic. A data tree is just lists, grouped and labelled - a filing system for your data. This final lesson of the module demystifies it by holding it up against something you already know cold: the plain Python list. Once you see a tree as nested lists with addresses, flatten and graft stop being incantations and become obvious.
Tree = list of lists + addresses. Flatten = one list. Graft = one item each. Match the shapes.
A tree is lists inside a filing system
Start from what you know. A Python list is a flat, ordered run of items - [a, b, c, d] - one after another, no grouping. A Grasshopper data tree is a step up: it holds several lists at once, each kept in a separate branch, and every branch has an address called a path, written in braces like {0;0} or {1;2}.
So where a list is one line of items, a tree is a set of labelled lines:
{0;0} -> [a, b, c]
{0;1} -> [d, e]
{1;0} -> [f]The whole point of the branches is that structure carries meaning. If each branch is one floor of a building, the tree keeps every floor's columns grouped and labelled by floor - lose the tree and you have one anonymous pile of columns with no idea which belongs where. In Python terms, a tree is closest to a list of lists (or a dictionary keyed by path), and that is exactly how you will handle it in code. The reason Grasshopper uses trees rather than flat lists everywhere is that design data is naturally grouped: panels per facade, rooms per floor, points per curve. The tree remembers the grouping; a flat list forgets it.
The path notation rewards a second look, because the semicolons are not decoration. A path like {0;1} describes a position in a hierarchy: think of it as folder 0, subfolder 1 - trees can nest several levels deep, just as a project nests building, floor, room. Most everyday work stays one or two levels deep, but knowing the path is an address with levels explains why some components add a level (grafting) and others remove one (flattening). When Grasshopper shows you {0;0}, {0;1}, {1;0} in a Panel, it is simply listing the addresses of the branches it is holding - read them like a table of contents for your data, and the tree stops feeling opaque.
List = one run of items. Tree = many lists, each with an address {branch;index}. Structure = meaning.
Flatten, graft and the matching problem
Two operations reshape trees constantly, and understanding them cures most Grasshopper confusion. Flatten collapses every branch into a single one - it throws the grouping away and gives you one long list. Graft does the opposite - it pushes each item down into its own new branch, so a list of three becomes three branches of one.
Why does this matter so much? Because Grasshopper components match items between inputs by their tree structure. Give a Line component ten start points in one flat branch and ten end points in one flat branch, and it draws ten lines, pairing them in order. But give it ten start points flat and end points grafted into ten separate branches, and the matching goes haywire - you get a tangle, because the shapes do not correspond. The infamous moment when almost right but weirdly duplicated geometry appears is nearly always a flatten-versus-graft mismatch between two inputs.
The practical skill is to picture the tree shape of each input and make them correspond. Two inputs that should pair item-for-item need matching structures. When they do not line up, the fix is usually a Flatten, a Graft or a Simplify on one side - not more components, just the right reshaping. Seeing data trees is, more than anything, the Grasshopper skill.
A concrete example makes the matching rule stick. Suppose you have three rooms, each with its own list of furniture points, held as a tree of three branches, and you want to move each room's furniture by that room's own offset vector. If the points are a tree of three branches and the vectors are a flat list of three, the shapes do not correspond and the Move component will misbehave. Graft the three vectors so each sits in its own branch, and now branch {0} of points meets vector {0}, branch {1} meets vector {1}, and everything lines up. The lesson generalises: when you want per-group behaviour, make the group structures match. That single instinct - look at both trees, make them correspond - resolves the overwhelming majority of Grasshopper mysteries without a line of code.
Flatten = all one branch. Graft = each item its own branch. Mismatched shapes = wrong matching.
Reading and building trees in a script
When you set a script component input to Tree access (lesson 6.1), your code receives the whole structure as a DataTree object, and you can walk it branch by branch. The pattern is to loop over the tree's paths and pull each branch as a list:
# x set to Tree access -> x is a DataTree
totals = []
for i in range(x.BranchCount):
branch = x.Branch(i) # this branch as a list
totals.append(sum(branch))
a = totalsThat sums each branch independently - one total per floor, say - which is exactly the kind of per-group operation trees exist for. To build a tree to send back out, you create a DataTree and add items at chosen paths:
from Grasshopper import DataTree
from Grasshopper.Kernel.Data import GH_Path
tree = DataTree[object]()
for i, row in enumerate(grid): # grid is a list of lists
for value in row:
tree.Add(value, GH_Path(i)) # branch i gets this row
a = treeHere every sublist of grid becomes its own branch {i} - you are grafting-by-construction. This is the honest picture of trees in code: a DataTree on the Grasshopper side maps neatly onto a list of lists on the Python side, and Branch(i) and GH_Path(i) are how you cross between them. Most of the time you convert the tree to nested lists, do ordinary Python, and convert back.
That convert-work-convert rhythm is the single most useful habit for tree scripting, and it keeps your code readable. Rather than fighting DataTree methods throughout, many people write a tiny helper that turns a tree into a plain list of lists at the top of the script, do all their thinking in ordinary Python where every skill from Modules 2 and 3 applies, then rebuild a DataTree only at the end for the output. The enumerate in the build example is doing real work: it pairs each sublist with its index i, and that index becomes the branch path, so the output tree mirrors the structure of your Python nesting. Keep the two pictures aligned in your head - branch equals list, path equals index - and moving data across the boundary stops being fiddly.
Tree access -> DataTree. x.Branch(i) = a list. Build back with DataTree + GH_Path(i).
When code beats components - and when it does not
So when should you drop into a script for tree work, and when should you stay visual? An honest guide, because both extremes waste time.
Code tends to win when the logic is genuinely complex: multi-level conditionals, iteration where each step depends on the last, custom matching rules, or reshaping that would take a long, unreadable chain of Tree components. In a script you can loop over branches, use ordinary Python if and for, and express the intent directly - a paragraph of code instead of twenty tangled components.
Components tend to win for the ordinary flow: standard reshaping (a single Flatten, Graft or Path Mapper), anything you want to see and tweak visually, and work others must read and edit - a wired definition is often more legible to a collaborator than a dense script. Components also give live preview at every step, which is invaluable while you are still finding the shape of a problem.
The mature answer, as in lesson 6.1, is both: reshape and flow with components, and drop a script component in for the one knotty branch of logic that code expresses cleanly. Avoid the two failure modes - a heroic all-visual contraption straining to do what four lines of Python would, and a monolithic script that swallows work the canvas shows more clearly. Match the tool to the stretch, and let the tree structure - not habit - decide.
There is also a middle path worth knowing: you do not have to choose script-or-components for a whole definition, and you rarely should. The best Grasshopper users move fluidly - a Path Mapper here, a two-line script there, a Graft on one input - treating code as one more component that happens to be very flexible. A useful test when you are stuck is to ask, would I rather read this as a picture or as a sentence? Reshaping and flow are pictures; conditional rules and iteration are sentences. Answer honestly for the stretch in front of you and the right tool usually names itself. Over a career, this judgement - not memorised syntax - is what makes someone quick and reliable in Grasshopper, and it is the note this module ends on.
branch
One list of items inside a data tree
Maps to a single Python list. x.Branch(i) hands you branch i as a list you can loop over.
path (GH_Path)
The address of a branch, like {0;1}
Just a label for a branch. Build one with GH_Path(i) when adding items to a DataTree in code.
flatten
Collapse all branches into one flat list
Equivalent to concatenating every sublist. Discards grouping - use when you truly want one list.
graft
Put each item into its own new branch
Wraps each item in its own list. The opposite of flatten; used to make structures correspond.
DataTree
The Grasshopper tree object in a script
Received with Tree access; built with DataTree() plus tree.Add(value, GH_Path(i)). Think list of lists.
Workshop - per-branch totals with a tree
Build a script that receives a data tree of numbers - imagine areas grouped by floor - and returns one sum per branch, keeping the grouping. It cements Tree access, walking branches, and the tree-as-list-of-lists picture.
Rhino 8 with Grasshopper. Entwine or Merge to build the test tree. No external libraries.
Goal: turn a tree of numbers (one branch per floor) into one total per branch Inputs: a data tree of numbers wired into a script input set to Tree access Time: ~35 minutes
- 1Create a small data tree upstream: use an
Entwinecomponent, or several number lists merged, so you have (say) three branches with different counts of numbers - your stand-in for areas per floor. - 2Drop a Python 3 script component, wire the tree into input
x, then right-clickxand set access to Tree. Confirm your code now receives a DataTree, not a flat list. - 3Loop with
for i in range(x.BranchCount):, pull each branch withbranch = x.Branch(i), and computesum(branch). Collect the totals into a Python list. - 4Assign the list of totals to output
aand check you get one number per branch. Compare against a visual approach: wire the same tree into aMass Additioncomponent and confirm the numbers match - proof your script and the components agree. - 5Bonus: instead of returning a flat list, build a DataTree that puts each total back on its original path using
GH_Path(i), so downstream components still know which floor each total came from.
You’ll walk away with
A Grasshopper Python 3 component, input set to Tree access, that returns one sum per branch of an incoming tree - optionally rebuilt as a tree so the per-floor grouping is preserved downstream.
Three altitudes on the same idea
Read the band that fits you — or all three.
Trees are how Grasshopper keeps your project organised - panels per facade, columns per floor, rooms per level - and reading them is the skill that unlocks serious definitions. When a facade script mysteriously duplicates or drops panels, it is almost always a tree mismatch, and the fix is a Flatten or Graft, not more components. Learn to picture the branch structure of each input and the hardest part of Grasshopper becomes routine.
Even a modest layout definition uses trees the moment you group things - fixtures per room, tiles per surface, items per zone. You rarely need to build trees in code, but you do need to recognise when a Flatten or Graft will make two lists line up, so your lighting points actually match your ceiling regions. Understanding the list-versus-tree distinction turns Grasshopper from unpredictable to controllable for everyday interiors work.
Data trees are the concept that separates people who dabble in Grasshopper from those who are fluent, and employers can tell the difference fast. Being able to explain a path, flatten versus graft, and when to reach for a Tree-access script marks you as genuinely computational. It maps directly onto the list-of-lists thinking from Module 2, so you already have the mental model - you are just learning Grasshopper's vocabulary for it.
“Data trees are a weird Grasshopper thing with no relation to normal programming - I just have to memorise flatten and graft.”
Do it yourself
Think in branches and paths.
- 1In plain Python terms, what everyday data structure is a data tree most like?
- 2What does Flatten do to a tree, and what does Graft do - describe each in one sentence.
- 3Two inputs to a component produce oddly duplicated geometry. What is the most likely cause and the usual fix?
- 4With an input set to Tree access, how do you get branch number i as a list you can sum?
- 5Give one situation where a script beats components for tree work, and one where components are the better choice.
The one line to carry out
Peer-reviewed journals & authoritative standards
- 01Grasshopper 3D — Wikipedia, 2026.
- 02List (abstract data type) — Wikipedia, 2026.
- 03Associative array — Wikipedia, 2026.
- 04Parametric design — Wikipedia, 2026.
That completes scripting Rhino and Grasshopper - from the script component to geometry to the trees that organise it. Next module moves to the other giant of design software: Python inside Revit and Dynamo, where the same skills meet BIM data and the Revit API.
The author
Amogh N P
Architect, interior designer, and creative polymath. Studio Matrx began in his notebooks — his vision of design made honest, useful, and open to everyone. Its Academy is written and taught in his memory, and free, forever.
More about Amogh →