The language guide
Pegmatite Script.
Start with a shape. Build on its faces.
Turn a few readable lines into a 3D model.
On this page
01 / Getting started
Your first model
Pegmatite Script builds 3D models by creating solids and placing them against one another.
Save scripts with the .pegm extension, or write directly in the editor.
color steelblue block 40 30 6 [ x left color gold cylinder 12 20 [ sphere 12 ] x right color coral cylinder 12 10 ]open in editor

Make a base
A blue block, 40 × 30 × 6.
Build a branch
A gold post at the left edge carries a sphere.
Return to the parent
A coral post aligns with the base's right
edge.
Write the model as you would assemble it: start with a base, attach a feature, then continue from it or branch back to its parent. Use anchors and face references for connections, alignment for matching edges, and offsets for local clearances.
Lists [ … ] let features share a parent; nested lists build on a child. Use labels
for later returns, patterns for copies, and macros for reusable components. Changing the base's
width above carries both posts with its edges.
Repeated on root plus XYZ offsets for individual features is an antipattern.
Reserve it for deliberate work from the context's origin. Coordinates still belong in profiles,
meshes and local adjustments. Keep names meaningful and indent branches so the construction
is easy to read.
X and Y form the ground plane; Z points up. Right is +X, back is +Y, and front is −Y. Angles are in degrees. Lengths are model units; 3MF and STEP export treat one unit as one millimeter.
Curves use more segments as their size grows. The editor uses a lighter mesh for
preview; Export 3MF rebuilds the complete model with finer curves, including all
construction steps. Explicit polygon side counts, such as prism 6, stay
unchanged.
Export STEP writes the model for CAD programs with its curves exact: a cylinder is a true cylinder, a fillet a true round. What has no exact counterpart yet, such as a helix that widens as it climbs, is written as the flat facets of its mesh.
02 / The essentials
Reading a script
Read from top to bottom. Each statement builds geometry, changes a setting, selects a reference, or assigns a variable. Spaces and newlines separate tokens; indentation is for readability.
In command signatures and editor hints, bold keywords and symbols are
typed literally; italic names are placeholders for your values, expressions, or
statements. Plain brackets mark optional parts and are omitted when typing; bold brackets
are literal lists. A plain | separates alternatives, and … means repetition.
grid columns rows [gap] [with index $name] body
For example, sphere diameter takes a diameter
value, while prism sides [diameter | edge | across] size height uses diameter as a literal measurement keyword. After with index, choose your own variable name following the literal $.
color red @right 4cube 20| fillet 2Properties go before their solid. Geometry modifiers go after it,
separated by |. Together they form one statement.
| Syntax | Meaning |
|---|---|
// a comment | A note ignored by the script, up to the end of the line. |
/* a comment */ | A note that may span lines. Block comments do not nest. |
@top | A placement direction, called an anchor. |
#top | A face reference, used with commands such as on. |
!color | A property that stays in effect for following solids. |
$size / base: | A variable / a label naming a volume. |
[ … ] / { … } | A branching list / a compound solid. |
Keep prefixes attached: !color, @top, #top, $size, and base:. A one-shot property must belong to the solid
or pattern immediately following it; one left standing alone changes nothing, and the
editor warns about it.
Numbers can be integers or decimals, such as 10, 0.5, or -2.5. A number cannot touch a word, so write (2 * pi) rather
than 2pi. Write strings in double quotes; a backslash starts an escape, such
as \" for a quote. Strings are covered in Text. Names are
case-sensitive.
03 / Your building blocks
Solids & colors
Start with four simple solids. Width follows X, depth follows Y, and height follows Z. Dimensions are zero or greater: a solid of size zero builds no material but still takes its place. Round solids use a diameter.
cube
size Equal on all three axes
block
width depth height A rectangular solid
cylinder
diameter height A circular base with height
sphere
diameter A sphere measured across
!@right 6 color steelblue cube 20 color gold block 16 20 12 color coral cylinder 16 24 color mediumseagreen sphere 20open in editor

color accepts CSS color names and hex RGB values such as #ff0000. It affects the next solid only. Use !color steelblue to
keep a color in effect, or color unset to use the built-in default for one solid.
3MF and STEP export include colors once a model sets any; a model in the default color exports
without them.
More primitives
| Command & arguments | Shape |
|---|---|
cone diameter height | A circular cone. |
pyramid sides size height | A regular pyramid; at least 3 sides. |
prism sides size height | A regular prism; at least 3 sides. |
starprism spikes outer inner height | A star with at least 3 spikes; outer and inner diameters are both required. |
torus outer inner | A ring, using outer and inner diameters. |
tube outer inner height | A hollow cylinder. |
tetrahedron edgeoctahedron edgedodecahedron edgeicosahedron edge | Regular polyhedra, measured along an edge as a cube is. |
mesh vertices [x y z] … faces [i j k] … | Any closed solid with flat faces. Corners are indexed from 0; faces are #f1, #f2, … in the order written. |
Prism and pyramid sizes measure the circle through the base corners, and polyhedra their
edge. A word before the size measures another way: diameter through the
corners, as in icosahedron diameter 20; edge along an edge, as
in pyramid 4 edge 20 30; or across between two parallel flats,
as in prism 6 across 17 8. For tubes, toruses, and stars, the inner
diameter must be smaller than the outer diameter.
04 / Building together
Placement & anchors
The first solid starts at the construction origin. Each ordinary solid after it stacks on the previous one, centered on X and Y. An anchor changes that direction for the next solid.
color steelblue cube 20 @right 5 color gold cube 10 @top 3 color coral sphere 10open in editor

| Axis | Negative direction | Positive direction |
|---|---|---|
| X | @left | @right |
| Y | @front | @back |
| Z | @bottom | @top |
The optional number after an anchor is the gap. @center centers a volume inside
its reference when no other alignment is given. All directions are relative to the current placement
frame.
05 / A precise fit
Alignment & offsets
Use x, y, and z to align edges or add an offset.
Each axis has two independent settings: alignment and offset.
| Example | Effect |
|---|---|
x right | Align the two right edges; short for x right:right. |
x right:left | Align the reference's right edge with the new solid's left edge. |
x 100%:25% | Align the reference's 100% point with the new solid's 25% point. |
x 5 | Move 5 units along +X, keeping the current alignment. |
x left 4 | Align left edges, then offset 4 units along +X. |
color steelblue block 50 40 6 on #top [ x left y front color gold cube 10 x right y back color coral cube 10 x left:center y back:center color mediumseagreen cube 10 ]open in editor

Use left / center / right on X, front / center / back on Y, and bottom / center / top on Z. These correspond to 0%, 50%, and 100%.
A single percentage uses the same point on both boxes: x 25% is short for x 25%:25%. Names and percentages can be mixed, as in x right:25%. Percentages must be literal numbers followed immediately by %; variables and expressions cannot be suffixed with %.
An anchor supplies alignment and a gap on one axis. Axis commands override whichever settings they name. An offset always follows the axis direction, even when aligning to the opposite edge.
06 / A new point of view
Faces & references
on selects what the next solid is built against. Selecting a face also changes
the placement frame: the new solid's local Z points outward from that face.
base: color steelblue block 40 30 10 on base #front color gold cylinder 12 16 on base #top color coral cube 12open in editor

| Reference | Build against |
|---|---|
on #front | The previous volume's front face. |
on base | The volume named with base:. |
on base #top | The top face of that labelled volume. |
on #bounds.top | The top of the bounding box, even after a rotation. |
on root | The current construction context's origin. |
The standard faces are #front, #back, #left, #right, #top, and #bottom. Curved solids offer
faces at their outer bounds. Prisms and polyhedra also offer numbered planar faces such as #f2.
To move across a selected face, use alignment such as on #top x right cube 10. A sideways anchor on the first solid against a face is rejected. @top 5 can
still leave a gap outward from it.
A label and setters may stand before the on of their solid, so plate: on #front cube 10 keeps the label at the start of the line; a face
list takes no label in front. A face selected before any volume exists chooses the build
plane. Inside a compound or macro body, name it through the body's root: on root #front. on itself creates no scope and needs no closing delimiter.
Place below, or build outward?
@bottom places a solid below its reference in the current frame and keeps its
orientation. on #bottom selects the bottom face and turns the construction frame
so local +Z points outward. A cone makes the difference visible:
color steelblue block 24 16 4 @bottom color gold cone 18 26open in editor

color steelblue block 24 16 4 on #bottom color gold cone 18 26open in editor

A local work surface on every face
The selected face supplies X and Y across its surface, and +Z outward. Write a macro upright once, then use it on a top, side, bottom or slanted face. Its internal dimensions and placement stay the same; the face supplies its orientation.
def %boss { cylinder 12 4 cylinder 6 8 } color steelblue block 40 30 20 [ on #top color gold %boss on #front color coral %boss on #left color mediumseagreen %boss ]open in editor

Starting with on #front turns the build frame: local +Y points world-up and
local +Z points world-front. A block built there has its #front facing world-down, #back world-up, and #top world-front. Names
describe the solid in its own frame, not the camera view. Once a solid exists, another on #front selects that solid's front face.
See the public guide's face coordinate table for all six orientations.
07 / Set it once
Persistent properties
Properties normally apply once. Prefix a setter with ! to keep it active for following
solids in the current scope. A one-shot setter temporarily overrides it.
!color steelblue !@right 4 cube 12 color gold cube 12 cube 12 !color unset cube 12
The first and third cubes are blue; the second is gold. !color unset clears
the persistent color. x unset clears both alignment and offset for that axis for
one solid.
Appearance settings (color, font-size, and font)
reach into compound and macro bodies. Placement settings apply to the finished group, so
its internal layout stays independent.
08 / One base, many branches
Branching
Inside a list, [ … ], every item starts from the reference saved at the
opening bracket, including the build plane. Closing the list restores that reference, so
the surrounding sequence can continue from it.
color steelblue block 20 20 20 [ @left color coral cube 6 @right color mediumseagreen cube 6 @front color gold cube 6 ] @top color lightblue sphere 10open in editor

All three small cubes are placed against the large block. The sphere after the list also follows that block. To continue from a list item, open a nested list immediately after it.
When every item is the same statement on a different face, list the faces instead: on [#f2 #f3 #f4] cube 6 builds the cube on each of them, exactly as a list
with one on per face would. Put a label first to name another volume's faces.
color steelblue prism 6 edge 20 20 on [#f2 #f3 #f4 #f5 #f6 #f7] color coral cylinder 6 4open in editor

A list gives settings and labels a local scope. It does not isolate variables or boolean operations. Use a compound when a part needs its own construction context.
09 / Add, subtract, assemble
Compounds & booleans
Wrap solids in { … } to make a compound. Its children are fused into one placeable
solid with its own origin. Properties and modifiers can apply to the whole group.
color steelblue { block 40 30 6 on #top cylinder 20 12 on #top -cylinder 10 18 }open in editor

| Sign | Operation | What remains |
|---|---|---|
| None | Union | Both shapes, joined. |
- | Difference | Existing material outside the new solid. |
& | Intersection | Only the overlapping material. |
^ | Symmetric difference | Material belonging to exactly one shape. |
A sign precedes the solid: -cylinder 10 18 removes material. Against a face, a
subtraction drills inward. A signed solid needs existing geometry in its construction context.
A subtraction that misses it, removing no material, builds with a warning.
Boolean operations affect earlier material in the same construction context. Braces isolate that work; square brackets do not. The operand also leaves a placement reference. Put a cut in a list to return to its parent afterward, or use a label to select the volume you need.
10 / The finishing touches
Geometry modifiers
Pipe a solid through modifiers with |. They run from left to right, before
placement. The finished bounding box determines how the result fits against its reference.
A swept solid's pipeline begins with its profile, and the profile's own offset, round and fill come before the sweep,
covered in Profiles & 2D operations.
color steelblue block 40 30 20 | fillet 3 at parallel z | fillet 1 at #topopen in editor

| Modifier | Effect |
|---|---|
| rotate 45 about z | Turn around an axis or a face normal. |
| scale 2| scale 1 2 1 | Scale uniformly or per axis. Zero is invalid; negative factors reflect. |
| flip x | Reflect along an axis. |
| shell 2 | Hollow inward, leaving a bottom and an opening in local +Z. open #f2 or open [#top #front] opens those faces instead, closed none. |
| cut 25% 75% | Keep the portion between two Z levels; absolute heights also work. |
| chamfer 2 at #top | Bevel selected edges by a distance. |
| fillet 2 | Round edges by a radius, every edge or those at a selector. |
Edge selectors include parallel z, a face such as #top, #top and #front, nearest 0 0 10, and
conditions such as z > 10. Any of them combine with and, or and not, as in | chamfer 2 at #top or parallel z, and tangent carries a
selection on round curved edges an earlier fillet left, as in | fillet 1 at tangent parallel z. Leaving out at selects every
edge, so cube 20 | fillet 5 rounds the whole cube. Chamfers and fillets work on any sharp
edge, whatever angle its faces meet at, so a taper or a hexagonal prism takes them too.
Select edges in the original frame and rotate afterward. A face name follows the solid
through transforms; #bounds.top always selects the top of its current bounding
box.
11 / Make it parametric
Variables & expressions
Declare a value with set, then refer to it with $. Wrap
arithmetic in parentheses so each command's arguments stay unambiguous; a sign alone needs
none, as in x -$size.
set $size 20 set $height ($size / 2) color steelblue block $size (2 * $size) $height color gold cylinder ($size / 2) $height
Expressions support +, -, *, /,
remainder %, power **, nested parentheses, and unary minus. $i % 4 and floor($i / 4) read the column and row of a grid
iteration. A choice such as ($i % 2 == 0 ? 10 : 5) picks a value by a test:
comparisons joined by and, or and not, where a text compares by == and !=. Functions
such as sin($a), sqrt($area), lerp(10, 20, $t) and curve(10, 30, 12, $t) stand wherever a number does, with angles in degrees; pi is π. Set a variable before using it; reassignment affects only subsequent statements.
Variables can also hold strings for text and path. They cannot
supply colors or references. Percentages such as 25% must be literal numbers.
A label reads its volume back as numbers: base.width, base.depth and base.height measure the slot in the frame the
volume was placed in, so block base.width base.depth base.height is the slot
again. A slot measures without building, which sizes a plate to its text. A
flat shape is measured in its own frame instead: width(circle 20) is 20, and width(path $vase, 40) is how wide a silhouette is 40 up, which sizes a varying
profile at every height.
title: slot text "PEGMATITE" | extrude 1 @bottom color steelblue block (title.width + 6) (title.depth + 6) 2 on #top color white text "PEGMATITE" | extrude 1open in editor

12 / Define a part
Reusable macros
A macro is a named compound with numeric parameters. Write a def among the
statements of the script, then call it by name with space-separated arguments anywhere
below. A macro name starts with %, in the definition and in every call, so it
never clashes with a built-in command.
def %tower $height { block 12 12 4 on #top cylinder 6 $height } color steelblue %tower 14 @right 8 color gold %tower 24open in editor

Calls behave like any other solid: anchor, color, subtract, or modify them. A macro body
sees its parameters and the variables set above its definition, with the values they had
there, so a later set does not change what a call builds; anything that
should vary from call to call is a parameter. A body of one statement needs no braces: def %b block 10 20 30.
A macro can also name a 2D shape. Its call then stands wherever a shape goes, so one outline can be filled for a floor and walled for the sides without writing it twice.
def %tray $width rect $width 30 color tan %tray 50 | extrude 2 on #top color peru %tray 50 | extrude 12 wall 2 insideopen in editor

if makes a part optional. It builds the statement after its test when the
test holds, and the one after else when it does not; with nothing picked it is
as though nothing were written, so what follows is placed where it would be anyway. A list builds
several statements as one branch.
def %case $ears { block 40 30 20 if $ears == 1 [ on #left block 10 30 3 on #right block 10 30 3 ] } !color steelblue !@right 10 %case 1 %case 0open in editor

A bare test compares two arguments, such as $style == "flange"; a longer one
goes in parentheses: if ($w > 50 and $holes == 1). else if chains,
and a macro may call itself behind a test.
Names start with a letter and can contain letters, digits, and underscores. No word is
reserved: $, % and a label's : keep names clear of
commands, so cube: cube 10 is fine. Only root and the constant pi are taken.
13 / Let the script repeat
Repeating patterns
repeat continues a sequence. linear spaces iterations along a
line, circular places each iteration around a circle, grid in columns and rows, and mirror adds a reflected copy. Each pattern
leaves a combined placement slot for what follows.
!@right 2 repeat 5 with index $i { block 10 10 (5 + $i * 3) on #top sphere 6 }
The optional index starts at 0. The body is one statement, and an on written before
it belongs to the body too. Braces make several statements one compound: each iteration builds
one volume, placed by the setters before the brace, and keeps its variables and settings to
itself. Counts must be whole numbers from 0 to 10,000.
!color seagreen linear 16 5 with index $i cylinder (4 + $i * 2) (6 + $i * 2)open in editor

A linear pattern takes the distance between the centres of neighbouring
iterations, then the count. Where repeat keeps the gaps even, a linear pattern
keeps the centres even, so the difference shows once the iterations differ in size.
linear, grid and mirror can take inset and a margin in place of the distance or gap. The
pattern then spans what it is placed against: its outermost centres stand the margin in
from the edges, and the rest are spaced evenly between. That is how holes dimensioned from
the edges are written: on #top grid 2 2 inset 9 -cylinder 5.5 10 drills four
corner holes 9 mm in, on #top linear inset 10 3 -cylinder 4 10 a row of
three, and on #top mirror x inset 8 -cylinder 4 10 a pair. A second margin sets a grid's Y
inset.
color steelblue cylinder 60 10 on #top circular 40 6 @top -cylinder 6 10open in editor

A circular pattern takes the circle's diameter, then the count. Optional angle and start set its sweep and starting angle; defaults are 360° and 0°. Use shrink to size its slot to the generated material instead of symmetric bounds.
!color goldenrod grid 3 3 2 with index $i pyramid (3 + $i) (6 + $i) 6open in editor

A grid takes the number of columns, then rows, then an optional gap between cells. Every cell is the size of the largest iteration, so the rows stay straight when the index changes the size. The index runs along a row first.
color steelblue mirror x { block 30 24 4 [ on #top x 6 -cylinder 8 4 ] [ on #top x left 4 path "M0,12 H12 V0 Z" | extrude 4 | rotate 90 about x | flip x ] @top x left block 4 24 20 }open in editor

A mirror takes the axis its plane faces, then an optional gap. The body is built against one side of the plane and again as a reflected copy against the other, so a symmetric part is written once as a half. Nest a mirror in another to get four quarters.
14 / Keep the model predictable
Slots & scope
Material is the geometry you see. A slot is the box used
for placement, which can include empty space. Writing slot before any solid,
as in slot block 60 40 20 or slot prism 6 100 20, keeps the room
and faces of that solid with no material at all.
gap: slot block 60 40 20 block 60 40 5 on gap #back block 60 20 5
The plate stands 20 units above the ground. The named slot also lets a wall be built
against the back of that space. Empty slots contribute to compound bounds, but cannot be
used as boolean operands. Cutters are the other way round: a - or & volume claims none of a compound's room, so a hole drilled past both ends
of a block leaves the compound the block's size.
| Construct | Placement | State & scope |
|---|---|---|
List [ … ] | Each item branches from the saved reference. | Settings and labels are local. Variables and booleans remain in the surrounding context. |
Compound { … } | Own origin; placed as one volume. | Own labels and booleans. Inherits variables and appearance; internal changes stay local. |
| Macro | Like a compound. | Only parameters and local variables are visible. Appearance is inherited. |
| Pattern | A sequence, linear, circular, grid or mirrored arrangement; leaves a combined slot. | No additional scope for a single statement; a braced body is a compound, with its own. The loop index is restored afterward. |
on | Selects a reference and frame. | Creates no scope. |
part | Carries on as though it were not written. | Own booleans. Settings, labels and variables carry on. |
Parts
A model printed in pieces is divided into parts. part "Lid" begins one, and the material built after it, up to the next part, belongs to it. Each part is
exported to 3MF as an object of its own carrying its name, and to STEP as bodies carrying it,
and the editor shows or hides it as one, so you can look under the lid.
part "Box" box: block 60 40 30 | shell 2 part "Lid" on box #top block 60 40 3 on #bottom block 55 35 3
A part divides the material and nothing else: the lid is placed on the box as though no part were written. Only booleans stop at it, so a hole cut in the lid never reaches the box. A part is written at the top level of the script, and its name is a string written out.
15 / Draw an outline
Profiles & 2D operations
A 2D profile is a flat shape with no volume of its own. Draw it first, then give it
thickness by piping it into extrude: rect 30 20 | extrude 4 makes
a plate 4 units thick. The same profiles can be revolved, carried along a helix, or swept along
another outline. A profile on its own, with no sweep after it, is a sketch: the editor draws
it flat where the solid would stand and builds nothing, so you can see where a part goes before
choosing how to sweep it.
!@right 6 color steelblue rect 20 14 | extrude 10 color gold circle 20 | extrude 10 color coral ellipse 24 14 | extrude 10 color mediumseagreen path "M0,0 H20 L10,16 Z" | extrude 10 color slateblue font-size 16 text "Pg" | extrude 6open in editor

A path is drawn in SVG coordinates and filled, so each of its subpaths closes. Text
follows the current font-size and font, and letters with holes,
such as P and g, keep them. Text has a chapter of its own.
Choosing a profile
| Profile | Arguments |
|---|---|
circle | diameter |
rect | width height |
ellipse | diameter-x diameter-y |
star | spikes outer-diameter inner-diameter |
polygon | points [x y]… or sides [diameter|edge|across] size |
#face | A face of the solid before, or label #face: the outline of that
face. |
path | A quoted SVG path, such as "M0,0 H20 V10 Z". |
text | A quoted string; font-size sets capital-letter height (default 10),
and font "Roboto" 700 picks a Google Fonts family and weight. |
polygon points [0 0] [30 0] [20 15] is an outline of its corners, Y up and
left where its coordinates put it. polygon 6 20 is the regular hexagon a prism 6 20 stands on, centered on its origin.
Faces as shapes
A face reference written where a shape goes stands for the outline of that face, in the
frame on places a solid in. So on #f2 #f2 | extrude 2 covers the face
exactly, whatever its shape, and a face is the natural area for a pattern on it.
Through a label, box #top is the face as the statement labelled box built it, so a later cut does not reach it. Label a compound, box: { … }, to read the surface the cut leaves.
2D modifiers
A profile takes modifiers of its own after |, before its sweep: | offset 2 grows the outline and | offset -2 shrinks it, with
sharp corners unless round follows the distance; | round 3 rounds every corner; | fill fills the holes.
The sweep is where the shape becomes a solid: steps before its word treat the outline, steps after it the solid. A 2D modifier written after the sweep is an error.
2D groups
Braces where a profile stands combine shapes into one, placed and combined the way solids
are in a compound: each against the one before it, centered unless x, y or an anchor says otherwise, and cut with -, kept with & or cancelled with ^. A shape in a group, and a profile
before its sweep, can also be turned with | rotate 90, mirrored with | flip x or resized with | scale 2 1.
color steelblue { rect 60 30 | round 4 -rect 40 6 | round 3 [ x left 6 y front 6 -circle 5 x right -6 y front 6 -circle 5 ] } | extrude 3open in editor

The plate combines rounded rectangles and circles into a single profile. The first shape
keeps the coordinates it is drawn in, so { x left rect 10 10 } puts the square against a revolve's axis, and a group of one shape is that shape.
16 / Raise any outline
Extrude
extrude raises the flat profile written before it into a solid. The height comes
first after the word, then any options. The result is placed like any other solid: centered
on X and Y, with its base at Z = 0.
rect 30 20 | extrude 4
Walls
wall draws the outline with a thickness instead of filling it. The wall is
open at the top and bottom. By default it is centered on the outline. Use inside or outside to keep the drawn outline as its outer or inner
face.
!@right 6 color steelblue rect 30 30 | extrude 16 wall 2 color gold rect 30 30 | extrude 16 wall 2 inside color coral rect 30 30 | extrude 16 wall 2 outsideopen in editor

All three walls are drawn from a 30 × 30 square, so they are 32, 30, and 34 across. A hole in a shape is an outline of its own and gets its own wall. A wall thick enough to close the opening entirely is an error.
!@right 8 color steelblue path "M0,0 H30 V20" | extrude 10 color gold path "M0,0 H30 V20" | extrude 10 wall 2open in editor

A path that never returns to its start encloses nothing. Filling it closes it into a
triangle, while wall follows the line itself. An open path has no inside or outside,
so its wall is always centered.
A lid that fits
Use an offset for clearance: the lip of this lid is the box's outline shrunk by the wall thickness and another 0.2 units. Both parts reuse the same rounded profile.
def %outline rect 60 40 | round 4 color steelblue { %outline | extrude 2 %outline | extrude 28 wall 2 inside } @right 10 color gold { %outline | extrude 2 %outline | offset -2.2 | extrude 4 }open in editor

Towards an apex
apex [x y] runs the sides towards a point rather than straight up. With no z, the apex sits at the height and the solid closes to a point there: a
pyramid on any outline. The apex is written in the profile's own coordinates. Circles,
rectangles, and ellipses are centered on their origin, and a path's origin is wherever its
coordinates put it.
!@right 8 color steelblue polygon 6 20 | extrude 30 apex [0 0] color gold rect 20 20 | extrude 30 apex [10 0] color coral path "M0,0 H20 L15,12 L2,10 Z" | extrude 30 apex [0 0]open in editor

The gold point leans over the right edge. The coral pyramid has its point above M0,0, the corner where the path starts.
Give a z to place the apex away from the top. Above the height, the sides are
cut short on their way to it, giving a frustum. Below the base, a negative z spreads them, so the top is larger than the base.
!@right 8 color steelblue circle 24 | extrude 30 apex [0 0 60] color gold circle 24 | extrude 30 apex [0 0 -60]open in editor

With the apex at twice the height, the top is half the base. With it the same distance below, the top is one and a half times the base. An apex between the base and the height is an error, because the sides would cross before they reached the top.
Tapered walls
wall and apex combine. The wall keeps the same thickness all the way
up instead of thinning with the outline. Where the outline becomes narrower than the wall, near
a point or along a tight curve, the wall closes into solid material, so a walled pyramid ends
in a solid tip.
!@right 8 color steelblue circle 30 | extrude 30 wall 2 inside apex [0 0 -90] color gold rect 30 30 | extrude 30 wall 2 inside apex [0 0 60]open in editor

Long outlines that close over along their whole length, such as text walled to a point, are refused because they would take too long to build. Move the apex further above the height to keep the top open.
To another shape
to names the shape the top is, so the side runs from one outline to another:
a square base rising into a round neck, or a duct changing its section. The line reads
from the base to the top, and both are in the profile's own coordinates, so a top drawn
off-centre leans the solid over. A top that needs 2D modifiers is written in braces, to { circle 20 | round 2 }, since steps after the sweep treat the
solid.
!@right 8 color steelblue rect 30 30 | extrude 30 to circle 20 color gold rect 40 40 | extrude 40 wall 2 inside to circle 12 color tomato rect 30 30 | extrude 40 to { rect 30 30 | rotate 45 }open in editor

Each line of the side is straight. Outlines with as many corners as each other pair corner
to corner, so a square runs to a turned square with smooth twisted faces. Otherwise each
line joins the points the same share of the way round both outlines. Either way the top
starts where the side twists least, so the square's corners meet the circle at 45 degrees.
A wall has its thickness at the base and at the top and runs straight between them, open
at both ends. Each shape must be a single outline without holes, and to cannot be combined with apex.
Twisting and resizing
twist turns the outline as it rises and size grows or shrinks
it, both about the profile's own origin. A number is where the top ends up: twist 60 turns the top 60 degrees counter-clockwise, and size 0.5 makes it half the size of the profile, every level between taking its share.
!@right 8 color steelblue star 6 40 28 | extrude 40 twist 60 color gold star 6 40 28 | extrude 40 size 0.5 color coral star 6 40 28 | extrude 40 twist 60 size 0.5open in editor

A list gives the value at levels spaced evenly from the base to the top, joined by a
smooth curve that never overshoots them. That is a vase that swells and narrows again, and
turns faster near its rim. With wall, every level is drawn with the same
thickness, however wide the outline has grown.
color steelblue star 9 80 64 | round 4 | extrude 160 twist [0 30 120] size [1 1.3 0.85 1] wall 2 insideopen in editor

A size can be zero only at the base or the top, where the solid closes to a point. apex and to already say how the outline changes, so neither
takes a twist or a size.
| Option | Default | Rule |
|---|---|---|
wall thickness | Filled profile | Zero or greater; must leave an opening. |
inside / outside | Centered | Only with wall on a closed outline. |
twist degrees | No turn | A number or a list of two or more; not with apex or to. |
size factor | The drawn size | As twist; zero only at the base or the top. |
apex [x y z] | Straight sides | z defaults to the height; it may not lie between 0 and the height. |
to top | The profile itself | One outline without holes; not with apex. |
17 / Fill an area with cells
Tilings & Voronoi
These profile generators fill a boundary with cells. Extrude the cells and subtract them to perforate a plate, or use the generated profile with another sweep operation.
Tilings
honeycomb, squares, triangles, and stripes lay cells over the area of another shape, written last after in. Each takes the size of a cell and the strut between cells. A polygon cell
is measured by its diameter across the corners, as a prism is; a stripe by its width, and
it runs left to right through the whole area.
color goldenrod { block 44 44 3 -honeycomb 8 1.5 rim 2 in rect 44 44 | extrude 3 } @right 6 color steelblue { block 44 44 3 -squares 8 1.5 rim 2 in rect 44 44 | extrude 3 } @right 6 color coral { block 44 44 3 -triangles 8 1.5 rim 2 in rect 44 44 | extrude 3 } @right 6 color seagreen { block 44 44 3 -stripes 4 2 rim 2 in rect 44 44 | extrude 3 }open in editor

The cells are the holes, so subtracting a tiling perforates a plate. Along the outline of
its area the tiling leaves a rim, as wide as the strut unless rim says
otherwise; here rim 2 keeps an edge of 2 on every plate. The area can be any shape, so a tiling
cut into a plate of the same outline keeps its edge whatever the outline is:
set $outline "M0,0 H80 L100,20 V40 H0 Z" path $outline | extrude 3 -honeycomb 8 1.5 rim 3 in path $outline | extrude 3open in editor

angle turns the cells inside the area, which stays where it is: squares 8 1.5 angle 45 in rect 40 40 is a diamond lattice in the same square. | rotate would turn the area too.
Voronoi
voronoi divides its area among sites, each cell the part nearest its own
site. cells 20 scatters sites at random inside the area and seed picks
the arrangement, so the same seed always draws the same cells. relax evens
them out, twice by default. points [x y] … writes the sites out instead.
vase: rect 40 40 | extrude 50 apex [0 0 -170] | shell 2 on vase #f2 -voronoi 1.5 cells 20 seed 1 in #f2 | extrude 2 on vase #f3 -voronoi 1.5 cells 20 seed 2 in #f3 | extrude 2 on vase #f4 -voronoi 1.5 cells 20 seed 3 in #f4 | extrude 2 on vase #f5 -voronoi 1.5 cells 20 seed 4 in #f5 | extrude 2open in editor

Each sloped side is the area of its own pattern, cut from its own seed. The rim, as wide as the strut, keeps the corners of the vase standing, and the label brings every cut back to the vase rather than to the cut before it.
Modifying the boundary or the cells
A 2D modifier treats the innermost shape before it. After a tiling, it changes the boundary shape; braces around the tiling make it change the cells instead.
/* Round the boundary before filling it with cells */ honeycomb 8 1.5 in rect 40 30 | round 3 | extrude 3 /* Round the cells themselves */ @right 8 { honeycomb 8 1.5 in rect 40 30 } | round 1 | extrude 3
18 / Letters as solids
Text
text is a profile like rect or circle, so letters
become solids the way every other outline does: raise them with extrude. Put
on a face, they stand out of it; with -, they are carved into it.
color steelblue block 70 22 3 [ on #top color white text "RAISED" | extrude 1.5 ] @back 6 color steelblue block 70 22 3 [ on #top -text "CARVED" | extrude 1 ]open in editor

The extrusion height is the depth of the letters, not their size. A string may also come
from a variable, so one set $label "…" can feed several texts.
Size
font-size sets the height of a capital letter in model units, 10 by default.
The other letters keep their proportion to it, so accents, tall lowercase letters, and
descenders reach above or below it. Like color, it applies to the next solid,
or to everything after it with !.
!@right 4 font-size 6 text "Aa" | extrude 2 font-size 10 text "Aa" | extrude 2 font-size 16 text "Aa" | extrude 6open in editor

Fonts
Without a font, text is drawn in a built-in sans-serif covering Latin-1 and
Greek. Name any family from Google Fonts to
use it instead, with an optional weight from 100 (thin) to 900 (black). The capital height stays the same in every font, so switching fonts
does not resize the model.
!@right 6 !font-size 14 font "Lobster" text "Ag" | extrude 3 font "Roboto" 900 text "Ag" | extrude 3 font "Roboto" 100 text "Ag" | extrude 3 font "Playfair Display" 700 text "Ag" | extrude 3open in editor

A font can also come from a file of your own, by URL or, from the command line, by path:
font "https://example.com/fonts/Logo.woff2" font "fonts/Logo.otf" /* beside the script, command-line tool only */
| Family | Example | Notes |
|---|---|---|
| Google Fonts name | "Roboto" 700 | Weight defaults to 400; a weight the family lacks is an error. |
https URL | "https://…/Logo.woff2" | Any font file; the URL needs no extension. |
| File path | "fonts/Logo.otf" | Ends in .ttf, .otf, .woff, .woff2, or .ttc; relative to the script. Read only by
the command-line tool. |
Font files are TrueType (TTF), OpenType (OTF), WOFF, or WOFF2, up to 10 MB; a TTC collection gives its first font. A file holds one weight, so a weight given with a file only selects one from a variable font. The family is always written out in quotes, never as a variable, because fonts are loaded before the script runs. A character the font has no glyph for is left out, with a warning.
Several lines and special characters
A string may span lines, or break them with \n. Each new line starts below
the previous one, aligned on the left.
color slategray block 46 34 3 on #top color gold text "OPEN\nDAILY" | extrude 1.5open in editor

A backslash starts an escape, for the characters a string cannot hold as they are:
| Escape | Stands for |
|---|---|
\n | A line break |
\" | A double quote |
\\ | A backslash |
\u{…} | The Unicode character with that hex code point, such as \u{B0} for ° |
!@right 6 text "\" | extrude 2Hi\"" text "20\u{B0}C" | extrude 2open in editor

Any other escape is an error, so a backslash meant literally, as in a Windows path, is
written \\.
Outlined letters
Every option of extrude works on text. A wall draws the letters
hollow, and sweep runs a profile along their outlines for tube lettering.
!@right 6 color coral text "Hi" | extrude 4 wall 0.8 color steelblue circle 1.2 | sweep along text "Hi"open in editor

A sign sized to its text
A label measures the text it names, so the plate below grows with the word. The top face
of the plate then serves as a shape, raised into a rim with wall. Change the
word or the font, and the sign follows.
!font "Lobster" !font-size 16 !color #555555 title: text "Pegm" | extrude 1 @bottom color gold block (title.width + 12) (title.depth + 12) 2 | fillet 2 at parallel z #top | extrude 1 wall 2 insideopen in editor

19 / Carry a profile through space
Revolve, helix & sweep
A profile can follow a circular route with revolve, rise as it turns with helix, or follow another outline with sweep. Each follows its
profile after | and places the resulting solid like any other model part.
Revolve
circle 20 | revolve 40
circle 20 | revolve 40 sweeps a circle around a path of diameter 40 to form a
torus. Options include angle, start, about x (or
Y/Z), and shrink.
Helix
color gold circle 5 | helix 40 pitch 10 turns 6open in editor

helix rises as it turns. pitch is the rise per turn; specify
either turns or height. An optional second diameter tapers the
coil. Leave enough pitch for the profile so adjacent coils do not overlap.
height is the rise of the path, and the profile reaches half its size past
each end, so the helix's box is one profile taller. Center a thread on its rod with @center, and trim it flush with an intersection rather than a compound. In a path profile Y grows downwards, so the top of the drawing is the top of the solid.
Sweep
color steelblue circle 4 | sweep along path "M0,0 H30 C40,0 40,20 50,20 H70" @right 10 color gold rect 4 4 | sweep along rect 30 20 roundopen in editor

sweep along carries a profile along the outline of another shape, the
trajectory. The trajectory lies in the build plane, where an extruded shape lies, so on #top draws it on a face. The profile stands across the route: its X to the right
of an open path's travel or to the outside of a closed outline, its Y up off the plane. An open
path is capped at both ends; a closed outline makes a ring.
Sharp corners are mitred, the way a wall's are; add round to round their
outside. about x or about y stands the trajectory upright. A profile wider
than the inside of a bend has room for is refused.
Varying profiles
A shape macro called before a sweep with its last argument left off is a varying profile: the sweep gives that argument itself, from 0 where it starts to
1 where it ends, and draws the shape anew as it goes. One used once is written in place,
with % and its parameter instead of a name.
def %handle $t circle (8 - 4 * $t) !@right 10 color steelblue % $t rect 30 30 | round (1 + 13 * $t) | rotate (45 * $t) | extrude 40 color gold % $t circle (8 + 12 * $t) | revolve 50 angle 270 color coral %handle | sweep along path "M0,0 C0,-30 36,-30 36,0" about yopen in editor

The last argument runs over an extrude's height, a revolve's turn, a helix's turns and each outline a sweep follows. Every outline is joined from the point it is drawn from, so a profile that turns turns the solid. It can change its size, corners and turn, but not how many outlines and holes it has, and a whole turn or a closed route has to end on the shape it started from.