OpenSCAD User Manual/General
Scripts in the OpenSCAD language are functional descriptions of how a designer's intent may realized in a solid model.
Program Structure
The statement is the basis of the language:
<perform named operations>;
The end of a statement is marked by a literal semi-colon (';').
Each statement either :
- assigns the result of an expression to a variable
- invokes one or more modules to instantiate a shape that appears in the preview panel
- modifies the script's flow of execution.
Evaluating Expressions
Expressions are evaluated before any module in a statement.
The evaluation of an expression results in a value of a specific type and may replace a single variable or literal wherever syntax requires a value.
When used in a simple statement the result must be assigned to a variable
<Named Variable> = <expression>;
but in general the result of an expression is used immediately as a syntactic element.
- for( <loop variable> = <condition> ) <statement>
- [vector] = [for( <loop variable> = [<start>:<incr>:<end>] ) make_list_element( <loop variable> ) ]
- make_shape( <param>, <name>=<param> )
- echo( "giving feedback", <function_under_test>( <param> ) );
where <condition>, the <start>:<incr>:<end> values in the range, each parameter value in the call to the module, and the <function_under_test>, and <param> may all be expressions.
Modules are Parents
Modules are not like the subroutines that are the building blocks of procedural languages. OpenSCAD is all about constructing a model out of solid geometry elements, and has been designed to organize it elements in parent-child relationships.
Syntactically, the first module in a statement is a parent, and all following modules are its children. Likewise, every child is parent to any modules following them in a statement.
parent() { child(); child() { grandson() ggchild(); granddaughter() } }
The built-in modules are implemented without the ability to have children, meaning they should appear last in a statement. But user-defined modules may draw shapes and accept child modules().
Module parent-child structures may be created in the statements that are part of an If-Then-Else or For-Loop and vice versa:

module xx() {
sphere();
children();
}
xx() for(i=[1,3]) translate([i*2,0,0]) cube();
This works because the xx module used a call to children() to "register" that it will accept the "responsibilities" of parenthood.
Note: Any flow of control or module following a built-in module will emit a warning and do nothing else. The module will draw its shape, but cannot handle children.
Constructing Solid Geometry
The normal operation of Constructive Solid Geometry (CSG) is to form complex shapes by combining primitive and derived shapes and performing operations on them.
To create a shape an Object Module is called:
make_shape();
where "make_shape()" is a user defined module, but equally represents a call to any of the built-in modules. The built-in modules have default values for their parameters and will draw an appropriate shape using them when called with an empty argument list.
Note: data structure objects
have been added to the language as of the 2025.07.* development snapshot releases. To clarify the language of this document, modules make curves and shapes, not objects. Only the object() function can create data structures of the object type.
To manipulate or modify an object any number of Operator Modules may be called before the make_shape()
:
operator() make_shape(); operator() operator() make_shape();
It is important to understand that the ability to act as parent, child, or both is part of the implementation of the "module" in OpenSCAD.
That said, it is convenient to treat the built-in modules as being of two types, operators, and shape builders, as a consequence of their implementation, as explained in the section on writing user-defined modules.
2D Shapes and Extrusion
Any of the 2 Dimensional Primitives, Polygons, Text, and Imported 2D shapes may be the basis for extrusions that create 3D shapes.
3D Shapes and Combinations
Any of the 3 Dimensional Primitives, Polyhedrons, and Imported 3D Shapes may be the basis for Boolean Combinations that create 3D shapes. It is also possible to project a 3D shape onto a coordinate place to obtain its 2D silhouette.
Structure and Scope
Normally a statement does just one thing, and for assignment expressions that is enough. The choice between alternatives in an if-then-else, or the body of a for-loop, often need to perform more than one operation. OpenSCAD borrows the lexical structure of other programming languages to extend a single statement into a block of them.
A block of statements is created using braces to mark the beginning and end of the block:
{ operator() object(); operator() object(); }
This simple block is not much use as such, but it shows that any single statement may be replaced by a block of statements inside a pair of braces.
A better illustration would be:
{ for(...) { // A make_shape_1(); if(...) then { // B operator() make_shape_2(); operator() make_shape_3(); } // end B else make_other_shape() } // end A }
There is an overlap between the previously mentioned parent-child relations between modules and the lexical structure of this last example. Although the outer for-loop is not a module, its body being a block of statements means that it has children, the shapes drawn by make_shape_1() and the if-then-else. And that, further, it has grandchildren ... either two when the <condition> expression is true, or just one if false.
Flow Of Control
As with any structured programming language decision making and looping statements are critical features of OpenSCAD scripts. In a functional language the path, or flow, of execution through the code is controlled by the FoC statements.
The statements available are the if(cond)-else and the for([range]) loop. FoC statements are written to operate on single statements, thusly:
if(<condition>) <when true do this statement>; else <when false do this statement>;
and with looping
for(i=[0:9]) object(); // make 10 objects
A more readable example:
if( is_string(s) ) operator() object(file=s); // do this when <condition> == true else object(file="default.dat"); // otherwise do this
Of course a single statement is often not enough. Different statement types and use cases have these coping mechanisms:
- When the statement is an assignment expression the calculation may be written into a function as calling a function is a valid expression.
- A sequence of actions may be written into a user-defined module as calling that is a single statement.
- in every case a block "{}" can replace a statement
if( is_string(s) ) { // <condition> == true operator() object(file=s); if(<condition>) answer = MyOwnFunction(); else answer = 12; } else // <condition> == false // no block - the for() loop is the single statement for() { // but the target of the for loop is this block object(file="default.dat"); operator() My_Own_Object(); }
Other Uses of If and For
The for()
and if()
statements have an important secondary role in vector initialization, though it is not technically flow of control.
A third use of the for loop
concept is special iteration_for() Operator Module for intersecting multiple instances of an object into a composite.
Again, not technically not flow of control.
Names
Named objects in OpenSCAD include Variables, Functions, and Modules.
To be valid a name:
- must begin with an alphabetic character [a..zA..Z] or an underscore ('_')
- and may continue with any mix of alphabetic characters, numeric digits, and the underscore
- and may not include accented characters, punctuation, nor Unicode characters.
Note: older versions of OpenSCAD allowed names to start with numeric digits but will generate run-time errors in more recent versions
The OpenSCAD convention on multi word names is to use the underscore to delimit them, as this_is_a_name
Variables
Named variables are created when they appear as the Left Hand Side of an assignment statement and will be given the value of the Right Hand Side expression.
result = <calculate something>;
Variables should normally be defined only once in a program as when the program runs the variable will have the last value assigned to it.
The location of a variable definition determines where it can be used, thus it has "scope", a concept discussed fully in the literature, with only a few issues germane to functional programming and OpenSCAD in particular needing to be covered in the following section.
Vectors
A vector or list is a sequence of zero or more OpenSCAD values. Vectors are collections of numeric or boolean values, variables, vectors, strings or any combination thereof. They can also be expressions that evaluate to one of these. Vectors handle the role of arrays found in many imperative languages. The information here also applies to lists and tables that use vectors for their data.
A vector has square brackets, [] enclosing zero or more items (elements or members), separated by commas. A vector can contain vectors, which can contain vectors, etc.
Examples
[1,2,3] [a,5,b] [] [5.643] ["a","b","string"] [[1,r],[x,y,z,4,5]] [3, 5, [6,7], [[8,9],[10,[11,12],13], c, "string"] [4/3, 6*1.5, cos(60)]
use in OpenSCAD:
cube( [width,depth,height] ); // optional spaces shown for clarity translate( [x,y,z] ) polygon( [ [x0,y0], [x1,y1], [x2,y2] ] );
Creation
Vectors are created by writing the list of elements, separated by commas, and enclosed in square brackets. Variables are replaced by their values.
cube([10,15,20]); a1 = [1,2,3]; a2 = [4,5]; a3 = [6,7,8,9]; b = [a1,a2,a3]; // [ [1,2,3], [4,5], [6,7,8,9] ] note increased nesting depth
Vectors can be initialized using a for loop enclosed in square brackets.
The following example initializes the vector result with a length n of 10 values to the value of a.
n = 10;
a = 0;
result = [ for (i=[0:n-1]) a ];
echo(result); //ECHO: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
The following example shows a vector result with a n length of 10 initialized with values that are alternatively a or b respectively if the index position i is an even or an odd number.
n = 10;
a = 0;
b = 1;
result = [ for (i=[0:n-1]) (i % 2 == 0) ? a : b ];
echo(result); //ECHO: [0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
Indexing elements within vectors
Elements within vectors are numbered from 0 to n-1 where n is the length returned by len(). Address elements within vectors with the following notation:
e[5] // element no 5 (sixth) at 1st nesting level e[5][2] // element 2 of element 5 2nd nesting level e[5][2][0] // element 0 of 2 of 5 3rd nesting level e[5][2][0][1] // element 1 of 0 of 2 of 5 4th nesting level
e = [ [1], [], [3,4,5], "string", "x", [[10,11],[12,13,14],[[15,16],[17]]] ]; // length 6 address length element e[0] 1 [1] e[1] 0 [] e[5] 3 [ [10,11], [12,13,14], [[15,16],[17]] ] e[5][1] 3 [ 12, 13, 14 ] e[5][2] 2 [ [15,16], [17] ] e[5][2][0] 2 [ 15, 16 ] e[5][2][0][1] undef 16 e[3] 6 "string" e[3 ][2] 1 "r" s = [2,0,5]; a = 2; s[a] undef 5 e[s[a]] 3 [ [10,11], [12,13,14], [[15,16],[17]] ]
String indexing
The elements (characters) of a string can be accessed:
"string"[2] //resolves to "r"
Vector Swizzling
[Note: Requires version Dev Snapshot 2021.12.23 or later]
The components of vectors can be accessed using the following syntax:
e = [1, 2, 3, 4]; v = e.x + e.y; // yields 1 + 2 = 3
This is called swizzling. You can use x, y, z, or w, referring to the first, second, third, and fourth components, respectively. Alternatively, r, g, b, and a can be used instead of x,y,z,w.
Swizzling is named so because you can use it to repeat, reorder, or select a subset of components.
v1 = e.xyxx; // -> [1,2,1,1] v2 = e.zyy; // -> [3,2,2] v3 = e.rrr; // -> [1,1,1] v4 = e.rg; // -> [1,2]
You can use any combination of up to 4 of the letters to create a vector. Attempts to reference components which doesn't exist will result in an undef.
Vector Functions
concat() Function
[Note: Requires version 2015.03]
concat()
combines the elements of 2 or more vectors into a single vector. No change in nesting level is made.
vector1 = [1,2,3]; vector2 = [4]; vector3 = [5,6]; new_vector = concat(vector1, vector2, vector3); // [1,2,3,4,5,6] string_vector = concat("abc","def"); // ["abc", "def"] one_string = str(string_vector[0],string_vector[1]); // "abcdef"
len() Function
len()
is a function that returns the length of vectors or strings.
Indices of elements are from [0] to [length-1].
- vector
- Returns the number of elements at this level.
- Single values, which are not vectors, raise an error.
- string
- Returns the number of characters in a string.
a = [1,2,3]; echo(len(a)); // 3
See example elements with lengths
Matrix
A matrix is a vector of vectors.
Example that defines a 2D rotation matrix mr = [ [cos(angle), -sin(angle)], [sin(angle), cos(angle)] ];
Language Components
Most of the other commonly available components of a structured programming language are included in OpenSCAD, namely
- Named Objects and Scope
- variables, functions, and modules
- Data Types, Values, and Constants
- 64-bit floating point, boolean, string, and vector (or list)
- Data Structures
- Vectors and Objects
- Expressions
- arithmetic, string, bitwise, and logical
- Built-In Functions
- all the standard functions for use in expressions of all types
- Syntactic Operators
- The language has features that work like built-in modules but are different in detail, Echo, Let, Assert
- User Defined Objects
- custom modules and functions
Language Elements particularly needed for modelling are
- Built-in 2D Object Modules
- circle(), square(), polygon(), etc.
- 2D file import
- Adding drawn shapes from SVG, DXF, image, and text data files
- 3D File Import
- adding geometry from external sources
- Built-In 3D Object Modules
- sphere(), cube(), polyhedron(), etc.
- Built-In Operator Modules
- 3D boolean operations: intersection(), difference(), union()
- Shape Modifications : linear_extrude(), rotate_extrude(), hull(), etc.
- Transformations : translate(), rotate(), etc.
- Color
- Customizer
- Compile time user input
Values and Data Types
A value in OpenSCAD is either a Number (like 42), a Boolean (like true), a String (like "foo"), a Range (like [0: 1: 10]), a Vector (like [1,2,3]), or the Undefined value (undef). Values can be stored in variables, passed as function arguments, and returned as function results.
[OpenSCAD is a dynamically typed language with a fixed set of data types. There are no type names, and no user defined types.]
Numbers
Numbers are the most important type in OpenSCAD, and they are written in the decimal notation common to most programming languages:
- 42
- the answer for everything
- -1
- a negative value
- 0x22
- a hexadecimal value (3410)
- 0.5
- a decimal fraction
- 1.0123e-5
- a very small fraction (0.000010123) in scientific notation
- 2.99792458e+8
- a very large value in scientific notation
The internal form of numbers follows the 64-bit IEEE floating point number specification. Zero (0) and negative zero (-0) are treated as two distinct numbers by some of the math operations, and are displayed as such by 'echo', although they compare as equal. Complex numbers are not supported.
Integers are just 52-bit numbers without a fractional part. Literal hexadecimal values in a script are also evaluated to their integer value and stored as 52-bit binaries.
Values used in bitwise operations are an exception as their 64-bit encoding is binary, but only during the operation. The results of a bitwise expression are converted to the normal 64-bit floating format.
Limitation: Fractions are not represented exactly unless the denominator is a power of 2
- The largest value possible is about 1308. If an expression evaluates to a larger value it will be treated as "infinity" and printed as "inf" by echo.
- The most negative value is about -1308. Negative infinity is printed as "-inf" by echo.
- very small fractions may be displayed as 0 or -0 by
echo()
.
Nearly Equal is as Close as You Get
As noted in the limitation above, power of 2 fractions, like 0.25 (1/4) and 0.375 (3/8), are represented exactly, but 2 divided by 10, 2/10 = 0.2
is only approximated.
Also, 64-bit floating values are only accurate to 15-17 decimal digits, meaning that the least significant digit of a 17 digit number is undetermined.
In practice this means that two FP values calculated differently, but that should come to same result, may encoded to slightly different digits, which will cause a test for equality to fail. This happens when their differences are close to, or less than, the Min.Normal value derived from the numerical representation (see geekley's list of Number Limits).
A Quick and Dirty solution to the Nearly Equal problem is to see if the difference of the two values is smaller than an acceptable value, epsilon.
/** returns true when two floating point values are within
epsilons value of each other. */
function _nearly_equal( a, b, epsilon=0.00001 ) =
abs(( a - b )/ b ) < epsilon
;
A more elegant, and more numerically correct version is available in the numbers.scad file of the relativity library (soon to be released), provided here as an extract from the library.
Hexadecimal Literals
C style hexadecimal constants are allowed to be assigned to variables.
hex1 = 0x32; // ASCII space hex2 = 0x98 - 1; // ASCII 'a' num1 = 0x2334; // 9012 num2 = 0x233411 // 2.30709e+6
[Note: Requires version Development snapshot].
OpenSCAD does not support octal notation for numbers.
Predefined Numeric Constants
These special numbers are defined:
PI
- π, 3.141592 approximately. The ratio between the diameter and circumference of a circle.
infinities
- there is no constant for
inf
nor-inf
NaN
- Not a Number, nor is
nan
a real constant.
While there are no constants for inf, -inf, and nan they can be created by arithmetic operations and be assigned to variables for when needed:
inf = 1e200 * 1e200; nan = 0 / 0; echo(inf,nan); // ECHO: inf, nan
As mentioned at the top of this page, the 64-bit encoding of floating point values requires that both positive and negative infinity have to be able to be displayed in output messages.
To be able to display something for undefined math results, like or Divide by Zero the industry "standard" is "Not A Number", or nan
.
The background for this issue is covered at the Open Group's site on math.h and the page on the IEEE 754 staqndard.
OpenSCAD has not implemented everything as it is in C/C++. For example, it uses degrees angles and trigonometric functions. This table shows the results of giving undefined inputs to built-in math functions taken from the regression test suite in 2015.
0/0: nan | sin(1/0): nan | asin(1/0): nan | ln(1/0): inf | round(1/0): inf |
-0/0: nan | cos(1/0): nan | acos(1/0): nan | ln(-1/0): nan | round(-1/0): -inf |
0/-0: nan | tan(1/0): nan | atan(1/0): 90 | log(1/0): inf | sign(1/0): 1 |
1/0: inf | ceil(-1/0): -inf | atan(-1/0): -90 | log(-1/0): nan | sign(-1/0): -1 |
1/-0: -inf | ceil(1/0): inf | atan2(1/0, -1/0): 135 | max(-1/0, 1/0): inf | sqrt(1/0): inf |
-1/0: -inf | floor(-1/0): -inf | exp(1/0): inf | min(-1/0, 1/0): -inf | sqrt(-1/0): nan |
-1/-0: inf | floor(1/0): inf | exp(-1/0): 0 | pow(2, 1/0): inf | pow(2, -1/0): 0 |
Testing for NaN
The value nan
is the only OpenSCAD value that is not equal to any other value, including itself.
The conditional expression x == 0/0
will fail with a run-time error.
Instead, you must use 'x != x'
to test if x is "nan".
The Undefined Value
The undefined value is a special value written as undef
. It is the initial value of a variable that hasn't been assigned a value, and it is often returned as a result by functions or operations that are passed illegal arguments. Finally, undef
can be used as a null value, equivalent to null
or NULL
in other programming languages.
All arithmetic expressions containing undef
values evaluate as undef
. In logical expressions, undef
is equivalent to false
. Relational operator expressions with undef
evaluate as false
except for undef==undef
, which is true
.
Note that numeric operations may also return 'nan' (not-a-number) to indicate an illegal argument. For example, 0/false
is undef
, but 0/0
is 'nan'. Relational operators like < and > return false
if passed illegal arguments. Although undef
is a language value, 'nan' is not.
Variables cannot be changed
A variable is created by the assignment statement that sets its value, thus defining it, and it cannot thereafter be changed. But, unlike constants in other languages, assignments may be overridden.
A second assignment to the same variable will cause a warning about its value being reassigned, and has the effect of replacing first assignment with the second one. In fact, the value that the variable holds throughout the run-time of the script is that set by the last assignment that uses it as the LHS.
a = 1; // effectively replaced and never executed echo(a); // 2 a = 2; // as if executed at the a=1 statement echo(a); // 2
There are two exceptions to this behavior:
- an assignment in an included file may be given a new value in the including file.
- an assignment in a script will take a new value from
-D
command line option - likewise for a variable set in the Customizer.
This allows variables to be given default values in a shared library that are then given new values in scripts that include it.
// main.scad include <lib.scad> a = 2; echo(b); // ECHO: 3
// lib.scad a = 1; b = a + 1;
Run-Time Data Sources
An OpenSCAD program cannot prompt the user for interactive input while running, but it is possible to access data from files, the Customizer Panel, and the command line.
- the Customizer may be used to set values that modify the parsing and rendering of an .scad program
- If the app is opened from the command line a
-D
at the-D value
argument - read data from .stl, .dxf, or .png files.
Import Objects from STL Files
OpenSCAD can import objects from STL files to be manipulated (translation, clipping, etc.) and rendered. The data in the STL file cannot be accessed.
Access Data in DXF Files
Data in a DXF file may be accessed using this functions:
- [X,Y,Z] = dxf_cross( file="name.dxf", layer="a.layer", origin=[0,0], scale=1.0 )
- This function returns the intersection of two lines on the given layer.
- N= dxf_dim( file="name.dxf", name="namedDimension", layer="a.layer", origin=[0, 0], scale=1);
Function dxf_cross()
The function looks for two lines on the given layer and calculates their intersection. The intersection may not be defined as a point entity.
OriginPoint = dxf_cross(file="drawing.dxf", layer="SCAD.Origin",
origin=[0, 0], scale=1);
Function dxf_dim()
Dimensions in a DXF file that are named may be accessed for use in an OpenSCAD program using a call to dxf_dim();
TotalWidth = dxf_dim(file="drawing.dxf", name="TotalWidth",
layer="SCAD.Origin", origin=[0, 0], scale=1);
DXF File Data Access Example
For a nice example of both functions, see Example009 and the image on the homepage of OpenSCAD.