IT
OmnvertImage • Document • Network

STL vs 3MF vs OBJ: picking the right 3D file format

7 min read
A pencil design drawing of a curved seat on gridded paper, with faint construction lines ruled through it.

STL stores a surface and nothing else, OBJ is really a bundle of files, and 3MF puts indexed XML plus a unit inside a zip. The choice depends on who receives the file.

The short answer, then the reasoning

Hand a model to a slicer or to another person: 3MF. Upload to a print bureau or feed software written before 2016: binary STL. Move geometry into a renderer with UVs and materials: OBJ, or glTF if it is going on the web. Everything below explains why those defaults hold and where each one bites.

The three formats are not competing implementations of the same idea. They come from different decades and different industries. STL was built to drive a stereolithography machine and contains nothing but a surface. OBJ came out of Wavefront's rendering software and carries texture coordinates and materials. 3MF was published by the 3MF Consortium in 2015 specifically because the manufacturing chain had run out of patience with STL.

What is actually inside each file

STL is a bag of triangles

The entire binary format is: an 80-byte free-form header, a 4-byte triangle count, then exactly 50 bytes per triangle. Forty-eight of those bytes are twelve 32-bit floats (one normal plus three corners); the remaining two are an attribute byte count that the specification expects to be zero. File size is 84 + 50 × triangle count bytes, every time.

What is missing matters more. There is no vertex sharing, so a point shared by six triangles is written six times. There is no unit, no colour, no object hierarchy and no claim that the surface is even closed. A few vendors repurposed those two spare bytes to store colour, but there are two mutually incompatible conventions for doing it and most readers ignore the field entirely. The normal is not trustworthy either: plenty of exporters write zeros there, and readers recompute the normal from vertex winding order instead.

OBJ is indexed, and it is text

An OBJ file is plain ASCII. v lines declare vertices, vt texture coordinates, vn normals, and f lines declare faces by referencing those lists. Indices are one-based, and negative indices are legal: they count backwards from the most recently declared vertex, which is why naively concatenating two OBJ files does not work.

Faces are not restricted to triangles, so quads and n-gons are common. Materials live in a separate .mtl file pulled in by an mtllib line, and the textures that file names are separate images again. An OBJ is usually a small bundle of files rather than one file. There is no unit declaration and no agreed axis convention.

3MF is XML in a zip

A 3MF is an OPC package, which in practice means a zip archive containing [Content_Types].xml, a relationships folder and the model itself at 3D/3dmodel.model. Inside that XML, vertices are indexed once, faces are triangles only, and the root element states the unit explicitly: micron, millimeter, centimeter, inch, foot or meter. A separate build section records which object goes on the plate and with what transform, so placement survives the round trip.

On top of the core specification sit extensions for materials and properties, production, beam lattice, slice and secure content. This is why "it supports 3MF" is an incomplete statement. Which extensions it supports is a separate question, and the answer decides whether your file opens correctly.

Same mesh, three files: do the arithmetic

Take a closed surface of 300,000 triangles. On a closed triangle mesh the face count is roughly twice the vertex count, so there are about 150,000 distinct vertices in play. Work each format out by hand.

  • Binary STL: 84 + 50 × 300,000 = 15,000,084 bytes, or 14.3 MiB. Fixed and predictable, with no compression anywhere.
  • ASCII STL: the same geometry as text runs 250-300 bytes per triangle, so 75-90 MB. The only thing you gain is being able to read it in a text editor.
  • OBJ: 150,000 vertex lines at roughly 30 bytes each is 4.5 MB, plus 300,000 face lines at roughly 22 bytes each is 6.6 MB, landing near 11 MB. Write texture and normal indices into those face lines and they triple in length.
  • 3MF: the XML is wordier than OBJ, but it is deflated inside the zip. A fifth to a tenth of the binary STL is the usual outcome, so 1.5-3 MB for this mesh.

The lesson hiding in those numbers is that 3MF has no clever geometry codec. It wins by doing two ordinary things: storing each vertex once and running deflate over the result. Zip an OBJ and you get close on size alone. What zipping cannot give you is the unit, the placement and the material data. To see how many triangles you are actually carrying, open the file in the 3D viewer; the count is usually higher than people guess.

Size alone should not drive the decision. A 14 MB STL and a 2 MB 3MF of the same mesh slice to nearly identical G-code. The saving shows up on disk, in email attachments and in slicing time. If the triangle count itself is the problem, that is a different job: how to reduce STL file size.

The real difference is units

Neither STL nor OBJ records a unit. The file contains numbers, and nothing in it says whether 25 means millimetres or inches. The reading program assumes. That is exactly how the same file opens at the right size in one slicer and 25.4 times off in another, and the failure is silent: the model looks perfectly normal on screen until you compare it against the build plate.

3MF removes the ambiguity because the unit is an attribute on the model element. If you are stuck with STL or OBJ, the only reliable defence is measuring the bounding box and checking one known dimension against what you expect. Doing that in the 3D viewer before you start a print costs seconds rather than half an hour of filament.

Where each one breaks in ways you did not expect

3MF: a project file is not a model file

This trips up more people than any other 3MF issue. When a slicer saves a 3MF it usually writes a project file: alongside the geometry, the zip holds that slicer's own settings, plate layout, painted supports and material profiles. Open it in a different slicer and you get the geometry while the settings are quietly dropped. Nothing errors, and the part simply prints with someone else's defaults. When you share a model, export it as a model rather than saving the project.

  • The production extension splits a model across several parts linked by UUID. Readers that do not follow those links can show you an empty scene.
  • Beam lattice geometry is stored as beams rather than triangles, so software without that extension sees only whatever mesh remains.
  • Some print bureaus and older CAM chains still accept STL only. Check the upload page before you prepare the file.

OBJ: the files that have to travel with it

Send an OBJ on its own and the recipient sees untextured grey, because the materials are in the .mtl and the textures are separate images again. Exporters that write an absolute map_Kd path produce a file that resolves on exactly one machine. The second trap is triangulation: a non-planar quad splits into two different surfaces depending on which diagonal is chosen, and two programs will not always choose the same one. The third is orientation, since CAD tools generally work Z-up while animation and sculpting tools work Y-up, which is why imported models so often arrive lying on their side.

STL: nothing guarantees the surface is solid

STL never claims the mesh is watertight, it just lists triangles. Open edges, flipped normals and self-intersections all sit in a valid file. Worse, because each vertex is written independently for every triangle touching it, float rounding can separate what should be one point by a micron and turn a closed shell into one with open edges. That is the reason repair comes before reduction: decimation algorithms assume a closed surface. When you do need to reduce, work in steps with the STL reducer and inspect after each one.

Choosing quickly

GoalFormatWhy
Hand to a slicer or another maker3MFUnits, colour and multi-part in one small file
Upload to a print serviceBinary STLUniversally accepted, nothing to negotiate
Rendering, texturing, DCC handoffOBJCarries UVs and materials, read by everything
Web and real-time viewersglTFScene graph, PBR materials and animation together
ArchivingCAD source plus 3MFThe parametric source can be rebuilt, the 3MF stays readable

Keep the order of operations straight when converting: inspect first, reduce only if you need to, convert last. The 3D model converter handles STL, OBJ and 3MF, and it is worth re-checking the bounding box after any conversion.

If you are generating the model from an image or a code rather than from CAD, the format argument mostly goes away. Choosing an output resolution that matches your print scale beats converting and decimating afterwards, and both PNG/SVG to STL and QR code to STL let you set that before anything is generated.

Frequently asked questions

Can 3MF replace STL completely?

For slicing, sharing and archiving, yes. It records units, colour and multiple parts, and the same mesh lands between a fifth and a tenth of the binary STL size. You will still need STL for print bureaus and older software that accept nothing else.

Why does the 3MF my slicer saved behave differently from the one I exported?

A slicer save is a project file: the zip carries settings, plate layout and material profiles next to the geometry. Another slicer reads the geometry and silently ignores the rest. Use the export-as-model option when the file is going to someone else.

My OBJ opens untextured. What went missing?

Materials live in a separate .mtl file and the textures are separate images, so all of them have to travel together. Some exporters also write an absolute texture path that only resolves on the machine that made the file; making those paths relative fixes it.

Why did my model import 25 times too large?

STL and OBJ carry no unit, so the reading program assumes one. Assume inches where millimetres were intended and everything scales by 25.4. 3MF avoids the problem by putting the unit on the model element itself.

Which format should I use for rendering?

OBJ is still the widest-accepted option because it carries UVs and materials. For web or real-time viewers, glTF fits better since it keeps the scene, materials and animation in one package. STL is the wrong tool here, as it has no texture coordinates at all.

Tools used in this post

Sources

MethodologyImage credits