A fast, self-hostable image toolkit in Rust β two tools, one core.
Turn rasters into clean SVGs, and slice one image into many crops in a single pass. Runs as a web app, an HTTP API, a CLI, a Docker container, a WASM module, or a Rust library.
- The two tools
- Surfaces at a glance
- Quick start
- Web app
- HTTP API
- CLI
- Docker
- WASM / npm (in-browser)
- Rust library
- Presets
- Build from source
- Notes vs. the Python reference
- Roadmap
- License
| Tool | What it does | |
|---|---|---|
| πΌοΈ β π | Vectorize | Raster β SVG with multiple tracing pipelines ("variants"). Generate one, or generate several and compare side by side. Pipeline: preprocess β trace (vtracer) β postprocess. |
| βοΈ | Multicrop | Upload one image, mark multiple bounding boxes, and get one PNG crop per box in a single pass. Boxes that overhang the edge are trimmed, not rejected. |
Why PathSmith
- β‘ Fast β pure-Rust, in-memory pipeline; conversions and crops run in parallel.
- π Self-hostable & offline β one ~44 MB container, no external services, your images never leave your machine.
- π§© One core, many surfaces β the same
pathsmith-corecrate powers the CLI, server, browser (WASM), and any Rust app. - π Open source β AGPL-3.0 (commercial license available).
| Surface | Crate | Best for | Vectorize | Multicrop |
|---|---|---|---|---|
| Web app | pathsmith-server |
point-and-click, drawing crop boxes | β | β |
| HTTP API | pathsmith-server |
scripting, integrating into other apps | β | β |
| CLI | pathsmith-cli |
local/batch/CI, benchmarking | β | β |
| Docker | pathsmith-server |
self-hosting the web app + API | β | β |
| WASM / npm | pathsmith-wasm |
fully client-side, no upload | β | β |
| Rust library | pathsmith-core |
embedding in your own Rust code | β | β |
Note
Multicrop is served by the HTTP API and the library. The CLI and WASM build currently cover Vectorize only.
The fastest way to get the web app and the API is Docker:
docker build -t pathsmith .
docker run -p 8080:8080 pathsmith
# open http://localhost:8080Or run it straight from source (builds core + CLI + server):
cargo run --release -p pathsmith-server # http://0.0.0.0:8080, UI at /Tip
Everything runs locally. Set PATHSMITH_UI=0 (or pass --no-ui) to run a headless, API-only server.
pathsmith-server embeds a single-page UI (bundled into the binary at build time via rust-embed β no separate web build step). It has two tabs:
- Vectorize β drop an image, tick one or more variants, hit Generate. Each result is previewed on a checkerboard, can be opened in a zoom/pan inspector, and downloaded individually or all together as a
.zip. - Multicrop β drop an image, drag rectangles on it to mark crops, optionally name each box, then Generate crops. Download each PNG, or all of them as a
.zip.
cargo run --release -p pathsmith-server # UI + API at http://0.0.0.0:8080
cargo run --release -p pathsmith-server -- --no-ui # API only (or PATHSMITH_UI=0)Configuration (flag / env var):
| Flag | Env | Default | Purpose |
|---|---|---|---|
--port |
PORT |
8080 |
Port to listen on |
--host |
PATHSMITH_HOST |
0.0.0.0 |
Interface to bind |
--no-ui |
PATHSMITH_UI=0 |
UI on | Disable the bundled web UI (pure API) |
--max-bytes |
PATHSMITH_MAX_BYTES |
20 MiB |
Max upload size |
--max-concurrency |
PATHSMITH_MAX_CONCURRENCY |
64 |
Max in-flight conversions |
--timeout-secs |
PATHSMITH_TIMEOUT_SECS |
60 |
Per-request timeout |
The request body is always the raw image bytes (curl --data-binary @img.png, or a browser fetch(url, { method: 'POST', body: file })). Accepts PNG, JPEG, WebP, GIF, and BMP.
| Method & path | Returns |
|---|---|
GET /healthz |
ok |
GET /presets |
[{ name, description }, β¦] |
POST /convert?pipeline=<name> |
image/svg+xml (single variant) |
POST /convert/batch?pipelines=all |
JSON { results: {name:{svg,bytes}}, errors } |
POST /convert/batch?pipelines=β¦&zip=1 |
application/zip of every SVG |
POST /crop?boxes=<json> |
JSON { results: {name:{png,bytes}}, errors } (png is base64) |
POST /crop?boxes=<json>&zip=1 |
application/zip of every PNG crop |
# single variant
curl --data-binary @icons/10_hero.png \
"http://localhost:8080/convert?pipeline=outlined" -o hero.svg
# several variants in one request
curl --data-binary @icons/10_hero.png \
"http://localhost:8080/convert/batch?pipelines=raw,flat,outlined"
# all variants as a zip
curl --data-binary @icons/10_hero.png \
"http://localhost:8080/convert/batch?pipelines=all&zip=1" -o variants.zip
# list variants + descriptions
curl http://localhost:8080/presetsboxes is a URL-encoded JSON array of { x, y, width, height, label? } in source-image pixels. Without a label, crops are named crop-1, crop-2, β¦
# two crops -> JSON with base64 PNGs (results keyed by label)
curl --data-binary @icons/10_hero.png \
'http://localhost:8080/crop?boxes=[{"x":0,"y":0,"width":64,"height":64,"label":"corner"},{"x":80,"y":80,"width":120,"height":120}]'
# all crops as a zip (corner.png, crop-2.png, β¦)
curl --data-binary @icons/10_hero.png \
'http://localhost:8080/crop?zip=1&boxes=[{"x":0,"y":0,"width":64,"height":64,"label":"corner"}]' -o crops.zipcargo build --release # builds core, cli, server
# convert one image with a chosen variant
./target/release/pathsmith convert input.png out.svg --pipeline hybrid
# list available variants
./target/release/pathsmith presets
# benchmark every variant over input/png/ -> output/report.md (parallel;
# renders each SVG back to PNG and scores match% / MAE / SSIM)
./target/release/pathsmith bench --input input/png --out output --tolerance 5The image builds only pathsmith-server; the web UI is embedded into the binary, so the runtime stage is a tiny distroless image (~44 MB, no shell or package manager).
docker build -t pathsmith .
docker run -p 8080:8080 pathsmith # UI + API
docker run -p 8080:8080 -e PATHSMITH_UI=0 pathsmith # API onlyTip
All the server configuration env vars (PORT, PATHSMITH_HOST, PATHSMITH_MAX_BYTES, β¦) work with docker run -e.
Run Vectorize fully client-side β no upload, no server.
wasm-pack build crates/wasm --target web # outputs the npm package to crates/wasm/pkg/import init, { convert, presets } from "pathsmith-wasm";
await init();
const svg = convert(new Uint8Array(await file.arrayBuffer()), "hybrid");See crates/wasm/example/ for a runnable demo. Conversion is CPU-bound and synchronous β run it inside a Web Worker for large images so the UI thread stays responsive.
Embed the pipeline (and multicrop) directly:
[dependencies]
pathsmith-core = { git = "https://github.com/TheAnimatrix/PathSmith" }// Vectorize
let svg = pathsmith_core::convert_bytes(&png_bytes, "hybrid")?;
// Multicrop β one PNG per box
use pathsmith_core::multicrop::CropBox;
let boxes = [CropBox { x: 0, y: 0, width: 64, height: 64, label: Some("corner".into()) }];
for (name, result) in pathsmith_core::multicrop::crop_bytes(&png_bytes, &boxes)? {
let png: Vec<u8> = result?; // write `{name}.png`
}pathsmith-core is pure Rust with no filesystem or platform dependencies, which is why the same crate drives every surface above.
There are 14 Vectorize presets β raw, raw_sealed, smooth, smooth_sealed, flat, hybrid, outlined, mono_lineart, clean_color, max_fidelity, passthrough, β¦ Run pathsmith presets (or GET /presets) for the full list with descriptions.
This is a Cargo workspace: pathsmith-core (library), pathsmith-cli, pathsmith-server, and pathsmith-wasm.
cargo build --release # core + cli + server
cargo test # unit tests (pipeline + multicrop)pathsmith-wasm targets wasm32-unknown-unknown and is built separately with wasm-pack (see WASM).
The original Python tool is preserved under legacy/. The Rust rewrite keeps the same presets and pipeline structure but is not bit-identical: the Rust k-means/bilateral differ from OpenCV's, so verify quality with pathsmith bench (match% and MAE are computed identically; the SSIM column uses a structural-similarity metric for relative comparison). It also drops the temp-file round-trip (in-memory tracing), replaces regex SVG post-processing with a real XML pass, relies on vtracer's path_precision instead of post-hoc rounding, and seeds k-means for reproducibility.
- π Easy result comparison β compare Vectorize outputs side by side (or with a slider / diff) so picking the best variant is quick without opening each preview separately.
- πͺ Remove background (PNG β SVG) β optional background removal in the Vectorize flow so logos and icons trace cleanly without a solid or checkerboard backdrop in the SVG.
- βοΈ SVG editor β tweak and clean up traced output in the browser (paths, colors, nodes) before downloading, instead of re-running with a different preset.
- π― Better raster β SVG β improve the tracing pipelines: sharper edges, fewer/cleaner paths, smaller files, and closer fidelity to the source.
- π SVG in Multicrop β upload SVGs (not just rasters), draw crop boxes in the same UI, and export cropped regions as SVG (or raster) so vector assets can be batched without rasterizing first.
- π€ Auto-detect crop boxes β in Multicrop, propose bounding boxes automatically (detect distinct regions / objects) so you can crop a batch without drawing every box by hand.
AGPL-3.0-or-later β commercial licensing available on request via GitHub. Copyright Β© 2026 TheAnimatrix.