Node.js library to convert raster images to svg using VTracer, with time complexity of O(n).
npx @neplex/vectorizer ./raster.png ./vector.svgOptimize while vectorizing:
npx @neplex/vectorizer ./raster.png ./vector.svg --optimize --multipass --multipass-iterations 5Optimize an existing SVG:
npx @neplex/vectorizer optimize ./vector.svg ./vector.optimized.svg --plugin preset-defaultUse --help to see all available options.
npm install @neplex/vectorizerimport { vectorize, ColorMode, Hierarchical, PathSimplifyMode } from '@neplex/vectorizer';
import { readFile, writeFile } from 'node:fs/promises';
const src = await readFile('./raster.png');
const svg = await vectorize(src, {
colorMode: ColorMode.Color,
colorPrecision: 6,
filterSpeckle: 4,
spliceThreshold: 45,
cornerThreshold: 60,
hierarchical: Hierarchical.Stacked,
mode: PathSimplifyMode.Spline,
layerDifference: 5,
lengthThreshold: 5,
maxIterations: 2,
pathPrecision: 5,
});
console.log(svg); // <svg>...</svg>
await writeFile('./vector.svg', svg);The generated SVG string is generally quite large, so it's recommended to optimize it before use. See the SVG Optimization section for more details. If you want to use a synchronous API, you can use vectorizeSync instead. See the API section for more details on available functions and options.
yarn bench
clk: ~5.32 GHz
cpu: Intel(R) Core(TM) i7-14700K
runtime: node 24.14.1 (x64-win32)
benchmark avg (min … max) p75 / p99 (min … top 1%)
-------------------------------------------------- -------------------------------
@neplex/vectorizer raw sync 529.82 µs/iter 533.70 µs ▅█▅
(517.00 µs … 607.00 µs) 552.90 µs ▅██▆▇████▄
( 1.65 kb … 168.67 kb) 1.94 kb ▃███████████▆▄▃▂▁▂▂▂▁
@neplex/vectorizer encoded sync 549.19 µs/iter 552.70 µs ▃█▆▇
(534.80 µs … 597.10 µs) 573.50 µs ▃▆▆████▆▄
( 1.60 kb … 74.57 kb) 1.70 kb ▂▆██████████▆▄▂▂▂▂▂▁▁
@neplex/vectorizer encoded async 585.94 µs/iter 586.00 µs ▇█
(561.70 µs … 745.10 µs) 691.10 µs ██
( 2.27 kb … 76.16 kb) 2.34 kb ▂▅███▅▃▂▂▁▁▁▁▁▁▂▁▁▁▁▁
@neplex/vectorizer encoded callback 684.02 µs/iter 698.80 µs ▃▅▃▃█▂
(632.30 µs … 844.90 µs) 770.60 µs ▂▅██████▇▃
( 4.27 kb … 44.40 kb) 4.48 kb ▂████████████▇▅▆▆▅▃▃▂
@neplex/vectorizer raw async 565.45 µs/iter 565.20 µs ▂█
(548.70 µs … 706.00 µs) 646.00 µs ██▃
( 2.33 kb … 186.42 kb) 2.58 kb ▂███▅▃▃▂▂▁▂▁▂▁▁▂▁▁▁▁▁
@neplex/vectorizer raw callback 625.91 µs/iter 636.20 µs ▃█▇
(591.60 µs … 779.20 µs) 704.80 µs ███▇
( 3.98 kb … 374.13 kb) 4.62 kb ▂████████▄▅▆▅▆▄▄▃▁▂▁▂
imagetracerjs 2.41 ms/iter 2.42 ms █▇▂
(2.33 ms … 2.93 ms) 2.73 ms ████▂
( 1.14 mb … 6.92 mb) 4.52 mb ▂█████▃▃▃▃▂▂▂▂▁▂▁▁▁▁▁
potrace trace 3.59 ms/iter 4.45 ms █▅
(1.70 ms … 7.43 ms) 6.36 ms ▆▃█▂██▃ ▃▅▂ ▄
(736.00 b … 11.91 mb) 2.27 mb ▃██████████████▇▃█▁▆▃
summary
@neplex/vectorizer raw sync
1.04x faster than @neplex/vectorizer encoded sync
1.07x faster than @neplex/vectorizer raw async
1.11x faster than @neplex/vectorizer encoded async
1.18x faster than @neplex/vectorizer raw callback
1.29x faster than @neplex/vectorizer encoded callback
4.54x faster than imagetracerjs
6.78x faster than potrace traceSee benchmark for more details.
Takes an image buffer and returns a promise that resolves to an SVG string.
Takes an image buffer and returns an SVG string synchronously.
vectorizeRaw(data: Buffer, args: RawDataConfig, config?: Config | Preset, signal?: AbortSignal): Promise<string>
Takes a raw pixel data buffer and returns a promise that resolves to an SVG string.
Takes a raw pixel data buffer and returns an SVG string synchronously.
vectorizeToCallback(data: Buffer, config: Config | Preset | null | undefined, callback: (chunk: string, progress: number) => void): void
Vectorizes an image buffer and emits SVG chunks to the callback. progress is a number from 0 to 100 indicating how much of the vectorization has been written.
vectorizeRawToCallback(data: Buffer, args: RawDataConfig, config: Config | Preset | null | undefined, callback: (chunk: string, progress: number) => void): void
Vectorizes raw pixel data and emits SVG chunks to the callback. progress is a number from 0 to 100.
Decodes an encoded image buffer, or a raw RGBA buffer when args is provided, and resolves image width, height, and pixels.
Decodes an encoded image buffer, or a raw RGBA buffer when args is provided, synchronously.
Checks asynchronously whether a decoded image contains the given RGB color.
Checks synchronously whether a decoded image contains the given RGB color.
findUnusedColorInImage(image: ImageData, options?: InternalOptions, signal?: AbortSignal): Promise<Color>
Finds asynchronously a color that does not exist in decoded image data.
Finds synchronously a color that does not exist in decoded image data.
Optimizes an SVG string asynchronously. Supports the same options as optimizeSync.
Optimizes an SVG string synchronously.
OptimizeOptions supports:
preset?: OptimizePreset:Default,Safe, orNone.plugins?: Array<string | { name: string, params?: unknown }>: SVGOConfig["plugins"]compatible job config. When provided, this defines the optimizer job set.omit?: string[]: optimizer job names to skip.multipass?: boolean: run optimization until output stops changing or the iteration limit is reached.multipassIterations?: number: maximum multipass iterations. Defaults to10.
Determines if the given chunk data indicates the end of the SVG output. Useful for callback-based vectorization to know when the final chunk has been received. Note that EOF here refers to the final chunk of svg, which is </svg>\n.
const { promise, resolve } = Promise.withResolvers<void>();
vectorizeToCallback(src, config, (chunk, progress) => {
if (isEOF(chunk, progress)) {
resolve();
}
});
await promise; // block until all the SVG chunks have been received
console.log('SVG output complete!');The raw output from vectorize can be significantly reduced in size using the built-in optimizer powered by oxvg_optimiser:
import { optimize, vectorize } from '@neplex/vectorizer';
import { readFile, writeFile } from 'node:fs/promises';
const src = await readFile('./input.png');
const svg = await vectorize(src);
const optimized = await optimize(
svg,
/* optional config */ {
plugins: ['preset-default', { name: 'removeTitle' }], // supports svgo plugin config
multipass: true, // enable multipass optimization
multipassIterations: 5, // limit multipass iterations to 5
},
);
await writeFile('./output.svg', optimized);Note that the optimizer included in this library is a thin wrapper around
oxvg_optimiserand does not intend to provide all the features ofoxvg. For advanced needs, consider using@oxvg/napidirectly.
Generated under the following configuration:
{
colorMode: ColorMode.Color,
colorPrecision: 8,
filterSpeckle: 4,
spliceThreshold: 45,
cornerThreshold: 60,
hierarchical: Hierarchical.Stacked,
mode: PathSimplifyMode.Spline,
layerDifference: 6,
lengthThreshold: 4,
maxIterations: 2
}| Raster Image (PNG Input) | Vector Image (Generated SVG) |
|---|---|
![]() CC-BY-SA 3.0 by Niabot |
CC-BY-SA 3.0 by Niabot |
