Live UI
Try the pixel-art converter instantly at https://gametorch.app/image-to-pixel-art
Free forever · no sign-up required · runs 100 % in your browser
A tiny Rust → WebAssembly library that turns any raster image into low-color pixel-art.
Features
K-means palette extraction with user-selectable color count or supply your own palette.
supply your own palette. Keeps transparency intact – only opaque pixels are processed.
Down-samples to a fixed tile grid (e.g. 64 × 64) using nearest-neighbour then scales back up – aspect-ratio preserved.
Pure client-side: the heavy lifting happens completely inside your browser thanks to WASM.
The public API exported via wasm-bindgen :
// palette is optional: string[ ] of 6-char HEX, e.g. ["FFAA00", "112233"] // returns { image: Uint8Array, palette: string[ ] } function pixelate ( input : Uint8Array , n_colors : number , scale : number , output_size ?: number , palette ?: string [ ] ) : { image : Uint8Array ; palette : string [ ] } ;
It returns PNG-encoded bytes plus the palette actually used (either supplied or discovered).
Building the WebAssembly bundle
Prerequisites
Rust nightly or stable (≥ 1.70). wasm-pack ( cargo install wasm-pack ).
Compile:
wasm-pack build --release --target web
Under pkg/ you will get:
image_to_pixel_art_wasm_bg.wasm – the WebAssembly binary.
– the WebAssembly binary. image_to_pixel_art_wasm.js – a tiny ES module wrapper generated by wasm-bindgen .
Building with plain cargo
If you prefer not to use wasm-pack , compile directly via Cargo after setting the getrandom backend flag:
# Unix RUSTFLAGS= ' --cfg getrandom_backend="wasm_js" ' \ cargo build --release --target wasm32-unknown-unknown # Windows (PowerShell) $Env :RUSTFLAGS= ' --cfg getrandom_backend="wasm_js" ' cargo build --release --target wasm32-unknown-unknown
This produces the raw WebAssembly binary at
target/wasm32-unknown-unknown/release/image_to_pixel_art_wasm.wasm
You will then need to run the wasm-bindgen CLI to create the JavaScript glue files (equivalent to what wasm-pack did automatically):
wasm-bindgen --target web --out-dir ./pkg \ target/wasm32-unknown-unknown/release/image_to_pixel_art_wasm.wasm
After this step the pkg/ directory will contain the same two files described earlier ( *.wasm and the corresponding ES-module wrapper).
Using from vanilla JavaScript / TypeScript
< script type =" module " > import init , { pixelate } from './pkg/image_to_pixel_art_wasm.js' ; // Wait for WASM to finish loading await init ( ) ; const file = await fetch ( 'my_photo.jpg' ) . then ( r => r . arrayBuffer ( ) ) ; const pngBytes = pixelate ( new Uint8Array ( file ) , /* n_colors */ 8 , /* scale */ 64 ) ; const blob = new Blob ( [ pngBytes ] , { type : 'image/png' } ) ; document . getElementById ( 'out' ) . src = URL . createObjectURL ( blob ) ; script > < img id =" out " />
No bundler required — modern browsers understand ES modules & WASM directly.
crate-type = ["cdylib", "rlib"] — what & why?
Rust's crate type controls which artifacts Cargo builds:
rlib – Rust static library. Other Rust crates can link to it, enabling unit tests, benches or workspace integration. This is the default for library crates.
– Rust static library. Other Rust crates can link to it, enabling unit tests, benches or workspace integration. This is the default for library crates. cdylib – C dynamic library. It strips out Rust-specific metadata, producing a clean binary that can be loaded by foreign tool-chains. In the WASM world wasm-bindgen requires cdylib so it can massage the output into a .wasm file plus the JS glue code.
By declaring both we keep the crate usable as a normal Rust dependency and ready for WebAssembly packaging.
License
This project is released under the MIT license.
CLI Tool and Bulk Conversions
If you prefer a native command-line workflow (and want to batch-process many files) you can build the optional pixelate-cli binary.
Build it (native only – no WASM):
cargo build --release --features native-bin --bin pixelate-cli # binary at target/release/pixelate-cli
Usage overview:
pixelate-cli < INPUTS > ... [options] OPTIONS -k, --n-colors < N > Number of colors for k-means (ignored when you supply a palette) [default: 8] -s, --scale < S > Down-sample size for the longest side [default: 64] --output-size < S > Final upscale size (longest side). Defaults to the original dimensions. -c, --palette < HEX,… > Comma-separated list of 6-char hex colors to use instead of running k-means. -d, --out-dir < DIR > Directory to write results into (keeps original stems). -p, --prefix < STR > Prefix for output filenames when not using --out-dir [default: pixelated_]
Examples
# Basic bulk conversion with default options target/release/pixelate-cli photos/ * .jpg # Custom palette & write into `out/` directory target/release/pixelate-cli sprites/ * .png \ --palette " FF0000,00FF00,0000FF " \ --scale 32 \ --out-dir out
Original WASM crate authored entirely with o3 in 30 minutes — see the git commit timestamps for proof.