WASM Image Filter
Build image filters in Rust (grayscale, brightness, blur, sharpen, edge detection) and run them in the web browser by compiling them to WebAssembly. A small web page lets you load a photo, pick filters with sliders, and see the result instantly, with all the processing done by your Rust code on the user's device. The same filter code is also used by a native command-line tool that uses Tokio to process a whole folder of images at once, and filter settings are described as JSON presets read with Serde. Every filter is deterministic: the same input always produces exactly the same output. You'll learn to structure a Rust workspace, pixel math and convolution, wasm-bindgen and wasm-pack, passing data efficiently between JavaScript and Rust, and testing numerical code.
- Estimate
- ~15h
- Steps
- 7
- Completed by
- 0
- Proposed by
- codeseed.app
Cargo · Tokio · Serde
Project roadmap
- 01
Set up the workspace
~1.5hCreate a Cargo workspace with three crates: filters (a pure library with no dependencies on the web or the file system), filters-wasm (a cdylib using wasm-bindgen) and filters-cli (a binary). The filters crate works on a simple Image struct holding the width, height and RGBA bytes. Done when: cargo build --workspace compiles all three crates.
- 02
Write the basic filters
~2.5hIn the filters crate, implement grayscale, brightness (from -100 to 100) and contrast, working in place on the RGBA buffer. Keep alpha untouched and clamp results to 0 to 255. Add invert and a threshold filter. Done when: unit tests on small hand-made images check the exact output bytes of each filter.
- 03
Add convolution filters
~3hWrite a generic convolve(image, kernel) function that applies a 3×3 or 5×5 kernel, handling the image edges by repeating the edge pixels. Use it for box blur, Gaussian blur, sharpen and Sobel edge detection (combining the horizontal and vertical passes). Write to a new buffer, not in place, since every output pixel must read the original neighbors. Done when: tests confirm that blurring a solid color changes nothing, and that edges are detected on a simple test image.
- 04
Describe presets with Serde
~1.5hDefine a Pipeline type: a list of filter steps with their parameters, deserialized from JSON with Serde, for example [{"filter":"brightness","amount":20},{"filter":"sharpen"}]. Validate parameter ranges and return clear errors for unknown filters or invalid values. Done when: a JSON preset applies its filters in order, and an invalid preset returns a readable error.
- 05
Compile to WebAssembly
~2.5hIn filters-wasm, expose an apply_pipeline(pixels: &mut [u8], width, height, preset_json) function with #[wasm_bindgen], so it works directly on the canvas's pixel buffer without extra copies. Build it with wasm-pack build --target web. Return errors to JavaScript as exceptions with clear messages. Done when: wasm-pack produces a pkg/ folder, and calling the function from a test page works.
- 06
Build the web page
~2hCreate a web/ folder with a page that loads an image into a canvas, reads its pixels with getImageData, and shows sliders and toggles for the filters. When a control changes, build the preset JSON, call the WebAssembly function and draw the result with putImageData. Add a download button that saves the result as PNG. Done when: filters apply instantly to a loaded photo in the browser.
- 07
Batch process with Tokio
~2hIn filters-cli, read a folder of PNG or JPEG files (decoded with the image crate), apply a preset JSON file to each, and write the results to an output folder. Process several images at once with Tokio, running the CPU-heavy filtering in spawn_blocking tasks and limiting concurrency with a semaphore. Done when: filters-cli --preset p.json in/ out/ processes a folder, and the output matches the web version for the same image and preset.
Resources
Ready to build this?
Get a GitHub repo and start building. Your AI reviewer checks each step as you go.
Tech stack