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.
- 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).
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.image_to_pixel_art_wasm.js
– a tiny ES module wrapper generated bywasm-bindgen
.
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).
<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.
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.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 worldwasm-bindgen
requirescdylib
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.
This project is released under the MIT license.
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.