3mf to stl
Unzip the package, apply the build transform and the declared unit, write binary or ASCII STL — in this tab.
Convert .3mf to .stl — 0 B of WebAssembly, 84+50×n bytes out, both measured in the tab.
This page converts one direction only: mesh in, mesh out.
Nothing is read until you press it.
model.stl
- Triangles
- —
- Bounding box (mm)
- —
- Build items → one STL
- —
- Model parts read
- —
- Source .3mf
- —
- STL size
- —
- Declared unit → mm
- —
- Time
- —
- Triangles in our bracket sample
- 2,696
- Bytes that STL weighs — 84 + 50 × 2,696
- 134,884
- WebAssembly this page downloads
- 0 B
- The size identity, any triangle count
- 84+50×n
Both fixed cells are measured from this page's own bracket package on 2026-10-29: 2,696 triangles unpacked from a 39,173-byte 3MF and written out as 134,884 bytes, re-checked by npm test. 0 B of WebAssembly holds here because a 3MF already contains triangles — no geometry kernel is involved on either side of this page. The last row is the binary-STL size identity, 84 + 50 × n, true for any triangle count, including this sample.
What a 3MF file actually is
Every .3mf is a ZIP package that follows the Open Packaging Conventions. The archive carries a content-type map and a relationships part, and _rels/.rels points at the root model part — usually /3D/3dmodel.model, but that path is a recommendation rather than a guarantee, so this reader follows the relationship instead of the filename. The model part then holds the two things that matter: <resources>, which declares objects, meshes and materials, and <build>, which lists the <item> entries actually being output. The specification fixes the difference — a consumer must not output any object that no item references — so this page walks the build and leaves unreferenced resources unread.
Real manufacturing packages go further. Four of the five packages this page is tested against split their geometry across several model parts and point at them with a p:path component, and their root model part holds zero vertices. A reader that opens only the standard filename returns an empty mesh from such a file; this one loads every referenced part before it counts a single triangle.
Two pages carry the whole 3MF footprint of this site, and there will not be a third: this page reads a package, the STL to 3MF page writes one, and STEP↔STL remains the main line here. Any further 3MF question is answered inside these two pages rather than on a page of its own.
What this conversion keeps, and what it drops
Geometry is kept exactly as the file declares it: the vertices and triangles of every build item, moved by that item’s transform. Everything that is not a vertex is dropped, because an STL record has nowhere to put it.
| Dropped | How the 3MF stores it | Why the STL cannot keep it |
|---|---|---|
| Colour | basematerials display colours, or a Materials-extension colour group | An STL triangle is 50 bytes: a normal, three vertices, and a 2-byte attribute count that this writer sets to zero. |
| Materials | per-object and per-triangle material indices | STL has no material field at all. |
| Print settings | an optional PrintTicket part, plate position and layer height | PrintTicket is a separate part of the package; it never reaches the STL. |
| Unit | <model unit>, one of six values | STL has no unit field; folding to millimetres is this page’s choice, printed in the stats grid. |
| Names and hierarchy | object ids, names, part numbers, nested components | An STL is one unnamed triangle soup; every build item is merged into one file. |
| Textures, thumbnails, signatures | separate package parts | None of them is geometry, and none of them changes a triangle. |
That last row explains why an STL is often larger than the 3MF it came from: the package compresses its text and stores thumbnails, while the STL stores every triangle at a fixed 50 bytes, uncompressed. The missing-size guide walks through that arithmetic with the same identity this page prints.
How the build transform and the unit are applied
An <item> or <component> may carry a transform attribute holding 12 numbers. They are the first three rows of a 4×4 matrix in row-major order, with the last column fixed at 0 0 0 1, so the tenth, eleventh and twelfth numbers are the translation. A point is transformed as [x y z 1] × M, which puts the translation in the result’s constant column. A negative determinant means the matrix mirrors, and a mirror must have its triangle winding reversed or the solid turns inside out; this reader flips the winding whenever the determinant is below zero.
Nested components multiply outward: the leaf mesh is multiplied by its component chain, then by the build item’s own matrix. The declared unit is applied at the same time, so a model written in inches arrives in millimetres rather than 25.4× too small.
| Declared unit | Factor to millimetres | 10 units become |
|---|---|---|
micron | 0.001 | 0.01 mm |
millimeter (default) | 1 | 10 mm |
centimeter | 10 | 100 mm |
inch | 25.4 | 254 mm |
foot | 304.8 | 3,048 mm |
meter | 1000 | 10,000 mm |
A missing unit attribute is read as millimeter, which is the specification’s default. Any other value is refused rather than guessed. Coordinates are not centred or rescaled: a part that the package places 1,530 mm from the origin keeps that offset, which is why our bracket sample arrives off the print-bed centre.
When a 3MF will not convert
- Not a ZIP package. The first bytes must be
PK. A file renamed to.3mfwithout being a package is refused before anything is read. - No root model relationship. Without the 3D-model relationship in
_rels/.relsthere is no model part to read. - A unit outside the six values. Anything but
micron,millimeter,centimeter,inch,footormeteris an error, not a guess. - A malformed transform. The attribute must hold exactly 12 numbers; anything else is refused.
- An empty build, a missing component target, or a component cycle. Each is named in the message rather than silently dropping geometry.
- A ZIP entry that is neither stored nor deflate. Only those two methods are read.
- A browser without
DecompressionStream('deflate-raw'). The page checks for the capability before it reads a byte and says so plainly. Only Chrome 156 has been tested here; Safari, Firefox and iOS are unverified. - Memory. The largest package exercised is 16,342,603 bytes, which expands to a 108,044,263-byte model part and a 68,135,384-byte STL, at about 214 MB of process memory in Node. Browser peak memory on that file is unverified, and larger packages are unverified.
A mesh with holes comes out with the same holes. This page writes triangles; it does not stitch, heal or repair, and the edge count is how you check a shell before a slicer refuses it.
The numbers on this page, and where they come from
Published · Last updated
| Package | Input | Build items | Triangles | Bounding box | STL bytes | What it proves |
|---|---|---|---|---|---|---|
M3-CRETE_inner-corner-bracket.3mf | 39,173 B | 1 | 2,696 | 38 × 22.5 × 17 mm | 134,884 | a ZIP64 package with a colour group and no build transform |
MSRC_case_zero.3mf | 84,013 B | 2 | 5,664 | 69.786 × 47.653 × 9 mm | 283,284 | two build items, one rotated 180°, merged into a single STL |
Both byte counts obey the identity this site prints everywhere: 84 + 50 × 2,696 = 134,884, and 84 + 50 × 5,664 = 283,284. The packages live in _dev/samples-3mf/, outside the deploy tree, and never become downloadable.
npm test
That command re-runs every number above from those two files, then runs the same packages through the shipped OpenCASCADE engine and records that it cannot read a 3MF at all — the control that keeps this page’s own reader honest.
Questions people actually ask
Is this 3MF to STL converter really running in my browser? Yes. The page is static, the package is unpacked in this tab with the browser's own DecompressionStream, and nothing is sent to a server. Unlike the STEP and IGES page, this route downloads no WebAssembly kernel at all — 0 bytes — because a 3MF already contains triangles.
What does the STL lose? Colour, material assignments, PrintTicket settings such as layer height and plate position, object names and hierarchy, and the unit declaration itself. A binary STL record is 50 bytes per triangle: a normal, three vertices and a two-byte attribute count that this writer sets to zero. Nothing in that record can hold colour or a name.
Why is my model the wrong size after converting? Read the unit the model declares. The default is millimetres; a file declared in inches is multiplied by 25.4, one in metres by 1000, and the stats grid prints the multiplied bounding box, not the raw coordinate. If another tool ignores the unit, its result differs by exactly that factor.
Will my multi-object 3MF become one STL or several? One. Every item in the build gets its own transform applied and is merged into the same triangle list, so a two-item package such as our 5,664-triangle sample arrives as a single STL with no per-object names and no per-object files.
Does it need a CAD kernel or a WebAssembly download? No. This route only unzips the package and reads XML; the STEP and IGES route is the one that downloads the 7,604,031-byte OpenCASCADE kernel. Keeping the 3MF reader in plain JavaScript is why this page works with no engine download.
This page does not claim to recover CAD features. A 3MF mesh goes in, an STL mesh comes out — the same triangles, re-serialised.
Sponsored link: Creality’s official store (3D printers, scanners, filament and parts) is an affiliate partner — clicking it may earn this site a commission. It changes nothing about the converter; see what sponsored links mean here.