pyvista-stl 0.5.3


pip install pyvista-stl

  Latest version

Released: Sep 02, 2026


Meta
Author: PyVista Developers
Requires Python: >=3.10

Classifiers

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

pypi MIT

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:

https://github.com/pyvista/pyvista-stl/raw/main/bench0.png

Same data on log-log axes:

https://github.com/pyvista/pyvista-stl/raw/main/bench1.png

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.

Extras:
Dependencies:
numpy