Repository navigation
Releases
vectors (latest)
vectors
Topics: svg, vectorization, rust, cli, wasm, mcp
Live demo: https://hieudoanm.github.io/vectors/
Turn PNG and JPEG images into clean SVG paths — and rasterize SVG back to PNG at any size — entirely offline.
vectors is a classical raster-to-vector tracer written from scratch in Rust. It quantizes an image into a palette, segments pixels into regions, walks their boundaries, simplifies the result, fits Bézier curves, and emits SVG. No machine-learning models, no network calls, no service dependency — and the same input always produces byte-identical output.
The tracer ships as a CLI, an HTTP API, an MCP server, a Rust library (compiled to WebAssembly for the web app), and a Next.js landing page with an in-browser converter.
Table of Contents
- vectors
The Problem
Turning a raster logo back into usable vector paths is usually either an online upload form, a heavyweight design tool, or a black box that changes its output between runs. A practical tracer needs to:
- Accept ordinary PNG and JPEG input without a pre-processing step.
- Handle colour palettes, transparency, holes, and anti-aliasing.
- Produce editable SVG —
pathelements withfill, not an embedded bitmap. - Be deterministic, so a committed fixture or baseline does not churn.
- Work offline as a single binary, a library, and a server, from the same code.
- Go the other way too: render an SVG to a sized PNG for icons and previews.
vectors is built around those constraints.
The Solution
vectors runs one linear pipeline — raster → preprocess → segment → contour → simplify → Bézier → vector model → SVG — where each stage consumes the previous stage's type and knows nothing about how the next one is rendered. The tracer is deterministic by construction (no RNG, no hash-order dependence), and is measured by rasterizing its own SVG output with an independent renderer rather than by eyeballing the paths.
The inverse direction, SVG → PNG, parses the document with resvg, draws it straight into a straight-alpha buffer, and encodes it with image. Width and height are explicit flags, so the same binary can produce a 512 px icon or a 4096 px comparison raster.
Features
Tracing
- Palette and binary modes — median-cut colour quantization (default 8 colours) or a single luminance threshold for black-and-white output.
- 8-connected segmentation — flood fill per region, with an area filter for anti-aliasing specks.
- Proper holes — outer contour plus holes per path, emitted with
fill-rule="evenodd". - Douglas–Peucker simplification — fewer points without moving the shape.
- Least-squares Bézier fitting — fit–measure–split, tunable per pixel.
- Debug artefacts —
--debug-dirwrites the quantized image, regions, curves, and curve SVG so you can see which stage went wrong.
Surfaces
- CLI — one command for a single file; omit the output path to stream SVG to stdout.
- HTTP API —
vectors serveexposes/health,/info, andPOST /vectors(multipart or URL-safe base64), with per-request overrides. - MCP server —
vectors mcp servespeaks newline-delimited JSON-RPC on stdio and advertises avectors_tracetool for agent clients. - Library —
trace,to_svg,Raster,TraceConfig, andSvgOptions;VectorImageis SVG-independent so another serializer is additive. - Web app — a Next.js landing page with an in-browser converter that runs the same tracer compiled to WebAssembly; conversion happens entirely client-side.
SVG → PNG
- Exact or proportional sizing —
--widthalone preserves the aspect ratio;--widthand--heighttogether set the exact grid. - Transparent by default — add
--background '#ffffff'to composite an opaque backdrop. - No extra tooling —
resvgcomes from the module'srasterfeature, not from a system package.
Quality harness (optional)
- Measured accuracy —
--features evaladdseval,baseline,golden, andsweepsubcommands that rasterize the emitted SVG, compare it to the source, and score MAE, PSNR, SSIM, edge agreement, primitives, and bytes. - Committed baselines — one row per image, so a regression names the image that broke instead of hiding in an average.
- Parameter sweeps — explore
colors,threshold, tolerances, and area limits across the golden suite.
Key Benefits
- Offline and private — a static binary with no network calls; images never leave the machine.
- Deterministic — identical input yields byte-identical SVG, which makes fixtures, baselines, and diffs meaningful.
- One pipeline, many surfaces — CLI, HTTP, MCP, library, and desktop all call the same
trace; the surfaces cannot drift. - Editable output — real
<path>geometry with fills and holes, not a bitmap wrapped in SVG. - Bidirectional — trace raster to SVG and render SVG back to PNG, including sized exports.
- Honest quality claims — accuracy is measured against a committed baseline by rasterizing the actual emitted SVG.
Get Started
Prerequisites
- Rust 1.88 or newer (for the CLI and library).
- Node.js 26.12.1 and pnpm 12.15.0 (only for the landing page and workspace scripts).
Build the CLI
git clone https://github.com/hieudoanm/vectors.git
cd vectors/crates/vectors-cli
cargo build --release
../target/release/vectors --helpThe release binary lands at crates/target/release/vectors (the workspace target). A prebuilt vectors
binary is also attached to the rolling vectors-latest
release; crates/vectors-cli/scripts/install.sh downloads it.
Convert your first image
vectors logo.png logo.svg
vectors logo.png | pango-view - # SVG to stdout
vectors logo.svg logo.png --width 512 # back to a sized PNGBuild everything
pnpm install
pnpm build # turbo: Rust crates and web
pnpm lint
pnpm test
pnpm formatCLI
vectors [OPTIONS] [INPUT] [OUTPUT]
vectors [OPTIONS] [INPUT] [OUTPUT] <COMMAND>
The output extension decides the format: PNG/JPEG input traces to .svg, while a
.png output rasterizes the result (and is the only valid output for an SVG
input). Omit the output path to print SVG to stdout.
| Flag | What it does |
|---|---|
-c, --colors <N> |
Palette size; 1 or 2 selects black-and-white tracing (default 8). |
-t, --threshold <L> |
Luminance cut for binary mode (default 128). |
-d, --detail <full|balanced|bold> |
How aggressively small shapes are dropped. |
--min-area <PX> |
Ignore regions smaller than this; floored by --detail. |
--simplify-tolerance <PX> |
Douglas–Peucker tolerance (default 1). |
--bezier-tolerance <PX> |
Maximum Bézier deviation (default 0.5). |
--backdrop <COLOR> |
Backdrop under transparent pixels during tracing (default #ffffff). |
--background-index <N> |
Palette index treated as background and never traced. |
--precision <N> |
Decimal places in emitted coordinates (default 2). |
--width <PX>, --height <PX> |
Target PNG size; width alone keeps the aspect ratio. |
--background <COLOR> |
Opaque backdrop when writing a PNG (default: keep alpha). |
--debug-dir <DIR> |
Write per-stage debug PNGs. |
-q, --quiet |
Suppress the summary written to stderr. |
Subcommands: info (palette without tracing), serve (HTTP), mcp serve
(stdio JSON-RPC), plus the feature-gated eval, baseline, golden, and
sweep.
Examples
vectors icon.png icon.svg --colors 4
vectors photo.jpg out.svg --colors 1 --threshold 140
vectors logo.png logo.svg --detail bold --debug-dir ./trace
vectors logo.png logo.png --width 1024
vectors logo.svg logo.png --width 512 --height 512 --background '#ffffff'
vectors info logo.png
vectors serve --bind 0.0.0.0:8080
vectors mcp se...
