npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mcp-png-to-svg

v0.1.0

Published

MCP server that converts PNG logos to animation-ready centerline SVG (for stroke-dashoffset path drawing).

Readme

mcp-png-to-svg

An MCP server that converts a PNG logo into a small number of clean, animation-ready centerline SVG paths — the kind you can actually drive with stroke-dashoffset for path-drawing animations in React Native Reanimated, SVG strokeDasharray, GSAP DrawSVG, or Lottie.

Most PNG→SVG tracers (potrace, imagetracerjs, vtracer fill mode) produce fill-based tracing that explodes into thousands of micro-polygons. Those are useless for stroke animation. This tool does centerline tracing — Guo-Hall thinning + tangent-based stroke linking — so that two intertwined wavy lines come out as two <path> elements, not two thousand.

Install

Add one entry to your Claude Code .mcp.json:

{
  "mcpServers": {
    "png-to-svg": {
      "command": "npx",
      "args": ["-y", "mcp-png-to-svg"]
    }
  }
}

That's it. No system dependencies, no Python, no Inkscape. Node 18.17+ required.

First-run cold start is 3–5 s while npx fetches sharp's prebuilt binaries.

What it's for

  • Logos and icons with clean strokes
  • Hand-drawn sketches and signatures
  • Letter glyphs, monograms
  • Anything you'd animate by "drawing" it in mid-air

What it's NOT for

  • Photos
  • Filled illustrations with large solid regions
  • Screenshots with text blocks

The tool refuses these with a clear NOT_LINE_ART error rather than returning 27,000 garbage paths. If you hit the refusal, your source is telling you to find or create a vector asset — centerline tracing cannot meaningfully help you.

Tool: png_to_animatable_svg

Input

| Field | Type | Default | Notes | |------------------------|--------------------|--------------------|-------| | inputPath | string | — | File path. One of inputPath / inputBase64 is required. | | inputBase64 | string | — | Base64 PNG bytes. | | outputPath | string | next to input | SVG output path. | | maxPaths | number | 12 | Hard ceiling. Exceeded → error. | | minBranchLength | number | 5 | Prune skeleton spurs shorter than this (pixels). | | simplifyTolerance | number | 1.5 | RDP tolerance in pixels. | | junctionMergeRadius | number | 4% of diagonal | Collapse junction clusters smaller than this. | | strokeWidth | number | 2 | SVG stroke width. | | threshold | "otsu" | number | "otsu" | Binarization threshold. |

Output

{
  svg?: string;              // inlined if <= 8 KB
  outputPath?: string;       // always set if the SVG was written to disk
  pathCount: number;
  totalLength: number;
  viewBox: string;
  suggestedDurationMs: number;
  warnings: string[];
  usage: string;             // copy-paste Reanimated snippet
}

All emitted paths have fill="none", stroke="currentColor", and — crucially — pathLength="1", so your animation math is just strokeDashoffset: 1 - progress, regardless of the path's real length.

React Native Reanimated snippet

import { useEffect } from "react";
import Animated, {
  useSharedValue,
  useAnimatedProps,
  withTiming,
} from "react-native-reanimated";
import Svg, { Path } from "react-native-svg";

const AnimPath = Animated.createAnimatedComponent(Path);

export function LogoDraw() {
  const progress = useSharedValue(0);
  useEffect(() => {
    progress.value = withTiming(1, { duration: 1400 });
  }, []);

  const props = useAnimatedProps(() => ({
    strokeDasharray: "1 1",
    // NOTE: iOS drops numeric updates; coerce to string.
    strokeDashoffset: String(1 - progress.value),
  }));

  return (
    <Svg viewBox="-8 -8 416 416" width={240} height={240}>
      <AnimPath
        d="M49,120L85,146L...L351,280"
        pathLength={1}
        {...props}
        stroke="#D4AF37"
        strokeWidth={2}
        fill="none"
        strokeLinecap="round"
      />
      {/* ...one AnimPath per <path> in the generated SVG... */}
    </Svg>
  );
}

Pipeline

  1. sharp → grayscale → Otsu threshold → binary mask
  2. Morphological close (heal AA pinholes)
  3. Guo-Hall 1-pixel thinning
  4. Spur pruning (remove short anti-alias branches)
  5. 8-connected skeleton → multigraph (endpoints + junctions)
  6. Junction cluster collapse (real crossings rarely thin to a single pixel)
  7. Tangent-based stroke linking — at each junction, pair edge stubs by straightest-through tangent; walk pairings to produce long continuous polylines
  8. Ramer–Douglas–Peucker simplification
  9. Emit <path> elements with pathLength="1", sorted longest-first

Error codes

| Code | Meaning | |------------------|---------| | MISSING_INPUT | Neither inputPath nor inputBase64 was provided. | | AMBIGUOUS_INPUT| Both inputPath and inputBase64 were provided. | | INPUT_NOT_FOUND| File at inputPath does not exist. | | NOT_LINE_ART | Too much ink coverage — looks like a photo or filled shape. | | TOO_MANY_PATHS | Tracing produced more paths than maxPaths. Simplify the source or raise the ceiling. | | OUTPUT_TOO_LARGE | inputBase64 was used without outputPath and the SVG exceeds 8 KB. |

Limitations

  • Variable-width strokes (calligraphic logos) lose their width variation — centerline is inherently uniform. If your logo depends on varying stroke weight, use the original vector asset instead.
  • Filled regions inside line art (e.g. a solid dot on top of an "i") will skeletonize to their own small strokes; use minBranchLength to suppress them.
  • Text in complex fonts will produce many paths — intentionally. This tool is for icons and logos, not for rasterized type.

License

MIT