Grasshopper and Rhino Scripting Automation with Claude Code
How architects can use Claude Code skills to generate Grasshopper definitions, RhinoScript, and Python scripts for computational design workflows
Go deeper with Archgyan Academy
Structured BIM and Revit learning paths for architects and students.
Computational design in architecture has always required a specific combination of geometric thinking and programming fluency. If you work with Grasshopper and Rhino, you have probably experienced the friction: you know exactly what surface you want to generate, what panel logic should drive the facade, or how the structural grid needs to respond to site constraints, but translating that intent into working GhPython components or RhinoCommon calls takes hours of debugging. Claude Code changes this dynamic entirely. By building reusable skills that understand Grasshopper’s data tree structures, Rhino’s geometry kernel, and the conventions of computational design, you can go from a plain-language description to a working parametric script in minutes rather than days.
This guide covers twelve practical areas where Claude Code skills accelerate Grasshopper and Rhino scripting workflows. Every section includes real code patterns you can adapt to your own projects. The audience is architects, computational designers, and BIM professionals who already work inside the Grasshopper canvas and want to move faster.
Why Computational Designers Need AI-Assisted Scripting
The typical computational design workflow involves three distinct phases: defining the geometric logic, implementing it as a visual program or script, and iterating on parameters until the output meets design intent. The second phase, implementation, is where most time gets lost. A designer might spend twenty minutes sketching the parametric logic on paper and then four hours wrestling with data tree mismatches, type casting errors, and RhinoCommon API calls that do not behave as documented.
Claude Code skills address this bottleneck directly. A skill is a reusable prompt template that you invoke from the command line. When you build a skill for Grasshopper scripting, you encode your knowledge of data tree conventions, common component patterns, and RhinoCommon geometry types into a template that Claude can use every time you ask for a new script. The result is not a generic code snippet from a forum post. It is a script shaped by your project’s specific conventions, your preferred variable naming, and the exact Rhino version you are targeting.
The practical benefits are concrete. You eliminate the lookup cycle for API methods you use infrequently. You reduce data tree errors by having the skill enforce proper tree path handling. You generate documentation alongside the code. And you maintain a library of skills that any team member can invoke, which means your computational design knowledge becomes institutional rather than personal.
The Grasshopper and Rhino Scripting Ecosystem
Before diving into specific skills, it helps to map out the scripting landscape that Claude Code will be generating code for. Rhino 7 and 8 support multiple scripting interfaces, each with different strengths.
GhPython is the most common entry point. It lets you write Python scripts inside Grasshopper components. The runtime is IronPython 2.7 in Rhino 7 and CPython 3 in Rhino 8 via the new ScriptEditor. GhPython components receive inputs as Grasshopper data trees and produce outputs that feed directly into downstream components. This is where most of the skills in this guide operate.
RhinoScript (VBScript-based) is the legacy scripting language built into Rhino. It is still useful for quick macros and batch operations that do not need the Grasshopper canvas. RhinoScript methods map closely to Rhino commands, which makes them intuitive for users who are comfortable with the command line.
RhinoCommon is the .NET SDK that underlies everything. Both GhPython and C# scripting components access Rhino’s geometry through RhinoCommon types like Point3d, Curve, Brep, and Mesh. Understanding RhinoCommon is essential because Claude Code needs to generate code that uses the correct types and methods for your target Rhino version.
Rhino 8 Script Editor introduces CPython 3 support with full access to pip packages. This is a significant shift because it means you can use NumPy, SciPy, pandas, and scikit-learn directly inside Grasshopper scripts. Several skills in this guide take advantage of this capability.
When building Claude Code skills for this ecosystem, the key context to encode is: which Python runtime you target (IronPython 2.7 or CPython 3), which RhinoCommon version your Rhino installation uses, and whether the generated code needs to handle Grasshopper data trees or operate as a standalone Rhino script.
Skill: GhPython Component Generation from Natural Language
This is the foundational skill that every other skill in this guide builds on. The goal is to describe a Grasshopper component’s behavior in plain language and receive a complete GhPython script with properly typed inputs, outputs, and inline documentation.
Here is a skill template that handles the core requirements:
# Skill: ghpython-component
# Description: Generate a GhPython component from a natural language description
# Inputs: component_description, input_params, output_params, rhino_version
"""
GhPython Component: {component_description}
Inputs:
{input_params}
Outputs:
{output_params}
Target: Rhino {rhino_version}
"""
import Rhino.Geometry as rg
import ghpythonlib.treehelpers as th
from Grasshopper import DataTree
from Grasshopper.Kernel.Data import GH_Path
# Input validation
if x is None:
raise ValueError("Input 'x' is required")
# Core logic placeholder - Claude fills this based on description
# Example: generate a series of points along a curve
def process_geometry(curve, count):
"""Divide curve into equal segments and return points."""
params = curve.DivideByCount(count, True)
if params is None:
return []
points = [curve.PointAt(t) for t in params]
return points
# Execute and assign outputs
a = process_geometry(x, y)
The critical detail in this skill is the data tree handling. Grasshopper passes inputs as either single items, lists, or data trees depending on how upstream components are connected. A robust skill template includes tree-aware input processing:
import ghpythonlib.treehelpers as th
from Grasshopper import DataTree
from Grasshopper.Kernel.Data import GH_Path
def ensure_list(input_val):
"""Convert single item or data tree branch to a flat list."""
if isinstance(input_val, DataTree[object]):
return [item for branch in input_val.Branches for item in branch]
if not isinstance(input_val, list):
return [input_val]
return input_val
curves = ensure_list(x)
counts = ensure_list(y)
When you invoke this skill, you provide a description like “create a component that takes a surface and a point count, populates the surface with random points using UV parameterization, and outputs the points along with their UV coordinates.” Claude generates the complete script including imports, input validation, the UV sampling logic using surface.PointAt(u, v), and properly formatted output assignments.
Skill: Parametric Facade Pattern Generator
Facade design is one of the most common applications of Grasshopper in practice. This skill generates the scripting backbone for parametric facade panels driven by attractor points, solar orientation, or programmatic rules.
The skill encodes the standard facade workflow: subdivide a surface into panels, evaluate a condition at each panel centroid, and map that condition to a geometric transformation. Here is the core pattern:
import Rhino.Geometry as rg
import math
def generate_facade_panels(surface, u_count, v_count, attractor_pts,
min_opening, max_opening):
"""
Subdivide surface into panels with attractor-driven openings.
Each panel's opening size is inversely proportional to its
distance from the nearest attractor point.
"""
panels = []
openings = []
u_domain = surface.Domain(0)
v_domain = surface.Domain(1)
u_step = (u_domain.Max - u_domain.Min) / u_count
v_step = (v_domain.Max - v_domain.Min) / v_count
for i in range(u_count):
for j in range(v_count):
# Panel corner parameters
u0 = u_domain.Min + i * u_step
u1 = u0 + u_step
v0 = v_domain.Min + j * v_step
v1 = v0 + v_step
# Panel corner points
p00 = surface.PointAt(u0, v0)
p10 = surface.PointAt(u1, v0)
p11 = surface.PointAt(u1, v1)
p01 = surface.PointAt(u0, v1)
# Panel centroid
centroid = surface.PointAt((u0 + u1) / 2, (v0 + v1) / 2)
# Distance to nearest attractor
min_dist = min(centroid.DistanceTo(apt) for apt in attractor_pts)
# Normalize distance to opening ratio
max_dist = 50.0 # calibrate to project scale
ratio = max(0, min(1, min_dist / max_dist))
opening_size = min_opening + ratio * (max_opening - min_opening)
# Create panel outline
panel = rg.Polyline([p00, p10, p11, p01, p00]).ToNurbsCurve()
panels.append(panel)
# Create opening (scaled rectangle at centroid)
normal = surface.NormalAt((u0 + u1) / 2, (v0 + v1) / 2)
plane = rg.Plane(centroid, normal)
opening_rect = rg.Rectangle3d(
plane,
rg.Interval(-opening_size / 2, opening_size / 2),
rg.Interval(-opening_size / 2, opening_size / 2)
).ToNurbsCurve()
openings.append(opening_rect)
return panels, openings
a, b = generate_facade_panels(srf, uCount, vCount, attractors,
minOpen, maxOpen)
This skill accepts variations through the natural language prompt. You can ask for hexagonal panels instead of rectangular ones, for openings that respond to solar angle rather than attractor distance, or for panels that rotate rather than scale. The underlying structure remains the same: subdivide, evaluate, transform. Claude fills in the specific geometric operations based on your description while maintaining the data flow patterns that Grasshopper expects.
Skill: Structural Grid and Column Layout Automation
Structural grid generation is a task that benefits enormously from scripting because the rules are clear but the implementation involves tedious coordinate arithmetic. This skill generates column grids, beam lines, and structural bay definitions from high-level parameters.
import Rhino.Geometry as rg
def generate_structural_grid(origin, x_bays, y_bays, x_spacing, y_spacing,
column_radius, rotation_angle=0):
"""
Generate a structural grid with columns and grid lines.
Parameters:
origin: Base point for grid
x_bays: List of bay widths in X direction
y_bays: List of bay widths in Y direction
x_spacing: Default X spacing (used if x_bays is empty)
y_spacing: Default Y spacing
column_radius: Radius of column circles
rotation_angle: Grid rotation in degrees
"""
columns = []
grid_lines_x = []
grid_lines_y = []
grid_labels = []
# Build cumulative coordinates
x_coords = [0]
for bay in x_bays:
x_coords.append(x_coords[-1] + bay)
y_coords = [0]
for bay in y_bays:
y_coords.append(y_coords[-1] + bay)
# Rotation transform
rotation = rg.Transform.Rotation(
math.radians(rotation_angle), rg.Vector3d.ZAxis, origin
)
# Generate column positions
for i, x in enumerate(x_coords):
for j, y in enumerate(y_coords):
pt = rg.Point3d(origin.X + x, origin.Y + y, origin.Z)
pt.Transform(rotation)
# Column as circle
plane = rg.Plane(pt, rg.Vector3d.ZAxis)
col = rg.Circle(plane, column_radius).ToNurbsCurve()
columns.append(col)
# Label: A1, A2, B1, B2...
label = chr(65 + i) + str(j + 1)
grid_labels.append(label)
# Generate grid lines
total_x = x_coords[-1]
total_y = y_coords[-1]
for x in x_coords:
start = rg.Point3d(origin.X + x, origin.Y, origin.Z)
end = rg.Point3d(origin.X + x, origin.Y + total_y, origin.Z)
start.Transform(rotation)
end.Transform(rotation)
grid_lines_x.append(rg.Line(start, end).ToNurbsCurve())
for y in y_coords:
start = rg.Point3d(origin.X, origin.Y + y, origin.Z)
end = rg.Point3d(origin.X + total_x, origin.Y + y, origin.Z)
start.Transform(rotation)
end.Transform(rotation)
grid_lines_y.append(rg.Line(start, end).ToNurbsCurve())
return columns, grid_lines_x, grid_lines_y, grid_labels
a, b, c, d = generate_structural_grid(
origin, xBays, yBays, xSpacing, ySpacing, colRadius, angle
)
The value of this skill shows up when you need irregular grids. Instead of manually placing each column, you describe the bay pattern: “6m, 9m, 6m in X; 7.5m, 7.5m in Y; columns rotated 15 degrees to align with site boundary.” Claude generates the coordinate arrays and rotation transforms. You can extend the skill to handle curved grids by replacing the linear coordinate system with points sampled along guide curves.
Skill: Environmental Analysis Scripting
Environmental analysis scripts connect Grasshopper geometry to performance metrics. This skill generates components for solar exposure calculation, basic wind pressure estimation, and daylight factor approximation. These are not replacements for full simulation engines like Ladybug/Honeybee, but they provide fast feedback during early design phases.
import Rhino.Geometry as rg
import math
def calculate_solar_exposure(test_points, test_normals, sun_vectors,
context_geometry):
"""
Calculate hours of direct solar exposure for each test point.
For each point, cast a ray toward each sun position.
If the ray intersects context geometry, that hour is shaded.
Parameters:
test_points: List of Point3d on analysis surface
test_normals: Surface normals at each test point
sun_vectors: List of Vector3d pointing toward sun positions
context_geometry: List of Brep/Mesh for obstruction testing
"""
import Rhino
results = []
for i, pt in enumerate(test_points):
normal = test_normals[i]
exposed_hours = 0
total_hours = len(sun_vectors)
for sun_vec in sun_vectors:
# Check if sun is above horizon relative to surface normal
angle = rg.Vector3d.VectorAngle(normal, sun_vec)
if angle > math.pi / 2:
continue # Sun is behind the surface
# Cast ray toward sun
ray = rg.Ray3d(pt, sun_vec)
is_shaded = False
for geo in context_geometry:
if isinstance(geo, rg.Mesh):
hit = Rhino.Geometry.Intersect.Intersection.MeshRay(
geo, ray
)
if hit >= 0:
is_shaded = True
break
elif isinstance(geo, rg.Brep):
hits = Rhino.Geometry.Intersect.Intersection.RayShoot(
ray, [geo], 1
)
if hits and len(hits) > 0:
is_shaded = True
break
if not is_shaded:
exposed_hours += 1
exposure_ratio = exposed_hours / total_hours if total_hours > 0 else 0
results.append(exposure_ratio)
return results
a = calculate_solar_exposure(pts, normals, sunVecs, context)
The skill generates sun vectors from latitude, longitude, date, and time range inputs using basic solar geometry equations. For wind analysis, it produces simplified pressure coefficient distributions based on building orientation relative to prevailing wind direction. These quick-feedback scripts let designers evaluate massing options without leaving Grasshopper.
Skill: Mesh Optimization and Paneling Automation
Mesh operations are among the most script-intensive tasks in computational design. This skill handles common paneling workflows: converting NURBS surfaces to meshes with controlled density, optimizing mesh topology for fabrication, and generating panel outlines with offsets for mullion clearances.
import Rhino.Geometry as rg
def panelize_mesh(mesh, target_edge_length, planarize_iterations=10,
offset_distance=0.02):
"""
Process a mesh for fabrication paneling.
Steps:
1. Remesh to target edge length
2. Planarize quad faces iteratively
3. Generate panel outlines with offset for joints
Parameters:
mesh: Input Mesh
target_edge_length: Target panel size
planarize_iterations: Steps for planarization
offset_distance: Inset distance for panel edges
"""
panel_outlines = []
face_centers = []
face_normals = []
planarity_errors = []
# Analyze each face
for fi in range(mesh.Faces.Count):
face = mesh.Faces[fi]
# Get face vertices
v0 = mesh.Vertices[face.A]
v1 = mesh.Vertices[face.B]
v2 = mesh.Vertices[face.C]
if face.IsQuad:
v3 = mesh.Vertices[face.D]
pts = [
rg.Point3d(v0), rg.Point3d(v1),
rg.Point3d(v2), rg.Point3d(v3)
]
# Planarity check: distance of 4th point from plane of first 3
plane_ok, plane = rg.Plane.FitPlaneToPoints(pts)
if plane_ok == rg.PlaneFitResult.Success:
max_dev = max(abs(plane.DistanceTo(p)) for p in pts)
planarity_errors.append(max_dev)
# Face center and normal
center = rg.Point3d(
(pts[0].X + pts[1].X + pts[2].X + pts[3].X) / 4,
(pts[0].Y + pts[1].Y + pts[2].Y + pts[3].Y) / 4,
(pts[0].Z + pts[1].Z + pts[2].Z + pts[3].Z) / 4
)
else:
pts = [
rg.Point3d(v0), rg.Point3d(v1), rg.Point3d(v2)
]
planarity_errors.append(0) # Triangles are always planar
center = rg.Point3d(
(pts[0].X + pts[1].X + pts[2].X) / 3,
(pts[0].Y + pts[1].Y + pts[2].Y) / 3,
(pts[0].Z + pts[1].Z + pts[2].Z) / 3
)
face_centers.append(center)
face_normals.append(mesh.FaceNormals[fi])
# Create offset panel outline
offset_pts = []
for pt in pts:
direction = center - pt
direction.Unitize()
offset_pt = pt + direction * offset_distance
offset_pts.append(offset_pt)
offset_pts.append(offset_pts[0]) # Close the polyline
panel_outline = rg.Polyline(offset_pts).ToNurbsCurve()
panel_outlines.append(panel_outline)
return panel_outlines, face_centers, face_normals, planarity_errors
a, b, c, d = panelize_mesh(mesh, edgeLen, iterations, offset)
A key advantage of generating this code through a Claude Code skill is consistency. Every panel script your team produces uses the same planarity check method, the same offset convention, and the same output structure. When you need hexagonal panels or diagrid patterns instead of quads, you modify the prompt while the structural conventions stay locked in.
Skill: Data-Driven Design from CSV and Excel Inputs
Many real-world computational design projects start with spreadsheet data: room areas from a brief, structural loads from an engineer, or site coordinates from a surveyor. This skill bridges the gap between tabular data and Grasshopper geometry.
import Rhino.Geometry as rg
import csv
import os
def load_csv_to_geometry(file_path, x_col, y_col, z_col=None,
value_col=None, scale_factor=1.0):
"""
Read a CSV file and generate geometry from coordinate columns.
Parameters:
file_path: Absolute path to CSV file
x_col: Column name for X coordinates
y_col: Column name for Y coordinates
z_col: Column name for Z coordinates (optional, defaults to 0)
value_col: Column name for scalar values (optional)
scale_factor: Multiply coordinates by this factor
Returns:
points: List of Point3d
values: List of float (if value_col provided)
headers: List of column names
"""
if not os.path.exists(file_path):
raise FileNotFoundError(f"CSV file not found: {file_path}")
points = []
values = []
with open(file_path, 'r') as f:
reader = csv.DictReader(f)
headers = reader.fieldnames
for row in reader:
try:
x = float(row[x_col]) * scale_factor
y = float(row[y_col]) * scale_factor
z = float(row[z_col]) * scale_factor if z_col else 0.0
points.append(rg.Point3d(x, y, z))
if value_col and value_col in row:
values.append(float(row[value_col]))
except (ValueError, KeyError) as e:
continue # Skip rows with invalid data
return points, values, headers
def generate_sized_geometry(points, values, min_radius, max_radius):
"""
Create circles at points, sized proportionally to values.
Useful for visualizing density, load, or program area data.
"""
if not values:
return [rg.Circle(rg.Plane(pt, rg.Vector3d.ZAxis), min_radius).ToNurbsCurve()
for pt in points]
val_min = min(values)
val_max = max(values)
val_range = val_max - val_min if val_max != val_min else 1.0
circles = []
for pt, val in zip(points, values):
normalized = (val - val_min) / val_range
radius = min_radius + normalized * (max_radius - min_radius)
plane = rg.Plane(pt, rg.Vector3d.ZAxis)
circle = rg.Circle(plane, radius).ToNurbsCurve()
circles.append(circle)
return circles
pts, vals, hdrs = load_csv_to_geometry(
filePath, xColumn, yColumn, zColumn, valueColumn, scaleFactor
)
a = generate_sized_geometry(pts, vals, minR, maxR)
b = pts
c = vals
In Rhino 8 with CPython 3, you can extend this to use pandas for more sophisticated data manipulation. The skill can parse Excel files directly with openpyxl, handle multiple sheets, and apply filtering or aggregation before generating geometry. This is particularly powerful for urban design projects where GIS data needs to become 3D massing.
Skill: Grasshopper Definition Documentation Generator
One of the most overlooked productivity losses in computational design is undocumented definitions. A Grasshopper file opened six months later is almost unreadable without notes. This skill generates structured documentation from a Grasshopper definition’s component list and connections.
The approach works in two stages. First, you export the definition’s component data using Grasshopper’s built-in serialization. Then Claude Code processes that data into formatted documentation.
# Stage 1: Run inside GhPython to extract definition metadata
import Grasshopper as gh
import json
def extract_definition_metadata():
"""
Extract component names, positions, connections, and groups
from the active Grasshopper definition.
"""
doc = gh.Instances.ActiveCanvas.Document
metadata = {
"component_count": doc.ObjectCount,
"components": [],
"groups": [],
"wires": []
}
for obj in doc.Objects:
comp_data = {
"name": obj.Name,
"nickname": obj.NickName,
"type": type(obj).__name__,
"position": {
"x": float(obj.Attributes.Pivot.X),
"y": float(obj.Attributes.Pivot.Y)
},
"instance_id": str(obj.InstanceGuid)
}
# Extract input/output parameter names
if hasattr(obj, 'Params'):
comp_data["inputs"] = [
{"name": p.Name, "type": p.TypeName}
for p in obj.Params.Input
]
comp_data["outputs"] = [
{"name": p.Name, "type": p.TypeName}
for p in obj.Params.Output
]
metadata["components"].append(comp_data)
return json.dumps(metadata, indent=2)
result = extract_definition_metadata()
a = result
The second stage is the Claude Code skill itself. You feed the JSON output into a skill that produces a markdown document with: a component inventory, a data flow diagram described in text, input parameter descriptions with expected ranges, and a section explaining the design intent for each functional group. This documentation lives alongside the .gh file and can be regenerated whenever the definition changes.
This is especially valuable for teams. When a new member inherits a Grasshopper file, they get a readable overview instead of having to trace wires across a canvas.
Working with Rhino.Inside and Cross-Platform Workflows
Rhino.Inside is a technology that embeds Rhino and Grasshopper inside other applications, most notably Revit. This opens up powerful cross-platform workflows, and Claude Code skills can generate the bridging code.
A common workflow is to drive Revit family placement from Grasshopper geometry. The Rhino.Inside.Revit plugin exposes Revit’s API through Grasshopper components, but complex operations still require scripting. Here is the pattern for a GhPython component that creates Revit floor elements from Grasshopper curves:
# Rhino.Inside.Revit - GhPython component
# Requires: Rhino.Inside.Revit plugin active in Revit
import clr
clr.AddReference('RhinoInside.Revit')
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
from RhinoInside.Revit import Revit
doc = Revit.ActiveDBDocument
def create_floors_from_curves(boundary_curves, floor_type_name, level_name):
"""
Create Revit floor elements from Grasshopper boundary curves.
Parameters:
boundary_curves: List of closed planar curves (Rhino geometry)
floor_type_name: Name of the Revit floor type
level_name: Name of the target Revit level
"""
# Find floor type
collector = FilteredElementCollector(doc)
floor_types = collector.OfClass(FloorType).ToElements()
floor_type = None
for ft in floor_types:
if ft.Name == floor_type_name:
floor_type = ft
break
if floor_type is None:
return "Floor type not found: " + floor_type_name
# Find level
collector = FilteredElementCollector(doc)
levels = collector.OfClass(Level).ToElements()
level = None
for lv in levels:
if lv.Name == level_name:
level = lv
break
if level is None:
return "Level not found: " + level_name
floors = []
with Transaction(doc, "Create Floors from GH") as t:
t.Start()
for curve in boundary_curves:
# Convert Rhino curve to Revit CurveLoop
# (Rhino.Inside handles the conversion layer)
curve_loop = CurveLoop()
segments = curve.DuplicateSegments()
for seg in segments:
revit_curve = seg.ToRevitType()
curve_loop.Append(revit_curve)
loops = [curve_loop]
floor = Floor.Create(doc, loops, floor_type.Id, level.Id)
floors.append(floor)
t.Commit()
return floors
The Claude Code skill for Rhino.Inside workflows needs to understand both APIs: RhinoCommon on the geometry side and Revit API on the BIM side. The skill template encodes the transaction pattern (all Revit modifications must be wrapped in a Transaction), the type conversion between Rhino and Revit geometry, and the element collector pattern for finding Revit types and levels.
You can also build skills for the reverse direction: reading Revit model data into Grasshopper for analysis. This is useful for extracting room boundaries, wall centerlines, or structural framing geometry and processing it through Grasshopper optimization routines.
Advanced: Combining Grasshopper with Machine Learning
Rhino 8’s CPython 3 support unlocks machine learning workflows directly inside Grasshopper. This is an advanced application, but Claude Code skills make it approachable by generating the boilerplate code for model training and inference.
A practical example: training a regression model to predict structural member sizes based on span length, load, and material properties. The training data comes from engineering tables or past projects.
# Rhino 8 CPython 3 - GhPython component
# Requires: scikit-learn installed via pip in Rhino 8's Python environment
import numpy as np
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.model_selection import train_test_split
import Rhino.Geometry as rg
import json
import os
def train_beam_predictor(training_data_path):
"""
Train a model to predict beam depth from span and load.
Training CSV format:
span_m, load_kn_m, material, depth_mm
6.0, 15.0, steel, 310
9.0, 25.0, steel, 460
"""
data = np.genfromtxt(training_data_path, delimiter=',',
skip_header=1, usecols=(0, 1, 3))
X = data[:, :2] # span, load
y = data[:, 2] # depth
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
model = GradientBoostingRegressor(
n_estimators=100, max_depth=4, random_state=42
)
model.fit(X_train, y_train)
score = model.score(X_test, y_test)
return model, score
def predict_beam_sizes(model, spans, loads):
"""
Use trained model to predict beam depths for design scenarios.
Returns depth values that can drive Grasshopper geometry.
"""
X_predict = np.column_stack([spans, loads])
predictions = model.predict(X_predict)
return predictions.tolist()
# Usage in Grasshopper
if train:
model, accuracy = train_beam_predictor(dataPath)
a = accuracy
else:
if model is not None:
depths = predict_beam_sizes(model, spanList, loadList)
a = depths
Other machine learning applications that work well as Claude Code skills include: clustering building footprints by morphological similarity, classifying facade images to extract pattern rules, and using generative models to produce novel plan layouts from a training set of precedents. The skill template handles the data pipeline (geometry to numeric features to model to geometric output) while you focus on the design problem.
For production use, a practical approach is to train models outside Grasshopper using full Python environments, export them as serialized files (joblib or ONNX), and build a Grasshopper skill that loads the pre-trained model for inference only. This avoids the overhead of training inside the canvas.
Getting Started with Your First Computational Design Skill
If you have read this far and want to build your first Grasshopper-oriented Claude Code skill, here is a concrete starting workflow.
Step 1: Identify a repetitive scripting task. Look at your last three Grasshopper projects. What GhPython components did you write from scratch? What code did you copy from old definitions? That repetition is your first skill candidate.
Step 2: Write the skill template. Create a file in your .claude/skills/ directory. A minimal Grasshopper skill template looks like this:
# Skill: gh-surface-subdivide
# Trigger: "subdivide surface", "panel surface", "surface grid"
You are generating a GhPython component for Rhino {version}.
## Context
- Target runtime: {ironpython27 | cpython3}
- Data tree handling: use ghpythonlib.treehelpers
- Always validate inputs before processing
- Use RhinoCommon types (Rhino.Geometry namespace)
## Task
Generate a GhPython script that: {description}
## Output format
- Complete Python script with imports
- Docstring explaining inputs and outputs
- Inline comments for non-obvious operations
- Error handling for None inputs and empty lists
Step 3: Test with a real task. Invoke the skill with a specific request: “subdivide a surface into diamond panels with alternating open and closed faces.” Review the generated code. Does it handle edge cases? Are the RhinoCommon methods correct for your Rhino version? Refine the skill template based on what you find.
Step 4: Build a library. Over time, you accumulate skills for your most common patterns: facade generation, structural layouts, environmental analysis, data import, and documentation. Each skill encodes not just code patterns but your team’s conventions for naming, documentation, and error handling.
Step 5: Share with your team. Claude Code skills are text files. They can live in a shared repository. When a junior designer needs to write a paneling script, they invoke the skill instead of searching forums or asking a senior colleague to write it for them. The knowledge transfer happens through the skill itself.
The fundamental shift here is that computational design knowledge stops being locked inside individual Grasshopper definitions and starts living in reusable, composable skill templates. Every script your team generates through a skill follows the same conventions, handles the same edge cases, and produces the same documentation. That consistency compounds over projects and makes the entire team faster.
Start with one skill. Build it for the task you do most often. Use it for a week and refine it based on what you learn. Then build the next one. Within a month, you will have a personal library of computational design skills that meaningfully changes how fast you work inside Grasshopper and Rhino.
Level up your skills
Ready to learn hands-on?
- Project-based Revit & BIM courses for architects
- Go from beginner to confident professional
- Video lessons you can follow at your own pace