InkSight

An SVG is text, but it isn't legible — an agent can read every <path> and still not know how much ink is on the page, whether the plot is balanced, or what each layer means. InkSight measures a polyline plotter SVG into a structured report built for that question. Everything runs in your browser; nothing is uploaded.

Drop an SVG here

or

What you get

One drop produces the page and drawable geometry, ink totals (arc length, pen-up travel, bounding box, ink density), a per-layer table with pen colors, an 8×8 density heatmap with a spatial-balance verdict, plottability warnings (margin violations, saturated cells, clumping), a rendered thumbnail as a multimodal cross-check — and the full StatsReport JSON, which is the artifact the rest of this page is about.

Made for agents

A human looks at the thumbnail; an agent needs the numbers. The copyable JSON is the same report the CLI emits — deterministic, so two runs on the same file agree to the digit, and an agent can diff reports across variants or gate a print queue on them. The fields:

fieldwhat it says
page, drawablePhysical page in mm, and the margin-clipped drawable rect inside it.
unitsWhich unit the file declared on width/height (mm, cm, in, px, or assumed-px when it declared none), and the resulting mm per user unit. Every other number is in mm.
penWidthMm, penWidthSourceEffective pen width used for coverage math, and whether it came from a flag, the SVG, or the default.
scalePath-space → mm factor recovered from the export's group transform.
totalsLayers, paths, vertices, segments; arcLengthMm (ink drawn), penUpTravelMm (travel between paths in file order — high ratios mean vpype linesort will pay off), bounding box, bboxCoverageRatio, inkDensity (arc length × pen width / drawable area).
layers[]Per-layer paths, vertices, arc length, ink density, and the declared stroke color — the pen-change plan, in numbers.
densityGridCoverage per grid cell, plus max, mean, and cv — the coefficient of variation that scores spatial balance (≤0.5 even, ≥1.5 clumped).
warningsPaths crossing the margin clip (they will be cut off), cells at solid-ink saturation for this pen width.

That shape is published as a JSON Schema (draft 2020-12) at @endonny/inksight/schema.json — validate or generate types against it rather than inferring it from one example. A test validates a real report against the schema, with additionalProperties false at every level, so the schema cannot drift from what the analyzer emits.

A worked example of an agent consuming this: render N variants of a composition, run the report on each, reject any with margin violations or cv ≥ 1.5, and rank the rest by ink density against a target — no vision model in the loop until the final pick.

The reject step is a subcommand: inksight check plot.svg --max-cv 1.5 --no-margin-violations emits { pass, failures: [{ check, measured, threshold }] } and exits 0 or 1, so a queue can branch on the exit code alone. A file it cannot measure exits 1 with the error on stderr and no JSON, so broken never reads as rejected.

Use it from the command line

Same analyzeSvg core as this page, on stdout instead of in the DOM:

npx @endonny/inksight plot.svg                  # StatsReport JSON on stdout
npx @endonny/inksight plot.svg --grid 16        # finer density grid
cat plot.svg | npx @endonny/inksight            # or pipe it
npx @endonny/inksight diff a.svg b.svg c.svg    # variability across variants
npx @endonny/inksight check plot.svg --max-cv 1.5   # gate: exit 0 pass, 1 fail

npm i -g @endonny/inksight for a permanent install, or npm i @endonny/inksight to import analyzeSvg as a library.

Scope, honestly

v1 measures polyline SVGs: absolute M/L paths under a translate(cx,cy) scale(S) group, sized by a viewBox plus width/height. Files that declare mm, cm, in or px are converted to millimetres; a file with no unit is read as CSS px at 96dpi, and the report's units field says assumed-px so you can tell a measurement from a guess. Curves, nested transforms and arbitrary SVGs are rejected with a clear error rather than mis-measured — you still get the thumbnail. Plot-time estimation and pen-travel optimization modelling are deliberately deferred; that's vpype-grade work.