Modules | TS

I/O

Read and write mesh data in STL and OBJ formats.

The I/O module provides functions for reading and writing mesh data in STL format (binary and ASCII) and OBJ format (ASCII). All operations work with in-memory buffers, making them suitable for browser environments and Node.js alike.

Reading

STL Files

Read an STL file (binary or ASCII) from a buffer. Only vertex positions and face connectivity are read — per-face normals stored in the STL are ignored (use mesh.normals to compute them).

import * as tf from "@polydera/trueform";

// From fetch response
const response = await fetch("model.stl");
const bytes = new Uint8Array(await response.arrayBuffer());

const mesh = tf.readStl(bytes);
console.log(`${mesh.numberOfFaces} faces, ${mesh.numberOfPoints} points`);

OBJ Files

Read an OBJ file from a buffer. Only vertex positions and face connectivity are read — normals (vn), texture coordinates (vt), materials, and group names are ignored.

const response = await fetch("model.obj");
const bytes = new Uint8Array(await response.arrayBuffer());

const mesh = tf.readObj(bytes);

By default, readObj expects triangular faces. To read OBJ files with quads or mixed polygon sizes, pass { dynamic: true } — all polygons are read and automatically triangulated:

const mesh = tf.readObj(bytes, { dynamic: true });

From File Input

In browser environments, read from a file input element:

const file = input.files[0];
const bytes = new Uint8Array(await file.arrayBuffer());
const mesh = tf.readStl(bytes);
FunctionInputReturns
readStlArrayBuffer | Uint8ArrayMesh
readObjArrayBuffer | Uint8Array, ReadObjOptions?Mesh

Raw Data Access

readStlData and readObjData return raw faces and points as a MeshLike object instead of a Mesh. This gives direct access to the parsed geometry without creating a WASM-resident mesh.

// STL → MeshLike with NDArrayInt32 faces [F, 3]
const { faces, points } = tf.readStlData(bytes);

// OBJ (triangles only) → MeshLike with NDArrayInt32 faces [F, 3]
const data = tf.readObjData(bytes);

// OBJ (all polygons) → MeshLike with OffsetBlockedBuffer faces
const dynamicData = tf.readObjData(bytes, { dynamic: true });

// Triangulate dynamic data into a Mesh when ready
const mesh = tf.triangulate(dynamicData);
FunctionInputReturns
readStlDataArrayBuffer | Uint8ArrayMeshLike (NDArrayInt32 faces)
readObjDataArrayBuffer | Uint8Array, ReadObjOptions?MeshLike (NDArrayInt32 or OffsetBlockedBuffer faces)

Writing

Write functions serialize a mesh to an in-memory buffer. The returned NDArrayInt8 can be converted to a Blob for download or upload.

STL Files

Writes binary STL. Per-face normals are included if available, otherwise zero normals are written.

// Serialize to binary STL
const stlBuffer = tf.writeStl(mesh);

// Download in browser
const blob = new Blob([stlBuffer.toTypedArray()], { type: "model/stl" });
const url = URL.createObjectURL(blob);

OBJ Files

Writes ASCII OBJ with vertex positions and face connectivity only (no normals, UVs, or materials).

// Serialize to ASCII OBJ
const objBuffer = tf.writeObj(mesh);

const blob = new Blob([objBuffer.toTypedArray()], { type: "text/plain" });
FunctionInputReturns
writeStlMeshNDArrayInt8 (binary STL)
writeObjMeshNDArrayInt8 (ASCII OBJ)
When writing a mesh that has a transformation set, the transformation is automatically applied to the vertex positions in the output.

Round-Trip Example

import * as tf from "@polydera/trueform";

// Read
const response = await fetch("input.stl");
const mesh = tf.readStl(new Uint8Array(await response.arrayBuffer()));

// Process
const decimated = tf.decimated(mesh, 0.1);

// Write back
const stlData = tf.writeStl(decimated);
const blob = new Blob([stlData.toTypedArray()], { type: "model/stl" });

Async

All I/O functions are available as async variants via tf.async for off-main-thread execution:

const mesh = await tf.async.readStl(bytes);
const stlData = await tf.async.writeStl(mesh);
For implementation details, see the C++ I/O documentation.