← Back to Documentation

PPSCAD Language Reference

PPSCAD (PrintParams Scripted CAD) is a simple language for creating parametric 3D models. Inspired by OpenSCAD and JSCAD, designed for ease of use.

Quick start

// A box with a hole through it
param width: float = 30 [10:100] "Width (mm)"
param height: float = 20 [5:50] "Height (mm)"
param hole_r: float = 4 [1:15] "Hole radius (mm)"

difference {
  cube(width, height, 10)
  translate(width / 2, height / 2, -1)
    cylinder(h: 12, r: hole_r)
}

Parameters appear as sliders in the side panel. Change a slider and the model updates automatically.

Parameters

Parameters are first-class in PPSCAD. Declare them at the top of your code with the param keyword.

Syntax

param name: type = default [min:max:step] "Label" "Description"

Types

// Number with slider
param teeth: int = 20 [6:200:1] "Number of teeth"

// Float with custom step
param module: float = 2.0 [0.5:10:0.01] "Module (mm)"

// Boolean toggle (checkbox)
param chamfer: bool = true "Add chamfer"

// Dropdown select
param style: choice = round ["round", "square", "hex"] "Head style"

// Text input
param label: string = hello "Engrave text"

Variables

Use let to declare variables. They can hold numbers, booleans, strings, arrays, or geometry.

let outer_r = teeth * module / 2
let offset = [10, 0, -5]
let part = cube(20, 10, 5)

3D Primitives

cube(size)

Cube with equal sides.

cube(x, y, z)

Box with specified dimensions.

cube(size: [x,y,z], center: true)

Centered box with named args.

cylinder(h, r)

Cylinder with height and radius.

cylinder(h: 20, r1: 5, r2: 3)

Cone (different top/bottom radii).

cylinder(h: 20, d: 10)

Cylinder specified by diameter.

sphere(r)

Sphere with given radius.

sphere(d: 20, segments: 64)

Sphere with diameter and resolution.

2D Primitives

2D shapes are used with extrude and revolve to create 3D geometry.

circle(r)

2D circle.

square(w, h)

2D rectangle.

square(size, center: true)

Centered square.

Boolean operations (CSG)

Combine shapes using Constructive Solid Geometry. Each takes a block of children.

// Union: combine all children into one shape
union {
  cube(10)
  translate(5, 5, 0) sphere(8)
}

// Difference: first child minus all others
difference {
  cube(20)                    // base shape
  translate(10, 10, -1)       // shapes to cut away
    cylinder(h: 22, r: 5)
}

// Intersection: keep only the overlapping volume
intersection {
  cube(20, center: true)
  sphere(14)
}

Transforms

Transforms move, rotate, or scale their child geometry. They can take a single child or a block.

// Move by x, y, z
translate(10, 0, 5) cube(10)

// Rotate around x, y, z axes (in degrees)
rotate(0, 0, 45) cube(10)

// Scale uniformly or per axis
scale(2) sphere(5)
scale(2, 1, 0.5) cube(10)

// Mirror across an axis
mirror(1, 0, 0) {
  translate(5, 0, 0) cube(10)
}

// Chaining: transforms apply to the next statement
translate(20, 0, 0)
  rotate(0, 0, 45)
    cube(10)

Extrude and revolve

Turn 2D shapes into 3D geometry.

// Linear extrude a circle into a cylinder
extrude(20) circle(5)

// Extrude with twist and scale
extrude(h: 30, twist: 90, slices: 50)
  square(10, 10)

// Revolve a 2D shape around the Y axis
revolve()
  translate(15, 0) circle(3)

// Partial revolve
revolve(angle: 180)
  translate(10, 0) square(5, 8)

Control flow

// Conditional geometry
if (add_holes) {
  translate(10, 10, -1) cylinder(h: 22, r: 3)
}

// If-else
if (style == "round") {
  sphere(10)
} else {
  cube(16, center: true)
}

// For loop with range [start:end] (inclusive)
for (i in [0:4]) {
  translate(i * 15, 0, 0) cube(10)
}

// Range with step [start:step:end]
for (angle in [0:30:330]) {
  rotate(0, 0, angle)
    translate(25, 0, 0)
      cylinder(h: 10, r: 3)
}

// For with array
for (size in [5, 10, 15, 20]) {
  translate(size * 2, 0, 0) cube(size)
}

Functions and modules

Functions compute values. Modules create reusable geometry.

// Function: returns a computed value
fn gear_radius(teeth, module) = teeth * module / 2

// Function with block body
fn clamp(val, lo, hi) {
  if (val < lo) { return lo }
  if (val > hi) { return hi }
  return val
}

let r = gear_radius(20, 2.5)

// Module: creates reusable geometry
mod rounded_box(w, h, d, r) {
  hull {
    translate(r, r, 0) cylinder(h: d, r: r)
    translate(w - r, r, 0) cylinder(h: d, r: r)
    translate(r, h - r, 0) cylinder(h: d, r: r)
    translate(w - r, h - r, 0) cylinder(h: d, r: r)
  }
}

rounded_box(40, 20, 5, 3)

Method chaining

Geometry objects support method chaining for inline transformations and boolean operations.

// Chain transforms
cube(20, 10, 5)
  .translate(5, 0, 0)
  .rotate(0, 0, 45)

// Boolean operations
let base = cube(30, 20, 10)
let hole = cylinder(h: 12, r: 4).translate(15, 10, -1)
base.subtract(hole)

// 2D to 3D via method
circle(5).extrude(20)
square(10, 5).translate(15, 0).revolve()

Math functions

All trigonometric functions use degrees (not radians), matching OpenSCAD convention.

FunctionDescription
sin(deg), cos(deg), tan(deg)Trigonometric (input in degrees)
asin(x), acos(x), atan(x)Inverse trig (output in degrees)
atan2(y, x)Two-argument arctangent (degrees)
abs(x)Absolute value
ceil(x), floor(x), round(x)Rounding
sqrt(x)Square root
pow(x, y)Power (or use x^y operator)
ln(x), log(x)Natural log / log base 10
exp(x)e^x
min(a, b, ...), max(a, b, ...)Minimum / maximum
len(array)Array length
echo(values...)Print to console (debugging)

Constants

PI3.14159...
TAU6.28318... (2 * PI)
E2.71828...
INFInfinity

Operators

// Arithmetic
+  -  *  /  %  ^     // add, subtract, multiply, divide, modulo, power

// Comparison
==  !=  <  <=  >  >=

// Logical
&&  ||  !             // and, or, not

// Ternary
condition ? value_if_true : value_if_false

Named arguments

Primitives and transforms accept both positional and named arguments.

// These are equivalent:
cylinder(20, 5)
cylinder(h: 20, r: 5)

// Named args can be in any order:
cylinder(r: 5, h: 20)

// Mix positional and named:
cylinder(20, r: 5)

Comments

// Single-line comment

/* Multi-line
   comment */

Units

All dimensions are in millimeters. STL exports are also in mm.

Complete example: twisted tower

This example showcases most of PPSCAD's features: parameters, functions, for loops, computed variables, nested transforms, conditionals, and CSG.

// Twisted Tower — stacked layers with rotation and taper
param layers: int = 20 [4:60] "Number of layers"
param size: float = 25 [10:50] "Base size (mm)"
param height: float = 80 [20:200] "Total height (mm)"
param twist: float = 90 [0:720:5] "Total twist (deg)"
param taper: float = 0.5 [0.2:1.0:0.05] "Top/bottom ratio"
param hollow: bool = true "Hollow interior"
param wall: float = 2 [1:5:0.5] "Wall thickness (mm)"

fn lerp(a, b, t) = a + (b - a) * t

let layer_h = height / layers

for (i in [0:layers - 1]) {
  let t = i / max(layers - 1, 1)
  let s = lerp(size, size * taper, t)
  let angle = twist * t

  translate(0, 0, i * layer_h)
    rotate(0, 0, angle)
      translate(-s / 2, -s / 2, 0) {
        if (hollow) {
          difference {
            cube(s, s, layer_h + 0.1)
            translate(wall, wall, -0.05)
              cube(s - wall * 2, s - wall * 2, layer_h + 0.2)
          }
        } else {
          cube(s, s, layer_h + 0.1)
        }
      }
}