@buzz-dee/vtrace
A TypeScript image tracing library that converts ImageData to SVG using a VTracer-backed WebAssembly engine, with configurable tracing options and optional SVG path simplification.
The tracing backend uses VTracer. The WASM runtime is embedded in the distributed JavaScript bundle, so browser and Angular consumers do not need to copy or serve a separate .wasm asset.
Features
- Converts RGBA
ImageDatainto SVG markup or SVG path data. - Uses VTracer for bitmap-to-vector tracing.
- Ships as a self-contained JavaScript package with embedded WASM.
- Works in browser applications without additional WASM asset or MIME-type configuration.
- Supports output scaling, translation, foreground/background colors, thresholding, and speckle filtering.
- Includes optional SVG path simplification utilities.
Runtime Notes
The package is designed for canvas-style workflows where source pixels are available as ImageData:
const imageData = canvas.getContext("2d")!.getImageData(0, 0, width, height);
The embedded WASM approach increases the JavaScript bundle size compared with loading a separate .wasm file, but it avoids runtime failures caused by missing assets, wrong asset URLs, or servers that do not return application/wasm for WASM files.
The public class is named VTrace. Its tracing implementation is VTracer-backed.
Installation
npm install @buzz-dee/vtrace
pnpm add @buzz-dee/vtrace
Usage
import {
VTrace,
SvgPathSimplifier,
type VTraceOptions,
} from "@buzz-dee/vtrace";
const imageData = canvas.getContext("2d")!.getImageData(0, 0, width, height);
const options: VTraceOptions = {
threshold: VTrace.THRESHOLD_AUTO,
turdSize: 2,
optCurve: true,
colorMode: "binary",
mode: "spline",
hierarchical: "cutout",
};
const vtrace = new VTrace(imageData, options);
const svg = vtrace.getSVG();
const pathData = vtrace.getSVGPath({ x: 1, y: 1 }, { x: 0, y: 0 });
const defaultPathData = vtrace.getSVGPath();
const simplified = vtrace.getSimplifiedSVGPath(undefined, undefined, {
flattenTolerance: 0.5,
simplifyTolerance: 0.1,
});
const simplifiedPath = SvgPathSimplifier.simplifyPath("M 0 0 L 10 0 L 20 0");
API
new VTrace(imageData, options?)
Creates a tracer instance from ImageData.
imageData: RGBA image data to trace.options: optional tracing and output settings.
Methods
getSVG(scale?): returns a complete SVG document string. Ifscaleis omitted, it uses configuredwidthandheightscaling, or{ x: 1, y: 1 }.getSVGPath(scale?, trans?): returns SVG path data only. Ifscaleis omitted, it uses configuredwidthandheightscaling, or{ x: 1, y: 1 }. Iftransis omitted, it defaults to{ x: 0, y: 0 }.getSimplifiedSVGPath(scale?, trans?, options?): returns simplified SVG path data and simplification statistics.scaleandtransuse the same defaults asgetSVGPath.getPathTag(fillColor?, scale?, trans?): returns a<path>tag.getSymbol(id): returns an SVG<symbol>tag.setParameters(options): updates tracing/output parameters.
SvgPathSimplifier.simplifyPath(d, options?) can also simplify arbitrary SVG path data directly.
Scale and translation values use this shape:
type TransformPoint = { x: number; y: number };
Examples:
const path = vtrace.getSVGPath();
const scaledPath = vtrace.getSVGPath({ x: 2, y: 2 });
const movedPath = vtrace.getSVGPath(undefined, { x: 10, y: 20 });
const scaledAndMovedPath = vtrace.getSVGPath({ x: 2, y: 2 }, { x: 10, y: 20 });
Simplification options use this shape:
interface SimplifyOptions {
/** Resolution used while flattening curves. */
flattenTolerance?: number;
/** Ramer-Douglas-Peucker epsilon used to simplify flattened points. */
simplifyTolerance?: number;
}
The simplified path result contains the path data and statistics:
interface SimplifyResult {
originalPath: string;
d: string;
stats: {
pointsBefore: number;
pointsAfter: number;
reductionPercent: number;
subPaths: number;
};
}
Options
interface VTraceOptions {
/** Suppress speckles up to this size. Defaults to `2`. */
turdSize?: number;
/** Alias for `turdSize`, matching the VTracer option name. */
filterSpeckle?: number;
/** Whether spline curve fitting is enabled. Defaults to `true`. */
optCurve?: boolean;
/** Threshold below which luminance is considered black, from `0` to `255`, or `VTrace.THRESHOLD_AUTO`. */
threshold?: number;
/** VTracer color mode. Defaults to `binary`, which thresholds the image before tracing. */
colorMode?: "binary" | "color";
/** Whether darker pixels are traced as foreground. Defaults to `true`. */
blackOnWhite?: boolean;
/** Foreground color. Defaults to `VTrace.COLOR_AUTO`; ignored when exporting as `<symbol>`. */
color?: string;
/** Background color. Defaults to `VTrace.COLOR_TRANSPARENT`; ignored when exporting as `<symbol>`. */
background?: string;
/** Output SVG width. Defaults to the source image width. */
width?: number;
/** Output SVG height. Defaults to the source image height. */
height?: number;
/** VTracer curve fitting mode. Defaults to `spline`; `pixel` maps to VTracer's unsimplified mode. */
mode?: "pixel" | "polygon" | "spline";
/** VTracer hierarchical mode. Defaults to `stacked`; use `cutout` to subtract upper layers from lower layers. */
hierarchical?: "stacked" | "cutout";
/** VTracer minimum momentary angle, in degrees, to be considered a corner. Defaults to `60`. */
cornerThreshold?: number;
/** VTracer segment length threshold. Defaults to `4`. */
lengthThreshold?: number;
/** Alias for `lengthThreshold`, matching the VTracer CLI name. */
segmentLength?: number;
/** VTracer maximum smoothing iterations. Defaults to `10`. */
maxIterations?: number;
/** VTracer minimum angle displacement, in degrees, to splice a spline. Defaults to `45`. */
spliceThreshold?: number;
/** VTracer RGB channel precision. Defaults to `6`; mainly relevant with `colorMode: 'color'`. */
colorPrecision?: number;
/** VTracer RGB layer difference threshold. Defaults to `16`; mainly relevant with `colorMode: 'color'`. */
layerDifference?: number;
/** VTracer decimal places for generated path data. Defaults to `8`. */
pathPrecision?: number;
}
Defaults:
turdSize:2filterSpeckle: usesturdSizewhen omittedoptCurve:truethreshold:VTrace.THRESHOLD_AUTOcolorMode:'binary'blackOnWhite:truecolor:VTrace.COLOR_AUTObackground:VTrace.COLOR_TRANSPARENTwidth: source image widthheight: source image heightmode:'spline'hierarchical:'stacked'cornerThreshold:60lengthThreshold:4maxIterations:10spliceThreshold:45colorPrecision:6layerDifference:16pathPrecision:8
Compatibility notes:
optCurve: falsemaps tracing to polygon mode instead of spline mode.filterSpeckleis an alias forturdSize; when both are set,filterSpeckleis passed to VTracer.thresholdandblackOnWhiteonly affect tracing whencolorModeis'binary'.colorMode: 'color'traces the sourceImageDataas RGBA bytes. VTracer clusters colors in RGB channel space; it does not use a perceptual color space such as Lab.
WASM Bundle Strategy
src/vtracer-embedded.ts contains the generated VTracer JavaScript glue code plus an embedded base64 WASM payload. This keeps consumers zero-config:
- no
.wasmfile needs to be listed in Angularassets - no server MIME-type configuration is required
- no runtime
fetch()is needed to locate the WASM file
If a future package variant needs a smaller JavaScript bundle, an external-WASM entry point can be added separately. The default package favors reliable browser integration.
Development
This project uses pnpm.
pnpm install
pnpm build
pnpm lint
pnpm test
Available scripts:
pnpm build: builds the package with Rolldown.pnpm build:wasm: rebuilds the embedded VTracer WASM wrapper fromrust/vtracer-wasm.pnpm lint: lints source files.pnpm test: runs the Jest test suite.pnpm dev: runs TypeScript in watch mode.
Rebuilding VTracer WASM
The distributed package uses src/vtracer-embedded.ts, which is generated from the first-party Rust wrapper in rust/vtracer-wasm. Rebuild it after changing the Rust wrapper or updating VTracer:
pnpm build:wasm
Requirements:
- Rust with the
wasm32-unknown-unknowntarget installed. On rustup-based installs, runrustup target add wasm32-unknown-unknown. On Arch Linux, installrust-wasm. wasm-bindgenCLI version matching the Rust crate version inrust/vtracer-wasm/Cargo.toml; currently0.2.126. Install withcargo install wasm-bindgen-cli --version 0.2.126 --lockedor your distribution package.- The script invokes
wasm-bindgendirectly and does not usewasm-pack.
CI and Publishing
GitHub Actions runs build, lint, and test checks on pushes and pull requests targeting main.
Publishing is handled by the Publish workflow and runs only for version tags such as v1.2.3 or v1.2.3-beta.1.
License
This project is licensed under the MIT License. For more details, please check the LICENSE file.
VTracer is licensed under MIT OR Apache-2.0. The local WASM wrapper in rust/vtracer-wasm depends on the upstream vtracer crate.