Fast ThumbHash encode/decode – Rust-powered Python bindings
Project description
thumbhash
Fast ThumbHash encode/decode for Python — Rust-powered, zero mandatory dependencies.
A modern, OSS drop-in replacement for fast-thumbhash,
built with maturin and PyO3.
- ✅ Python 3.10 – 3.14 (including free-threaded
3.13t/3.14t) - ✅ Pillow > 11 integration (optional)
- ✅ Typed (
py.typed+.pyistubs) - ✅ Pure-Rust core — no C extensions, no NumPy required
Installation
pip install thumbleweed
# with Pillow helpers:
pip install "thumbleweed[pillow]"
Quick-start
Raw RGBA bytes
import thumbleweed as tw
# Encode -------------------------------------------------------------------
# rgba_bytes must be a bytes/bytearray of length w*h*4 (R G B A, non-premult.)
hash_bytes: bytes = tw.encode(w, h, rgba_bytes)
# Decode -------------------------------------------------------------------
w_out, h_out, rgba_out = tw.decode(hash_bytes)
# Helpers ------------------------------------------------------------------
r, g, b, a = tw.average_rgba(hash_bytes) # dominant colour [0, 1]
ratio = tw.approximate_aspect_ratio(hash_bytes) # width / height
Pillow images
from PIL import Image
import thumbleweed as tw
img = Image.open("photo.jpg")
hash_bytes = tw.encode_image(img) # any mode, any size
placeholder: Image.Image = tw.decode_image(hash_bytes) # RGBA, ≈32 px
placeholder.save("placeholder.png")
encode_image automatically converts the image to RGBA and shrinks it so
the longest side is ≤ 100 px (the algorithm's hard limit).
API reference
| Function | Description |
|---|---|
encode(w, h, rgba) → bytes |
Encode raw RGBA bytes → ThumbHash |
decode(hash) → (w, h, rgba) |
Decode ThumbHash → raw RGBA bytes |
average_rgba(hash) → (r,g,b,a) |
Dominant colour in [0, 1] |
approximate_aspect_ratio(hash) → float |
Width / height of the original image |
encode_image(img) → bytes |
Encode a Pillow Image (requires Pillow) |
decode_image(hash) → Image |
Decode a ThumbHash to a Pillow Image (requires Pillow) |
Building from source
Prerequisites
| Tool | Version | Install |
|---|---|---|
| Rust (stable) | ≥ 1.75 (tested with 1.95.0) | curl https://sh.rustup.rs -sSf | sh |
| Python | 3.9 – 3.14 | system / pyenv |
| maturin | ≥ 1.7, < 2 | pip install "maturin>=1.7,<2" |
Ubuntu 26.04 / Python 3.14 note — Ubuntu 26.04 ships Python 3.14. The steps below work unchanged; maturin detects the interpreter automatically.
1 — Clone and enter the project
git clone https://github.com/New-Elysium/thumbleweed.git
cd thumbleweed
2 — Set up a virtual environment
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install "maturin>=1.10,<2" "Pillow>12" pytest
3 — Development build (editable)
Compiles the Rust extension and installs the package in editable mode so
that changes to the Python files in python/thumbhash/ take effect
immediately without a rebuild.
maturin develop --release
4 — Run the tests
pytest -v
5 — Build release wheels
Local wheel (current platform only)
maturin build --release
# wheel lands in target/wheels/thumbhash-*.whl
pip install target/wheels/thumbhash-*.whl
Portable manylinux wheels (for PyPI)
Use the official maturin Docker image to build wheels that are compatible with virtually every Linux distribution:
docker run --rm \
-v "$(pwd)":/io \
-w /io \
ghcr.io/pyo3/maturin:latest \
build --release --out dist
Wheels for each CPython version land in dist/.
macOS (universal2)
rustup target add aarch64-apple-darwin x86_64-apple-darwin
maturin build --release --target universal2-apple-darwin
Windows
maturin build --release
Free-threaded Python 3.13t / 3.14t
maturin ≥ 1.7 detects the free-threaded interpreter automatically.
Just activate the python3.14t (or python3.13t) virtual environment and
run maturin develop or maturin build as normal.
Publishing to PyPI
One-time setup
pip install twine
# Create a PyPI account and generate an API token at https://pypi.org/manage/account/token/
Store the token in ~/.pypirc:
[pypi]
username = __token__
password = pypi-<your-token-here>
Or export it as an environment variable:
export MATURIN_PYPI_TOKEN=pypi-<your-token-here>
Build all wheels with maturin publish (recommended)
maturin publish builds and uploads in one step. It uses
cibuildwheel conventions internally when
run in CI.
maturin publish --token pypi-<your-token-here>
Manual upload with twine
If you built wheels separately (e.g. via the Docker approach above):
twine upload dist/*.whl dist/*.tar.gz
CI / GitHub Actions (recommended for cross-platform wheels)
Create .github/workflows/release.yml:
name: Release
on:
push:
tags: ["v*"]
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.x"
- uses: PyO3/maturin-action@v1
with:
command: build
args: --release --out dist
manylinux: auto # builds manylinux wheels on Linux
- uses: actions/upload-artifact@v4
with:
name: wheels-${{ matrix.os }}
path: dist
publish:
needs: build
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write # OIDC trusted publishing
steps:
- uses: actions/download-artifact@v4
with:
pattern: wheels-*
merge-multiple: true
path: dist
- uses: pypa/gh-action-pypi-publish@release/v1
# Trusted publishing — no API token needed when configured on PyPI
Tip: Enable Trusted Publishing on PyPI to avoid managing API tokens in GitHub secrets.
Project layout
thumbhash-py/
├── Cargo.toml # Rust crate manifest
├── pyproject.toml # maturin + PEP 621 metadata
├── README.md
├── src/
│ └── lib.rs # Rust implementation + PyO3 bindings
├── python/
│ └── thumbhash/
│ ├── __init__.py # Public Python API + Pillow helpers
│ ├── _core.pyi # Type stubs for the compiled extension
│ └── py.typed # PEP 561 marker
└── tests/
└── test_thumbhash.py
Algorithm
ThumbHash is a compact image placeholder algorithm by Evan Wallace. It encodes images using a discrete cosine transform (DCT) into a small byte string (typically 5–32 bytes) that decodes to a smooth, colourful placeholder.
Compared to BlurHash it supports a more accurate aspect ratio, alpha transparency, and better colour fidelity.
Licence
MIT — see LICENCE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file thumbleweed-0.1.1.tar.gz.
File metadata
- Download URL: thumbleweed-0.1.1.tar.gz
- Upload date:
- Size: 274.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
acfaf6055f491cfceadfc218e4b2939e2d5377b71dfcb983f04444a24088dd3d
|
|
| MD5 |
60add0ce43273b8dedd76c378e2415a1
|
|
| BLAKE2b-256 |
5fe6c868d9f90f2762e921ac774e49dc34971ae85da5399926c9a687f2895cd9
|
File details
Details for the file thumbleweed-0.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: thumbleweed-0.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 203.2 kB
- Tags: CPython 3.14t, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
da31f9335856809073c699ce73238ab35034604fa7f70db18bd0508bddeafb8b
|
|
| MD5 |
d095a3257e4f56c8a8fb71da5de2d225
|
|
| BLAKE2b-256 |
10583ae813aaa69dfa8586950effe6726a3fbe9fb031fb22ec11675cf436bfe4
|
File details
Details for the file thumbleweed-0.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: thumbleweed-0.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 204.1 kB
- Tags: CPython 3.14, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b932e8b6e65165f178347ae1b23ca7725626c823b25a1d8c3a3ca9435d3226bc
|
|
| MD5 |
547e3d418e8eb9816364ccca9b32bc38
|
|
| BLAKE2b-256 |
33693cfb913bce0748ba26a5829c0862266943f9f15c54fc4bd4b8833ba08196
|
File details
Details for the file thumbleweed-0.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: thumbleweed-0.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 203.5 kB
- Tags: CPython 3.13t, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
63b2c35a3d8520ef7e3ec26bb94d45bf8a9d69cac28a8b5d0be355d017f3477f
|
|
| MD5 |
744221902cc400744b347c84ef4e1f2e
|
|
| BLAKE2b-256 |
b6a3b8c1fc8b812e414d3b7004edd8adc4f11717e885eab49d19642e4599bd74
|
File details
Details for the file thumbleweed-0.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: thumbleweed-0.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 204.2 kB
- Tags: CPython 3.13, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aed2be1a2c1df03e34100b7555736ba0e57739a9d815b62a07f0df4211e8a60e
|
|
| MD5 |
afdb3d9a8d0a57e50e4fe379bb9db6b0
|
|
| BLAKE2b-256 |
61beb49016a8c0cf933feee613b315f4e9fef9edf6b5a6cb1b61b35da89d7eaf
|
File details
Details for the file thumbleweed-0.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: thumbleweed-0.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 203.9 kB
- Tags: CPython 3.12, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
78704769626864de0b0c8839e9a9c7985a4e629637916765f94cd1752c58cc35
|
|
| MD5 |
ebce4792d75cb70d0225d985e9f1fcae
|
|
| BLAKE2b-256 |
3ffe4f79d3f84f63cd71bf737f44051317c8664fc2eed7fdeaf18ae2e805b8f3
|
File details
Details for the file thumbleweed-0.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: thumbleweed-0.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 204.5 kB
- Tags: CPython 3.11, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
491528f037d8b35ebfd647df8a2ab72391b2746a0ef9bdb4845336efa0246d73
|
|
| MD5 |
2f5e528b09693338de6a0e8fd582dfbc
|
|
| BLAKE2b-256 |
14d801a935ff5b0767d83c7bb7e2940cff668604873a0915437f50d4d059fdb9
|
File details
Details for the file thumbleweed-0.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: thumbleweed-0.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 204.6 kB
- Tags: CPython 3.10, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
239666d8b5cd66833151636a2afce287ddbe34e04c01375453d4f79ad9c2c44d
|
|
| MD5 |
42276e1d4df32f61fe772f0dcd276013
|
|
| BLAKE2b-256 |
ddcc2676baf8754276fd246d544759b960fd1f1436d1c60348f95f6cc84bc74c
|