
How to reduce STL file size without ruining the print
Why STL files get large, how to read the triangle count straight from the file size, and what binary, re-export, decimation and 3MF each save in practice.

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.
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.
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.
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.
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.
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.
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.
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.
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.
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 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.
| Goal | Format | Why |
|---|---|---|
| Hand to a slicer or another maker | 3MF | Units, colour and multi-part in one small file |
| Upload to a print service | Binary STL | Universally accepted, nothing to negotiate |
| Rendering, texturing, DCC handoff | OBJ | Carries UVs and materials, read by everything |
| Web and real-time viewers | glTF | Scene graph, PBR materials and animation together |
| Archiving | CAD source plus 3MF | The 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.
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.
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.
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.
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.
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.

Why STL files get large, how to read the triangle count straight from the file size, and what binary, re-export, decimation and 3MF each save in practice.