Writing Binary STL files from THREE.js objects

Expanding on this blog post I wrote a while back on writing ASCII STL files from THREE.js objects from the browser, I wanted to show how easy it is to write binary STL files also.

The binary STL format is very straightforward. From Wikipedia:

UINT8[80] – Header
UINT32 – Number of triangles

foreach triangle
    REAL32[3] – Normal vector
    REAL32[3] – Vertex 1
    REAL32[3] – Vertex 2
    REAL32[3] – Vertex 3
    UINT16 – Attribute byte count
end

Be extra careful to write the floating point numbers as little-endian, as the wikipedia article warns:

Floating-point numbers are... assumed to be little-endian, although this is not stated in documentation.

Once we get the mechanics of writing binary data out of the way, it really becomes trivial, so lets tackle that first. To write binary data in javascript we'll need two objects:

  1. A container to put the raw data in: an ArrayBuffer
  2. A helper to write heterogeneous binary datatypes: a DataView

Since this is binary data, we need to know how many bytes the file will be before we are able to start writing it. Its pretty straightforward to compute from the layout above:

// sizeInBytes = 
//         80 * sizeof(UINT8)
//      +  1 * sizeof(UINT32)
//      + (12 * sizeof(REAL32) + 1 * sizeof(UINT16)) * N triangles
var buffer = new ArrayBuffer(84 + (50 * nTriangles));

That takes care of the ArrayBuffer container for the data, but how do we write data to it?

Because we are dealing with many different binary types (uint8, uint32, float32, etc.), we need an abstraction that allows us to write these types into the raw data buffer. That is what a DataView does. After creating a DataView using the raw ArrayBuffer it will write data into, you can easily write individual bytes of data in their corresponding types. You only have to keep track of the byte offset:

var dataview = new DataView(buffer);
var offset = 0;

// To skip data, no need to write zeros, just increase the offset
offset += 80 // For the STL header 

// To write a vector like the face normal, or a vertex
// need to pass in isLittleEndian=true / false as last param
dataview.setFloat32(offset, v.x, isLittleEndian);
offset += 4; // 4 = number of bytes in a float 32
dataview.setFloat32(offset, v.y, isLittleEndian);
offset += 4; 
dataview.setFloat32(offset, v.z, isLittleEndian);
offset += 4;

Putting those together, here is the full binary stl writer.

I've wrapped it up into a gist here for helping to write THREE.js geometries into binary STL files. I've also extracted the ascii stl file generator there also so you can easily compare.

You can play with a live example here.