Benchmark
What a GLB to STL conversion discards, field by field
What is actually lost when a GLB is converted to STL?
Result
A two part scene survives a GLB round trip with nothing changed: 2 named parts, 332 faces, 170 vertices, UV coordinates, 1 texture, 1 material and per-vertex colours all come back. Exporting the same scene to STL returns 1 unnamed part, 996 vertices instead of 170, no UVs, no texture, no material and no vertex colours. Triangles and bounding box are the only properties that cross. The STL is also 16,684 bytes against the GLB's 8,812, so the conversion costs almost twice the file size to carry strictly less information.
The same scene at three stages
| Stage | Separate parts | Part names | Faces | Vertices | UV coordinates | Textures | Materials | Vertex colours |
|---|---|---|---|---|---|---|---|---|
| source scene, in memory | 2 | ColouredBall, TexturedBox | 332 | 170 | Yes | 1 | BoxMaterial | Yes |
| after a GLB round trip | 2 | ColouredBall, TexturedBox | 332 | 170 | Yes | 1 | BoxMaterial | Yes |
| after export to STL | 1 | none | 332 | 996 | No | 0 | none | No |
Row two is the control. If the GLB round trip had lost anything, the third row could not be blamed on STL. It lost nothing, so every difference in row three belongs to the export.
Why the vertex count goes up while the information goes down
170 vertices become 996, which is 332 faces times three. STL has no vertex list to index into, so each triangle writes its own three corners and any sharing is thrown away. This is the same mechanism measured in the vertex welding benchmark, and it is why an STL of a simple scene can be larger than the richer format it came from.
Normals are stored differently, not merely fewer
GLB stores one normal per vertex, which is how smooth shading is authored. STL stores one normal per facet. A curved surface therefore stops being curved on export and reads as a ring of flat plates, and no repair step recovers it, because the per-vertex information was never written.
Two parts becoming one is not cosmetic
The two named parts arrive as a single unnamed soup of triangles. For 3D printing that usually does not matter, since the slicer separates by connectivity anyway. For anything downstream that addresses parts by name, an assembly, a configurator, an engine import, the naming is the thing that was being carried and it is gone.
How this was measured
A scene built to carry one of each thing STL cannot hold: two separately named parts (ColouredBall and TexturedBox), a UV mapped texture on one of them, a named material, and per-vertex colours on the other. 332 faces, 170 vertices, bounding box from -10, -10, -10 to 50, 10, 10. It was inspected in memory, again after writing and reading a GLB, and again after export to STL.
- Software and versions
- trimesh 5.1.0, numpy 2.5.2
- Date measured
- 2026-09-09
- Script
scripts/measure-glb-to-stl-loss.py- Raw data
- glb-to-stl-loss-2026-09-08.json
This data is published under CC BY 4.0. Reuse the numbers freely, with a link back so a reader can check the method.
What this does not show
Stated so the result is not read wider than it goes.
- animation, skinning, cameras and lights, which the test asset does not contain
- what any named converter other than this library does