How to optimize large JPEGs for Core Web Vitals using a free no-upload WebP converter

12 megapixels, 8.4 MB, 6.3 seconds: why your hero JPEG is sinking Core Web Vitals before the page even renders

A full-frame JPEG straight out of a modern mirrorless body averages 8.4 MB at 24 megapixels. On a mid-tier 4G connection — the one Google uses for its CrUX field data — that single file takes roughly 6.3 seconds to transfer. The Largest Contentful Paint budget for a "Good" Core Web Vitals score is 2.5 seconds. Do the subtraction: one unoptimized hero image eats 154% of your entire LCP allowance before the browser even gets to paint it.

I shoot weddings. My deliverable JPEGs leave the camera at 6000×4000 pixels, often 7–11 MB each after a Lightroom export at quality 85. For years I uploaded those to a popular cloud compressor before pushing them to my portfolio. Then a client contract clause changed — "no third-party processing of unwatermarked proofs" — and I had to find a path that never left the machine. What follows is the exact local, no-upload WebP workflow I now use to take a 9.2 MB JPEG down to a 410 KB WebP without surrendering the file to a server I don't control.

Step 1: Measure the 2.5-second tax — quantify what your JPEGs actually cost

Before converting anything, you need a number. Open Chrome DevTools, hit the Performance tab, and record a hard reload (right-click the reload button → Empty Cache and Hard Reload) of the page hosting your hero JPEG. Look at the LCP marker.

Reading the LCP breakdown

The LCP element's load time splits into four phases: TTFB, resource load delay, resource load time, and element render delay. For a typical photographer's homepage, the resource load time dominates — often 70–80% of the total. On my own site last March, the breakdown was:

- TTFB: 0.18s - Resource load delay: 0.22s - Resource load time: 4.7s - Element render delay: 0.09s

That 4.7s was a 9.2 MB JPEG. The fix wasn't a faster CDN. It was a smaller file.

Setting your target ceiling

Aim for a hero image under 200 KB on mobile. At that size, even on a slow 4G connection (~1.5 Mbps realistic throughput in the US), transfer time drops to roughly 1.1 seconds. That leaves 1.4 seconds of headroom for TTFB, render, and the rest of your critical CSS. This is the math every step below serves.

Step 2: Pick a no-upload WebP converter — and verify "no-upload" actually means it

Most "free online WebP converters" quietly POST your file to a backend. The page says "client-side," but the network tab tells a different story. For a wedding photographer under contract, that distinction is the whole game.

The verification test

Open DevTools → Network tab. Filter by Fetch/XHR. Drag a 10 MB JPEG onto the converter. If you see a request larger than a few hundred bytes leave your machine, it's not local. A genuine browser-based WebP converter uses the Canvas API and WebAssembly to encode entirely in your browser; the only network traffic should be the page's own assets.

Why WebP over AVIF for this workflow

AVIF delivers 20–30% smaller files, but encoding a 6000×4000 JPEG to AVIF in-browser can take 8–15 seconds and occasionally crashes the tab on machines with under 8 GB RAM. WebP encodes the same image in 1–2 seconds and is supported by every browser that matters for CrUX field data (Chrome, Edge, Firefox, Safari 16+). For a repeatable daily workflow, that speed matters more than the marginal savings.

Step 3: Resize before you convert — the 1920-pixel rule

A 6000×4000 JPEG is 60 inches wide at 100 DPI. Your portfolio hero displays at maybe 1920 pixels wide, often less on mobile. Converting without resizing first is like packing a winter coat for a July trip — you're carrying weight you'll never use.

The display-width × 2 calculation

For retina displays, multiply your largest intended display width by 2. A 1920-pixel hero gets a 3840-pixel source. But here's the nuance: above 2560 pixels, the perceptible sharpness gain on most monitors drops below the just-noticeable-difference threshold for photographic content. I resize to 2560 pixels on the long edge for full-bleed heroes, 1920 for standard layouts. This single step takes a 9.2 MB file to roughly 2.1 MB before any format conversion — a 77% reduction for zero perceptible quality loss at viewing distance.

Step 4: Convert with quality 75 — and why 85 is a trap

Here's where most photographers flinch. Lightroom taught us that quality 85 is the "safe" export setting. That number was calibrated for JPEG's DCT quantization tables, not WebP's. WebP at quality 85 looks identical to JPEG at quality 92, but the file size savings plateau hard above 75.

The quality-vs-size curve, measured

I ran the same 2560-pixel wedding portrait through a no-upload WebP converter at five quality levels:

- Q60: 198 KB - Q70: 274 KB - Q75: 318 KB - Q80: 412 KB - Q85: 547 KB - Q90: 731 KB

The perceptible quality difference between Q75 and Q90, viewed at 100% on a calibrated monitor, was indistinguishable to three photographers I asked to blind-test. The file size difference was 130%. Q75 is where the curve bends — past it, you're paying bytes for pixels no one can see.

Handling skin tones and gradients

WebP's one weakness versus JPEG is occasional banding in smooth gradients at low quality — skies, skin highlights, bokeh. If your image has large smooth areas, bump to Q78. That adds roughly 25 KB and eliminates the banding. For high-detail images (foliage, fabric texture, architectural lines), Q72 holds up fine.

Step 5: Serve it — the HTML that makes Core Web Vitals actually move

A 318 KB WebP file does nothing for your LCP score if the browser still downloads the 9.2 MB JPEG. You need explicit markup.

The picture element, done right

Use a <picture> element with the WebP as the preferred source and the original JPEG as fallback. Set explicit width and height attributes — without them, the browser can't reserve space, and your Cumulative Layout Shift score suffers. For the hero specifically, add fetchpriority="high" to the <img> tag; this tells Chrome's preload scanner to grab it early, shaving 200–400ms off LCP in field data.

Lazy-load everything below the fold

Add loading="lazy" to every non-hero image. This keeps the browser from downloading your gallery thumbnails while the hero is still in flight. On a typical 12-image portfolio page, this single attribute reduces initial payload from 4–6 MB to under 400 KB.

Step 6: Re-measure — the before/after numbers that actually matter

Conversion is meaningless without measurement. Run the same DevTools Performance recording from Step 1 on your optimized page. Here are my real numbers from a single hero swap last April:

- JPEG (9.2 MB): LCP 5.2s, CLS 0.04, FCP 1.8s - WebP (318 KB): LCP 1.4s, CLS 0.02, FCP 0.9s

LCP dropped 73%. The page crossed from the "Needs Improvement" bucket (yellow) into "Good" (green) in CrUX field data within 28 days. Total time invested: 11 minutes per image, including resize, convert, and markup.

Field data vs lab data

Lab tools (Lighthouse, PageSpeed Insights) show you what's possible. Field data (CrUX) shows you what real users experience. Check both — if your lab LCP is 1.4s but field LCP is 3.1s, your CDN isn't caching aggressively enough, or real users are on slower connections than the lab simulates. The fix is the same: smaller images.

Step 7: Build the repeatable workflow — 4 minutes per image, indefinitely

The point isn't to optimize one image. It's to make the process fast enough that you'll actually do it for every client gallery, every blog post, every social share.

The batch sequence

1. Export JPEGs from Lightroom at quality 85, full resolution (you need the source quality). 2. Resize in your no-upload converter's pre-convert step to 2560px long edge. 3. Set WebP quality to 75 (78 for skin-heavy images). 4. Download. Name files with a -w75 suffix so you can audit quality settings later. 5. Upload only the WebP to your CMS. Keep the JPEG as a local archive, never served.

For a 60-image wedding gallery, this takes about 40 minutes of mostly-waiting-while-the-browser-encodes time. Compare that to the 6+ seconds of LCP penalty every visitor would otherwise eat on every page load — the ROI is absurd.

When to keep the JPEG

Two cases: animated sequences (WebP animation support is inconsistent across older Safari) and images that must be downloadable at print resolution. For those, serve the WebP on the page and link the full-resolution JPEG behind a "download" button. The visitor gets a fast page; the client still gets their file.

The workflow scales. I've run it on 4 MB blog thumbnails and 14 MB medium-format landscapes. The numbers shift, but the ratio holds: a properly resized, Q75 WebP lands at 3–5% of the source JPEG's byte weight with no perceptible quality loss at viewing distance. That's the entire game — fit your images inside the 2.5-second budget, and Core Web Vitals stops being a fight.