Getting Started

Core concepts and your first model.

Try PolyScript

You can try it right now in your browser. Open the Playground and start typing code.

PolyScript Playground

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
✎ Edit

This single line means:

  • box 80 60 10 creates a box
  • | fillet 2 rounds the edges
  • | diff cylinder 10 10 cuts 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 10 as 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
✎ Edit

Use at:(x, y, z) to position a primitive. Without at:, 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
✎ Edit

How to remember selector symbols: > means “maximum direction”, < means “minimum direction”, = means “parallel”. So >Z means “topmost in Z” = top face, and =Z means “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:

  1. Creating something like box 80 60 10 puts you in a 3D context
  2. faces top selects a face (a virtual work surface called a workplane is created automatically on that face)
  3. Draw 2D primitives directly on that face
  4. Use cut or hole to machine the shape
  5. After machining, you return to 3D
box 80 60 10
 | faces top
 | circle 5
 | cut
✎ Edit

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
✎ Edit

Here’s how to read it:

  • box 80 60 10 creates a box centered at the origin
  • fillet 2 rounds the edges
  • diff cylinder 10 10 cuts out a cylinder-shaped hole at the center
  • diff cylinder 2.5 10 at:20 10 adds 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
✎ Edit

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 10 or cylinder 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
✎ Edit
  • faces top selects the top face (implicitly creating a workplane)
  • rect 20 10 draws a rectangle
  • cut 3 cuts 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
✎ Edit

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
✎ Edit
  • faces top selects the top face, entering 2D operations
  • points (polar 4 15) arranges 4 points in a circle. Parentheses are required when passing the result of a function call as an argument.
  • hole 5 drills 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
✎ Edit

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
✎ Edit

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)]
✎ Edit

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):

  1. CLI -D
  2. --params-file
  3. .poly.params.json parameterSets.default
  4. @param default 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
✎ Edit

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.