Why a 3D Model Imports at the Wrong Size: Only One Mesh Format Stores a Unit

Published 2026-09-08

A model arrives, you open it, and it is either a speck on the build plate or a wall taller than the printer. The usual advice is to scale it by 1000 and move on. That works, but it leaves the actual question unanswered: why does this keep happening, and is there a way to know the right size rather than guess it?

The answer turned out to be simpler and worse than expected. We exported one object to five formats and looked inside the files. Four of the five do not record a unit of length anywhere.

What we measured

One object: a cube that is physically 100 mm on each edge. Two authoring conventions that real pipelines actually use, metres and millimetres. Five formats. For each file we recorded the number that landed in the file, then opened the raw bytes to see whether a unit was written anywhere.

Authored inFormatNumber in fileUnit in the bytesUnit fixed by specIf read as mmIf read as m
metresGLB0.1nometres0.1 mm100 mm
metresSTL0.1nonone0.1 mm100 mm
metresOBJ0.1nonone0.1 mm100 mm
metresPLY0.1nonone0.1 mm100 mm
metres3MF0.1yesmillimeter0.1 mm100 mm
millimetresGLB100nometres100 mm100,000 mm
millimetresSTL100nonone100 mm100,000 mm
millimetresOBJ100nonone100 mm100,000 mm
millimetresPLY100nonone100 mm100,000 mm
millimetres3MF100yesmillimeter100 mm100,000 mm

The script that produces this table needs one pip install to reproduce. It writes each file, reads it back, and then inspects the container directly rather than trusting the library’s interpretation.

Read the first column and the third together. The number in the file is identical across all five formats. Nothing about STL makes a model small and nothing about GLB makes it large. Every difference in the last two columns comes from what the reading program decided the number meant.

What is actually in the bytes

This is the part worth being precise about, because “the format is in millimetres” is said constantly and is not true of any of these formats.

FormatWhat the container holds where a unit could go
STLAn 80 byte header. In our export it was empty. It is free text, and nothing reads it as a unit.
OBJLeading comment lines only. Ours carried the exporter’s URL.
PLYA comment line, same story.
GLBThe JSON chunk had top level keys accessors, asset, bufferViews, buffers, meshes, nodes, scene, scenes, and an empty extensionsUsed. No unit key exists in core glTF.
3MF3D/3dmodel.model opens with <model unit="millimeter">.

glTF says metres, but it is a rule, not a field

GLB behaves consistently across tools, and it is worth understanding why, because it is not because the file explains itself.

The glTF 2.0 specification settles it in the section on coordinates and units, in one sentence: “The units for all linear distances are meters.” It states the same thing again where meshes are defined: “After applying the node’s global transform, mesh vertex position values are meters.” (glTF 2.0 Specification, Khronos Group)

So every conforming reader agrees on the meaning of a GLB coordinate without the file saying anything. That is a real advantage over STL, where there is no rule to agree on. It is also why a GLB dropped into a slicer comes out tiny: the slicer is not a glTF renderer, it is a millimetre tool, and it reads the raw number.

If you want to see the numbers in a GLB without installing anything, our GLB viewer reports the bounding box and states which assumption it is applying. More on what else is inside a GLB is in What Is a GLB File.

The error runs both ways

The version everyone knows is the model that arrives too small. The opposite case is less discussed and bites harder, because the result does not look obviously broken.

  • Authored in metres, read as millimetres. The 100 mm cube arrives as a 0.1 mm cube. Obvious, and the fix is obvious.
  • Authored in millimetres, read as metres. The 100 mm cube arrives as a 100 metre cube. This is what happens when a CAD part goes into a game engine, and in a large scene it can be mistaken for a modelling error rather than a unit error.

Both are the same factor of 1000, from the same missing field. If you work between CAD and engines, STEP vs STL covers what else changes on that trip.

3MF records the unit, with one caveat

3MF is the only format in the test that writes the unit down. The 3MF Core Specification defines a unit attribute on the <model> element, describes it as specifying the unit used to interpret all vertices, locations and measurements in the model, and allows micron, millimeter, centimeter, inch, foot and meter, defaulting to millimeter. (3MF Core Specification, 3MF Consortium)

The caveat is visible in our own data, and it matters more than the feature.

Both of our 3MF exports say millimeter, including the one whose geometry was authored in metres. That file contains a cube of side 0.1 and declares the unit as millimetres, so it asserts that a 100 mm cube is a tenth of a millimetre across. The exporter did not know the authoring convention and wrote the default.

For what else differs between the two, see 3MF vs STL.

How to work out the unit when the file does not tell you

Since four of five formats cannot answer the question, you have to answer it from the object itself. This is inference, not measurement, so treat the result as a strong guess.

  1. Open the file and read the longest dimension. Any viewer will show it. Ours does it on upload for STL, OBJ and GLB.
  2. Ask what the object is in the real world. A phone case is about 150 mm. A chess piece is about 60 mm. A benchy is 60 mm by definition.
  3. Compare, and read off the convention.
Longest edge readsReal object is aboutAlmost certainly authored in
0.15150 mmmetres
150150 mmmillimetres
15150 mmcentimetres
5.9150 mminches

The inch case is the one people miss, because 5.9 and 150 do not look like the same measurement. If a number is off by roughly 25.4 rather than 1000, the file came from an inch based workflow.

What to do about it

Sending a model for printing: export 3MF when the receiving software accepts it, and let it carry the unit. Where STL is required, state the intended size in the filename or the message. It is a workaround, but the alternative is the recipient guessing.

Sending a model to a game engine or the web: export GLB and author in metres. The specification is doing the work, and it only works if you hold up your end.

Receiving a model from anyone: check the bounding box before anything else, and do it before you repair, decimate or slice. A scale error found at import costs seconds. The same error found after a repair pass costs the repair pass, because tolerances that are sensible at 100 mm are meaningless at 0.1 mm. If the mesh also needs work, how to repair a broken STL covers the order to do things in.

Converting between formats: expect the unit to be dropped, because in four of five cases there is nothing to drop it from. Our GLB to STL converter reports the size it read and the assumption it applied, so the number is visible at the point where it would otherwise silently change meaning.

Method

One axis aligned cube, 100 mm on the longest edge, built and exported with trimesh 5.1.0 and numpy 2.5.2 on 7 September 2026. Each file was written, read back, and separately opened as a raw container: the GLB JSON chunk was parsed out of the binary header, the STL header was read as its first 80 bytes, OBJ and PLY comment lines were read as text, and the 3MF archive was opened as a ZIP and its model part searched for a unit attribute.

Full results, method and limitations: Which 3D file formats record a unit of length, and which leave it to be guessed. The raw output is published as JSON, produced by scripts/measure-unit-declarations.py.

Not measured here: how any specific slicer or DCC application behaves on import, since that requires those applications rather than the file format. Formats that trimesh cannot write, including FBX and STEP, are also out of scope. CAD formats descend from a different tradition and generally do carry units, which is part of why the CAD to mesh handoff loses information in the direction it does.

Frequently asked questions

Why does my 3D model import at the wrong size?

Because most mesh files do not say what their numbers mean. We exported the same 100 mm cube to five formats and inspected the bytes: STL, OBJ, PLY and GLB store the coordinate 0.1 or 100 with no unit of length anywhere in the file. The importing program has to guess, and slicers guess millimetres while glTF tooling follows a specification that says metres. A cube authored as 0.1 metres arrives in a slicer as 0.1 mm, which is 1000 times too small.

Which 3D file formats store their unit of measurement?

Of the five mesh formats we tested, only 3MF does. Its model part carries a unit attribute on the root element, and the 3MF Core Specification allows micron, millimeter, centimeter, inch, foot and meter, defaulting to millimeter. STL, OBJ and PLY contain no unit field at all. glTF and GLB also contain no unit field, but the specification fixes the meaning: all linear distances are metres.

Is a GLB file in metres or millimetres?

Metres, by specification rather than by anything written in the file. The glTF 2.0 specification states that the units for all linear distances are meters, and repeats it for meshes: after applying the node's global transform, mesh vertex position values are meters. We confirmed there is no unit field in the file itself. The JSON chunk of a GLB we exported contained only accessors, asset, bufferViews, buffers, meshes, nodes, scene and scenes.

How do I tell what unit an STL file was made in?

You cannot read it from the file, because it is not there. You have to infer it from the size of the object. Open the file, look at the bounding box, and ask whether the number is plausible for the thing it depicts. A phone case whose longest edge reads 0.15 was almost certainly authored in metres. One that reads 150 was authored in millimetres. If it reads 5.9 someone worked in inches. There is no way to be certain from the bytes alone.

Should I use 3MF instead of STL for 3D printing?

For anything where size matters, 3MF removes a whole class of failure, because it records the unit instead of leaving it to be guessed. The caveat is that the field only helps when the exporter fills it in honestly. In our test, trimesh wrote unit equals millimeter into both files it produced, including the one whose geometry was authored in metres, so that file claims a 100 mm cube is a tenth of a millimetre across.