OpenSCAD User Manual/Transformations
Transforms Affect Nodes
Transformations are applied to modify position, orientation, or size by writing them at the beginning of a line of OpenSCAD code that defines a new node. Nodes are defined as primitive shapes or CSG Modelling Operations or looping and conditional statements that form syntactic groups.
The simplest form for statements that define shapes is
<transform>*<child node>;
meaning that zero or more (shown by *) calls to transform modules can precede code that defines a node. The node thus created may be as simple as a single shape, or may be a complex of nested language elements that define an assembly of shapes, but in all cases the combined effect of the transforms are applied to the single child node that must be the last item on the line. Indentation is commonly used to make the child nodes more visible for the reader, but as this example shows, these two statements are identical.
translate([10,20,30]) cube(10);
translate([10,20,30])
cube(10);
In the OpenSCAD language semicolons mark the end of a line of code so if one is placed after a transform module call it means there will be no child node created.
Transform modules can be applied to grouped statements using braces ( '{ }' to enclose the sub-tree in this manner:
translate([0,0,-5])
{
cube(10);
cylinder(r=5,h=10);
}
translate([0,0,-5]) { cube(10); cylinder(r=5,h=10); }
Like the previous code example the two statements are identical in meaning but indentation makes the first more readable. Also note that a statement group is not ended by a semi-colon, but each separate line in the group but be ended by one, including the last.
Indentation is commonly used to show the scope of transform module calls but they are always applied in the sequence written in the statement.
rotate([45,45,45])
translate([10,20,30])
cube(10);

Syntactic Order Versus Mathematical
The order of calls to transform modules makes no difference to the correctness of a statement syntactically, but for some the order of operations is significant. A call to set the colour of an object may be inserted anywhere, but those that affect the child geometry have to be correctly ordered.
color("red") translate([0,10,0]) rotate([45,0,0]) cube(5);
rotate([45,0,0]) translate([0,10,0]) color("green") cube(5);
Both translation and rotation module calls are the same, but moving the first cube and then rotating it, is not the same as first rotating the second cube, then moving it away from the origin. In the first case the rotation causes the cube to move along an arc 10 units from the origin, the second the cube is rotated them moved along the Y axis.
Differences in Preview Rendering
OpenSCAD uses a different library to preview simple transforms (translate, rotate, etc.) than for more advanced transforms, like resize. The effects of modifier characters, specifically "#" and "%", on the rendered objects may not match expectations. A shape with only simple transforms applied will be rendered in its post-transformation form, while the highlight for a resized shape will be applied to its original form.
Transform Modules
Scale
Scales its child elements using the specified vector. Using the name of the argument is optional.
Usage Example: scale(v = [x, y, z]) { ... }
cube(10);
translate([15,0,0]) scale([0.5,1,2]) cube(10);
Resize
This module actually changes the form of the child node by the amount given by the x,y, and z dimensions of the given vector. If an amount is zero that dimension is not changed
resize() is an operation from the CGAL library and as such operates on the full geometry of the child node and its use can make for longer rendering time in preview mode.
Usage Example:
// resize the sphere to extend 30 in x, 60 in y, and 10 in the z directions.
resize(newsize=[30,60,10]) sphere(r=10);
// resize the 1x1x1 cube to 2x2x1
resize([2,2,0]) cube();
If the 'auto' parameter is set to true, it auto-scales any 0-dimensions to match. For example.
// resize the 1x2x0.5 cube to 7x14x3.5
resize([7,0,0], auto=true) cube([1,2,0.5]);
The 'auto' parameter can also be used if you only wish to auto-scale a single dimension, and leave the other as-is.
// resize to 10x8x1. Note that the z dimension is left alone.
resize([10,0,0], auto=[true,true,false]) cube([5,4,1]);
Scale vs Resize
In many cases the effect of scale() will be the same as for resize(), but when the child to be modified is not a symmetrical shape the effects can be seen to be different.
This image shows a default cube and a cube([3,2,5]) in white, both transformed by resize([2,2,0]) and then scaled by scale([2,2,1]). The scale.z value must be 1 to preserve the cube's Z dimension.

The resize operation on the cube([3,2,5]) shortens it to match the [2,2,0] size while the scale just makes it bigger in all dimensions.
Rotate
This module acts to rotate its children through a given angle about an axis. The angle and the axis may be given in different forms to facilitate programming needs. The argument names are optional when values are given in the order of the following specification.
The angle of rotation must be given in degrees and may be a decimal fraction. In its simplest form the effect is to rotate the child around the Z-axis by the given value, "angle", in degrees. This is useful in 2D contexts where that is the only axis for rotation.
rotate( <angle> );
The angle may also be given as a named parameter "a", thus: rotate( a=<angle> ).
For example:
rotate(45) square(10);
as seen in this image
An optional argument, "v", may be given to define the axis about which the child is rotated. It its simplest form just one of the X, Y, or Z elements will have value one (1) to specify the single coordinate axis will be the basis for rotation. For example to rotate 180 degrees around the Y-axis we can use:
rotate(a=180, v=[0,1,0]) { ... }
The "v=[x,y,z]" parameter is as a Euler vector that can define any arbitrary axis for rotation. The three components may be given as signed, decimal float values that define a line in 3-space around which the child node will be rotated. This does not give the same result as using the a=[x_rot,y_rot,z_rot] form shown in a following section, but has the same mathematical basis.
For example using the "v" form to rotate a child node by 45 degrees around an axis halfway between the X and Y coordinate axes uses the the vector [1,1,0] as in this code:
rotate(a=45, v=[1,1,0]) { ... }
In its full form the module call has this form:
rotate(a = deg_a, v = [x, y, z]) // named parameters
rotate( a, [x, y, z] ) // positional parameters
rotate( 30, [.5,.5,.5] )
cube([2,4,6]);

Combination of Three Rotations
Alternately the angle of rotation may be given as a vector of angles:
rotate(a = [x_rot, y_rot, z_rot]) // named parameter "a"
rotate([deg_x, deg_y, deg_z]) // positional parameters
In this form the axis argument ("v") is ignored. The three angular rotations are applied in order of x_rot first, then y_rot, and finally z_rot. The following code fragment is a reminder that the transform "closest" to the child node is the operation carried out first.
rotate(a=[0,0,az])
rotate(a=[0,ay,0])
rotate(a=[ax,0,0])
{ <child node> }
Rotate vs Mirror
It is worth noting that while in some cases rotating an object by 180 degrees around an axis results in the same geometry as mirroring it in a suitable plane, mathematically these are different operations that create unique geometries, as the following code will illustrate:
transf = true; // [true,false]
mirr = transf ? 1 : 0;
rotat = transf ? 180 : 0;
mirror([0,0,mirr])
union(){
color("yellow", alpha=.30)
cube( [5,8,2],center=true );
translate( [-1,0,-2] )
color("red")
cube( [3,3,5] );
translate( [0,-5,-2] )
color("green")
cube( [3,3,5] );
}
translate( [-10,0,0] )
rotate([rotat,0,0])
union(){
color("yellow", alpha=.30)
cube( [5,8,2],center=true );
translate( [-1,0,-2] )
color("red")
cube( [3,3,5] );
translate( [0,-5,-2] )
color("green")
cube( [3,3,5] );
}


Rotation rule help

For the case of:
rotate([a, b, c]) { ... };
"a" is a rotation about the X axis, from the +Y axis, toward the +Z axis.
"b" is a rotation about the Y axis, from the +Z axis, toward the +X axis.
"c" is a rotation about the Z axis, from the +X axis, toward the +Y axis.
These are all cases of the Right Hand Rule. Point your right thumb along the positive axis, your fingers show the direction of rotation.
Thus if "a" is fixed to zero, and "b" and "c" are manipulated appropriately, this is the spherical coordinate system.
So, to construct a cylinder from the origin to some other point (x,y,z):
x= 10; y = 10; z = 10; // point coordinates of end of cylinder
length = norm([x,y,z]); // radial distance
b = acos(z/length); // inclination angle
c = atan2(y,x); // azimuthal angle
rotate([0, b, c])
cylinder(h=length, r=0.5);
%cube([x,y,z]); // corner of cube should coincide with end of cylinder
translate
Translate moves its child node for the distances given in the three elements of the given vector. The argument name, "v", is optional.
Example:
translate(v = [x, y, z]) { ... }
In the following code there is a cube of 2 units square centered on the origin with no transform applied, with a unit sphere as a child node of a X=5 units translation.
cube(2,center = true);
translate([5,0,0])
sphere(1,center = true);
Mirror
The effect of this transform is to project the geometry of its child node through a mirror plane to the equidistant position on the plane's other side. Mirrors are planes that pass through the origin with the orientation given by its Normal Vector. This specific normal vector is given as the "v=[x,y,z]" parameter to mirror() which is is a Euler vector with values being signed, decimal floats.
In its simplest form just one of the elements will have a non-zero value. So to mirror something in the Y-Z plane the vector is the X-axis ( v=[1,0,0] ). Note that a Euler vector takes the ratios between its components for its direction, not the absolute values. Thus the proportions of v=[1,0,0], v=[-1,0,0], v=[0.05,0,0], and v=[100,0,0] all describe the same vector, the X-axis.
In general the signs of the vector elements are significant, so for instance the planes for v=[1,1,0] and v=[1,-1,0] are at right angles to each other. But the lines for v=[1,1,0] and v=[-1,-1,0] are the same line, just in opposite directions.
The operation of the transform is to project each point of the child node to the mirror plane along a line drawn normal to the plane. Each point is then projected through the mirror for the same distance on the other side to create the mirrored child node.
For example, for a cube entirely in the positive X half of the modelling space applying mirror([1,0,0]), changes the x coordinate of its every point from positive to negative.
It is worth pointing out that mirror() "moves" the child node, it does not make a new copy of it.
Function signature:
mirror(v= [x, y, z] ) { ... }
Examples
The original is on the right hand side.
hand(); // original
mirror([1,0,0]) hand();
hand(); // original
mirror([1,1,0]) hand();
hand(); // original
mirror([1,1,1]) hand();
// original
rotate([0,0,-30]){
cube([23,12,10]);
translate([0.5, 4.4, 9.9]){
color("red", 1.0){
linear_extrude(height=2){
text("OpenSCAD", size= 3);
}
}
}
}
// mirrored
mirror([1,0,0]){
rotate([0,0,-30]){
cube([23,12,10]);
translate([0.5, 4.4, 9.9]){
color("red", 1.0){
linear_extrude(height=2){
text("OpenSCAD", size= 3);
}
}
}
}
}
multmatrix
Multiplies the geometry of all child elements with the given affine transformation matrix, where the matrix is 4×3 - a vector of 3 row vectors with 4 elements each, or a 4×4 matrix with the 4th row always forced to [0,0,0,1].
Usage: multmatrix(m = [...]) { ... }
This is a breakdown of what you can do with the independent elements in the matrix (for the first three rows):
Scale X | Shear X along Y | Shear X along Z | Translate X |
Shear Y along X | Scale Y | Shear Y along Z | Translate Y |
Shear Z along X | Shear Z along Y | Scale Z | Translate Z |
The fourth row is forced to [0,0,0,1] and can be omitted unless you are combining matrices before passing to multmatrix, as it is not processed in OpenSCAD. Each matrix operates on the points of the given geometry as if each vertex is a 4 element vector consisting of a 3D vector with an implicit 1 as its 4th element, such as v=[x, y, z, 1]. The role of the implicit fourth row of m is to preserve the implicit 1 in the 4th element of the vectors, permitting the translations to work. The operation of multmatrix therefore performs m*v for each vertex v. Any elements (other than the 4th row) not specified in m are treated as zeros.
This example rotates by 45 degrees in the XY plane and translates by [10,20,30], i.e. the same as translate([10,20,30]) rotate([0,0,45]) would do.
angle=45;
multmatrix(m = [ [cos(angle), -sin(angle), 0, 10],
[sin(angle), cos(angle), 0, 20],
[ 0, 0, 1, 30],
[ 0, 0, 0, 1]
]) union() {
cylinder(r=10.0,h=10,center=false);
cube(size=[10,10,10],center=false);
}
The following example demonstrates combining affine transformation matrices by matrix multiplication, producing in the final version a transformation equivalent to rotate([0, -35, 0]) translate([40, 0, 0]) Obj();. Note that the signs on the sin function appear to be in a different order than the above example, because the positive one must be ordered as x into y, y into z, z into x for the rotation angles to correspond to rotation about the other axis in a right-handed coordinate system.
module Obj() {
cylinder(r=10.0,h=10,center=false);
cube(size=[10,10,10],center=false);
}
// This itterates into the future 6 times and demonstrates how multimatrix is moving the object around the center point
for(time = [0 : 15 : 90]){
y_ang=-time;
mrot_y = [ [ cos(y_ang), 0, sin(y_ang), 0],
[ 0, 1, 0, 0],
[-sin(y_ang), 0, cos(y_ang), 0],
[ 0, 0, 0, 1]
];
mtrans_x = [ [1, 0, 0, 40],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
];
echo(mrot_y*mtrans_x);
// This is the object at [0,0,0]
Obj();
// This is the starting object at the [40,0,0] coordinate
multmatrix(mtrans_x) Obj();
// This is the one rotating and appears 6 times
multmatrix(mrot_y * mtrans_x) Obj();
};
This example skews a model, which is not possible with the other transformations.
M = [ [ 1 , 0 , 0 , 0 ],
[ 0 , 1 , 0.7, 0 ], // The "0.7" is the skew value; pushed along the y axis as z changes.
[ 0 , 0 , 1 , 0 ],
[ 0 , 0 , 0 , 1 ] ] ;
multmatrix(M) { union() {
cylinder(r=10.0,h=10,center=false);
cube(size=[10,10,10],center=false);
} }
This example shows how a vector is transformed with a multmatrix vector, like this all points in a point array (polygon) can be transformed sequentially. Vector (v) is transformed with a rotation matrix (m), resulting in a new vector (vtrans) which is now rotated and is moving the cube along a circular path radius=v around the z axis without rotating the cube.
angle=45;
m=[
[cos(angle), -sin(angle), 0, 0],
[sin(angle), cos(angle), 0, 0],
[ 0, 0, 1, 0]
];
v=[10,0,0];
vm=concat(v,[1]); // need to add [1]
vtrans=m*vm;
echo(vtrans);
translate(vtrans)cube();
More?
Learn more about it here:
color
Displays the child elements using the specified RGB color + alpha value. This is only used for the F5 preview as CGAL and STL (F6) do not currently support color. The alpha value defaults to 1.0 (opaque) if not specified.
Function signature:
color( c = [r, g, b, a] ) { ... } color( c = [r, g, b], alpha = 1.0 ) { ... } color( "#hexvalue" ) { ... } color( "colorname", 1.0 ) { ... }
Note that the r, g, b, a
values are limited to floating point values in the range [0,1] rather than the more traditional integers { 0 ... 255 }. However, nothing prevents you from using R, G, B
values from {0 ... 255} with appropriate scaling: color([ R/255, G/255, B/255 ]) { ... }
[Note: Requires version 2011.12]
Colors can also be defined by name (case insensitive). For example, to create a red sphere, you can write color("red") sphere(5);
. Alpha is specified as an extra parameter for named colors: color("Blue",0.5) cube(5);
[Note: Requires version 2019.05]
Hex values can be given in 4 formats, #rgb
, #rgba
, #rrggbb
and #rrggbbaa
. If the alpha value is given in both the hex value and as separate alpha parameter, the alpha parameter takes precedence.
Warning: alpha processing (transparency) is order-sensitive. Transparent objects must be listed after non-transparent objects to display them correctly. Some combinations involving multiple transparent objects cannot be handled correctly. See issue #1390.
The available color names are taken from the World Wide Web consortium's SVG color list. A chart of the color names is as follows,
(note that both spellings of grey/gray including slategrey/slategray etc are valid):
|
|
|
|
|
Example

Here's a code fragment that draws a wavy multicolor object
for(i=[0:36]) {
for(j=[0:36]) {
color( [0.5+sin(10*i)/2, 0.5+sin(10*j)/2, 0.5+sin(10*(i+j))/2] )
translate( [i, j, 0] )
cube( size = [1, 1, 11+10*cos(10*i)*sin(10*j)] );
}
}
↗ Being that -1<=sin(x)<=1 then 0<=(1/2 + sin(x)/2)<=1 , allowing for the RGB components assigned to color to remain within the [0,1] interval.
Example 2
In cases where you want to optionally set a color based on a parameter you can use the following trick:
module myModule(withColors=false) {
c=withColors?"red":undef;
color(c) circle(r=10);
}
Setting the colorname to undef keeps the default colors.
offset
[Note: Requires version 2015.03]
Offset generates a new 2d interior or exterior outline from an existing outline. There are two modes of operation: radial and delta.
- The radial method creates a new outline as if a circle of some radius is rotated around the exterior (r > 0) or interior (r < 0) of the original outline.
- The delta method creates a new outline with sides having a fixed distance outward (delta > 0) or inward (delta < 0) from the original outline.
The construction methods produce an outline that is either inside or outside of the original outline. For outlines using delta, when the outline goes around a corner, it can be given an optional chamfer.
Offset is useful for making thin walls by subtracting a negative-offset construction from the original, or the original from a positive offset construction.
Offset can be used to simulate some common solid modeling operations:
- Fillet:
offset(r=-3) offset(delta=+3)
rounds all inside (concave) corners, and leaves flat walls unchanged. However, holes less than 2*r in diameter vanish. - Round:
offset(r=+3) offset(delta=-3)
rounds all outside (convex) corners, and leaves flat walls unchanged. However, walls less than 2*r thick vanish.
- Parameters
The first parameter may be passed without a name, in which case it is treated as the r parameter below. All other parameters must be named if used.
r or delta
- Number. Amount to offset the polygon. When negative, the polygon is offset inward.
- r (default parameter if not named) specifies the radius of the circle that is rotated about the outline, either inside or outside. This mode produces rounded corners. The name may be omitted; that is,
offset(c)
is equivalent tooffset(r=c)
. - delta specifies the distance of the new outline from the original outline, and therefore reproduces angled corners. No inward perimeter is generated in places where the perimeter would cross itself.
- r (default parameter if not named) specifies the radius of the circle that is rotated about the outline, either inside or outside. This mode produces rounded corners. The name may be omitted; that is,
- chamfer
- Boolean. (default false) When using the delta parameter, this flag defines if edges should be chamfered (cut off with a straight line) or not (extended to their intersection). This parameter has no effect on radial offsets.
$fa, $fs, and $fn
- The circle resolution special variables may be used to control the smoothness or facet size of curves generated by radial offsets. They have no effect on delta offsets.
Examples

// Example 1
linear_extrude(height = 60, twist = 90, slices = 60) {
difference() {
offset(r = 10) {
square(20, center = true);
}
offset(r = 8) {
square(20, center = true);
}
}
}
// Example 2
module fillet(r) {
offset(r = -r) {
offset(delta = r) {
children();
}
}
}
fill
[Note: Requires version Development snapshot]
Fill removes holes from polygons without changing the outline. For convex polygons the result is identical to hull().
Examples

// Example 1
t = "OpenSCAD";
linear_extrude(15) {
text(t, 50);
}
color("darkslategray") {
linear_extrude(2) {
offset(4) {
fill() {
text(t, 50);
}
}
}
}
minkowski


Displays the minkowski sum of child nodes.
Usage example:
Say you have a flat box, and you want a rounded edge. There are multiple ways to do this (for example, see hull below), but minkowski is elegant. Take your box, and a cylinder:
$fn=50;
cube([10,10,1]);
cylinder(r=2,h=1);
Then, do a minkowski sum of them (note that the outer dimensions of the box are now 10+2+2 = 14 units by 14 units by 2 units high as the heights of the objects are summed):
$fn=50;
minkowski()
{
cube([10,10,1]);
cylinder(r=2,h=1);
}
NB: The origin of the second object is used for the addition. The following minkowski sums are different: the first expands the original cube by +1 in -x, +x, -y, +y from cylinder, expand 0.5 units in both -z, +z from cylinder. The second expands it by +1 in -x, +x, -y, +y and +z from cylinder, but expand 0 in the -z from cylinder.
minkowski() {
cube([10, 10, 1]);
cylinder(1, center=true);
}
minkowski() {
cube([10, 10, 1]);
cylinder(1);
}
Warning: for high values of $fn the minkowski sum may end up consuming lots of CPU and memory, since it has to combine every child node of each element with all the nodes of each other element. So if for example $fn=100 and you combine two cylinders, then it does not just perform 200 operations as with two independent cylinders, but 100*100 = 10000 operations.
Warning: if one of the inputs is compound, such as:
{
translate([0, 0, collar])
sphere(ball);
cylinder(collar, ball, ball);
}
it may be treated as two separate inputs, resulting in an output which is too large, and has features between surfaces that should be unaltered with respect to one another. If so, use union().
hull


Displays the convex hull of child nodes.
Usage example:
hull() {
translate([15,10,0]) circle(10);
circle(10);
}
The Hull of 2D objects uses their projections (shadows) on the xy plane, and produces a result on the xy plane. Their Z-height is not used in the operation.
Referring to the illustration of a convex hull of two cylinders, it is computationally more efficient to use hull()
on two 2D circles and linear_extrude the resulting 2D shape into a 3D shape, rather than using hull()
on two cylinders, even though the resulting object appears identical. Complex geometries involving hull()
can be rendered faster by starting out in 2D, if possible.