How to convert STEP to STL, in order
This is the whole procedure on one screen, in the order the steps actually matter. Every number below was measured on the machine that wrote this page, and npm test re-runs each one. Nothing here is a rule of thumb.
- Check the extension.
.step,.stp,.igesor.igs. The extension picks the reader, and STEP and IGES take different code paths. - Confirm the unit is what you expect. Numbers come out in millimetres by default, the default path touches no coordinate, and the part is never rotated, aligned, centred or rescaled.
- Choose a quality setting. Normal for printing, Draft to check fit on an assembly, Fine for a fillet that still looks faceted. The setting costs nothing on flat parts.
- Predict the output before you convert. A flat cube is 12 triangles and 684 bytes at every setting. A cone is 286, 516 or 2,142. File size is
84 + 50 × triangles, so the triangle count tells you the byte size. - Convert in the browser. Parsing and tessellation run on your own machine. Nothing is uploaded, and there is no queue.
- Verify the output. Check the file size against the triangle count, and check the mesh is closed. The verification page has all five checks with numbers.
Steps 1–4 are decisions. Steps 5–6 are the conversion itself and the proof it worked. Most bad STEP to STL results are decided in the first four, before a single triangle exists, which is why they are the ones worth being precise about.
1. The extension decides everything downstream
Two format families, four extensions, two readers. A .step or .stp file goes to the STEP reader; .iges or .igs goes to the IGES reader. Nothing else is accepted, and that is a property of the engine rather than a policy setting: the shipped WebAssembly build links readers for STEP, IGES, BREP and a generic fallback, and no importer for anything else.
If your file has any other extension, stop there rather than renaming it. Renaming does not convert anything; it just moves the problem to step 5, where the reader will fail on content that is not the format its name claims. Export from your CAD tool instead.
One difference between the two paths is worth knowing before you trust an IGES result: IGES stores surfaces rather than a solid. Surfaces that do not actually meet in the file stay separate pieces in the output, so a multi-piece result from an IGES source is not automatically a fault.
2. Units: the file’s declared unit in, millimetres out by default
Millimetres are the default output and the default path leaves every coordinate alone, so the number you see is the number the engine measured. Separately, the converter applies the unit declared inside your file before it reports anything — that is not the same as assuming millimetres, and the difference is worth proving rather than asserting.
We built a control pair for it: two geometrically identical 1,000 mm cubes, one declaring UNIT('INCH') with its coordinates written as multiples of 39.370078740157 (which is exactly 1000 ÷ 25.4), and one declaring millimetres directly. Both read back as a 1,000 × 1,000 × 1,000 mm bounding box. The declaration inside the file is what decides, and it is honoured.
| Unit of output | Millimetres by default. The unit declared in the STEP file is applied first. |
|---|---|
| The inch option | Divides the coordinate values by 25.4 on the way out. It re-expresses numbers; it does not scale the geometry, and it does not add or remove a triangle. |
| Position and axes | The part is not rotated, aligned, centred or scaled. A 200 × 150 × 84 mm assembly stays 200 × 150 × 84 mm. |
| If the size is wrong | A clean factor of 25.4 means the file was read in the wrong unit, not converted badly. |
3. The quality choice, and what it costs before you spend it
Each setting is a pair of numbers: a linear deviation and an angular deviation. Draft is 0.02 and 0.5, Normal is 0.005 and 0.3, Fine is 0.001 and 0.1. These are this site’s own choices, not a CAD standard, and they are compared against the shipped worker’s values by npm test so the two copies cannot drift apart.
The important thing about that pair is that you cannot reason about the result from either number alone — you can only measure it. So here is what each setting actually produced, on real files, with this engine.
| Test file | Shape | Draft tri | Normal tri | Fine tri | Fine ÷ Draft | Normal bytes | Fine bytes |
|---|---|---|---|---|---|---|---|
| simple-cube.stp | Flat cube | 12 | 12 | 12 | 1.0× | 684 B | 684 B |
| cube-10x10.stp | Flat cube | 12 | 12 | 12 | 1.0× | 684 B | 684 B |
| cube-10x10.igs (IGES) | Flat cube | 12 | 12 | 12 | 1.0× | 684 B | 684 B |
| rounded-cube.step | Rounded edges | 40 | 56 | 140 | 3.5× | 2,884 B | 7,084 B |
| conical-surface.step | Curved surface | 286 | 516 | 2,142 | 7.5× | 25,884 B | 107,184 B |
| as1_pe_203.stp (18 parts) | Assembly | 3,848 | 6,088 | 17,848 | 4.6× | 304,484 B | 892,484 B |
| as1-oc-214.stp (18 parts) | Assembly | 4,900 | 7,372 | 21,116 | 4.3× | 368,684 B | 1,055,884 B |
Read that table as a cost, not a menu. Going Draft to Fine multiplies the cone by 7.5 and the assemblies by roughly 4.3–4.6, while leaving the flat cubes untouched. If your part is mostly flat, Fine is free; if it is mostly curved, Fine is a real cost and you should look at the preview before accepting it.
Predicting the count before you convert
For a flat part you can predict the answer exactly: a cube is 12 triangles, because six flat faces need exactly two each, and no closed solid can use fewer. 684 bytes, every time, at every setting.
For a curved part you can predict the byte size from any triangle count, because the binary STL layout is fixed at 84 bytes of header plus 50 bytes per triangle. What you cannot do is look at a STEP file and derive its triangle count from its byte size — the two are unrelated, and the table above shows a 9,640-byte cone producing a 107,184-byte STL at Fine. Curvature, not file size, drives the count.
4. Scaling the part up does not give you a finer mesh
This is the most useful thing we measured, and it is not what the setting names suggest. The presets are expressed as a ratio of the part’s own size, which reads like “bigger part, same relative accuracy, more triangles”. We tested that directly instead of assuming it.
We took one curved solid and scaled the entire solid to 100× its size. Measured at Normal, the bounding box went from 27.033 mm to 2,703.298 mm — a clean 100× on all three axes — and the triangle counts did not move at all:
| Setting | Triangles at 1× | Triangles at 100× | Bytes at 1× | Bytes at 100× |
|---|---|---|---|---|
| Draft | 286 | 286 | 14,384 B | 14,384 B |
| Normal | 516 | 516 | 25,884 B | 25,884 B |
| Fine | 2,142 | 2,142 | 107,184 B | 107,184 B |
Same triangles, same bytes, hundred times the physical size. Enlarging a model is not a way to get a better mesh in this engine. If your mesh looks faceted, the fix is the quality setting, not a bigger model.
We are deliberately not going further than that, and here is the honest reason. The presets name their linear deviation as a bounding_box_ratio, which implies a physical tolerance of “ratio × the part’s size”. We could not confirm that arithmetic: passing the same value as absolute instead produces identical triangle counts, so the wrapper does not visibly distinguish the two modes on our test files. The scale invariance above is measured; any formula behind it is not, so we do not print one.
Which of the two numbers actually binds
Because the two numbers interact, one of them can stop mattering entirely. Holding the cone at each angular value and varying only the linear value gives:
| Angular value | linear 0.02 | linear 0.005 | linear 0.001 |
|---|---|---|---|
| 0.1 (Fine) | 2,142 | 2,142 | 2,142 |
| 0.3 (Normal) | 504 | 516 | 1,318 |
| 1.0 (relaxed) | 186 | 408 | 1,318 |
Read the top row. At the angular value Fine uses, the linear value makes no difference at all — 0.02, 0.005 and 0.001 all give 2,142 triangles, because the angular constraint is already the tighter one. Drop it to 0.3 and linear starts to matter; relax it to 1.0 and linear takes over completely and the counts fall to 186 and 408.
The practical consequence: you cannot predict the output by arithmetic on the two numbers, which is exactly why step 4 exists. Convert, look at the count, and only then decide whether to pay for a finer setting.
5. The conversion itself
Drop the file on the converter. Parsing and tessellation run in your tab as occt-import-js 0.0.23, a WebAssembly build of the OpenCASCADE kernel. The file is read on your machine and is never uploaded; there is no server-side step and no queue. That also means the result comes from your own hardware rather than from a remote service, which is a fair trade for a file that never leaves the tab.
The two ends of this step are fixed, whatever you choose in between: what goes in is one of the four extensions from step 1, and what comes out is one .stl file — binary by default, ASCII if you switch it. A .stl file is an output here, never an input. The format page explains why that direction cannot be reversed, so this page does not restate it.
The page reports three numbers after every run, and they are the ones to look at before you download: the bounding box in millimetres (compare it to the size you know), the triangle count, and the solid-part count. For an assembly the part count is the useful one — our two 18-part test files both report 18, and every part lands in the single STL you download.
If your file is refused, it is not a quality problem. No setting will make an unreadable file readable, and no setting will repair a broken model: this triangulates, it does not fix geometry that arrived with holes or self-intersections.
6. Verify, because finished is not the same as correct
The single cheapest check costs nothing: a binary STL is 84 + 50 × triangles bytes, so subtract 84 from the file size and divide what is left by 50. If that recovers the triangle count the converter reported, the download is intact. There is no rounding in that identity, so any mismatch is a real fault rather than a rounding artefact.
Then check the mesh is closed by counting edges: every edge shared by exactly two triangles means a watertight solid, and an edge used once is a hole. A cube gives 12 triangles across 18 distinct edges, and that arithmetic generalises to any closed solid: 12 × 3 = 36 edge-uses, divided by two, is 18.
Both checks, plus the unit check, the curvature check and the reader check, are laid out with their numbers on the STEP to STL verification page.
The whole thing on one screen
| 1. Extension | .step, .stp, .iges, .igs. Decides the reader. Nothing else is read. |
|---|---|
| 2. Units | Millimetres out by default, axes unchanged. A 25.4× error means a unit mismatch, not a bad conversion. |
| 3. Quality | Draft 0.02/0.5, Normal 0.005/0.3, Fine 0.001/0.1. Costs nothing on flat parts, 7.5× on a cone. |
| 4. Expected output | Flat cube 12 tri / 684 B at every setting. Cone 286–2,142 tri. Bytes = 84 + 50 × triangles. |
| 5. Convert | Runs in your tab. Reports bounding box, triangle count and part count. Nothing uploaded. |
| 6. Verify | (size − 84) ÷ 50 recovers the triangle count. Every edge used twice means closed. |
Scaling the model up does not belong in that list, because it changes nothing: 100× the size returned identical counts and identical bytes at all three settings.
Where these numbers come from
- Every triangle count, byte size and bounding box on this page — produced by running the shipped engine (occt-import-js 0.0.23, a WebAssembly build of OpenCASCADE) in Node.js over the project’s own test files on this machine.
npm testre-runs all of them and fails on a mismatch. The Fine ÷ Draft column is recomputed from the raw counts rather than trusted. - 84 + 50 × triangles — the published binary STL record layout, cross-checked against the reference STL shipped with the engine vendor’s own test set.
- The 100× scaling result — a second test file built from the same curved solid with its inch-to-millimetre factor multiplied by 100, so coordinates, radii and the file’s own declared uncertainty all scale together while the topology and surface types stay identical. Its bounding box is re-measured in the test to confirm it really is 100× the original, and the counts are then compared against the original.
- The angular/linear coupling table — measured by calling the same STEP reader at each combination of the two values. The shipped settings are held fixed everywhere else on this site.
- The 1,000 mm inch/mm control pair — two geometrically identical cubes, one declaring
UNIT('INCH')and one declaring millimetres;npm testasserts both bounding boxes and the inch declaration. - The preset values — compared cell by cell against the shipped
engine/worker.jsso the page and the code that runs cannot disagree. - We do not publish the test files — they live in a development folder that is never deployed, so nothing on this site asks you to trust a file you cannot inspect. The check you can run yourself is arithmetic: take any binary STL this converter gives you, subtract 84 from its size, divide by 50, and compare what you get with the triangle count the page reported.
The verification page covers step 6 in full, and STEP vs STL covers what the two formats actually store. This page is the order of operations and the numbers each step produces.