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:
| field | what it says |
|---|---|
page, drawable | Physical page in mm, and the margin-clipped drawable rect inside it. |
units | Which 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, penWidthSource | Effective pen width used for coverage math, and whether it came from a flag, the SVG, or the default. |
scale | Path-space → mm factor recovered from the export's group transform. |
totals | Layers, 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. |
densityGrid | Coverage per grid cell, plus max, mean, and cv — the coefficient of variation that scores spatial balance (≤0.5 even, ≥1.5 clumped). |
warnings | Paths 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.