Development Status
- 4 - Beta
Intended Audience
- Science/Research
Operating System
- Microsoft :: Windows
- POSIX
- MacOS
Programming Language
- Python :: 3
- Python :: 3.10
- Python :: 3.11
- Python :: 3.12
- Python :: 3.13
- Python :: 3.14
Topic
- Scientific/Engineering :: Visualization
A fast STL reader for Python. Reads binary and ASCII files, merges duplicate vertices on the way in, and returns NumPy arrays.
On the synthetic 1M-point binary benchmark below, the default single-threaded path reads in about 100 ms on a Ryzen 9 8945HS, roughly 11x faster than VTK and 28x faster than meshio. Opting into the multi-threaded path with threads=0 (auto) reads the same file in 50 ms — about 21x faster than VTK and 55x faster than meshio. The implementation is a memory-mapped parser, an optional multi-threaded ASCII path, and a concurrent open-addressing hashtable for vertex deduplication. See Benchmarks for the numbers and the reproduction script.
The vertex hash function (final96) and the iterative table sizing helper (nextpow2) are taken from aki5/libstl; see src/hash96.h. The rest of the parser is independent.
Installation
pip install pyvista-stl
To build from source:
git clone https://github.com/pyvista/pyvista-stl.git
cd pyvista-stl
pip install .
Usage
Read an STL file as merged (vertices, indices) arrays:
>>> import pyvista_stl
>>> vertices, indices = pyvista_stl.read("example.stl")
>>> vertices
array([[-0.01671113, 0.5450843 , -0.8382146 ],
[ 0.01671113, 0.5450843 , -0.8382146 ],
[ 0. , 0.52573115, -0.8506509 ],
...,
[ 0.5952229 , -0.57455426, 0.56178033],
[ 0.56178033, -0.5952229 , 0.57455426],
[ 0.57455426, -0.56178033, 0.5952229 ]], dtype=float32)
>>> indices
array([[ 0, 1, 2],
[ 1, 3, 4],
[ 4, 5, 2],
...,
[9005998, 9005988, 9005999],
[9005999, 9005996, 9005995],
[9005998, 9005999, 9005995]], dtype=int32)
vertices is the deduplicated (n_points, 3) float32 array. indices is the (n_triangles, 3) int32 array of vertex indices into vertices. Both binary and ASCII files are accepted; the format is detected automatically.
By default the reader runs single-threaded, which produces a deterministic vertex ordering. Pass threads=N (an integer >= 2) to opt into the multi-threaded parser, or threads=0 to auto-select hardware_concurrency():
vertices, indices = pyvista_stl.read("example.stl", threads=0)
mesh = pyvista_stl.read_as_mesh("example.stl", threads=8)
To get a pyvista.PolyData directly:
>>> import pyvista_stl
>>> mesh = pyvista_stl.read_as_mesh('example.stl')
>>> mesh
PolyData (0x7f43063ec700)
N Cells: 1280000
N Points: 641601
N Strips: 0
X Bounds: -5.000e-01, 5.000e-01
Y Bounds: -5.000e-01, 5.000e-01
Z Bounds: -5.551e-17, 5.551e-17
N Arrays: 0
With pyvista >= 0.49 installed, pyvista.read automatically dispatches .stl files to pyvista_stl via the pyvista.readers.override entry point:
>>> import pyvista as pv
>>> mesh = pv.read("example.stl") # uses pyvista_stl
PyVista reads .stl natively, so this entry point replaces a built-in reader and is declared in the pyvista.readers.override group, which states that intent. On pyvista 0.48 the group is unknown and ignored: pyvista.read keeps using the built-in STL reader, and pyvista_stl.read_as_mesh can still be called directly.
Benchmarks
Reading a 1,002,001-point STL (pyvista.Plane(i_resolution=250, j_resolution=250).triangulate().subdivide(2), 2,000,000 triangles), median of 5 runs on a 16-core Ryzen 9 8945HS. The two right-hand columns show how much faster pyvista-stl is than the reader in that row, in single-threaded (threads=1, the default) and multi-threaded (threads=0, all cores) configurations.
Binary STL (~95 MB on disk):
Reader |
Time (seconds) |
pyvista-stl ST speedup |
pyvista-stl MT speedup |
|---|---|---|---|
pyvista-stl (1 thread) |
0.100 |
(baseline) |
2.0x slower |
pyvista-stl (16 thr.) |
0.051 |
2.0x faster |
(baseline) |
numpy-stl |
0.206 [1] |
2.1x faster |
4.0x faster |
pyvista (VTK) |
1.080 |
10.8x faster |
21.2x faster |
meshio |
3.041 |
30.5x faster |
59.7x faster |
ASCII STL (~425 MB on disk):
Reader |
Time (seconds) |
pyvista-stl ST speedup |
pyvista-stl MT speedup |
|---|---|---|---|
pyvista-stl (1 thread) |
0.388 |
(baseline) |
3.4x slower |
pyvista-stl (16 thr.) |
0.114 |
3.4x faster |
(baseline) |
pyvista (VTK) |
2.761 |
7.1x faster |
24.2x faster |
meshio |
9.464 |
24.4x faster |
83.0x faster |
Across the fixture corpus in benchmarks/bench.py (binary and ASCII files from a few KB to roughly 100 MB), single-threaded pyvista-stl is a median of 10.6x faster than VTK, ranging from 4.0x to 134.3x, and is never slower than VTK on any tested file. The multi-threaded path widens the gap further on the larger ASCII files.
Reproduce these numbers with the script in benchmarks/:
python benchmarks/make_readme_figures.py
Comparison with VTK across mesh sizes
The gap widens with file size. pyvista-stl scales near-linearly on both the single-threaded and multi-threaded paths; VTK’s reader scales super-linearly. By the time the mesh reaches ~10 M points (~20 M triangles, ~1 GB binary), pyvista-stl is roughly 65x faster single-threaded and 160x faster multi-threaded:
Same data on log-log axes:
Configuration
The threads keyword argument on read and read_as_mesh controls worker concurrency:
threads=1 (default): single-threaded, deterministic vertex ordering. The safest choice for embedded/server use.
threads=N (N >= 2): use N workers. Worker counts are capped at 32.
threads=0: auto-select using std::thread::hardware_concurrency().
PYVISTA_STL_MAX_TRIS (environment variable, default: 200_000_000) caps the declared triangle count the reader will accept. Files claiming more triangles than the cap raise RuntimeError before any large allocation, which prevents an attacker-controlled header from forcing multi-GB allocations.
License and acknowledgments
This project began as a wrapper around aki5/libstl; the binary-format reader and the hash-based vertex merge are derived from that library, used under its MIT License.
Significant changes since: mmap-backed input, ASCII reader, a multi-threaded path with a concurrent hashtable, hugepage-backed scratch buffers, and a nanobind interface.
This repository is also licensed under the MIT License.
Support
Please open an issue at pyvista/pyvista-stl if you hit a problem.
Wheel compatibility matrix
| Platform | CPython 3.10 | CPython 3.11 | CPython >=3.12 (abi3) |
|---|---|---|---|
| macosx_10_14_x86_64 | |||
| macosx_11_0_arm64 | |||
| manylinux_2_26_aarch64 | |||
| manylinux_2_27_x86_64 | |||
| manylinux_2_28_aarch64 | |||
| manylinux_2_28_x86_64 | |||
| win_amd64 |