Image Dimensions
An image has two sizes. Its natural size is the real pixel width and height of the file, read from naturalWidth and naturalHeight. Its display size is the box it fills on the page, set by CSS. A 2000px photo can display in a 200px box, so the two rarely match.
Why it matters
Filtering by size only makes sense against the natural size: you keep high-resolution files and drop tiny icons and thumbnails. A tool that measured the display box instead would judge a huge photo as small just because CSS shrank it. You'd lose the file you wanted.
Conversely, a 10000×10000 image might display in a 200px box on the page. It's massive in the file, but the display box looks tiny. Knowing the two separately lets you make the right choice about what to keep.
How it works
The browser reports two different measurements for every image element.
Natural size is the real resolution of the image file. When you load a JPEG that's 2400 pixels wide and 1600 pixels tall, those numbers are baked into the file. JavaScript reads them from element.naturalWidth and element.naturalHeight. These values don't change even if CSS stretches the display box to 3000 pixels or squeezes it to 100 pixels.
Display size is the box the image fills on the page. CSS controls it through width, height, and max-width rules. JavaScript reads the display box with element.getBoundingClientRect(), which returns width and height in CSS pixels (accounting for transforms and zoom). You can also read the width and height HTML attributes, but those are ignored by browsers in favour of CSS.
An image that's responsive (via srcset or <picture>) might load different files for different screen sizes. On mobile you get a 800×600 version; on desktop you get a 2400×1600 version. The naturalWidth and naturalHeight report the file you actually got, not the others that could have been picked.
What does not matter
The intrinsic aspect ratio set by aspect-ratio: 2400 / 1600 doesn't affect naturalWidth. It's purely a rendering hint that shapes the layout box before the image loads. Once the image loads, the natural size from the file takes over.
Also, CSS transforms like transform: scale(2) or rotate(45deg) don't affect naturalWidth or getBoundingClientRect() in the way you might think. Transforms happen after layout, so getBoundingClientRect() includes them (the rotated box gets bigger). But naturalWidth is still just the file's pixel count, untouched.
Image substitution (like swapping images via JavaScript before display) is an implementation detail. All that matters is what the browser loaded and what CSS is doing to it right now.
Code example
Here's how you'd check both sizes:
const img = document.querySelector('img');
// Natural size: the actual file resolution
console.log(`Natural: ${img.naturalWidth}×${img.naturalHeight}`);
// → Natural: 2400×1600
// Display size: how big it appears on the page
const rect = img.getBoundingClientRect();
console.log(`Display: ${Math.round(rect.width)}×${Math.round(rect.height)}`);
// → Display: 800×533
// You might also read the HTML attributes (though CSS overrides them)
console.log(`HTML width attribute: ${img.width}`);
Here's a practical example: filtering images to keep only those larger than 400 pixels in either dimension:
function isLargeEnough(img, minDimension = 400) {
return img.naturalWidth >= minDimension || img.naturalHeight >= minDimension;
}
const allImages = Array.from(document.querySelectorAll('img'));
const largeImages = allImages.filter(isLargeEnough);
console.log(`Found ${largeImages.length} images at least ${minDimension}px in one direction`);
How Scalpel Images shows it
The size filter lets you set a minimum width and height. Scalpel Images matches against the natural size, not the display box, so setting min width 400px captures every file that's at least 400 pixels wide, regardless of how CSS has scaled it. Each image tile carries a dimensions badge showing width×height of the file. A very small display box with huge natural dimensions gets marked as a high-resolution image, because that's what you usually want to save.