Custom plugins
Bring the Grasshopper plug-ins your team already uses to NodePen. Once one is enabled, its components sit in the component library for every script your team owns, and they solve, preview, export and work with your agent like any built-in component.
Plug-ins are part of the Team and Custom plans; see the pricing page. They live in the team's own Plugins tab, under Files. Only a team's admins upload, enable, disable and remove them. A team that moves to the Personal plan keeps the plug-ins it has, still enabled as they were, but can no longer change them.
What NodePen supports
NodePen runs Grasshopper on Linux, under .NET. A plug-in works when it is .NET code from top to bottom.
| Works | Does not work |
|---|---|
| Components written in C#, or any other .NET language | Native Windows libraries (.dll files written in C or C++), such as a geometry kernel built for Windows |
Plug-ins that ship their own .NET .dll files beside the .gha, uploaded as a .zip |
Anything that needs Windows itself, or Rhino's interface: dialogs, the viewport, the command line |
A plug-in that calls into a native library usually uploads fine, and its components fail when they solve, with the error on the component.
Each plug-in also has to:
- Declare a library and at least one component. The library's id is how NodePen tells one plug-in from another, and its version from the next.
- Bring its own components. A plug-in is refused if any component has the same id as one Grasshopper ships, or one another of your team's plug-ins already has: there would be no way to tell which one a script meant.
- Be one version per team. Uploading a new version of a library your team has replaces the plug-in that has it, in place.
Upload a plug-in
In Team settings → Grasshopper plug-ins, choose Upload plug-in. Upload either:
- The
.ghafile itself, when the plug-in is that one file. - A
.zip, when the plug-in needs.dllfiles beside it. Put the.ghaat the top of the zip, or inside a single folder at the top (what Compress makes of a folder on Windows and macOS), with the.dllfiles it needs.
A zip can hold any number of .gha files at that level, as plug-ins that ship several assemblies
sharing one set of .dll files do. Each .gha is a library of its own, with its own name, and
together they are one plug-in: enabled, updated and removed together. The plug-in is named after
the library with the most components, and the settings list shows each library under it. A .rhp
beside them is a Rhino plug-in, not a Grasshopper one, and is left out. If any .gha in the zip
can't be loaded, the whole upload is refused, naming it.
Either can be up to 64 MB. A zip can hold up to 500 files and unpack to up to 256 MB. Files the
macOS Finder adds to a zip (__MACOSX, .DS_Store) are ignored.
NodePen loads the plug-in to check it before keeping it, then lists it with its version and how many components it has, along with anything Grasshopper had to say while loading it. If it can't be used, the upload is refused with the reason.
A new plug-in starts disabled, so nobody's scripts change until you choose. Enable it, and its components appear in every script your team owns from the next time they load.
A .dll that Grasshopper or another of your plug-ins has already loaded, such as
Newtonsoft.Json, is used as already loaded, whatever version your zip carries. That is how
Grasshopper behaves on the desktop too.
Update, rename, disable or remove
- Update by uploading the new version. It replaces the plug-in that has any of the same libraries, keeping its name and staying enabled if it was. A zip whose libraries are spread across two or more of your plug-ins is refused: remove all but one of them first.
- Rename to call it what your team calls it. Its libraries keep their own names, and so do their components.
- Disable to stop your team's scripts from using it. Its components stay where they are, but stop solving until it is enabled again.
- Remove to delete it. A script that uses one of its components stops solving, with the missing component named, until the plug-in is uploaded again.
Each takes effect on your team's next solve.
From a script
Everything above can be done through the API as well, with an API token a team
admin minted with the plugin:write scope — to publish a new build of your plug-in from CI, say.
An upload goes straight to storage, in three steps: start it with POST /api/v1/plugins/uploads
and the file's size, PUT the file to the uploadUrl that answers with, then complete it with
POST /api/v1/plugins/uploads/{uploadId}.
START=$(curl -s -X POST https://app.nodepen.io/api/v1/plugins/uploads \
-H "Authorization: Bearer $NODEPEN_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"fileName\": \"MyPlugin.gha\", \"sizeBytes\": $(wc -c < MyPlugin.gha)}")
curl -T MyPlugin.gha "$(echo "$START" | jq -r .uploadUrl)"
curl -X POST "https://app.nodepen.io/api/v1/plugins/uploads/$(echo "$START" | jq -r .uploadId)" \
-H "Authorization: Bearer $NODEPEN_TOKEN"
The upload is checked exactly as one from the settings page is, and a new version replaces the old
one and stays enabled if it was. Listing plug-ins, and the components each contributes, needs
team:read. See Plugins in the API reference for every endpoint.