I built objtostl.com, a free converter that turns a Wavefront OBJ file into an STL for 3D printing. The whole conversion happens in the browser tab. Here's how that works, and the things that go wrong if you skip them.
The two formats
OBJ is a text format. The lines that matter for printing look like this:
v 0.0 0.0 0.0 # vertex position
v 1.0 0.0 0.0
v 1.0 1.0 0.0
f 1 2 3 # face, 1-based vertex indices
Faces can have more than three vertices, indices can be negative (relative to the end of the list so far), and a face entry can carry texture and normal indices (f 1/1/1 2/2/2 3/3/3). OBJ files often reference an .mtl material file too.
STL is much simpler: a flat list of triangles. Binary STL is:
- an 80-byte header
- a
uint32triangle count - 50 bytes per triangle: a normal (3 × float32), three vertices (9 × float32) and a 2-byte attribute field
STL has no materials, no textures, no UVs, and no units.
The pipeline
File → parse OBJ → triangulate → scale to mm → fix axis → write binary STL → Blob → download
-
Read locally. The browser File API gives you the bytes. No
fetch, noPOST. - Parse in a Web Worker. A 40 MB OBJ can freeze the main thread if you parse it there. Handing the work to a worker keeps the UI responsive.
- Triangulate. Any face with more than 3 vertices has to be split into triangles. A simple fan (v0, vi, vi+1) works for convex faces.
-
Resolve indices. Convert 1-based and negative indices to absolute ones, and ignore the
vtandvnparts. -
Write the binary STL into an
ArrayBufferwith aDataView, little-endian, then wrap it in aBloband trigger a download.
Units: the 1000× bug
Because STL is unitless, slicers such as Bambu Studio, Cura and PrusaSlicer usually read 1 unit as 1 mm. If the OBJ was authored in meters, your 2 m-tall statue becomes a 2 mm speck. So the converter asks for the source unit (mm, cm, m or inches) and multiplies every coordinate before writing. It also shows the bounding box in millimeters and the triangle count, which lets you sanity-check the size before you slice.
Axis: Y-up vs Z-up
Blender and many OBJ exporters use Y-up. Printers use Z-up. Rotating Y-up to Z-up swaps the axes, and an optional "sit on bed" step translates the mesh so its lowest Z is 0.
What I deliberately don't do
- Materials and textures are dropped. STL can't hold them.
- There's no mesh repair. If your slicer reports a non-manifold mesh, fix it in Blender. Silently "repairing" geometry can hide real problems.
- Binary is the default. It's far smaller than ASCII STL, though ASCII is still available as an option.
Verifying "no upload"
Open DevTools → Network and drop a file. You shouldn't see any request whose body is your mesh. The only mesh the site ever serves is its own sample cube. Anyone can check that claim in about ten seconds.
The tool also converts STL back to OBJ, and has guides for FBX → OBJ → STL via Blender.
I run two other small browser-only tools in the same spirit: Free Text Symbols, a Unicode symbol picker, and Simple Case Converter. Feedback on the converter's edge cases (odd OBJ exporters, huge files) is very welcome.
Top comments (0)