Dithering algorithms for e-paper/e-ink displays. Rust core with Python and JavaScript/TypeScript bindings.
Dithering algorithms (Spectra 6-color, Marienplatz U-Bahn station):
Color schemes (Burkes, river scene):
DBS refinement (Spectra 6-color, auto tone + gamut). Direct Binary Search optimizes the dithered pixels against a model of the eye at a given viewing distance: here it recovers the blue sky that plain error diffusion washes out to grey. The zoomed crops show the trade-off up close, including a few colored specks where it mixes inks. It is opt-in and slower (about 0.3 s per 800×480 panel on a multi-core machine, vs. ~30 ms).
Published to PyPI as epaper-dithering.
pip install epaper-ditheringfrom PIL import Image
from epaper_dithering import dither_image, ColorScheme, DitherMode, SPECTRA_7_3_6COLOR_V2
img = Image.open("photo.jpg")
# Idealized palette
dithered = dither_image(img, ColorScheme.BWR, mode=DitherMode.FLOYD_STEINBERG)
# Measured palette — calibrated color matching
dithered = dither_image(img, SPECTRA_7_3_6COLOR_V2)
# Opt in to automatic tone + gamut compression for photos
dithered = dither_image(img, SPECTRA_7_3_6COLOR_V2, tone="auto", gamut="auto")
# Opt in to DBS refinement (slower; optimizes for how the image is perceived)
dithered = dither_image(img, SPECTRA_7_3_6COLOR_V2, dbs=True)See packages/python/README.md for full documentation.
Published to npm as @opendisplay/epaper-dithering.
npm install @opendisplay/epaper-ditheringimport { ditherImage, ColorScheme, DitherMode, SPECTRA_7_3_6COLOR_V2 } from '@opendisplay/epaper-dithering';
// ImageBuffer from Canvas API or Node.js (sharp, etc.)
const dithered = ditherImage(imageBuffer, ColorScheme.BWR, { mode: DitherMode.BURKES });
// Measured palette — calibrated color matching
const dithered = ditherImage(imageBuffer, SPECTRA_7_3_6COLOR_V2);
// Opt in to automatic tone + gamut compression for photos
const compressed = ditherImage(imageBuffer, SPECTRA_7_3_6COLOR_V2, { tone: 'auto', gamut: 'auto' });
// Opt in to DBS refinement (slower; run it in a Web Worker in the browser)
const refined = ditherImage(imageBuffer, SPECTRA_7_3_6COLOR_V2, { dbs: true });See packages/javascript/README.md for full documentation.
- Rust Core: All dithering logic in
packages/rust/core/— shared by both packages - 10 Dithering Algorithms: NONE, ORDERED, BURKES, FLOYD_STEINBERG, ATKINSON, STUCKI, SIERRA, SIERRA_LITE, JARVIS_JUDICE_NINKE, DIZZY
- 10 Color Schemes: MONO, BWR, BWY, BWRY, BWGBRY (Spectra 6), GRAYSCALE_4, GRAYSCALE_16, SEVEN_COLOR (Spectra/ACeP 7), BWGBRY_SPLIT, GRAYSCALE_8 (library-local, e.g. Inkplate 10 — not a firmware wire value)
- Measured Palettes: Calibrated RGB values for real displays, linked to their canonical firmware palette
- OKLab Color Matching: Weighted Cartesian OKLab — preserves hue without the achromatic-attractor bug of LCH-weighted approaches
- Pre-dither Knobs: Per-image exposure, saturation, shadows, highlights, dynamic-range (tone) compression, and gamut compression — all orthogonal
- DBS Refinement: Optional Direct Binary Search pass that optimizes the dithered pixels for how they are perceived at a given viewing distance and panel resolution — opt-in, slower
epaper-dithering/
├── packages/
│ ├── rust/
│ │ ├── core/ # Shared Rust algorithms (OKLab, dithering, tone/gamut)
│ │ └── wasm/ # wasm-bindgen bindings for JavaScript
│ ├── python/ # PyO3/maturin extension + Python wrapper
│ └── javascript/ # TypeScript wrapper + bundled WASM
├── docs/ # Calibration and color science documentation
└── .github/workflows/ # CI for all packages
# Python (requires Rust toolchain: https://rustup.rs)
cd packages/python
uv sync --all-extras
uv run maturin develop --release
uv run pytest tests/ -v
# JavaScript (requires wasm-pack and the wasm32-unknown-unknown target)
wasm-pack build packages/rust/wasm --target web --out-dir ../../javascript/src/wasm-core
cd packages/javascript
bun install
bun run test
# Rust
cd packages/rust
cargo test --workspace- py-opendisplay: Python library for OpenDisplay BLE e-paper devices
- hass-eink-dashboard: Home Assistant custom component that renders e-ink dashboard images for Kindle, TRMNL, and OpenDisplay devices.
- OpenDithering: Browser tool by Guy Sie for previewing dithering on e-paper palettes and uploading images to OpenDisplay devices over Bluetooth (source). An independent implementation, so results differ from this library; our DBS refinement is ported from it.



