A comprehensive Rust implementation of Robert Ulichney's void-and-cluster algorithm for generating blue noise textures and applying high-quality dithering to images. Blue noise produces evenly distributed, visually pleasing dithered images without the clustering artifacts of white noise or the repetitive patterns of Bayer dithering.
✨ Features
- 🎨 Generate blue noise textures using the void-and-cluster algorithm
- 🖼️ Dither images with customizable colors and contrast
- ⚡ FFT-optimized Gaussian blur for power-of-two dimensions (~50% faster)
- 🔄 Seamless tiling - all textures use toroidal topology
- 🎲 Reproducible - seeded random number generation
- 📊 Progress indicators for long-running operations
- 🦀 Pure Rust - fast, safe, and dependency-minimal
📦 Installation
From crates.io
From source
The binary will be in target/release/blue-noise.
🚀 Usage
CLI Tool
Generate Blue Noise Textures
# Generate a 128x128 blue noise texture
# Generate with custom sigma and seed for reproducibility
# Quick 64x64 texture (2-5 seconds)
# Larger 256x256 texture (several minutes)
Options:
-s, --size <SIZE>- Texture size (8-512, default: 128)-o, --output <PATH>- Output file path (default: blue-noise.png)--sigma <SIGMA>- Gaussian sigma (1.0-3.0, default: 1.9)--seed <SEED>- Random seed for reproducibility-v, --verbose- Show detailed progress
Dither Images
# Basic dithering
# Custom colors
# Resize and adjust contrast
# Use custom noise texture
Options:
-i, --input <PATH>- Input image path (required)-o, --output <PATH>- Output image path (required)-n, --noise <PATH>- Blue noise texture (default: blue-noise.png)-f, --foreground <HEX>- Foreground color (default: #000000)-b, --background <HEX>- Background color (default: #ffffff)-w, --width <PIXELS>- Output width (maintains aspect ratio)--height <PIXELS>- Output height (maintains aspect ratio)-c, --contrast <FLOAT>- Contrast adjustment (1.0 = normal)
Library Usage
Add to your Cargo.toml:
[]
= "0.2"
Generate Blue Noise
use ;
// Create configuration
let config = BlueNoiseConfig ;
// Generate texture
let generator = new?;
let result = generator.generate?;
// Save to file
save_blue_noise_to_png?;
Apply Dithering
use ;
// Load blue noise texture
let noise = load?;
// Configure dithering
let options = DitherOptions ;
// Apply dithering
apply_dithering?;
🎨 Examples
Before and After


Different Colors
# Sepia tone
# Blue on white
# Green matrix style
🔬 Algorithm
This implementation uses Ulichney's void-and-cluster algorithm, which produces high-quality blue noise with evenly distributed energy at high frequencies and minimal low-frequency content.
Why Blue Noise?
- White noise: Random distribution creates visible clusters and voids
- Bayer dithering: Regular patterns create repetitive artifacts
- Blue noise: Evenly distributed with minimal low-frequency patterns ✓
Generation Process
- Phase 0: Generate initial binary pattern with random points
- Phase 1: Serialize initial points by removing from tightest clusters
- Phase 2: Fill to half capacity by adding to largest voids
- Phase 3: Invert and fill to completion
- Phase 4: Convert ranks to threshold map (0-255)
The algorithm uses toroidal topology (wraparound edges) to ensure seamless tiling, making it perfect for dithering large images with small noise textures.
Performance
For power-of-two dimensions, Gaussian blur is performed in the frequency domain using FFT, providing ~50% performance improvement through the convolution theorem:
convolution(A, B) = IFFT(FFT(A) × FFT(B))
Generation times:
- 64×64: ~2-5 seconds
- 128×128: ~30-60 seconds
- 256×256: Several minutes
Tip: Pre-generate textures rather than generating at runtime.
📚 References
-
Ulichney, R. (1993). "Void-and-cluster method for dither array generation" Proceedings of SPIE 1913, Human Vision, Visual Processing, and Digital Display IV https://doi.org/10.1117/12.152707
-
Ulichney, R. (1988). "Dithering with blue noise" Proceedings of the IEEE, 76(1), 56-79
🛠️ Development
# Build
# Run tests
# Run benchmarks
# Generate documentation
📄 License
MIT License - see LICENSE for details
🙏 Acknowledgments
- Robert Ulichney for the void-and-cluster algorithm
- Surma's Ditherpunk for inspiration
- TIGSource discussion
🤝 Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.