Skip to content

Output JSON fields ​

scan writes one JSON file per page into --out (default .imgtune/scan). --summary-json writes one file with every page. The numbers are per page view, and the savings are shares of the image bytes, as described in How it measures. The fields below come from the scan's output types.

The primary number is the estimated saving: exactEstPct on a page, and savedBytes on a group. The CLI prints it as saving exact + est.. The other savings fields are its breakdown: exactPct (and exactSaved on a viewport or an image) is the smaller-file part, and estSaved is the re-encode part. The JSON keeps the names exact and est, so existing consumers still read the same numbers.

Page file ​

fieldmeaning
urlthe page that was scanned
hostits host
scannedAtISO time of the scan
imgtuneVersionthe tool version that wrote the file
metricssimulacra2
scorer{ name: "cpu" | "gpu", device, deviceName, cpuFallbacks }: the scorer that made the numbers. cpuFallbacks counts the scores the CPU took after the GPU failed during this page, and is absent when there were none. Scans from before --gpu have no field: they were scored on the CPU
deltathe delta of the relative rule (2 in a default scan)
aggressiveDelta4, only with --aggressive
viewportsthe viewport names measured
statusok, blocked, robots, redirected or failed (see below)
errorthe failure message, when there is one
robotsskipped (local host) when robots.txt was not checked, because the page is on a loopback or private host
robotsIgnoredtrue when robots.txt disallowed the page and it was scanned anyway (--ignore-robots)
robotsRulethe robots.txt rule that disallowed the page, when it was scanned anyway
finalUrlthe URL the page ended on, when it differs from url
redirectNotea note that the page ended on another URL, and that it was measured there
loadTimedOuttrue when the page's load event did not come in time. Its figures were taken anyway
nextjstrue when the page is detected as a Next.js app
pagethe page's numbers (below). Present when the page was measured
groupsthe top groups of images (below), up to ten
countshow many images, how many could not be scored for quality, how many were skipped, the blocked requests, and the count per loader
perViewportthe measurements at each viewport, keyed by viewport name (below)
imagesone fix record per image element, merged across viewports (below)
notLoadedthe number of lazy images that never loaded at any viewport
completetrue once the page is done. A page without it (an interrupted run) is scanned again by --resume
optionsthe options the page was scanned with: imgtuneVersion, aggressive, quality, viewports, ignoreRobots and storageState (a boolean; the state itself is not recorded). survey: true on a --survey page and idleTimeoutMs when it is not the default, each present only when set. --resume reuses a page only when they match

status values:

  • ok: measured.
  • blocked: a challenge page or bot wall. It is not retried.
  • robots: robots.txt disallows the page, so it was not scanned.
  • redirected: the page ends on another site, or on a URL that robots.txt disallows. Nothing is measured, and finalUrl says where it went.
  • failed: an error. error says what.

page ​

fieldmeaning
meanImageBytesmean image bytes per page view, over the viewports
exactPctbreakdown: the smaller-file part of the estimated saving, as a percentage of the image bytes
exactEstPctthe estimated saving (primary): the smaller-file part plus the re-encode part, as a percentage of the image bytes
fixablePctthe same savings as a share of the bytes of the images that have a saving
sharpnessKBsharpness growth, in KB per view
aggressiveExactEstPctonly with --aggressive

perViewport ​

One entry per viewport name. Each has:

fieldmeaning
imageBytesthe image bytes at this viewport
exactSavedexact bytes saved at this viewport
estSavedest. bytes saved at this viewport
estAggressiveSavedonly with --aggressive
sharpnessGrowthsharpness growth in bytes at this viewport
imagesone entry per image at this viewport (below)

perViewport.images ​

fieldmeaning
urlthe image URL the browser fetched
bytesthe bytes served
formatthe format reported for the file
pixelWidththe file's width in pixels
renderedWidththe width it renders at, in CSS px
dprthe device pixel ratio of the viewport
neededthe device pixels the image needs: rendered width x dpr, capped at the largest candidate
firstViewporttrue when the image is in the first viewport, before scrolling
kindsrcset (the site offers candidates), plain (a plain img), or skipped
skippedthe reason, when the image was skipped (for example under 16 px)
sizesthe sizes attribute, when the element has one
rightthe next candidate the site offers, with its URL, bytes and width
exactSavedexact bytes saved on this image
estSavedest. bytes saved on this image
estAggressiveSavedonly with --aggressive
sharpnessGrowthsharpness growth in bytes for this image
groupthe key of the group the image belongs to
qualitycurrent, qualityAssumed, target, format and sampled, when a quality estimate was made; sweep: "skipped-small" when the served file was under 2 KB and was not swept (it gets its group's ratio, if any)
qualityNAtrue when the quality could not be scored
loaderthe loader the URL came from, such as next-default or generic
duplicateOfthe index of the element measured instead, when this element repeats its URL. Its savings are zero

Each viewport's entry also has lcp: image (the LCP is an image of the page, its record is in images with lcpViewports), other (the LCP is another element) or none (no LCP entry). It is absent in scans from before LCP was read.

locate ​

Set by scan --locate-only on the located copy of a page: url (the dev page), viewport, scannedAt, imgtuneVersion, seconds, flagged (images with a saving or sharpness growth), joined, notJoined, devLeft (dev images of a flagged URL with no measured partner), noOrder (elements without domIndex) and reasons (not joined, by code: not-measured, no-dev-image, count-differs, alt-differs).

groups ​

fieldmeaning
keythe group key
sizesthe sizes in use, when the group has one
suggestedSizesthe sizes ImgTune suggests
estQualitythe median quality the estimates reached, when there is one
loaderthe loader most of the group comes from
qualityHinthow the loader's quality parameter limits the saving
savedBytesthe estimated saving in bytes per page view for the group (exact + est.), averaged over the viewports
membersthe number of distinct elements in the group
samplesup to two image URLs
hinta ready line, such as set sizes="28px", when the suggestion differs from the current sizes

counts ​

fieldmeaning
imagesthe number of distinct image elements
qualityNAimages whose quality could not be scored
skippedimages skipped, for example under 16 px
blockedRequestsrequests the egress check refused
byLoaderthe number of images per loader

images ​

images holds one record per distinct image element, merged across the viewports the element was seen at. A record is a fix hint when it saves bytes, or when its sizes differs from the one in use. hints prints those records.

fieldmeaning
id12 hex digits. A hash of the page URL and the element's key: its source file and line, else its component, its test id or its selector, plus its order among identical elements. An element keeps its id across scans of the same page while its key stays the same
pagethe page URL
locatorwhere the element is (below)
tunablewhat can be changed at the call site (below)
currentwhat the page serves now (below)
lazytrue when the element has loading="lazy"
lcpViewportsthe viewports at which the element is the page's largest contentful paint, read before the first scroll ([] when none; absent in older scans)
perViewportwhat the element does at each viewport it was seen at (below)
layoutChainthe ancestors of the element at the reference viewport, with the viewports whose chain differs (below)
fixthe fix and its two byte numbers (below)
evidencethe rung, and notes on how the numbers were made (below)

images[].locator ​

fieldmeaning
componentthe component names, nearest first. Dev builds of React only
file, linethe project-relative source file and line of the element, through the source map. Dev builds only. The file is read from the directory the scan ran in
kindpresent with file: image (the line, or a window of three lines, holds an image tag, a srcset prop, or the component's definition), owner (the line is the call site of the component that renders the image: the image is inside that component), unread (the file was not found in the scan's directory), or unchecked (the page is not local, so no file was read)
verifiedtrue only for kind: image: the source is a local project file and its line renders an image. Everything else is false and must be checked before it is trusted
testidthe data-testid of the element
altthe alt text of the element
selectora CSS selector for the element
classesthe element's class attribute, capped
urlPatternthe loader URL with its width, quality and CDN transform taken out
loadernext-default, a CDN adapter's name, cdn:<host>, plain-img, or unknown
searchHintspresent when no file resolved: the component, the file name it would have, a glob to search the project with, and the alt and URL pattern. These are clues only; nothing is read from the project

images[].locate ​

Set on a flagged image that scan --locate-only joined to a dev page element: viewport (the dev viewport), by (alt-order: URL pattern, alt and document order; url-unique: the only image with that URL on both pages), and devIndex (its place among the dev page's <img> elements). Its locator.file, line, component, kind and verified come from the dev page; the bytes do not.

images[].perViewport[].domIndex ​

The element's place among the page's <img> elements at that viewport. The locate join pairs same-URL elements in this order. Scans from before --locate-only do not have it.

images[].tunable ​

fieldmeaning
widthtrue when the loader takes a width
qualitytrue when the loader takes a quality
reasonpresent when either is false: fix in the loader. The fix goes into the loader function, not the call site

images[].current ​

fieldmeaning
srcthe URL the browser loaded at the reference viewport
sizesthe sizes attribute in use
srcsetCandidatesthe site's candidates, each with url and width
qualitythe current quality, estimated from the file. Absent when it could not be scored
qualityAssumedtrue when the quality is assumed rather than measured
formatthe format the file is served in
naturalWidththe file's width in pixels

images[].perViewport ​

One entry per viewport the element was seen at:

fieldmeaning
viewportthe viewport name
dprthe device pixel ratio
renderedCssPxthe rendered width, in CSS px
neededPxthe device pixels the image needs at this viewport
servedPxthe served file's width in pixels, when it was decoded
servedBytesthe bytes served
sizesResolvedPxthe width the browser resolved from sizes, in CSS px. Absent without sizes
sizesMatchedConditionthe media condition of the sizes branch that matched, or default
overFetchRatioserved pixels per device pixel needed. Above 1, the file is larger than the screen can show
undersizedtrue when the file is smaller than the slot it fills
loadedtrue when the element's own file was received at this viewport
lcptrue when the element is the largest contentful paint at this viewport (absent in older scans, and at a viewport whose LCP was not read)
skippedwhy the element is not measured here (a duplicate of another element, a data URI, and so on)

images[].layoutChain ​

The layout of the element's ancestors, from the image up to the root, at the reference viewport:

fieldmeaning
viewportthe reference viewport the chain was taken at
nodesthe ancestors, nearest first (below)
skippedancestors that do not constrain the width (display: contents, inline wrappers), each with its reason
truncatedtrue when the walk stopped at the depth cap before reaching the root
diffsfor each other viewport whose chain has a different structure, its viewport and its nodes

Each node in nodes:

fieldmeaning
tag, classesthe element's tag and up to three class names
widththe computed content-box width, in CSS px
maxWidthmax-width in px. Absent when none, or a percentage
paddingX, borderX, marginXthe horizontal padding, border and margin, in CSS px
displaythe computed display
gridCols, gridUniform, gridSpanfor grids: the tracks in grid-template-columns, whether they are equal, and how many columns the node spans
gapcolumn-gap in px, for grid and flex containers
flexDirection, flexBasis, flexGrowflex properties, as computed
borderBoxtrue when max-width caps the border box, not the content box

images[].fix ​

fieldmeaning
sizespresent when a sizes value was derived: value (the CSS value), basis (layout-formula, fit or measured-max), rung (measured-page) and branches. fit is a fitted line through the measured widths, used when the layout formula does not match them (for example when no layout chain was captured); it is rare but reachable
sizes.branchesone per media branch: lo and hi (the viewport widths it covers), value, basis and condition (the media condition, or default)
widthplain images only: the device pixels the file needs at the reference viewport
qualitypresent when the image's group has a quality target and the current quality is known: perFormat (the target quality per format), groupRule (max-of-targets), group (the component, or the URL pattern, the maximum comes from), samples (the number of images scored in the group), current, and qualityAssumed
formatthe best format for the image. avif for a JPEG or PNG that has a saving
bytes.savedthe bytes the fix saves per page view, averaged over the viewports
bytes.addedForSharpnessthe bytes the fix adds on undersized images, per page view
cdnQualitypresent on a CDN's auto quality (q_auto): handled (true when the CDN's quality is left alone, because its estimated saving is below a tenth of the group's served bytes; that saving is then not counted), and saved, the group's estimated quality saving per view
formatHandledtrue on a jpeg or png whose format the CDN negotiates (f_auto, auto=format): no format saving is counted or proposed

bytes.saved and bytes.addedForSharpness are two numbers and are never netted. An undersized image adds bytes and saves none.

images[].evidence ​

fieldmeaning
rungmeasured-page for a scanned page, measured-harness for a harness route (/__imgtune/..., or --harness). ceiling-code and not-measured are added by the agent. See Fix it with your agent
kindcss-background for a CSS background layer; absent for an <img>, srcset or <picture> element
noteshow the numbers were made: where the sizes came from, which source was resolved, and any limit (for example a layout chain cut at the depth cap)

Summary JSON ​

--summary-json holds imgtuneVersion, scorer (the run's scorer: the GPU when any page used it, with cpuFallbacks summed) and pages: one entry per page with url, host, status, and, when present, error, finalUrl, loadTimedOut and page (the page numbers above). --markdown writes the same numbers as a Markdown table, with each page's top groups and the method. The CI action posts that Markdown as its comment.

Dropped sizes proposals ​

An image record's fix may carry noSaving: the reason a proposed sizes was left out (its predicted outcome adds bytes, or changes no bytes). In that case fix.sizes is absent and fix.bytes.sizes is 0. The reason also appears in the record's evidence.notes.

MIT License. Third-party code is listed in NOTICE.