Getting Started
Try PolyScript
You can try it right now in your browser. Open the Playground and start typing code.
As you type code, the 3D model appears on the right in real time. Just copy and paste the examples on this page to get started.
What is PolyScript?
PolyScript is a CAD DSL for writing parametric 3D models concisely.
The basic idea is simple: create a primitive, chain operations with |, and select only the parts you need to modify.
box 80 60 10 | fillet 2 | diff cylinder 10 10
This single line means:
box 80 60 10creates a box| fillet 2rounds the edges| diff cylinder 10 10cuts out a cylinder-shaped hole
PolyScript has three key characteristics:
- Common CAD operations can be written concisely (no parentheses needed)
- The flow of operations reads left to right through
| - As a declarative DSL, it is safe to execute
About units: Numbers in PolyScript have no fixed unit. The convention for 3D printing is to treat them as millimeters. Think of
box 80 60 10as 80mm × 60mm × 10mm.
Core Concepts
1. Shapes start from primitives
3D models start from basic shapes called primitives. For solids: box (box), cylinder (cylinder), sphere (sphere). For flat shapes: rect (rectangle), circle (circle), and more. Arguments are separated by spaces.
box 40 30 10
cylinder 5 10 at:(40, 0, 0)
cone 10 0 20 at:(70, 0, 0) # a full cone (top radius 0)
torus 20 5 at:(120, 0, 0) # a donut shape
Use
at:(x, y, z)to position a primitive. Withoutat:, shapes are placed at the origin, so spread them out like above when showing several primitives side by side.
2. Operations are chained with pipes
PolyScript chains “create → select → modify” with |. The pipe also closes the preceding argument list.
box 80 60 10
| edges =Z
| fillet 3
How to remember selector symbols:
>means “maximum direction”,<means “minimum direction”,=means “parallel”. So>Zmeans “topmost in Z” = top face, and=Zmeans “edges running parallel to Z”.
3. Context switches
In PolyScript, the available operations depend on what “working mode” you are in. This is called the context. It often trips up beginners, but for now, just remember this flow:
- Creating something like
box 80 60 10puts you in a 3D context faces topselects a face (a virtual work surface called a workplane is created automatically on that face)- Draw 2D primitives directly on that face
- Use
cutorholeto machine the shape - After machining, you return to 3D
box 80 60 10
| faces top
| circle 5
| cut
cut is an operation used on faces of 3D objects. You cannot write circle 5 | cut without selecting a face first.
Making a rounded box
diff is a “carve out” operation – it removes the specified shape from the original. Let’s build a finished part with a short piece of code.
box 80 60 10
| fillet 2
| diff cylinder 10 10
| diff cylinder 2.5 10 at:20 10
Here’s how to read it:
box 80 60 10creates a box centered at the originfillet 2rounds the edgesdiff cylinder 10 10cuts out a cylinder-shaped hole at the centerdiff cylinder 2.5 10 at:20 10adds a smaller hole at an offset position
at: is a named argument meaning “place the shape at this position.” For simple coordinates (literals or variables), parentheses can be omitted (at:20 10). When expressions are involved, parentheses are required (at:($x+1, $y+1)). For one-off placements, at: is more readable than translate. For array duplication, use the | grid or | polar pipe operations.
Creating 3D from 2D
2D primitives are just outlines on their own. Use extrude or revolve to make them 3D.
rect 60 40 | extrude 15
rect 10 30 at:(15, 0) | revolve Y
For starters, these guidelines are enough:
- For plates and blocks:
rect 60 40 | extrude 15 - For axially symmetric shapes:
rect 10 30 at:(15, 0) | revolve Y(the profile must be placed on one side of the rotation axis) - For ready-made solids: start with
box 80 60 10orcylinder 5 10
Selecting faces and machining
The most distinctive feature of PolyScript is selecting faces and machining them.
Creating a pocket on the top face
box 80 60 10
| faces top
| rect 20 10
| cut 3
faces topselects the top face (implicitly creating a workplane)rect 20 10draws a rectanglecut 3cuts a pocket 3 deep
Drilling a hole at the center of a face
hole can be used directly from a face selection. It drills a hole at the center of the selected face.
box 80 60 10
| faces top
| hole 5
hole 5 drills a through-hole of radius 5 (diameter 10). Same result as faces top | circle 5 | cut, just one step shorter.
Drilling multiple holes at once
To specify hole positions, use points to place them, then hole.
box 80 60 10
| faces top
| points (polar 4 15)
| hole 5
faces topselects the top face, entering 2D operationspoints (polar 4 15)arranges 4 points in a circle. Parentheses are required when passing the result of a function call as an argument.hole 5drills a through-hole of radius 5 at each point
After selecting a face, you can also write polar / grid directly, omitting points.
box 80 60 10
| faces top
| polar 4 15
| hole 5
Both forms produce the same result. Use whichever you prefer.
The points + hole combination is the basic pattern for creating bolt holes and mounting holes.
Common Operations
At the beginner stage, learn these operations first:
| Operation | Purpose | Example |
|---|---|---|
fillet r |
Round edges | box 40 30 10 | fillet 2 |
chamfer r |
Chamfer edges | box 40 30 10 | chamfer 1 |
diff shape |
Subtract a shape | box ... | diff cylinder ... |
union shape |
Add a shape | box ... | union cylinder ... |
faces sel |
Select faces | faces top |
edges sel |
Select edges | edges =Z |
workplane axis |
Sketch on a specific axis | workplane XZ |
verts |
Select vertices (place 2D/3D primitives) | rect 70 50 | verts |
cut |
Cut with a sketch shape | circle 5 | cut |
hole r |
Drill a hole (at face center or each point) | faces top | hole 5 |
at:x y |
Place at position (named argument) | cylinder 2 10 at:20 0 |
Parametric design with variables and functions
Writing fixed dimensions is fine, but the real strength of PolyScript is parameterization.
Variables
$w = 80
$h = 60
$t = 10
box $w $h $t
| fillet 2
Functions
def standoff($r, $h, $hole_r) =
cylinder $r $h
| diff cylinder $hole_r $h
box 80 60 3
| union standoff 4 10 1.5 at:[(10, 10), (70, 10), (10, 50), (70, 50)]
With this pattern, you can reuse parts by just changing the hole diameter or height.
Overriding Parameters from the CLI
Once your model has parameters, you can change their values at build time without editing the source.
Direct override with -D key=value
poly build my_box.poly -D width=100 -o out.stl
Pass multiple parameters at once:
poly build my_box.poly -D width=100 -D height=30
Values are type-inferred automatically: 100 is int, 1.5 is float, true/false is bool, anything else is string.
Loading from a JSON file with --params-file
When you have many parameters, put them in a JSON file:
{
"width": 120,
"height": 80,
"material": "ABS"
}
poly build my_box.poly --params-file dims.json
Combining both
-D and --params-file can be used together. When the same key appears in both, -D wins.
poly build my_box.poly --params-file presets/small.json -D width=120
The full precedence order (highest to lowest):
- CLI
-D --params-file.poly.params.jsonparameterSets.default@paramdefault values
Try it out
Save the following as my_box.poly:
@param 10..200 step:5 desc:"Box width"
width = 60
@param 5..50 desc:"Height"
height = 20
box width width height
Build it with different values from the CLI:
poly build my_box.poly -D width=100 -D height=30
poly build my_box.poly --params-file variations/large.json
The GUI is fun for exploration, but cranking out variations from the CLI is where parametric modeling really shines.