Guide · 8 min read

Parametric design guide

Parametric CAD lets one model become many parts — change a dimension, the whole thing regenerates. To get there, your variables, modules, and naming need a small amount of structure. This guide collects the conventions that make models survive 50 turns of edits with an AI agent (or with another human).

Why parametric?

The same model serves more use cases:

  • A bracket with leg_length becomes both a 30 mm corner bracket and a 60 mm shelf bracket.
  • A cable clip with cable_d becomes a USB-C clip and a Cat-6 clip without rewriting anything.
  • A gear with teeth and module becomes an entire gear family.

And — because every variable is named — you (and the AI agent) can change one thing without breaking the rest.

Variables: top of the file, declared once

Put every dimension into a named variable at the top of the file, in one block, with a comment explaining the unit and intent:

// === Parameters =====================================
leg_length   = 30;     // [10:1:100] — bracket leg in mm
leg_width    = 20;
thickness    = 4;      // wall thickness (min 1.6 for FDM)
hole_d       = 3.2;    // M3 clearance
hole_inset   = 5;
$fn          = 48;     // global facet count
The // [min : step : max] comment is special.

OpenSCAD's Customizer (and the PrintPal Build mode) reads it to render a slider for that variable. The PrintPal AI CAD agent adds these automatically when it can infer reasonable bounds.

Naming conventions

  • Dimensions in mm: plate_w, plate_t, hole_d.
  • Counts: n_holes, n_teeth, cells.
  • Booleans: has_lid, chamfer_top, countersink.
  • Vectors: 2- and 3-vectors when things group naturally — bolt_pattern = [25, 25] beats bolt_x = 25; bolt_y = 25;.
  • Hardware specs: prefix with the standard — m3_clear_d = 3.2, m3_csk_d = 6.

Match the built-ins: d not diam, r not radius, h not height when the meaning is obvious from context.

Design intent: derive, don't duplicate

If two values move together, write the dependent one as an expression of the independent one:

// Don't do this — they drift on edits:
plate_w = 40;
hole_x  = 20;          // should be plate_w/2

// Do this — design intent is explicit:
plate_w = 40;
hole_x  = plate_w / 2;   // always centered, even after edit

Modules: one per feature, with a tag

Wrap every feature in a named module, even features used only once. Then prefix it with a // @feature: name tag so it can be referenced from chat across turns:

// @feature: corner_holes
module corner_holes() {
  for (x = [hole_inset, leg_length-hole_inset])
    translate([x, leg_width/2, -1])
      cylinder(h=thickness+2, d=hole_d, $fn=$fn);
}

// @feature: leg_a
module leg_a() {
  difference() {
    cube([leg_length, leg_width, thickness]);
    corner_holes();
  }
}

This buys you three things:

  1. Re-use: leg_a() + mirror for a symmetric bracket.
  2. Targeted edits: the AI agent can rewrite one module without touching the others.
  3. Click-to-highlight: chips above the chat composer let you click @feature[corner_holes] to outline the geometry in 3D.

Layered defaults & overrides

For libraries, give every module a sensible default and let the caller override. Pattern:

module m3_hole(depth = 10, d = 3.2) {
  translate([0, 0, -1])
    cylinder(h=depth+2, d=d, $fn=32);
}

m3_hole();                          // defaults
m3_hole(depth=20);                // override one
m3_hole(d=3.5, depth=12);          // override both

Assertions catch impossible configurations

Use assert to enforce constraints. The error surfaces in the agent's tool card and the next turn knows about it:

assert(thickness >= 1.6, "Wall too thin for FDM — min 1.6 mm.");
assert(leg_length > 2 * hole_inset,
       "leg_length must clear two hole insets.");

Echo your design summary

End the file with an echo dumping the final dimensions, weight estimate, or BOM. The agent reads this from the OpenSCAD console:

echo("Bracket envelope:", [leg_length, leg_width, thickness]);
echo("Holes:", 4, "× M3 clearance");

A file structure that scales

// ===========================================================
// Bracket — parametric M3 corner mount
// ===========================================================
//
// Parameters above; geometry below. Every named feature group
// is tagged with @feature: name so it can be referenced from
// chat across regenerations.
// ===========================================================

// === Parameters ====================================
leg_length = 30;       // [10:1:100]
leg_width  = 20;
thickness  = 4;
hole_d     = 3.2;
hole_inset = 5;
$fn = 48;

// === Assertions ====================================
assert(thickness >= 1.6, "Wall too thin for FDM");

// === Modules =======================================
// @feature: corner_holes
module corner_holes() { /* ... */ }
// @feature: leg_a
module leg_a()       { /* ... */ }
// @feature: leg_b
module leg_b()       { /* ... */ }
// @feature: bracket
module bracket()     { union() { leg_a(); leg_b(); } }

// === Entrypoint ====================================
bracket();
echo("Envelope:", [leg_length, leg_width, thickness]);

Anti-patterns

AvoidBecause
Magic numbers in geometryCan't refactor a dimension you can't search for. Always name it.
Single 400-line top-level difference()Impossible to reuse, hard to read, breaks the @feature workflow.
Module names like part1(), thing()Future-you and the agent both want descriptive names.
Inline expressions deep in transformsHard to debug. Pull the value into a named variable.
Renaming features mid-conversationBreaks all your @feature[...] references. Add a new feature instead.

Designing for AI handoff

If you intend the agent to keep editing the file, follow these extra rules:

  • Don't delete @feature tags by hand — the agent uses them as the contract between turns.
  • Don't combine multiple features into one module — the agent can't selectively edit half a module.
  • Use vertical whitespace between feature blocks — the agent uses it as a parse hint.
  • Comment intent, not mechanics. "Stiffening rib — must clear the M3 boss above" is far more useful than "Adds a 3 mm × 10 mm cube here".

Let the agent set up the structure for you

The AI CAD agent emits this structure by default — named parameters, @feature tags, sensible assertions, an echo summary at the bottom. Just describe the part.

Open the agent