A real-time performance comparison tool demonstrating the speed advantages of Rust + WebAssembly over vanilla JavaScript for image processing operations.
This project implements various image filter algorithms in both Rust (compiled to WebAssembly) and JavaScript, allowing you to directly compare their execution times on the same image. The results typically show significant performance improvements when using WASM, especially for computationally intensive filters.
- Grayscale - Convert images to black and white
- Invert - Invert all colors
- Sepia - Apply a vintage sepia tone
- Brightness - Increase image brightness (1.3x factor)
- Blur - Box blur with configurable radius (5px default)
- Sharpen - Enhance image edges and details
- Edge Detection - Sobel operator for edge detection
Each filter is implemented in both:
- Rust - Compiled to WebAssembly for near-native performance
- JavaScript - Native browser implementation for comparison
WebAssembly typically provides:
- 2-10x faster execution for simple pixel operations (grayscale, invert, sepia)
- 10-50x faster execution for computationally intensive filters (blur, sharpen, edge detection)
- More consistent performance across different browsers
- Better utilization of CPU resources
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh- Clone the repository
git clone https://github.com/Shakib448/rust-pixel
cd rust_pixel- Compile Rust to WebAssembly
wasm-pack build --target web --out-dir pkg- Serve the application
python3 -m http.server 8000
# Or use any other static file server- Open your browser
http://localhost:8000
rust_pixel/
├── src/
│ └── lib.rs # Rust implementations of all filters
├── js-filter.js # JavaScript implementations
├── main.js # UI logic and event handlers
├── index.html # Main interface
├── Cargo.toml # Rust dependencies
├── pkg/ # Generated WASM files (after build)
└── README.md
- Upload an Image - Click the file input and select any image
- Choose a Filter - Click any button to apply a filter
- Compare Performance - Try both WASM (red) and JS (green) versions
- Reset - Click the reset button to restore the original image
For comprehensive performance testing:
- Open
benchmark.htmlin your browser - Upload an image
- Click "Run Full Benchmark" (5 iterations) or choose:
- "Quick Test" - 3 iterations, faster results
- "Thorough Test" - 10 iterations, more accurate
- View detailed results including:
- Median execution times for each filter
- Speedup calculations (WASM vs JS)
- Statistics on WASM wins vs JS wins
- Average speedup across all filters
The Rust code uses wasm-bindgen to expose functions to JavaScript:
#[wasm_bindgen]
pub fn grayscale(data: &mut [u8]) {
// Direct memory manipulation for maximum speed
}Standard Canvas API operations:
export function grayscaleJS(data) {
// Pixel-by-pixel processing
}- LTO (Link Time Optimization): Enabled for cross-function optimizations
- Single codegen unit: Maximum optimization at compile time
- Bulk memory operations: Faster memory operations (fill, copy)
- Non-trapping float-to-int: Hardware-level float conversions
- chunks_exact iterator: SIMD-friendly memory access patterns
- Unsafe optimizations: Removed bounds checks in hot loops where safe
- Zero-copy data sharing: Direct memory access between JS and WASM
- No panic unwinding: Smaller binary size with
panic = "abort"
- Typed arrays: Using Uint8ClampedArray for better performance
- Minimal allocations: Reusing buffers where possible
- Hot loop optimization: Simple, JIT-friendly code patterns
- Cache array length: Store
data.lengthin local variable to avoid repeated property access - Bitwise operations: Use
| 0for fast float-to-int conversion (faster than Math.floor) - Ternary over Math.min/max:
x > 255 ? 255 : xis faster thanMath.min(255, x) - Bit shifting for multiply by 4:
x << 2instead ofx * 4 - Loop unrolling: Manually unrolled kernel operations in sharpen/edge detect
- Pre-computed bounds: Calculate min/max once instead of per-pixel
- Inverse multiplication:
x * invCountfaster thanx / count(one division vs many) - Local variable caching: Reduce property lookups in hot loops
- Flattened array access: Direct index calculation instead of nested lookups
[profile.release]
opt-level = 3 # Maximum optimization
lto = true # Link-time optimization
codegen-units = 1 # Single unit for better optimization
panic = "abort" # No unwinding overhead
strip = true # Remove debug symbols
[package.metadata.wasm-pack.profile.release]
wasm-opt = ["-O3", "--enable-bulk-memory", "--enable-nontrapping-float-to-int"]- Edit
src/lib.rsfor Rust implementations - Edit
js-filter.jsfor JavaScript implementations - Rebuild WASM:
wasm-pack build --target web --out-dir pkg - Refresh browser
- Implement in
src/lib.rswith#[wasm_bindgen] - Implement JS version in
js-filter.js - Add buttons in
index.html - Wire up event handlers in
main.js - Rebuild WASM
- Chrome/Edge 57+
- Firefox 52+
- Safari 11+
- Any modern browser with WebAssembly support
- Use larger images (1000x1000+) for more noticeable performance differences
- Advanced filters (blur, edge detection) show the most dramatic improvements (10-50x)
- Simple filters (grayscale, invert) still show 2-5x speedup
- Warmup runs: First execution may be slower due to JIT compilation
- Use benchmark.html: For accurate, statistical measurements with median times
- Results may vary based on:
- Image size and complexity
- Browser engine (V8, SpiderMonkey, JavaScriptCore)
- CPU architecture and capabilities
- Available memory
- Make sure you're using a release build:
wasm-pack build --release - Test with larger images - overhead dominates on small images
- Use the benchmark page for accurate median measurements
- Check browser console for WASM compilation errors
- Ensure bulk memory is supported by your browser
- WASM modules require compilation on first load
- Browser JIT warmup for JavaScript code
- Use the benchmark tool which includes warmup iterations
MIT
Contributions are welcome! Feel free to:
- Add new image filters
- Optimize existing implementations
- Improve the UI/UX
- Add benchmarking features
- Write tests
- Built with Rust and wasm-bindgen
- Inspired by the need to demonstrate WASM's real-world performance benefits
Made with Rust + WebAssembly