thumbleweed

Rust under the hood · Python on the reins

Five tools. One wheel. Zero dependencies.

thumbleweed unifies ThumbHash, BlurHash, ColorThief, real thumbnails (images, videos, PDFs) and mozjpeg-class compression into a single Python package — powered by a pure-Rust core. No C extensions. No NumPy. No fuss.

Try it in your browser→
0 µs
ThumbHash encode
33× faster than pure Python
5
subsystems
one import away
3.11–3.15
Python versions
incl. free-threaded
0
dependencies
Pillow optional

What's in the wheel

One package that herds the whole flock

Every subsystem is a drop-in replacement for the library it replaces — same import names, same API shapes, just dramatically faster.

ThumbHash

Compact, high-fidelity placeholders

5–32 byte hashes with alpha support. Store one tiny blob per image and render blurry-but-faithful previews instantly.

↻ thumbhash↻ fast-thumbhash

BlurHash

Smooth gradient placeholders

The classic base-83 string format, encoded and decoded entirely in Rust. Drop-in compatible with the blurhash-python API.

↻ blurhash-python

ColorThief

Dominant colour & palettes

Median-cut palette extraction from any image format — bytes, BytesIO, paths or PIL Images. Strict, validated quality range.

↻ colorthief↻ fast-colorthief

Thumbnail

Real thumbnails, any source

Rasterised thumbnails for images, MP4 videos and PDFs. A pure-Rust fallback engine needs no ffmpeg and no pdfium.

↻ no direct equivalent

Compress

mozjpeg-class re-encoding

Optional pixo-powered JPEG & PNG optimisation, auto-applied to thumbnail output. Never returns a larger file — guaranteed by a strict size guard.

↻ pixo feature

Built like farm equipment

  • ✓ GIL released for every CPU-bound call
  • ✓ Fully typed — py.typed + .pyi stubs
  • ✓ 500+ tests across Python & Rust
  • ✓ Wheels for Windows, Linux, macOS — x86 64-bit, ARM64, i686, armv7

— the thumbleweed warranty

Live · runs in your browser

The Rust core, compiled to WebAssembly

This playground runs the actual ThumbHash and BlurHash algorithms from thumbleweed — the same pure-Rust code shipped in the Python wheel, cross-compiled to a 52 KB .wasm module. Nothing is sent to a server.

Loading the Rust module…

Horsepower

Measured against the old workhorses

Mean per-call latency over the real test-fixture images (5 rounds × 100 iterations). Lower is better; bars are log-scaled. Full methodology lives in the repo README.

ThumbHash encodevs thumbhash-python (pure Python)
thumbleweed
825.4 µs
competitor
27.3 ms
⚡ 0.0× faster
ThumbHash decodevs thumbhash-python (pure Python)
thumbleweed
56.4 µs
competitor
5.5 ms
⚡ 0.0× faster
BlurHash encodevs blurhash-python (CFFI)
thumbleweed
5.0 ms
competitor
4.4 ms
~ on par
BlurHash decode 64×64vs blurhash-python (CFFI)
thumbleweed
902.4 µs
competitor
842.2 µs
~ on par
ColorThief dominantvs fast-colorthief (C ext + NumPy)
thumbleweed
5.1 ms
competitor
18.6 ms
⚡ 0.0× faster
ColorThief palette-10vs fast-colorthief (C ext + NumPy)
thumbleweed
5.2 ms
competitor
18.4 ms
⚡ 0.0× faster

ThumbHash & ColorThief timings include full image decode — real end-to-end usage, not just the inner loop.

Saddle up in seconds

Same APIs, Rust speed

If you already use thumbhash, blurhash or colorthief, switching is a one-line change of the import. The higher-level helpers accept Pillow images, raw bytes, BytesIO or file paths — Pillow is only needed for PIL.Image inputs.

# the whole flock, one import
import thumbleweed
from PIL import Image
import thumbhash as th

img = Image.open("photo.jpg")
hash_str = th.encode_image(img)      # base64, ~20-40 chars
placeholder = th.decode_image(hash_str)  # RGBA preview, ≈32 px

# Raw pixel API — no Pillow needed
hash_bytes = th.encode(w, h, rgba_bytes)

Python versions

3.113.123.133.143.14free-threaded3.153.15free-threaded

Pre-built wheels for every version above, built fresh in CI on each release.

Platforms

Linuxx86_64 · aarch64 · i686 · armv7
macOSx86_64 (Intel) · arm64 (Apple Silicon)
Windowsx86_64 · i686 · aarch64

Linux wheels tagged manylinux_2_28; macOS and Windows wheels are universal installs.