Node Tree Organisation CookBook

Hello !

This is something I have in the back of my head since a long time. It’s all the tricks we can share to make node trees easier to read and to work with.

This has many benefits like :

  • You can work faster by being a little less lost in your own mess.

  • It’s easier to manage bigger projects .

  • It’s easier to go back to an old project and to understand the logic behind it…

  • It’s easier to collaborate with others .

  • You avoid being quoted in scripts from another dimension

Therefore I’ll post some tricks from time to time. Basically the kind of stuff I wished to have known when I started, or advanced tricks I just find out !
Everyone is welcome to share anything they like as well :slight_smile:

Sometime I took the liberty to pick a few “bad” examples from internet. I hope it’s fine and I apologize in advance if it’s not. Feel free to tell me and I’ll replace those image with personal examples.

Data-Flow and Grouping
This one is about the overall look of a tree. It’s basically making things readable from a bird-eye view. And it’s basically the end goal of having your tree organised.


The idea is to have a clear representation of how the data flows in the tree. Kind of what the green line represents in B.

And grouping is visually grouping elements so the tree looks simpler from a distance. Basically it’s mostly done by using frames, but putting stuff next to each-other is already enough to form something we interpret as a group.
So even if we have ~100 nodes in the tree. We can make it visually look like 15 elements (in yellow) by using frames. And the way they are arranged we might see ~6 main zones ( in purple) . So our brain is less overwhelmed by the complexity.

This is a good example of the things to avoid :

This is the same principle but with a different tree structure :

Obviously the same principle can be applied at a lower scale within the tree :

Less is more
I tend to reduce visual clutter by collapsing everything that isn’t meant to be tweaked… I also hide the options (menu Node / Show-Hide / Node Option) when changing them doesn’t make sense.

That way I can generally forget about everything that is collapsed as I know it’s meant to stay untouched and I can only tweak the exposed parts. Sometime I add a green color to a parameter that is meant to be tweaked.
And I add a label to the node using F2 when that make sense.

Using multiple input nodes
This one is an easy and well known one, but in any case :
Instead of having one group input node connected to everything else like so :

I tend to prefer having one per parameter scattered all across the tree.
I made a little script that assign automatically colors and collapse those like so :

And this is all the inputs within the tree :

Using node alignement tools


To easily align node so the tree looks cleaner I use the operator nw_align_nodes from node wrangler. It’s not fancy but it works for me. I added it to a custom shortcut.

Naming reroutes
I like using capture attribute and in big trees it’s super useful to name reroutes as their names propagates through the tree. It’s super useful !

It’s ok to stay a bit dirty !
Obviously we don’t want to spend too much time organizing the tree in most cases.
When the tree is simple it’s ok to keep it messy. Also in general I tend to clean things up as a second step once things works.

What I generally do also is to clean things in several passes. Once I open a group / tree I start inspecting it and do a bit of cleanup. Therefore the trees that I open a lot ( generally the most complicated ones) tend to end up being the most well organized.

On Using Frames…
When using frames I tend to prefer having small groups of nodes that describe one small step in the process , rather than a bunch of nodes that do one big step :

It’s very personal but I find it much harder to follow the logic when the frame is too big. I also avoid using too many colors, unless I have a very strict convention.

However sometimes I use colors temporarily :
Purple means something is marked as TODO or to improve,
Orange/Red is that a part of the tree is buggy or need to be fixed ASAP.

Sometimes it’s useful to annotate the tree using Frames as labels :

And I think that’s enough for a first round of info !
Obviously it’s basic stuff but I hope it’s useful already and feel free to post your own tricks or more advanced techniques !
Thanks for reading !

45 Likes

Man, I just want to thank you for putting all of this together. I’ve already learned many of these principles, but you are helping SO many people by putting this out here. Mad respect :saluting_face:

There is one more thing which I might contribute, and that is that you can select any node and press CTRL+H to hide its unused sockets, and ALT+H to show them. This is especially useful for group input nodes!

6 Likes

GREAT thread.

I didn’t know I could add a label/name to a reroute - never occured to me to look. Thanks for mentioning it!

Huge ditto to “reuse input nodes, mute all but required on the outputs”… it’s tempting to just drag lines everywhere when you’re quickly working, but that extra moment of “no, don’t” - big readability bonus in the end.

4 Likes

Thanks a bunch @TheWorkshopWarrior and @thorn, I’m glad this could be useful ! So here is another round of tips ! Now with the basics out of the way we can make a ride into the niche/nerdy land :smiley:

Using text in frames
This one I don’t use often because it’s a bit cumbersome to setup compared to other nodal editors ( nuke, natron…) but once in a while you might want to explain a bit more the graph :

Different use for groups
I tend to see 3 possible use case for node groups (in geometry nodes).
First it can be a Tool, that is generally meant to be used in the modifier stack, even though they can work in a tree as well.

For instance hair nodes fall into that category :

Or something like a procedural generator / modular asset …

The second category is Utility node groups. These are high level nodes that are meant to extend the functionality of geometry nodes and are used only within a nodetree :


Those generally ends up being used multiple time in a project and I store them as assets either within my current .blend or as part of my own assets library that I use on all my projects.

This way I always have access to a curated list of utility groups through the asset browser. Rather than accessing them through menus which tend to be quickly crowded :

The third category is Organisational Nodegroups. It’s when your tree becomes so big that you start to create nested groups to make the tree more readable/ better organised :
For instance on this building generator I have these groups that generates the first floor, the sidewalk, the roof and the windows respectively…

These groups are generally used only once, and to avoid them to clutter the menu selection I can hide them by making their name starting with . :

This in fact works with every datablock, and you can retreive them by using the . in the search :

My point is that depending on the type of nodegroup we might want to store or reuse them a bit differently. Basically I want some separation between Utilities that I’m going to reuse while working on the Tool/Asset, and the Organisational groups that I want to hide so they don’t get in the way…
Then, when our tool is ready to be used we might want to make it an asset and “un-mark asset” all the other utility nodegroups, so that the final .blend shows only the main tool in the asset browser.
Then it’s possible to have one asset library with Utility nodegroups to build geometry node tools, and another library to use these tools without having to fiddle with the node editor.

BTW hiding stuff with . in the name can be used on text that you want to put in frames too !

Avoid many inputs in a Tool by thinking modular
This is a kind of personal preference but I tend to dislike giant nodes like that :


This is from a custom particles system. Even if now we can use some panels to split the group into different categories to mitigate the giant list… I end up being bored by just looking at them :smiley:

First in general I try to expose the very least parameters I can. When I’m not 100% sure something is needed I let it un-exposed. Or I look for ways to combine multiple stuff into one input.
Then I work with the tool, and when I really need something more I add it.
→ It’s much simpler to add something later than to remove it later !

We might think that more choice is better, but many time it’s possible to remove stuff and simplify the UI therefore making the tool more straightforward to use. This at least fit well how blender works in general…

And when no matter what I do, I need a lot of parameters, then I split the functionalities into different modules / nodegroups.

Instead of the giant node from earlier I did this :

And most of the time I use these tools like this :
image

It’s even possible to group parameters visually, so our brain use less time/power to “decode”/read the UI…
image

I agree that, at the end of the day, the modifier stack looks like a giant nodegroup with panels, but I tend to find that simpler to work with anyway…

Eventually these modules become a set of tools as assets and their modular nature allows to put even more stuff by creating new modules. Since we won’t use all the modules at the same time anyway…
image

Ok and that’s about it for this one. Probably next time we’ll go back to more generic stuff ! Hope this is useful anyway ! Thanks for reading !

12 Likes

Yea but it’s in French, that’s confusing!

On a more serious note I do like using panels, often those node groups with loads of inputs are necessary and panels do clean them up a lot, many times you do not need to tweak all of the parameters. It’s a bit lie the panels in the existing particle system (or all over the place), if you open them all up it is overwhelming!

5 Likes

Fear no more… I made a RCS post for adding an IA translator to blender so it’s ok to write nonsense stuff, the IA will figure it out eventually ! :+1: :ok_hand: :crossed_fingers:

I agree ! In fact I use panels a lot too ! Menu switch are also a great way to make the UI simpler !
Anyway, some people might prefer a giant collapsible node with panels, it’s fine ! Even if I tend to prefer modular stuff in general, it depends on the case obviously…

However what I didn’t liked in some old tutorials ( I don’t watch a lot of them nowadays) is that once the tutor built the tree, they frenetically expose every possible input to the UI without further thinking…
Like having 5 different seed values when maybe at some point two could be more than enough…

Another basic situation I’ve seen sometimes is similar to that :

We have one button to activate the use of an external object, and an object selector… When in fact it’s possible to use only one parameter like so :


Or like this :

It’s simple stuff obviously but maybe easy to miss for a beginner…

Just like when we say that if we can tell a story in 3 minutes it’s better than in 5. It’s worth looking if anything can be made simpler in the UI rather than just calling it a day once the main logic is in place…
But obviously it’s subjective,some people might prefer a bit more parameters for some reasons, there is no absolute truth in that for sure !

7 Likes

That’s a good one… off to change my switch nodes :grinning:

1 Like

@sozap Hey :slight_smile: very nice thread! I was wondering if you could give some feedback on this patch: https://projects.blender.org/blender/blender/pulls/142386
Would love to hear what you have to say about it. Thanks!
EDIT: I updated the description so it’s a bit more detailed!

2 Likes

Hi @filedescriptor !! I’m glad if I can help ! I’ll do an informal review here, I hope it’s ok ! We can discuss this further in private if you like and/or I can also report my feedback on projects.blender.org as well…

So, on the overall I’m fine with nodes not being of the same height, to a degree :
image

It’s true that those giant spherical principled BSDF are a bit too much. And I suppose it might be the motivation behind the PR.

Principled volume, it’s still a capsule shape like other collapsed node so I’d say it works but maybe it’s some kind of a limit…

And, it’s true that having consistent thickness would also make the timing more integrated (https://projects.blender.org/blender/blender/issues/110397) so it looks like in any case some improvements would help.

Now before going further, to be honest, I tend to dislike a bit those recent “hidden” connections :
image
Since we have some connection that are clearly defined with a dot, like with the mix node above. And at the same time there are other connections that looks pretty similar to just a wire going under a node… Like the bevel node that isn’t connected at all. And on the second principled only the gray wire is connected…

In the meantime it looks like a necessary evil, since those collapsible panel are pretty useful, and something like this would be also confusing :
image
But I’d say, having something that shows that there is some kind of connection would help a lot IMO.

So yeah, to me the real issue here is the lack of something like that :
image

Anyway, back to the subject…
As said, different node thickness doesn’t bother me that much, if the goal is to try to avoid those giant nodal circles maybe when collapsing it’s possible to hide all the unused socket at the same time . Just like when doing H and Ctrl-H …


Not sure if people would like it, at least I think I always do both…

Or another option could be to consider that if a panel is collapsed , we don’t show their sockets when the node is collapsed…

But again, I think the PR could works fine too, the main issue IMO being those hidden sockets that lack a visual clue :slight_smile:

Ok, sorry to throw even more stuff at you ! Hope that helps somehow :slight_smile: !

6 Likes

Thanks for taking the time to look at it :slight_smile:

I think you’re right yea. Then the same could be done for collapsed panels in nodes as well.

That could help, but only if the number of connections is rather small. Same problem when many sockets are connected.

I think showing something where all the connections go in could help. Maybe instead of “on top” of the base node it could be underneath. To make a clear distinction between sockets that can be interacted with and sockets that just show that something is going into the node.

2 Likes

Large Group Input nodes are often hard to handle. Like sozap, i also often use multiple input nodes and hide the unused sockets with ctrl-h.

This is very helpful but it would be even better if the socket that lets you create new inputs by draging noodles into it was not hidden with ctrl-h. That would prevent the user from needing to unhide large input nodes to expose a new value.

2 Likes

Great ! I think it’s quite a good QOL improvement !

Yeah, indeed we need a distinction. Underneath sounds great !
What I had in mind was a slightly elongated slot like in the join geometry.
But it would keep a fixed size and if possible allowing all the wires to spread slightly so we induce the idea that they aren’t connected in the exact same spot. But that doesn’t communicate the fact that we can’t touch them.

image
Another random idea could be some kind of a pipe shape… But underneath is probably the easiest to understand.

Super cool that all this get some consideration !

3 Likes

Wow, I was hoping to see this thread, thanks for writing it! I hope it will reach more people in dire need of getting their spaghetti straightened out :ramen:

I’d like to add a bit on changing node colors: [depending on the user, of course] it can be useful to have consistent Color Presets properly set up and colorize the nodes as soon as you add them.
It’ll be easier to keep track not only of various inputs, but also some important outputs like stored attributes for use outside of the current tree.

image

8 Likes

Hey Thanks a bunch ! You’re right, Color is a great topic !!

My personal code is like this :
image
Inputs in mint-green, attributes in purple. In green a few nodes that are meant to be edited, that I might eventually expose in the nodegroup. And a orange color for warning/Bug.

I’ve seen in this very cool post from @eelh that she also colorize inputs and attributes as well. It’s fun to see that we tend to pick similar habits :slight_smile: .
Anyway, her post is worth checking for another round of little node tricks !

So here is another related trick : Use a script to cleanup the tree !
Since it can be tedious to set colors and label on nodes we can use a script to do it for us.
Here is what it do on the tree :

Here is the code if you want to play with it…
Keep in mind though, it clean stuff recursively, so if you have groups inside your tree it’s going to apply change inside those as well… It’s written while working so no undo, code isn’t standard … Stuff like that :smiley:

Anyway, if you like nodes you probably like python too, so I encourage you to write your own !

Code
import bpy

def count_links(node,inputs=False) :
    t = []
    if inputs :
        for l in node.inputs :
            if l.links : t.append(l.name)
    else :
        for l in node.outputs :
            if l.links : t.append(l.name)
    return(t)
def set_val(value) :
    if type(value) == int   : return(value)
    if type(value) == float : return(round(value,2))
    return(value)
def get_all_nodes_recursives(tree) :
    out = []
    if not tree : return(out)
    for node in tree.nodes :
        out.append(node)
        if node.type =="GROUP" :
            out += get_all_nodes_recursives(node.node_tree)
    return(out)
#def get_reroute_recursive(node) :
#    nodes = [node]
#    for link in node.outputs[0].links :
#        if link.to_node.type == "REROUTE" :
#            nodes += get_reroute_recursive(link.to_node)
#    return(nodes)
def hide_sockets(node,collapse = True) :
    for socket in node.outputs :
        if not socket.links : socket.hide = True
    for socket in node.inputs :
        if not socket.links : socket.hide = True
    if collapse : node.hide = True
#def set_reroute_follow(node):
#    if node.type != "REROUTE" : return
#    if not node.label         : return
#    for nd in get_reroute_recursive(node) : nd.label = node.label
def set_node_attribute(node) :
    ''' cosmetique : change le nom de la node '''
    if node.type in ["STORE_NAMED_ATTRIBUTE", "INPUT_ATTRIBUTE", "REMOVE_ATTRIBUTE", "ATTRIBUTE"] :
        if node.type == "ATTRIBUTE" :
            name = node.attribute_name
        else :
            name = node.inputs["Name"].default_value
        if node.type == "STORE_NAMED_ATTRIBUTE" : name = f"Store: {name}"
        if node.type == "REMOVE_ATTRIBUTE" : name = f"Remove: {name}"
        node.color = (0.427, 0.058, 0.887)
        node.use_custom_color = True
        node.name  = name
        node.label = name
        hide_sockets(node)

    if node.type == "ATTRIBUTE_DOMAIN_SIZE" : # DOMAIN SIZE
        node.label = f"{node.component.capitalize()} Domain Size"
def set_node_group(node) :
     if node.type == "GROUP" :
         node.name         = node.node_tree.name
         node.show_options = False

     if node.type == "GROUP_INPUT" :
         node.color            = (0.04,0.4,0.255)
         node.use_custom_color = True

         inp = count_links(node)
         if len(inp) == 1 :
             name = f"In: {inp[0]}"
             node.name  = name
             node.label = name
             hide_sockets(node)
def set_node_convert(node) :
    if node.type == "SEPXYZ" :
        node.label = f"Separate {''.join(count_links(node))}"
        hide_sockets(node, collapse=False)

    if node.type == "COMBXYZ" :
        node.label = f"Combine {''.join(count_links(node,inputs=True))}"
        hide_sockets(node, collapse=False)
def set_node_math(node) :

    if node.type == "MATH" :
        if node.operation == "SUBTRACT" :
            if node.inputs[0].default_value == 1.0 and not node.inputs[0].is_linked :
                node.label = "Invert (1-val)"
                hide_sockets(node)

        # colapse when both inputs are connected
        # if len(node.inputs) == 2 and node.inputs[0].is_linked and node.inputs[1].is_linked :
        #     hide_sockets(node)

        if node.operation in ["ADD","MULTIPLY","DIVIDE", "MODULO", "SUBTRACT"] :
            if not node.inputs[1].is_linked :
                node.label = f"{node.operation.capitalize()} {set_val(node.inputs[1].default_value)}"
                hide_sockets(node)
            # collapse when boths inputs are connected.th
            if len(count_links(node,inputs=True)) == 2 : hide_sockets(node)

    if node.type == "VECT_MATH" :
        # print(node.operation)
        if node.operation == "SCALE" :
            if node.inputs[3].default_value == -1.0 :
                node.label = "Scale * -1"
                hide_sockets(node)

        if node.operation in ["ADD", "MULTIPLY", "DIVIDE" , "SUBTRACT", "DISTANCE"] :
            # collapse nodes that got two connections ( no editable params)
            if len(count_links(node,inputs=True)) == 2 : hide_sockets(node)

    if node.type == "FIELD_ON_DOMAIN" :
        node.label = f"Evaluate on {node.domain.capitalize()}"
        hide_sockets(node)

    if node.type == "COMPARE" :
        if len(count_links(node,inputs=True)) != 1 :
            node.label = ""
            return
        socket = 0
        if node.data_type == "FLOAT"  : socket = 1
        if node.data_type == "INT"    : socket = 3
        if node.data_type == "STRING" : socket = 9
        if socket == 0 : return

        node.label = f"{node.operation.capitalize()} {set_val(node.inputs[socket].default_value)}"
        hide_sockets(node)

    if node.type == "RANDOM_VALUE" :
        node.label = f"Random {node.data_type.capitalize()}"
        if node.data_type == "FLOAT" or node.data_type == "INTEGER" and len(count_links(node,inputs=True)) ==2 :
            node.label += f"  {set_val(node.inputs[2].default_value)}/{set_val(node.inputs[3].default_value)}"
            #node.width_hidden = 160.0
            node.width        = 160.0
            hide_sockets(node)

    if node.type == "ABSOLUTE" :
        hide_sockets(node)

def set_node_input(node) :
    # Hide only (single input node)
    if node.type in ["INDEX","POSITION"] :
        # print(node)
        node.hide = True

    sockets = count_links(node)
    if len(sockets) != 1 : return
    if node.type == "SPLINE_LENGTH" :
        if sockets[0] == "Point Count" : node.label = "Spline Point Count"

    if node.type == "SPLINE_PARAMETER" : node.label =  sockets[0]
    if node.type == "INPUT_SCENE_TIME" : node.label =  f"Scene Time {sockets[0]}"

    if node.type == "MESH_ISLAND" :
        if sockets[0] == "Island Index" : node.label = "Mesh Island Index"
        if sockets[0] == "Island Count" : node.label = "Mesh Island Count"

    # Hide and Collapse
    if not node.type in ["SPLINE_LENGTH", "SPLINE_PARAMETER", "INPUT_SCENE_TIME", "MESH_ISLAND"] : return
    hide_sockets(node)
def set_node_geo(node) :
    if node.type == "SEPARATE_GEOMETRY" :
        node.label = f"Separate {node.domain.capitalize()}"
        hide_sockets(node)
    # print(node.type)
def set_modifier_names() :
    for ob in bpy.data.objects :
        if not ob.type in ["MESH","CURVE", "CURVES"] : continue
        for mod in ob.modifiers :
            if mod.name.startswith("GeometryNodes") :
                if not mod.node_group : continue
                mod.name = mod.node_group.name

def nodetree_format(tree) :
    print(tree)
    for node in tree.nodes :
        if node.type == "GROUP" : nodetree_format(node.node_tree)
        #print(node.type)
        #et_reroute_follow(node)
        set_node_attribute(node)
        set_node_math(node)
        set_node_convert(node)
        set_node_group(node)
        set_node_input(node)
        set_node_geo(node)
def main(operator, context):
    space = context.space_data
#    node_tree = space.node_tree
#    node_active = context.active_node
#    node_selected = context.selected_nodes
    nodetree_format(space.node_tree)
#    for node in space.node_tree.nodes :
#        #print(node_tree)
#        #print(node.type)
#        set_reroute_follow(node)
#        set_node_attribute(node)
#        set_node_math(node)
#        set_node_convert(node)
#        set_node_group(node)
#        set_node_input(node)
#        set_node_geo(node)
    set_modifier_names()


class NodeFormat(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "node.format_tree"
    bl_label = "Format Tree"

    @classmethod
    def poll(cls, context):
        space = context.space_data
        return space.type == 'NODE_EDITOR'

    def execute(self, context):
        main(self, context)
        return {'FINISHED'}


def menu_func(self, context):
    self.layout.operator(NodeFormat.bl_idname, text=NodeFormat.bl_label)


# Register and add to the "Node" menu (required to also use F3 search "Simple Node Operator" for quick access).
def register():
    bpy.utils.register_class(NodeFormat)
    bpy.types.NODE_MT_node.append(menu_func)


def unregister():
    bpy.utils.unregister_class(NodeFormat)
    bpy.types.NODE_MT_node.remove(menu_func)


if __name__ == "__main__":
    register()
5 Likes

Since coloring nodes can be annoying I will self promote my free Addon which makes that a lot easier here:

5 Likes

Nice thread!
I use this extension and I’m quite happy with it:

I may jump in later to share some of my own organization behaviors.
Just so I won’t forget, those are what I can think of from the top of my head:

  1. I like attaching small bunches of math operations in a collapsed stack under the next relevant node.
  2. I like creating (when possible) self sufficient sections that get a single input and outputs a single link.
  3. I tend to not use node group when prototyping the first pass of a node tree, I find it easier to debug and inspect
5 Likes

Cool @Lumpengnom and @aryeramaty ! It’s great to discover new tools and techniques !

Awesome ! Please do !

Here is another one :
If your nodes are named properly you can find them with Ctrl+F
Problem is that nodes got not super useful names by default… Like “Group.001”, or “Group Input.010”… But it’s possible to change that especially with a bit of python… The script I posted earlier does that too. Then finding for instance where is an input is super easy :
Peek 21-07-2025 00-22
It save quite some time when navigating big trees !

6 Likes

I do my nodes from left to right because the logic behind is same as reading. You read in west form left to right thus it automatically dictates the logic behind the flow of node causality.

Those who read dirrefently could with same logic made nodes from up to dow or right to left. For me left to right is the thing like I said :slight_smile:

1 Like

Yeah good point ! I don’t always follow that rule though, but yeah the node editor forces you to go from left to right and it’s best to go with the flow :slight_smile:

And here another round of personal tips …

Take some space !
It’s probably a bit controversial, but I like to space out stuff in the graph.
When working since we go from one side to another we tend to put everything close to each other. For instance this is a shader I left unfinished :


We can see some kind of grouping, but the flow is hard to get.

Now this is a cleaned up later version of the material :


By spacing things it gets easier to see the overall flow and groups in the tree. At some point we don’t want to change everything at the same time, so it’s ok to navigate the tree a bit more, especially when things are easier to get from a bird eye’s view !

Build visualization nodes !
This one isn’t about tree organisation, but it’s somewhat related to working efficiently. Basically I don’t use spreadsheet a lot, nor the attribute text.
But over time I build some nodes to help debug and visualize.

One that I always uses allows to draw a wireframe for curves and mesh :


There are option to draw points, normal, curve direction…

Another allows to draw some vectors :

Another one allows to draw some values exacly like the attribute text :

Having these nodes around helps to visualize what you’re doing, and on top of that you can add fancy debug features to your tools :

Finally , creating those groups is a great learning exercise !
Basically if building those tools are complicated for you maybe it’s that you lack fundamental knowledge. How would you be able to manipulate those data further if you can’t show them ? So I highly encourage people really motivated by geometry nodes to construct their own set of visualization nodes !

9 Likes

I’m adding this one here since it’s a great discussion about tree organisation in blender in comparison to Nuke :

This also showcase good practice and various well organised complex tree !

3 Likes