Nodes/ComfyUI PenPlotter/Optimize SVG with vpype
ComfyUI Node

Optimize SVG with vpype

Optimize SVG with vpype

By wouterverweirder·Created about a year ago·Updated 4 months ago· 0
Optimize SVG with vpype
    • optimized_svg
    svg_string
    fit_to_margins0.0
    landscapetrue
    linemergetrue
    tolerance0.10
    linesorttrue
    relooptrue
    linesimplifytrue
    page_width100
    page_height148

    Raw SVG is not plotting-ready SVG. The skeletons that Image to Centerline SVG produces are honest but clumsy - thousands of short segments, lines going in silly orders, duplicated strokes. Plot that as-is and your pen spends its life doing tiny zigzags and dead-heading across the page. Optimize SVG with vpype is the cleanup pass: it shells out to vpype - the standard Swiss-army tool for pen-plotter vector work - and returns a tidied SVG string that will actually plot fast and clean.

    In the pipeline it sits between the tracer and the plotter: ImageToCenterline → OptimizeSVG → Plot SVG with AxiDraw (or → SVG to Submission File → upload). You can also feed it any SVG string from anywhere; it doesn't care where the markup came from.

    How it works

    The node writes your svg_string to a temp file and builds a vpype command chain, which the code constructs one flag at a time:

    vpype read input.svg layout --fit-to-margins 0mm [--landscape] 100x148mm \
      linemerge --tolerance 0.1mm linesort reloop linesimplify \
      write --page-size ... output.svg
    

    The steps that matter, roughly in order: layout scales your drawing to fit the page with the margins you set, linemerge fuses collinear line fragments within a tolerance (mm), linesort orders the paths to minimize pen travel, reloop picks better start points so loops close up, and linesimplify thins out redundant points. The default page is page_width 100 by page_height 148 mm - roughly A6-ish, and landscape (on by default) rotates it to match.

    For a beginner, only a few inputs are worth touching. fit_to_margins (0–50, default 0) is the empty margin in mm around the drawing. tolerance (default 0.1) controls how aggressively collinear segments merge - too high and it starts swallowing real detail. Everything else you can leave on. If you only want one thing, this is the node that separates "plots eventually" from "plots well."

    Install

    It's part of the ComfyUI PenPlotter pack. Install via ComfyUI Manager (search "ComfyUI PenPlotter") or:

    cd ComfyUI/custom_nodes
    git clone https://github.com/wouterverweirder/comfyui-penplotter
    

    Restart ComfyUI. This node needs vpype>=1.0.0 on the PATH as a real binary - the pack installs it as a Python dependency, so it should be there, but note the whole bundle also includes numpy>=2.3.4, opencv-python, numba, websockets, requests, qrcode[pil] and the AxiDraw API (from a URL zip, so internet at install time).

    Common issues

    • "Error: vpype command not found" - the vpype binary isn't on your PATH. Install it (pip install vpype) and make sure the environment ComfyUI runs in is the one that has it.
    • Output is an "Error:..." string, not an SVG - like the tracer node, failures are returned as text. If the returned string starts with "Error:", read the console log for the vpype stderr - usually a malformed input SVG or a layout conflict.
    • Drawing comes out tiny or cropped - that's fit_to_margins and page size working as configured. Your SVG's natural dimensions don't match the page; let layout rescale and check the numbers.
    • Toggling the boolean flags - linemerge, linesort, reloop, linesimplify all default on. Turning them off is a diagnostic move, not a workflow choice; keep them on unless you have a specific reason not to.

    The honest version: this node is a thin wrapper around vpype, and vpype is battle-tested. If your plot looks bad, the culprit is usually your input geometry - not this node.

    CategoryPen Plotter

    Inputs (10)

    NameTypeDefaultDescription
    svg_stringSTRING
    fit_to_marginsoptFLOAT0.00–50
    landscapeoptBOOLEANtrue
    linemergeoptBOOLEANtrue
    toleranceoptFLOAT0.100.01–5
    linesortoptBOOLEANtrue
    reloopoptBOOLEANtrue
    linesimplifyoptBOOLEANtrue
    page_widthoptFLOAT10010–500
    page_heightoptFLOAT14810–500

    Outputs (1)

    NameTypeDescription
    optimized_svgSTRING