Images Are the Heaviest Part of the Web
The median web page transfers over 2 MB of images. For media-heavy sites — e-commerce, real estate, travel, news — that number climbs to 5-10 MB. On a 4G mobile connection, that’s 3-8 seconds of loading just for images.
Google measures this. Largest Contentful Paint (LCP) — the time until the largest visible element renders — is a Core Web Vital that directly affects search ranking. For most pages, the largest element is an image. Slow images mean lower rankings, higher bounce rates, and fewer conversions.
Image optimization isn’t a nice-to-have. It’s the single highest-impact performance improvement for most websites. This guide covers the complete workflow: choosing formats, compressing effectively, sizing for different screens, and building automated pipelines.
Image Formats: Choosing the Right One
Four formats dominate the web. Each has a specific use case.
JPEG
The workhorse of web images since 1992. Lossy compression with a quality parameter (1-100) that controls the size-quality tradeoff. Universal browser support.
Use for: photographs, complex images with many colors, images where a small quality loss is acceptable.
Quality guidelines: 80-90 for hero images, 70-80 for thumbnails and cards, 60-70 for background images.
Limitations: no transparency, no lossless mode, compression artifacts visible at low quality settings (blocky patterns around edges).
PNG
Lossless compression. Supports transparency (alpha channel). Files are significantly larger than JPEG for photographs.
Use for: screenshots, graphics with sharp edges and text, images requiring transparency, icons and logos.
Limitations: large file sizes for photos (3-5x larger than equivalent JPEG), no lossy mode for size reduction.
WebP
Developed by Google, now supported by 97%+ of browsers. Offers both lossy and lossless modes, plus transparency. 25-34% smaller than JPEG at equivalent quality.
Use for: everything on the web where browser support isn’t a concern. WebP should be your default format.
Quality guidelines: 80-85 for general web images, 75 for thumbnails, 85-90 for hero images.
Limitations: slightly less saturated colors than JPEG at very low quality settings, some older tools don’t support it.
AVIF
The newest contender. Based on the AV1 video codec. 50% smaller than JPEG in many cases. Supports transparency, HDR, and wide color gamut. 92%+ browser support.
Use for: hero images and product photos where maximum compression matters. Best results on photographic content.
Quality guidelines: 65-75 for general use (AVIF maintains quality at lower numbers than JPEG), 75-80 for hero images.
Limitations: slower to encode than WebP, slightly less browser support, some edge cases with very detailed textures.
The Decision Framework
For most websites, the hierarchy is:
- Serve AVIF when the browser supports it (hero images, product photos)
- Fall back to WebP for broad compatibility
- Fall back to JPEG for legacy contexts (email, old systems)
- Use PNG only when you need lossless quality or transparency
Implement this with the <picture> element:
<picture>
<source srcset="/images/hero.avif" type="image/avif">
<source srcset="/images/hero.webp" type="image/webp">
<img src="/images/hero.jpg" alt="Hero image" width="1200" height="800">
</picture>
Compression: Finding the Quality Sweet Spot
Compression quality has diminishing returns. The difference between quality 100 and quality 90 is invisible to most viewers. The file size difference is 40-60%.
The Quality Curve
At quality 100, you’re storing nearly every pixel perfectly. At quality 90, you’re losing imperceptible detail. At quality 80, most viewers can’t tell the difference from the original. Below quality 60, artifacts become visible — blocky patterns in JPEG, color banding in WebP.
The practical sweet spots by format:
| Format | High quality | Standard web | Thumbnails | Background |
|---|---|---|---|---|
| JPEG | 90 | 80-85 | 70-75 | 60-70 |
| WebP | 90 | 80-85 | 75 | 65-70 |
| AVIF | 80 | 65-75 | 60-65 | 50-60 |
AVIF maintains visual quality at lower numbers than JPEG and WebP — a quality 70 AVIF often looks as good as a quality 85 JPEG.
Compress to a Target File Size
Sometimes the requirement isn’t “quality 85” — it’s “under 500KB.” Email attachment limits, mobile app asset budgets, CMS upload restrictions.
The Image Transformation API has a compress_to_size operation that handles this:
import { IterationLayer } from "iterationlayer";
const client = new IterationLayer({ apiKey: "YOUR_API_KEY" });
const result = await client.transform({
file: { type: "url", name: "photo.jpg", url: sourceUrl },
operations: [
{ type: "compress_to_size", max_file_size_in_bytes: 500_000 },
],
});
The operation uses a quality-first strategy: it reduces JPEG quality from 85 down to 50. If the image is still over the target, it reduces dimensions. Quality-first preserves visual quality better than dimension-first because quality reduction removes imperceptible detail, while dimension reduction removes visible content.
Resizing: Never Serve Larger Than Display Size
A 4000x3000 photo displayed at 800x600 wastes 96% of its pixels. The browser downloads 5 MB to display 300 KB worth of content. This is the most common image performance mistake.
Resize to Display Size
If your hero image displays at 1200px wide on desktop, serve a 1200px wide image. Not the 4000px original from the photographer.
const operations = [
{ type: "resize", width_in_px: 1200, height_in_px: 800, fit: "cover" },
{ type: "convert", format: "webp", quality: 85 },
];
Fit Strategies
The fit parameter controls how the image fills the target dimensions:
-
cover— scales to fill the target, crops overflow. Result is exactly the target dimensions. Best for thumbnails, cards, hero images — anything needing exact dimensions. -
contain— scales to fit within the target, adds letterboxing. Result is at most the target dimensions. Best when the full image must be visible. -
inside— scales down to fit within the target, preserves aspect ratio, never upscales. Best for constraining maximum dimensions. -
fill— stretches to exactly the target dimensions. Distorts the image. Rarely what you want. -
outside— scales to cover the target, preserves aspect ratio, never downscales. Best for guaranteeing minimum dimensions.
For most web use cases, cover (exact dimensions, cropped) and inside (constrained, not cropped) handle everything.
Responsive Images: Different Sizes for Different Screens
A 4K monitor needs a larger image than a phone screen. Serving the desktop image to mobile wastes bandwidth. Serving the mobile image to desktop looks blurry.
srcset and sizes
HTML’s responsive image attributes let the browser choose the right image size:
<img
srcset="/images/hero-320.webp 320w,
/images/hero-640.webp 640w,
/images/hero-960.webp 960w,
/images/hero-1280.webp 1280w,
/images/hero-1920.webp 1920w"
sizes="100vw"
src="/images/hero-960.webp"
alt="Hero image"
width="1920"
height="1080"
>
The srcset tells the browser which sizes are available. The sizes tells the browser how wide the image will be at the current viewport. The browser picks the smallest image that covers the display area.
Generating Responsive Variants
Generate all variants from a single source image:
import { IterationLayer } from "iterationlayer";
const client = new IterationLayer({ apiKey: "YOUR_API_KEY" });
const widths = [320, 640, 960, 1280, 1920];
const variants = await Promise.all(
widths.map(async (width) => {
const result = await client.transform({
file: { type: "url", name: "hero.jpg", url: sourceUrl },
operations: [
{ type: "resize", width_in_px: width, height_in_px: Math.round(width * 0.5625), fit: "cover" },
{ type: "sharpen", sigma: 0.5 },
{ type: "convert", format: "webp", quality: 85 },
],
});
const { data: { buffer } } = result;
return { width, buffer: Buffer.from(buffer, "base64") };
})
);
Five sizes, WebP format, optimized quality, sharpened after downscaling. Each variant is a single API call.
Art Direction with picture
When different aspect ratios are needed for different screen sizes — landscape on desktop, square on mobile — use the <picture> element:
<picture>
<source media="(max-width: 768px)" srcset="/images/hero-mobile.webp" type="image/webp">
<source media="(min-width: 769px)" srcset="/images/hero-desktop.webp" type="image/webp">
<img src="/images/hero-desktop.jpg" alt="Hero image">
</picture>
Generate the mobile variant with different dimensions or a different crop. The smart_crop operation is useful here — it detects the subject and crops around it, so the focal point is preserved across aspect ratios.
// Mobile: square crop, subject-centered
const mobileOps = [
{ type: "smart_crop", width_in_px: 640, height_in_px: 640 },
{ type: "convert", format: "webp", quality: 85 },
];
// Desktop: landscape, standard resize
const desktopOps = [
{ type: "resize", width_in_px: 1920, height_in_px: 1080, fit: "cover" },
{ type: "convert", format: "webp", quality: 85 },
];
Sharpening After Downscaling
Downscaling images softens them slightly — a 4000px image resized to 800px loses some perceived sharpness even though the content is identical. A light sharpen pass after resizing restores crispness without introducing artifacts.
const operations = [
{ type: "resize", width_in_px: 800, height_in_px: 600, fit: "cover" },
{ type: "sharpen", sigma: 0.5 },
{ type: "convert", format: "webp", quality: 85 },
];
Keep the sigma low — 0.3 to 0.7. Higher values create visible halos around edges that look worse than the softness you’re trying to fix.
Order matters: sharpen after resize, convert last. Sharpening before resize wastes effort (the resize will soften it again). Converting before the final step may introduce artifacts that get amplified by later operations.
Smart Cropping for Responsive Thumbnails
Traditional center-cropping works when the subject is centered. It fails when the subject is off to one side — a portrait where the person stands left of frame, a product photo with the item in the lower-right corner.
The smart_crop operation uses AI object detection to find the main subject before cropping:
const operations = [
{ type: "smart_crop", width_in_px: 400, height_in_px: 400 },
{ type: "convert", format: "webp", quality: 80 },
];
The result is a 400x400 image with the subject centered, regardless of where it was in the original. No coordinate math, no face detection library, no per-image manual adjustment.
Automated Pipelines
The Basic Pipeline
A production image optimization pipeline combines resizing, sharpening, and conversion:
import { IterationLayer } from "iterationlayer";
const client = new IterationLayer({ apiKey: "YOUR_API_KEY" });
const optimizeImage = async (sourceUrl: string, targetWidth: number) => {
const result = await client.transform({
file: { type: "url", name: "image.jpg", url: sourceUrl },
operations: [
{ type: "resize", width_in_px: targetWidth, height_in_px: Math.round(targetWidth * 0.667), fit: "cover" },
{ type: "auto_contrast" },
{ type: "sharpen", sigma: 0.5 },
{ type: "convert", format: "webp", quality: 85 },
],
});
const { data: { buffer } } = result;
return Buffer.from(buffer, "base64");
};
Four operations, one API call. Resize, auto-contrast, sharpen, convert. No server to maintain.
The Complete Pipeline with Variants
For a production content pipeline, generate multiple formats and sizes from each source image:
import { IterationLayer } from "iterationlayer";
const client = new IterationLayer({ apiKey: "YOUR_API_KEY" });
const generateImageSet = async (sourceUrl: string) => {
const widths = [320, 640, 960, 1280, 1920];
const formats = ["webp", "avif"] as const;
const variants = await Promise.all(
formats.flatMap((format) =>
widths.map(async (width) => {
const quality = format === "avif" ? 70 : 85;
const result = await client.transform({
file: { type: "url", name: "image.jpg", url: sourceUrl },
operations: [
{ type: "resize", width_in_px: width, height_in_px: Math.round(width * 0.667), fit: "cover" },
{ type: "sharpen", sigma: 0.5 },
{ type: "convert", format, quality },
],
});
const { data: { buffer } } = result;
return { width, format, buffer: Buffer.from(buffer, "base64") };
})
)
);
return variants;
};
This generates 10 variants (5 widths x 2 formats) from a single source image. Upload each variant to your CDN and reference them in srcset and <source> elements.
Measuring Performance
Core Web Vitals
Three metrics that matter for image optimization:
Largest Contentful Paint (LCP) — time until the largest element renders. Smaller images load faster. Target: under 2.5 seconds.
Cumulative Layout Shift (CLS) — layout instability caused by elements loading and shifting content. Set width and height on <img> tags so the browser reserves space before the image loads. Target: under 0.1.
First Contentful Paint (FCP) — time until any content renders. Large images on the critical rendering path delay FCP. Lazy-load below-the-fold images.
Lighthouse
Run Lighthouse audits regularly. The “Opportunities” section flags specific images that could be resized, compressed, or converted to modern formats. It estimates the byte savings for each suggestion.
Real User Monitoring
Lab tools like Lighthouse test under controlled conditions. Real user monitoring (Vercel Analytics, web-vitals library, Google CrUX) shows actual performance across real devices and connections. An image that loads fine on fiber may be the bottleneck on 3G.
Lazy Loading
Images below the fold — not visible when the page first loads — should lazy-load. The browser fetches them only when they scroll into view.
<img src="/images/product.webp" alt="Product" loading="lazy" width="400" height="400">
The loading="lazy" attribute is supported by all modern browsers. No JavaScript needed.
Do NOT lazy-load the hero image or any above-the-fold content. Those images are on the critical rendering path — they need to load immediately.
CDN Delivery
Serve optimized images from a CDN (Content Delivery Network). The CDN caches images at edge locations close to the user, reducing latency.
Most CDNs support cache headers. Set a long Cache-Control max-age on image URLs that include a content hash — the URL changes when the image changes, so stale cache isn’t a concern:
Cache-Control: public, max-age=31536000, immutable
This tells the browser and CDN to cache the image for one year without revalidation.
Putting It All Together
A complete image optimization workflow:
- Upload — accept the original image (any format, any size)
- Process — resize to target dimensions, sharpen, convert to WebP and AVIF
- Generate variants — multiple sizes for responsive delivery
- Store — upload processed variants to object storage (S3, R2, GCS)
- Serve — deliver through a CDN with long cache headers
-
Reference — use
srcset,sizes, and<picture>in your HTML
Steps 2-3 are the API calls. Steps 4-6 are your infrastructure. The processing itself is one API call per variant — no servers to scale, no libraries to patch, no image processing infrastructure to maintain.
Get Started
Check the docs for the full operation reference and format-specific quality guidelines. The TypeScript and Python SDKs handle file input and response parsing.
Sign up for a free account — no credit card required. Take your heaviest page image, run it through the pipeline, and measure the before-and-after file size.