OpenSCAD: creating dimension-driven printable parts
OpenSCAD describes geometry through a script: you write shapes and their parameters, then the software calculates the model. This article shows a simple part whose dimensions change when script values are edited. It draws on the OpenSCAD manual and documentation index; the spacer is an original editorial example and was not run, exported or printed for this article.
The short answer
To parameterize a simple OpenSCAD part, place measurements in variables and pass them to parameters of a module that describes the geometry. The manual says variables are evaluated at compile time and the last assignment within a scope wins. Functions compute values; modules define objects or operators and can accept default parameters. In this example, changing a measurement means editing the corresponding script value, without redrawing the geometry with a mouse.
How a script differs from drawing with a mouse
In a visual modeler such as FreeCAD, you can sketch and edit dimensions with graphical tools. OpenSCAD follows a different modeling approach: its text script describes shapes, transforms and operations. The documentation index lists a tutorial, a user manual and a language reference; this page focuses on the language and its parameters.
The tutorial listed in the documentation includes chapters on scaling and parameterizing models, modules and loops. A script lets you name the values you want to make adjustable and reuse them in a geometry description. That approach fits an example such as the spacer below; it does not establish that script-based modeling is faster or better for every object.
The manual describes OpenSCAD variables as values set at compile time rather than as state changed step by step. That distinction matters when deciding which dimensions to expose and how to assign them in a script.
Variables as adjustable constants set at compile time
An OpenSCAD variable is created with a name, an equals sign, an expression and a semicolon. The manual shows simple examples: a number, a calculation using cosine, a boolean, a string, and a vector in square brackets. Vectors fill the role that arrays play in imperative languages, and a member is read with an index in brackets.
The key point, according to the manual, is that if a variable receives several values in the same scope, only the last assignment in that scope applies there and in its inner scopes. The manual suggests treating variables as redefinable constants rather than ordinary variables. This behavior also allows a command-line value to be supplied with the -D option, which is placed at the end of the source code. A library that defines root-level defaults can therefore be overridden by an assignment in your own file.
There is a firm limit too. The manual states that x = x + 1 is not valid, since values cannot change during run time. A variable that was never assigned holds the special value undef, which can be tested in a condition. The manual also notes that variables beginning with a dollar sign are special variables, more dynamic than regular ones, offering another way to pass arguments to modules and functions.
An original example: an adjustable spacer
Here is an editorial example written for this page and offered as a starting point, not as a test that was run. It describes a cylindrical spacer with a through hole, repeated several times in a row. Every measurement is a placeholder value for you to adapt:
$fn = 64; height = 10; outer_radius = 6; hole_radius = 2; count = 4; spacing = 15; module spacer(h, ro, ri) { difference() { cylinder(r=ro, h=h); translate([0, 0, -1]) cylinder(r=ri, h=h+2); } } for (i = [0:count-1]) translate([i*spacing, 0, 0]) spacer(height, outer_radius, hole_radius);
The parameter lines near the top gather the values chosen for this example: height, radii, part count and spacing. The module receives three parameters and passes them to the cylinder primitives; the manual documents radius, height and the special variable $fn. The difference() operation subtracts the inner cylinder from the outer one. In this example the inner cylinder starts at z=-1 and extends one unit beyond each end of the modeled height so it passes through the volume.
For this scheme, choose a positive height and radii satisfying 0 < hole_radius < outer_radius. Count must be a positive integer; spacing greater than twice the outer radius separates the copies in this row. With the suggested values, outer diameter is 12, hole diameter is 4, and height is 10 in model units. These are dimensions calculated from the script, without measurement of a manufactured part; adapt them and check the result in your installation.
Functions and modules to reuse the part with new values
The manual draws a clear line between two tools. A function operates on values and returns a new value. It is written with the function keyword, a name, parameters and an equals sign followed by an expression. A module defines an object or an operator. It is written with the module keyword and a block of actions in braces. The manual states that there is no mechanism for a module to return a value to the outside. If you need a numeric result, write a function.
A function can calculate hole radius from a diameter and radial clearance, for example function hole_radius(screw_d, radial_clearance=0.2) = screw_d/2 + radial_clearance;. In this example, the added diametral clearance is twice the radial clearance; 0.2 is illustrative. It is neither an assembly recommendation nor a measurement. Choose your values and verify dimensions in your own workflow; the dimensional accuracy guide helps organize that check.
The manual also shows modules that accept named options, such as its house example where the roof style and paint color are chosen at call time. You can follow the same pattern to offer a chamfered variant or a different side count for your spacer, with each variant remaining a single module call.
Loops, variable scope and common beginner traps
The for loop in the example repeats the module along one axis. The manual explains that loops are no exception to the variable rule. A copy of the loop body is created for each pass, each pass gets its own scope, and you still cannot write a = a + 1. For richer patterns, the tutorial chapters on control flow and loops are the natural next step.
Scope holds a few surprises. According to the manual, each pair of braces creates an inner scope, and since version 2015.03 you can assign variables inside it, but their values stay confined to those braces and never leak outward. An anonymous block of braces with no operator in front is not treated as a scope. If a value seems to be ignored, first look for a second assignment further down the file, because the last one wins.
Two more points deserve attention. The manual says modules may call themselves recursively, but without tail-recursion elimination, and its tree example warns to keep the depth low because the number of primitives grows exponentially. For functions, a built-in recursion limit raises an explicit error, and only tail-recursive functions benefit from a much higher limit. Finally, the manual mixes historical notes with newer references, so do not infer from it which version of the software you have installed.
From script to slicer: what still needs checking
Accepted syntax guarantees neither nonempty geometry nor a printable part. Before exporting the example, check its values, computed shape, and messages in your version. The official index points to Import and Export, Customizer, and command-line sections for the next steps. Then check dimensions and preparation in your slicer; no export was performed for this page.
If physical dimensions matter to your assembly, you can compare a part made in your own workflow with the target dimension and then adjust the parameter. This is a suggested validation step for your project, not a measurement performed for this page’s example or a universal clearance value.
OpenSCAD provides script-based modeling; visual modelers describe parts through different tools and interactions. Choose based on how you want to build and change geometry. This page’s example shows one case where named values can be reused in a script.
Sources and limits
- OpenSCAD - Documentation
- The OpenSCAD Language
We organize manufacturer and community documentation; we have not measured these products ourselves. The manual for your exact model takes precedence over general guidance.
A clearer shortlist starts with your projects.
Use the free buying checklist and budget worksheet before comparing offers.