ShapeJS Preview - Getting started


The ShapeJS language provides a powerful system for generating 3D printable objects via a simple Javascript program. ShapeJSs work on a voxel representation. A voxel is similar to a 2D pixel but it represents a volume element in 3 dimensional space. A script provides mechanisms to easily set the desired voxels using a series of data sources and transformations.

The voxel based representation gives ShapeJS greater flexibility in shape generation over traditional representation via triangle meshes. It is similar to advantages 2D raster graphics has over 2D vector graphics. Vector graphics is great for drawing diagrams and raster graphics is on practice the tool of choice for anything else.

The way to fill the voxel grid is to create a Datasource, which can calculate a signed distance function for each point in space. This object is fed to an instance of GridMaker which evaluates a voxel grid at all points. The distance function calculations may be very complex task for complex object. But this calculation can be simplified by composing complex object from many simpler one using several simple operations.

There are several predefined simple objects like Sphere,Box, Cylinder. More complex methods include functionality to convert 2D image to a 3D embossed object using ImageBitmap object and importing user provided 3D object using load() function.

The real power of ShapeJSs is in the flexibility of combining and transforming objects. A full set of boolean operations on objects are availble which includes Union, Intersection, Subtraction, Complement.

There is a flexible set of transformations which can be applied to any DataSource object. Transformations include standard 3D linear transformations like Rotation, Translation, Scale, Reflection. Flexibility of using volume based data allows to use arbitrary non linear transformation among those are RingWrap, SphereInversion, FrizeSymmetry, WalPaperSymmetry, ReflectionSymmetry. Several simple transformations can be composed into more complex transformation using CompositeTransform. A transformation can be applied to any DataSource object via its setTransform() method.

Development Environment

ShapeJS provides a web based development environment. You can access it here: ShapeJS Development Environment. The development environment is composed of several panels. The top Inspiration panel changes the example shown. When you click on an image the Editor panel will show the new ShapeJS, the Parameters and Files panels will revert to the example defaults and the 3D view will show the result of evaluating the script.

If a script's main function references arguments then you'll need to provide values in the Parameters or Files section. Click the Blue Plus buttons to add a value.

You can save a model to your local storage with the Save model button. Currently this generates an X3D binary file. You can view these files with Instant Reality, BS Contact or NetFabb Studio Basic

The upload button will upload the model to your Shapeways account. You'll be provided with a link to order the model.


ShapeJSs are Javascript programs over top the AbFab3D Voxel library. All standard Javascript functionality is supported. In particular we use the Rhino Scripting engine in Java. You can find the project documentation here: Rhino Documentation

Below is a simple ShapeJS program.

function main(args){  // this script will make a sphere

    var radius = 5*MM;        // radius of sphere 5 millimeters
    var voxelSize = 0.1*MM;   // size of grid voxel
    var gw = radius*1.1;      // make the grid size a little larger to have some clearance 
    var grid = createGrid(-gw,gw,-gw,gw,-gw,gw,voxelSize); // create the grid 
    var maker = new GridMaker();            // create instance of grid maker
    maker.setSource(new Sphere(radius));    // gives GridMaker object to work on 
    maker.makeGrid(grid);                   // GridMaker fills the grid with data 

    return grid;   // return the grid for final processing 

Click Simple Sphere to run this example.

All ShapeJS scripts should have function main(args). This function is executed by the ShapeJS scripting engine. It is possible to pass arbitrary arguments (including uploaded files ) to the main(args), but we will explain this later.

Unit of length in ShapeJS is meters. To avoid awkward numbers there are few useful conversion constants MM=0.001 for millimeters, IN=0.0254 for inches.

The primary object of a ShapeJS script is a volumetric grid. The result of executing main(args)should be a grid filled with scene data. The scripting engine converts the grid returned by main() into a traditional triangle mesh which is sent back to the browser and displayed in the 3D view.

The grid is created via this function:
grid = createGrid(xmin, xmax, ymin, ymax, zmin, zmax, voxelSize);

Here the xmin, xmax ... are the physical bounds of grid in the space. They define minimal and maximal coordinates where the final shape will be located. The last parameter voxelSize is the size of grid voxel. It defines the resolution of volume rendering. A Voxel size of 0.1 millimeter corresponds to the printing resolution of most 3D printers in 2013. This parameter greatly affects speed of all calculations and the best approach is to keep it larger (0.2mm or 0.3mm) during script development and set to printer resolution only at the final stage.

The primary tool of ShapeJS scripts is GridMaker. GridMaker fills the grid voxels with values calculated by objects called DataSource. Sphere is one of several prebuilt data sources. Users can also create new DataSource objects directly in their scripts.

Calling maker.makeGrid(grid) does the actual calculations of the grid. Returning that grid from main initiates the ShapeJS engine conversion of grid data into a triangle mesh which is returned to the browser.


Boolean Operations

In this example we'll show a powerful modeling technique creating objects by using boolean algebra. Many objects can be described by adding and subtracting primitive shapes from them. In this example we create a cool doodad by subtracting a 3D cross from a sphere. Try the example here: Boolean Example

function cross3D(size, thickness){
  var union = new Union();
  var boxX = new Box(0,0,0,size,thickness, thickeness);
  var boxY = new Box(0,0,0,thickeness, size, thickeness);
  var boxZ = new Box(0,0,0,thickeness, thickeness,size);
  return union;

function main(args) {
  var size = args[0];
  var thickness = args[1];
  var grid = createGrid(-16*MM,16*MM,-16*MM,16*MM,-16*MM,16*MM,0.1*MM);
  var diff = new Subtraction(new Sphere(15*MM), cross3D(size, thickness));
  var maker = new GridMaker();
  return grid;

This script takes two parameters, the size of the cross and its thickness. The 3D cross is created by unioning together 3 boxes. The boxes are aligned in the x,y and z planes. The source for the grid is the Subtraction of a Sphere and the result of the cross3D function.

Image Popping

One of the nice features of using Voxel grids is the ability to use 2D raster image data. In this example we'll take an image and turn it into a 3D object. Here we use an ImageBitMap source and give it a physical size. We'll place this object on both sides to create a mirror image.

Notice we changed the voxelSize in this example from the typical .1mm default. When dealing with greyscale images you'll likely want higher resolution to get smooth transitions between heights.

Run the example here: Image Popper
var voxelSize = 0.05*MM;

function makePart(path, width, height, thickness){
  var img = new ImageBitmap(path, width, height, thickness);
  return img;

function main(args) {
  var image = args[0];
  var x = 10*MM;
  var y = 22*MM;
  var z = 3*MM;
  dest = createGrid(-x,x,-y,y,-z,z,voxelSize);
  var th = 2*MM;
  var width = 12*MM;
  var height = 40*MM;
  var img = makePart(image, width, height, th);
  var maker = new GridMaker();
  return dest;

The ImageBitMap may have optional solid base. In this case we set base thickness to be 0, which in effect eliminates the base. The base thickness is given as fraction of the ImageBitMap object thickness. For example, setBaseThickness(0.4) will make ImageBitMap base thickness equal to (0.4*thickness) and the thickness of image will be reduces to 0.6*thickness;

Another useful method of ImageBitMap is setBlurWidth(blurWidth). The blurWidth is given in physical units and should normally be of the same size as voxelSize. The blurring eliminates sharp edges in the image.