Studio Matrx Monthly · Volume 1 · Issue 3 · August 2026
Amogh N P
 In loving memory of Amogh N P — Architect · Designer · Visionary 
Generating Geometry with CodeLesson 6.3
PSD for Architecture, Planning & Urban Design/Module 6 · Scripting Rhino & Grasshopper

Lesson 6.3 · Scripting Rhino & Grasshopper

Generating Geometry with Code

Points, lines, curves and surfaces built from numbers - a column grid and a twisting tower, driven by parameters you can turn

14 min Interactive lessonFree · open lessonByAmogh N P· Architect & interior designer
The hook

Once geometry comes from numbers, the numbers become your design controls - turn one and the whole building responds.

Everything so far has been plumbing: getting data in and out, choosing a library. Now we do the thing that makes all of it worthwhile - make geometry from code. A point is three numbers. A line is two points. A curve is a list of points and a rule. A surface is a grid of them. Build these from code and the numbers behind them become dials you can turn.

That is the whole promise of parametric scripting. Instead of drawing a column grid and redrawing it when the bay changes, you describe it once - counts and spacing - and change the description. This lesson builds two real examples from scratch so the promise stops being abstract.

Numbers at the top = the dials. Loops build from them. Twist = accumulating rotation. Code builds; you judge.

The building blocks: points, vectors, lines

All geometry starts with the point. In Rhino, a 3D point is three numbers - x, y, z - wrapped in a Point3d:

python
import Rhino.Geometry as rg

a = rg.Point3d(0, 0, 0)
b = rg.Point3d(10, 0, 0)
line = rg.Line(a, b)

A vector (Vector3d) looks the same - three numbers - but means a direction and distance rather than a location; lesson 5.2 drew that distinction, and it matters here because you move and copy geometry by adding vectors. A line is just two points. Once you have points and lines, curves follow: a smooth NURBS curve is built from a list of points it passes through or is shaped by.

python
pts = [rg.Point3d(0, 0, 0),
       rg.Point3d(5, 8, 0),
       rg.Point3d(12, 4, 0),
       rg.Point3d(18, 10, 0)]
curve = rg.Curve.CreateInterpolatedCurve(pts, 3)

CreateInterpolatedCurve threads a degree-3 (smooth) curve through those points. Notice the pattern already forming: make a list of points, hand it to a constructor, get geometry back. That single move - points into a builder - underlies almost everything in this lesson. Assign the result to an output like a and it appears on the Grasshopper canvas.

It is worth internalising the vector-as-a-move idea, because it is how geometry travels. To copy a point ten units up, you do not compute new coordinates by hand - you add a vector:

python
up = rg.Vector3d(0, 0, 10)
top = a + up            # a new Point3d, ten units higher

That single move - point plus vector gives a new point - is the basis of extruding, arraying and offsetting everything later in the lesson. Surfaces extend the same logic one dimension further: where a curve is shaped by a list of points, a NURBS surface is shaped by a grid of them, which is precisely why the nested-loop grid you are about to build is so central - it is the raw material a surface is made from. Points beget lines and curves; grids of points beget surfaces; and every one of them starts as a few numbers you chose.

This is also the moment to connect the geometry to Module 5, which built the mathematical intuition - coordinates, vectors, transformations - underneath everything here. You do not need to recompute any of that maths yourself; Rhino.Geometry provides ready objects and methods for it. But understanding what a Vector3d means, or why a curve needs a degree, makes the difference between copying code you do not trust and writing code you can reason about and fix. The library supplies the machinery; Module 5 supplied the mental model; this lesson puts them together to actually make things. That layering - concepts, then geometry, then generation - is deliberate, and it is why the earlier modules pay off precisely here.

Point3d = a place. Vector3d = a move. Line = two points. Curve = points + a rule.

Worked example: a parametric column grid

Time for the classic. A column grid is a point at every intersection of a set of bays in two directions - exactly what two nested loops produce. The outer loop walks across, the inner loop walks up, and the same instruction places a point each time:

python
import Rhino.Geometry as rg

cols, rows = 5, 4      # inputs from sliders
step_x, step_y = 6.0, 5.0

points = []
for i in range(cols):
    for j in range(rows):
        x = i * step_x
        y = j * step_y
        points.append(rg.Point3d(x, y, 0))

a = points   # 20 column base points onto the canvas

Wire cols, rows, step_x and step_y to sliders and you have a live, adjustable grid: 5 by 4 becomes 8 by 6 by dragging, and the spacing follows the numbers. To turn each base point into an actual column, extrude a vertical line at every point:

python
height = 3.2
columns = []
for p in points:
    top = rg.Point3d(p.X, p.Y, height)
    columns.append(rg.Line(p, top))

b = columns

That is a complete, honest parametric model in a dozen lines: numbers in, a grid of columns out, everything responsive. The double loop you just wrote is the same engine behind facade panels, floor plates and seating layouts - learn it once, reuse it forever.

Notice a small but important detail in how the points were collected. The points list was created empty, before the loops, and each pass append-ed to it; if you create the list inside the inner loop it resets every time and you keep only the last point - a classic slip worth watching for. This gather-into-a-list pattern is everywhere in geometry code: start with [], build up inside the loop, use the full collection after. A neater, more Pythonic form of the same grid uses a nested list comprehension, which Module 2 introduced:

python
points = [rg.Point3d(i * step_x, j * step_y, 0)
          for i in range(cols)
          for j in range(rows)]

Both produce the identical 20 points; the comprehension is terser, the explicit loops are easier to extend when the logic grows. Choose whichever reads more clearly to you - both are correct, and clarity beats cleverness.

A GRID FROM TWO LOOPScols, rows = 5, 4step = 6.0for i in range(cols): for j in range(rows): x = i * step y = j * step pts.append((x, y))i -> across (5) j -> up (4)Change cols, rows or step and the entire grid regenerates. That is parametric.
Zoom
A parametric grid, expressed as code. Two nested loops over column indices i and j multiply a spacing to place every point; change the counts or the spacing and the whole grid rebuilds. This double loop is the workhorse behind column grids, facade panels and floor plates.

Grid = outer loop across x inner loop across = a point each pass. i*step, j*step.

Worked example: a twisting tower

Now something that looks impressive but is really the same idea plus a rotation. A twisting tower is a stack of identical floor plates, each lifted higher and rotated a little more than the one below. One loop, an accumulating angle:

python
import Rhino.Geometry as rg
import math

floors = 20
floor_h = 3.5
twist = 4.0            # degrees added per floor
size = 12.0

plates = []
for k in range(floors):
    z = k * floor_h
    centre = rg.Point3d(0, 0, z)
    plane = rg.Plane(centre, rg.Vector3d.ZAxis)
    rect = rg.Rectangle3d(plane, rg.Interval(-size/2, size/2),
                          rg.Interval(-size/2, size/2)).ToNurbsCurve()
    angle = math.radians(k * twist)
    rect.Rotate(angle, rg.Vector3d.ZAxis, centre)
    plates.append(rect)

a = plates

Each floor is a square, raised by k * floor_h and rotated by k * twist degrees around its own centre - so the twist accumulates up the building. The move that makes it parametric is that the character of the tower now lives in four numbers: more floors, taller floor_h, sharper twist, wider size. Set twist to zero and you have a plain extrusion; push it to eight and you have a dramatic corkscrew. You could Loft these plates into a skin inside Rhino (remember: lofting needs the kernel, lesson 6.2), but as a stack of profiles the design intent is already fully there, controlled entirely by numbers you can turn.

Two things in that snippet are worth pausing on. First, the math.radians(...) call: Rhino, like most geometry libraries, measures rotation angles in radians, not degrees, so you almost always convert - it is one of the most common small bugs in geometry scripting, a tower that barely twists because someone passed 4 (radians, a huge angle wrapped around) or one that spins wildly because they forgot to convert. Second, notice that the same handful of moves from the column grid returns: a loop, a running index k, and geometry placed from numbers. The tower is not a new skill - it is the grid skill plus a rotation. That is the quiet lesson of parametric scripting: impressive-looking form is usually a simple pattern with one extra idea layered on, and once you have the patterns, ambitious geometry stops being intimidating. The same recipe - a loop, an index, an accumulating transform - scales from a twisting tower to a spiral ramp, a fanned louvre array, or a helical stair, each just a different transform applied per step.

TWIST PER FLOOR = A NUMBER11 floors, +9 deg eachtwist = 9.0 (deg per floor)for k in range(floors): z = k * floor_h a = radians(k * twist) plate = square(z) plate.Rotate(a, Z, axis) floors_out.append(plate)Tune twist and floor_h;the form follows the number.
Zoom
A twisting tower as a stack of rotated floor plates. Each floor is the same square profile, lifted by its floor height and rotated a little more than the one below. A single loop with an accumulating angle produces the whole silhouette; the twist per floor is just a number you can tune.

Tower = one loop of floors. z = k*height. angle = k*twist (accumulates). Rotate each plate.

Parameters as design controls - and honest limits

Step back and notice what changed. In both examples, the design lives in a few named numbers at the top. That is the heart of parametric design: separate the parameters (the dials) from the construction (the loops that build from them), and exploring options becomes turning dials rather than redrawing. A good habit is to gather every meaningful number at the very top of your script, clearly named, so the controls are obvious - to you next month and to anyone reading it.

python
# --- controls ---
floors = 20
floor_h = 3.5
twist = 4.0
size = 12.0
# --- construction below; do not hand-edit ---

But keep two honest limits in view. First, code does not supply judgement. A script will happily generate a 90-degree twist that is unbuildable or a column grid that ignores the site; deciding which numbers are good is still design, and still yours. Second, not everything should be scripted. A one-off shape you will never vary is faster to draw by hand than to parameterise. Scripting geometry pays off when a form has real variations worth exploring, or a pattern too repetitive to draw - which is most of the interesting cases, and exactly what Module 9 pushes further into generative territory.

One more habit pays off enormously as scripts grow: keep the construction free of magic numbers. Every meaningful quantity should trace back to a named control at the top, so there is one place to change the design and no stray 3.5 buried deep in a loop that silently fights the slider above it. When you find yourself typing a raw number inside the construction, that is usually a signal it wants to become a parameter. This discipline is what lets a script survive contact with a real project - a colleague, or you in six months, can open it, read the controls block, and understand the whole design without deciphering the loops. Parametric thinking is as much about naming your intentions as about the geometry itself.

A GRID FROM TWO LOOPScols, rows = 5, 4step = 6.0for i in range(cols): for j in range(rows): x = i * step y = j * step pts.append((x, y))i -> across (5) j -> up (4)Change cols, rows or step and the entire grid regenerates. That is parametric.
Zoom
A parametric grid, expressed as code. Two nested loops over column indices i and j multiply a spacing to place every point; change the counts or the spacing and the whole grid rebuilds. This double loop is the workhorse behind column grids, facade panels and floor plates.
Geometry constructors you will use

rg.Point3d(x, y, z)

A location in 3D space from three numbers

The atom of all geometry. Access components as p.X, p.Y, p.Z. Everything else is built from points.

rg.Line(a, b)

A straight segment between two Point3d

Two points make a line. Extrude a vertical line at each grid point and you have columns.

Curve.CreateInterpolatedCurve

A smooth NURBS curve through a list of points

Threads a degree-3 curve through points you supply. The make-a-list-hand-it-to-a-constructor pattern.

nested for loops

Two loops, one inside the other, over i and j

The engine of grids: outer across, inner up, a point placed each pass. Reused everywhere in geometry code.

rect.Rotate(angle, axis, centre)

Rotate geometry in place about an axis

An accumulating angle per iteration builds a twist. angle in radians - convert with math.radians.

Hands-on workshop

Workshop - your own parametric tower

Build a twisting tower from scratch in a Grasshopper Python 3 component, then drive it entirely from sliders. You will practise a loop, an accumulating transform, and the discipline of putting controls at the top - the core of parametric scripting.

Rhino 8 with Grasshopper (for the CPython 3 component and Rhino.Geometry). No external libraries needed.

Given & goal
Goal: a stack of rotated floor plates whose form is controlled by four sliders
Inputs: sliders for floors, floor_h, twist, size wired into a script component
Time: ~40 minutes
  1. 1Drop a Python 3 script component and add four inputs; rename them floors, floor_h, twist, size. Set floors to Item access (a single count) and wire an integer slider; wire number sliders to the other three.
  2. 2At the top of your code, import Rhino.Geometry as rg and import math. Do not hard-code the four values - read them from the inputs so the sliders are live controls.
  3. 3Write one for k in range(floors): loop. Inside, compute z = k * floor_h, build a square profile centred at that height (a Rectangle3d on a Plane), and rotate it by math.radians(k * twist) about the vertical axis through its centre.
  4. 4Collect every plate into a list and assign it to the output a. Confirm the plates appear stacked and rotating as you drag the twist slider from 0 upward.
  5. 5Bonus: add a fifth slider taper and multiply size by (1 - taper * k / floors) so the tower narrows as it rises - proof that new design behaviour is often just one more number and one more line.

You’ll walk away with
A Grasshopper Python 3 component that outputs a stack of rotated (and optionally tapering) floor plates, fully driven by named sliders - a working parametric tower you can reshape by dragging.

The worked example

Three altitudes on the same idea

Read the band that fits you — or all three.

For the architectAutomate busywork & build custom tools

Parametric geometry turns option-testing from days into minutes. A massing you can twist, a bay grid you can re-space, a facade whose panel density follows a number - each lets you present three studied variants where you once had time for one. The discipline of gathering controls at the top of a script mirrors good design thinking: name the moves that matter, and let the construction follow. Judgement about which numbers are right stays firmly with you.

For the interior designerScripts for data, schedules & layouts

The same loops that stack a tower lay out a room. A grid of pendant lights over an island, a run of identical joinery modules, a repeating ceiling baffle, seating spaced evenly along a curve - all are points-from-numbers and a loop. You rarely need the twisting-tower drama; you need the reliable column-grid move, applied to furniture, fittings and finishes, so a spacing change ripples through the whole layout instantly.

For the studentA hireable computational skill

Generating geometry from code is the portfolio skill that reads as computational design. A parametric tower or an adaptive facade, driven by clearly named parameters, demonstrates exactly the thinking studios hire for. Master the two moves here - the nested-loop grid and the accumulating-transform stack - and you can build a surprising range of forms. They are the literal foundation of the Computational Design and Generative AI courses in this Academy.

Misconception check

Generating geometry with code means the computer designs the building for me.

Code generates geometry; it does not generate judgement, and conflating the two leads to bad work. A script does exactly what its numbers and rules say - it will produce an elegant tower or a nonsensical one with equal willingness, because it has no idea which is which. What scripting gives you is speed and range: the ability to build a form from parameters and explore many versions quickly. Deciding which version is good - which twist reads well, which grid suits the site, which spacing feels right - is design, and it remains entirely human. The most powerful stance is to treat the script as a tireless drafting assistant that builds whatever you specify, freeing you to spend judgement on what to specify. Generative tools (Module 9) widen the range further, but never replace the deciding.
Try it

Do it yourself

Trace the numbers into the form.

  1. 1What three numbers define a Point3d, and what does a Line need to be built?
  2. 2In the column-grid script, what do the outer and inner loops each control, and what does i * step_x compute?
  3. 3In the twisting tower, why is the rotation angle k * twist rather than just twist?
  4. 4What single change to the tower script turns the corkscrew back into a plain vertical extrusion?
  5. 5Why is gathering all the parameters at the top of a script a good habit, beyond tidiness?
Take this with you

The one line to carry out

Build geometry from numbers - points into constructors, loops for repetition, an accumulating transform for a twist - and those numbers become design controls you can turn. Code generates the form; judgement about which form is good stays yours.
Take it further
References & further reading

Peer-reviewed journals & authoritative standards

  1. 01Parametric designWikipedia, 2026.
  2. 02Non-uniform rational B-splineWikipedia, 2026.
  3. 03Grasshopper 3DWikipedia, 2026.
  4. 04Computational geometryWikipedia, 2026.
Related lessons
Recap
Geometry begins with the Point3d - three numbers - and builds up: two points make a line, a list of points makes a curve, a grid of them makes a surface. A parametric column grid is two nested loops placing a point per bay; a twisting tower is one loop stacking floor plates with an accumulating rotation. In both, the design lives in a few named parameters at the top, which is the essence of parametric design. Code supplies speed and range; it does not supply judgement, and not every one-off shape is worth scripting.
Carry forward →

Our scripts have been returning flat Python lists of geometry. But Grasshopper organises data into branching trees, and that structure carries real meaning - which floor a plate belongs to, which bay a column sits in. Next we learn to think in trees, and when code beats components for handling them.

A

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 →