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 :

  1. assigns the result of an expression to a variable
  2. invokes one or more modules to instantiate a shape that appears in the preview panel
  3. 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.

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:

a sphere and its children
a sphere and its children
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
example elements with lengths from len()
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:

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.