The Grasshopper Entropy Trap

Hi everyone,

After more than 15 years of using Grasshopper, in the midst of AI, I’ve been reflecting on a cycle that I think many of us fall into, where ones methods become a rigid, and persoannlly I’m looking to reshuffle my approach.

  • Day 1: You build a working mockup. It’s fast, it’s visual, and 90% of the functional geometry is done. It feels great.

  • Days 2 to: Entropy sets in. You start adding features just in case you need them later. Build fail-back options, logs, automate as much as can. You make it “super-parametric” for future reusability and make it friendly and annottate everything.

  • But .. Data trees become overly complex. New insights occur, and you realize midway through that a smarter approach is needed, meaning you have to rewire everything. It would take less time to delete it and start over, but you’ve already invested so much time into your “perfect” script that you just stick with it.

  • As the deadline approaches, panic sets in. You start installing external plugins just for a single node, make a mess of spaghetti code, and create ultimate script bloat. Where you intiially fiddled over ms delays and optimzied, you now accept ‘stupid’ compute-heavy solutions … But in the end somehow, you still deliver. :wink:

I’d love to hear how others deal with this. Lately, I’ve been leaning more and more toward using AI to generate customized C# and Python nodes. While this most of the times works and cleans up the canvas, it introduces a different problem: it masks the flow. You lose that immediate, visual feedback of data passing through the script, which is the exact reason I’ve starting use Grasshopper instead of pure code. I am more visually attuned and just fail at reading txt-based code … it doesn’t compute for me

  • How do you keep your definitions lean? Do you enforce a strict limit on parameters? I recall TomTom remark that any script over 100 node needs a rethink. :wink: to [The Hall Of Shame] thread
  • When do you decide to scrap and restart versus trying to patch an existing script?
  • What are your strategies for keeping things modular? (e.g., using Hops, Python/C# components, or strict clustering).

Looking forward to hearing your thoughts and experiences.

Have you seen this thread? Struggling with GH Definitions – Design Principles or Best Practices?

ah, damn, forgot all about that one :wink: Even shared my thoughts in it

I have to remind myself quite often that I don’t need to do absolutely everything in GH. Bake and finish / patch / fix that last piece in Rhino rather than obsess over doing it in GH.

Two days ago there was a post

I think per geometry modifiers can be a good alternative workflow in some cases.
At this stage its just vibecoded thing, and i imagine if you use modifiers on 120 different objects its not gonna work, but there is something to it.

I often find myself in the same loop you described in the post. Over time you lose control over algorithm, and forget many things, making yourself more and more prone for error, especially when you worked on the script two months ago, and have to come back to it.

Thanks for referencing the modifiers post here. We’ve already started to rebuild the project cleanly, and would be great to get more hands on it as well as your minds!

also @crz_06 if you’re vibe coding your scripts with c# and python but want the visual flow, www.raven.build lets you vibe code real grasshopper scripts, and now also lets you mix in python and c# components

Yeah saw it shared on the socials. Super interesting. My initial thought was good old memories of Discreet 3ds max 3 Modifiers

I usually do that for my clients
Make a visual studio project with a library and a grasshopper plugin
Make a script using the components
This enable me to do some test in c# component, then include that in components
This allow good reuse in project
image

I am not a developer so perhaps I miss some things, but I think doing a Log could be useful to trace error at the client level. Warning or error at component level are good, but one place will all problems could be also good. You can see one in Nautilus

public sealed class SingletonLog
{
    private static SingletonLog instance = null;
    private List<string> lst_keys;

    public void Clear()
    {
        lst_keys.Clear();
    }

    private SingletonLog()
    {
        lst_keys = new List<string>();

    }
    public void AddLog(string text)
    {
        lst_keys.Add(text);
    }
    public static SingletonLog Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new SingletonLog();
            }
            return instance;
        }
    }


    public string[] GetLogs()
    {
        return lst_keys.ToArray();
    }

}

}

using System;
using Grasshopper.Kernel;
using LDLIB;

namespace Nautilus.Various
{
public class LogComponent : GH_Component
{
GH_Document GrasshopperDocument;
IGH_Component Component;
string logTexts;
///


/// Initializes a new instance of the LogComponent class.
///

public LogComponent()
: base(“See Nautilus Log”, “NautilusLog”,
“Update Nautilus Log Output”,
“Nautilus”, “Various”)
{
}
///
///
///

///
///
///

public override GH_Exposure Exposure
{
get { return GH_Exposure.septenary; }
}
///
/// Registers all the input parameters for this component.
///

protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
{
pManager.AddBooleanParameter(“Update”, “Update”, “Update Log”, GH_ParamAccess.item, true);
pManager.AddBooleanParameter(“Clear”, “Clear”, “Clear Log”, GH_ParamAccess.item, true);
}

        /// <summary>
        /// Registers all the output parameters for this component.
        /// </summary>
        protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
        {
            pManager.AddTextParameter("Log", "Log", "Log information", GH_ParamAccess.list);
        }

        /// <summary>
        /// This is the method that actually does the work.
        /// </summary>
        /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param>
        protected override void SolveInstance(IGH_DataAccess DA)
        {
            Component = this;
            GrasshopperDocument = this.OnPingDocument();

            int indexOfOpenFile = 0;
            // Only generate it if the input has no source
            if (Component.Params.Input[indexOfOpenFile].SourceCount == 0)
            {
                InputTools.Button(ref Component, ref GrasshopperDocument, indexOfOpenFile, 11);
            }

            int indexClear = 1;
            // Only generate it if the input has no source
            if (Component.Params.Input[indexClear].SourceCount == 0)
            {
                InputTools.Button(ref Component, ref GrasshopperDocument, indexClear, 11);
            }
            bool clear = false;
            DA.GetData(indexClear, ref clear);
            if (clear) SingletonLog.Instance.Clear();


             logTexts = SingletonLog.Instance.GetLogs();
            DA.SetDataList(0, logTexts);

            //Set panel as output
            InputTools.SetPanelToOutput(ref Component, ref GrasshopperDocument, 0, 0);
        }

        /// <summary>
        /// Provides an Icon for the component.
        /// </summary>
        protected override System.Drawing.Bitmap Icon
        {
            get
            {
                return Properties.Resources.LogComponent;
            }
        }

        /// <summary>
        /// Gets the unique ID for this component. Do not change this ID after release.
        /// </summary>
        public override Guid ComponentGuid
        {
            get { return new Guid("7F6D6AB1-DE61-4287-A317-BECDEA585858"); }
        }
    }

}