Python in 3D Cheatsheet

Every function the playground understands, on one page.

3D Solids

Box(width, depth, height, x=0, y=0, z=0, hole=False, fill=None, align="center", center=False, fillet=0, opacity=100)

Sized along x/y/z in that order. The base sits at z -- it is not centered on z like it is on x and y, unless center=True.

Cylinder(radius, height, x=0, y=0, z=0, hole=False, fill=None, align="center", center=False, segments=32, fillet=0, opacity=100)

Stands upright along z. Base sits at z, same as Box.

Polyhedron(points, faces, fill=None, opacity=100, hole=False)

The rawest shape: matches OpenSCAD's polyhedron. points is a list of (x, y, z) corners; faces is a list of point NUMBERS (not coordinates) naming, in order, which corners trace each flat face -- e.g. a triangle from points 0, 1, 2 is face (0, 1, 2). Order matters within a face: list its corners walking around the OUTSIDE in one consistent direction (the right-hand-rule convention) so the face's normal points outward. Get it backwards and the shape still renders (every material here is double-sided) but that face's shading looks inside-out. Faces are fan-triangulated from their own first point, so this is only guaranteed correct for convex (or triangular) faces -- a genuinely non-convex face can triangulate wrong. See the Polyhedrons lesson for a full walkthrough of points vs. faces.

align="left-top" / "top" / "right-top" / "left" / "center" / "right" / "left-bottom" / "bottom" / "right-bottom"

Like CMU's align: a 9-way grid that changes what (x, y) points at. Redefined here since z is vertical, not y: "top" means farther away from you (+y), "bottom" means closer to you (-y).

center=True

Like OpenSCAD's center: also centers the shape on z, instead of sitting with its base at z.

segments=6

Like OpenSCAD's $fn (a leading $ isn't legal in Python). How many flat faces approximate a curve -- low numbers turn Cylinder into a visible prism (6 = hexagon, 3 = triangle). Cylinder only.

fillet=0.5

Rounds edges/corners with a smooth curve (a "fillet", not a "chamfer" -- a chamfer is a flat cut). On Box: all edges and corners. On Cylinder: just the rim, where the flat cap meets the curved side. Same units as everything else. Clamped automatically so it can't exceed the shape itself.

SEGMENTS = 6

A global default for segments=, like OpenSCAD's $fn special variable (as a plain variable instead, since a leading $ isn't legal in Python). Set it once, anything drawn after that point picks it up unless it gives its own segments=. Resets to 32 every Run, so it never carries over from a previous run.

2D Profiles

These render right away, as a thin flat slab (fill=/opacity=/hole= all work, same as any 3D shape) -- extrude()/rotate_extrude() replace that default thinness with real height, same as Rect/Circle in CMU graphics.

Rect(left, top, width, height, fill=..., opacity=100, hole=False, fillet=0)

fillet rounds the corners in the 2D profile -- extruded straight up, that rounds only the vertical edges. The flat top/bottom stay sharp (that's Box's own fillet= instead).

Circle(centerX, centerY, radius, segments=32, fill=..., opacity=100, hole=False)
Oval(centerX, centerY, width, height, fill=..., border=None, borderWidth=2, opacity=100, rotateAngle=0, dashes=False, align="center", hole=False)

CMU parity shape. border draws a real outlined stroke (a fat Line2, so borderWidth actually changes thickness, unlike a plain WebGL line) -- dropped if the Oval gets folded into a union()/etc., same as any other preview-only property. left/top/right/bottom read and write like CMU's, and centerX/centerY/width/height/rotateAngle stay live -- change any of them after creation and the shape updates, which none of the other 2D profiles here do yet.

Star(centerX, centerY, radius, points, fill=..., border=None, borderWidth=2, roundness=None, opacity=100, rotateAngle=0, dashes=False, align="center", hole=False)

CMU parity shape, same border=/left-right-top-bottom/live-mutation deal as Oval, including a settable width/height (a star only has one real size knob -- radius -- so setting either moves the same radius, keeping the bounding box square). points is the point count (radius>0, points>2, both required); star.points reads that count back, not an outline. roundness (0-100) fillets every vertex, inner and outer alike, using the same corner-rounding math as Rect/RegularPolygon's fillet= -- at 100 that erases the points into a soft blob. CMU doesn't document its own inner-point ratio or exact roundness math, so both are a reasonable approximation here, not a pixel-for-pixel match.

RegularPolygon(centerX, centerY, radius, numSides, rotateAngle=0, fillet=0, fill=..., opacity=100, hole=False)
Polygon(x1, y1, x2, y2, x3, y3, ..., fill=..., opacity=100, hole=False)

At least 3 points.

Label(text, x, y, size=1, font="helvetiker", bold=False, italic=False, fill=..., opacity=100, hole=False)

Centered at (x, y), like CMU's Label. font: helvetiker (default, no extra load) / arial / monospace / cursive / caveat / montserrat.

Extrude

extrude(profile, height, x=0, y=0, z=0, hole=False, fill=None, align=None, center=False, opacity=100)

Pushes a 2D profile straight up along z by height. Works on Rect/Circle/RegularPolygon/Polygon/Label alike. hole= inherits from the profile if you don't say otherwise -- extruding an already-hole shape stays a hole. Also callable as linear_extrude(...), the OpenSCAD name -- an alias, not a different function, kept so an old shared link never breaks.

rotate_extrude(profile, angle=360, x=0, y=0, z=0, hole=False, fill=None, opacity=100, segments=32)

Sweeps a profile around the z-axis instead -- a potter's wheel, not a cookie cutter. Each profile point is (radius, height); radius < 0 is clamped to 0, same as OpenSCAD. angle=360 goes all the way around; less than that leaves a wedge open. Doesn't work on Label -- text isn't a sensible radius/height profile. hole= inherits from the profile, same as extrude().

Boolean Operations

union(*solids, hole=False, fill=..., opacity=...)

Merges non-hole shapes into one solid. If some of what you pass in are already hole=True, those get cut into the non-hole ones immediately instead of just being glued on -- an all-hole group stays a hole, just bigger. Only matters if you're mixing hole=True shapes in; plain shapes behave exactly like a normal merge.

difference(base, *subtract, hole=False, fill=..., opacity=...)

base with every other shape cut out of it -- always cuts, regardless of whether base/subtract happen to be marked hole=True themselves.

intersection(*solids, hole=False, fill=..., opacity=...)

Only the overlap of every shape survives -- NOT "first minus the rest" (that's difference).

Leave fill=/opacity= out and they're inherited from the first (or base) shape -- not literally None/100, which would mean something different (fill=None specifically means hole=True). Give the boolean call its own fill=/opacity= to override that inheritance.

a.add(b) / a.subtract(b)

Grow or cut a shape in place -- a stays the same object, just bigger or with a bite taken out, instead of union()/difference() making a brand new one. add() is hole-aware the same way union() is: adding a hole=True shape cuts it into a instead of gluing it on. subtract() always cuts, like difference().

a + b / a - b

Same as union(a, b) / difference(a, b) -- makes a new shape, both operands consumed.

a += b / a -= b

Same as a.add(b) / a.subtract(b) -- mutates a in place. Python gives += its own meaning separate from +, which lines up with add() being "in place" and + being "make something new."

Modifier Characters

OpenSCAD marks these with a symbol (*, !, #, %) right before a shape. Python has no room for a prefix character before a function call, so these are attributes instead -- set on any shape, same as x= or fill=. All four work no matter how deep a shape is buried inside a union()/difference().

shape.visible = False

OpenSCAD's * -- completely excluded, as if commented out. Not rendered, not part of the real geometry, not in the STL. Every shape starts out visible=True; this is also exactly what happens automatically the moment a shape gets consumed by translate()/rotate()/union()/etc., so a shape that's stopped rendering on its own and one you've deliberately hidden are the same thing under the hood, not two separate ideas.

shape.show_only = True

OpenSCAD's ! -- only this shape renders; everything else in the whole design is set aside until you remove it.

shape.debug = True

OpenSCAD's # -- draws an extra highlighted (magenta, translucent) copy of this shape on top of the normal result. Doesn't change what the shape actually contributes to a union/difference -- it's purely a visual callout.

shape.background = True

OpenSCAD's % -- the opposite of debug: shown as a faint gray ghost for reference, but excluded from the real geometry and the STL entirely, same as visible=False.

Transformations

translate(solid, x=0, y=0, z=0)
rotate(solid, angle, axis="z")

angle in degrees, around one named axis ("x"/"y"/"z").

rotate(solid, [rx, ry, rz])

OpenSCAD-style: all three angles at once, x applied first, then y, then z.

translate(rotate(shape, 45), x=5)

These nest like OpenSCAD's do: innermost call applies first.

Holes

A shortcut for the common "drill a hole" case, without writing difference() by hand.

hole=True

On Box / Cylinder / extrude / rotate_extrude / union / difference / intersection. Every hole-marked shape is subtracted independently from every other (non-hole) shape it overlaps at the end -- a hole that doesn't touch a given shape just leaves it unchanged. Reach for difference() directly if you need finer control over what subtracts from what.

Colors (preview only)

fill= never reaches the exported STL -- 3D prints have no color. It just helps you tell shapes apart while you build.

fill="red"

Any CSS/X11 color name works, not just a fixed list.

red crimson darkorange gold green cornflowerblue mediumorchid gray

Opacity (preview only)

Like CMU's opacity: 0-100, not 0-1. 100 (the default) is fully solid, 0 is invisible. Also never reaches the exported STL. Works everywhere -- every 3D shape, extrude()/rotate_extrude(), and 2D profiles like Rect/Circle themselves, since those render on their own now too.

Box(2, 2, 2, fill="cornflowerblue", opacity=40)
opacity=100 opacity=70 opacity=40 opacity=15

Interactive

All optional. Defined or not, Run/Reset/Stop/Export work exactly the same either way.

def onKeyPress(key): ...

Called on every key press, like CMU's onKeyPress. Click the viewer first to give it keyboard focus (shown with a blue outline and a hint). Named keys match CMU: "Up" / "Down" / "Left" / "Right" / "Space". Letters and digits come through as themselves ("a", "A", "1").

def onNext(): ...

Not from CMU -- our own addition. Defining it adds a Next button next to Stop; each click calls it again. Handy for a manual, step-by-step walkthrough instead of a key-driven one.

shape = Box(2, 2, 2) shape.x += 3

Every shape stays a live, mutable handle -- any field you passed in (x, fill, opacity, radius, whatever the shape takes) can be read or reassigned later. This is what makes onKeyPress/onNext useful: keep a reference from your first run, then move or restyle it from inside the handler instead of redrawing everything from scratch. Calling Box()/Cylinder()/etc. again inside a handler still works too -- it just adds a new shape each time rather than replacing one.

print(...)

Works everywhere -- top-level code, onKeyPress, onNext. Output appears in a small panel between the code and the toolbar, but only when there's something to show: no output, no panel. A print() right before a crash still shows, so it's there for the case you need it most.

A real Run resets everything -- shape registry, any variables/functions you defined, all of it -- back to a clean slate. State only survives between individual onKeyPress/onNext calls within the same run.

Coordinate System

x y z

x, y is the flat ground plane -- the same plane you already draw on in 2D graphics. z is the new axis: straight up, out of the page.

A shape's height sits with its base AT z, not centered on it -- Box(2,2,2) sits on the ground, it doesn't straddle it.

Editor

Tab

Indents -- writes real Python, def blocks included.

Auto-runs about 0.6s after you stop typing, or click Run.

Drag the divider between the panes to resize them.

Scroll or pinch to zoom, drag to orbit.

Click an axis ball on the gizmo (lower left) to snap the view to it.

Download STL exports the model -- geometry only, no fill colors.