Back to Blog
Image Optimization

Responsive Images: Why My 4K Photo Looked Terrible on Phones

Uploaded beautiful 4K photos to website. Looked perfect on desktop. Tiny and blurry on phones. Took me days to figure out responsive images.

Jordan Lee8 min readGlobal
Responsive design across devices
Responsive design across devices
Tool Pairing

Use Image Kit while this guide is open

Compress, convert, and optimize the same image assets discussed in this guide.

Open Image Kit
Advertisement

Geographic Note: Responsive image techniques are universal and apply globally.

4K Photos That Looked Perfect on Desktop, Terrible on Mobile

Spent weekend taking professional product photos. 4K resolution. Perfect lighting. Beautiful detail. Uploaded to website. Desktop: gorgeous. Phone: tiny blurry mess. Spent next 3 days learning responsive images the hard way.

4K resolution. Perfect lighting. Beautiful detail.

Uploaded to website.

Desktop: gorgeous.

Phone: tiny blurry mess.

Spent next 3 days learning responsive images the hard way.

The Problem I Didn't Know I Had

What I did:

Uploaded 4000×3000 image.

Added to website:

<img src="product-4k.jpg" alt="Product photo">

On desktop (1920px wide):

Image displayed at 1200px wide. Looked great.

On phone (375px wide):

Image displayed at 375px wide.

But still downloaded 4000px wide image.

Wasted bandwidth. Slow loading. And image looked weird.

The waste:

Phone downloaded 3.2MB image.

Displayed it at 375px wide.

Could've downloaded 200KB image instead.

Downloaded 16× more data than needed.

Before vs After: Bandwidth & Performance Impact

Responsive images performance
Figure 1: Responsive images optimization impact
MetricBefore → AfterImprovementImpact on Users
Desktop Download3.2MB → 1.2MB-63%Faster page load
Tablet Download3.2MB → 800KB-75%Better experience
Mobile Download3.2MB → 180KB-94%Huge data savings
Desktop Load Time2.5s → 1.2s-52%Acceptable → Great
Mobile Load Time (4G)8.3s → 1.9s-77%Terrible → Good
Mobile Load Time (3G)24s → 4.2s-83%Unusable → OK
Bounce Rate68% → 42%-38%Users stay longer
Data Usage (50 images)160MB → 9MB-94%Mobile-friendly
Loading diagram...
3.2 MB
Mobile download before
180 KB
Mobile download after

Cost Analysis for 10,000 Mobile Users:

  • Before: 32GB total bandwidth, $0.08/GB = $2.56/image
  • After: 1.8GB total bandwidth, $0.08/GB = $0.14/image
  • Savings: $2.42 per image × 50 images = $121 saved per 10k users
  • Annual savings (1M users): $12,100

My First Failed Solution

Googled "make image responsive."

Found CSS:

img {
  max-width: 100%;
  height: auto;
}

Added this. Image scaled down on phone.

Problem:

Image still downloaded full 4K resolution.

Just displayed smaller.

Didn't fix bandwidth waste.

Didn't fix load time.

Just made big image display at small size.

CSS-Only vs Srcset: The Real Difference

ApproachHow It WorksPerformance / BandwidthBest For
CSS Onlymax-width: 100% scales imageSlow / Full size alwaysQuick fix, not production
SrcsetBrowser chooses right sizeFast / OptimizedProduction sites
Picture + SrcsetDifferent crops per deviceVery fast / OptimizedArt direction needed
Single Large ImageOne 4K image for allVery slow / WastefulNever use

Real Performance Comparison (Product Page with 8 Images):

MethodDesktop / TabletMobile 4G / 3GScore
CSS-only responsive28MB, 3.5s / 4.2s28MB, 8.3s / 24sFail
Srcset (3 sizes)8MB, 1.2s / 1.8s2MB, 2.1s / 4.5sGood
Srcset + WebP6MB, 1.0s / 1.5s1.5MB, 1.8s / 3.8sExcellent

The Srcset Discovery

Learned about srcset attribute.

Lets you provide multiple image sizes.

Browser picks appropriate size.

Example:

<img
  src="product-800.jpg"
  srcset="product-400.jpg 400w,
          product-800.jpg 800w,
          product-1200.jpg 1200w,
          product-2000.jpg 2000w"
  sizes="(max-width: 600px) 400px,
         (max-width: 1000px) 800px,
         1200px"
  alt="Product photo">

What this means:

"I have 4 versions of this image: 400px, 800px, 1200px, and 2000px wide.

On screens under 600px: use 400px version.

On screens 600-1000px: use 800px version.

On screens over 1000px: use 1200px version."

Browser downloads only what it needs.

Srcset & Sizes Configuration Guide

DeviceScreen / DisplayPixel Density → Actual SizeSrcset & Sizes
iPhone SE375px / 375px2× → 750px actualimage-800.jpg 800w + (max-width: 600px) 400px
iPhone 14390px / 390px3× → 1170px actualimage-1200.jpg 1200w + (max-width: 600px) 400px
iPad768px / 768px2× → 1536px actualimage-1600.jpg 1600w + (max-width: 1000px) 800px
Laptop1440px / 1200px1× → 1200px actualimage-1200.jpg 1200w + 1200px
Desktop 4K3840px / 1200px2× → 2400px actualimage-2400.jpg 2400w + 1200px

Recommended Image Size Strategy:

Target DeviceRecommended SizesWhy These SizesFile Size TargetExample Srcset
Mobile-first400w, 800w, 1200wCovers 99% of phones + retina180KB, 400KB, 800KBimg-400.jpg 400w, img-800.jpg 800w, img-1200.jpg 1200w
Tablet-optimized400w, 800w, 1200w, 1600wAdds iPad landscape support+1MBAdd img-1600.jpg 1600w
Desktop-heavy600w, 1200w, 2000wSkip 400w, focus on desktop300KB, 800KB, 1.5MBimg-600.jpg 600w, img-1200.jpg 1200w, img-2000.jpg 2000w
4K displays800w, 1600w, 2400wHigh-end displays500KB, 1.2MB, 2MBimg-800.jpg 800w, img-1600.jpg 1600w, img-2400.jpg 2400w

Sizes Attribute Cheat Sheet:

LayoutSizes ValueWhen to UseExample
Full width100vwHero images, full-width bannerssizes="100vw"
Sidebar content(max-width: 1000px) 100vw, 300pxSidebar imagessizes="(max-width: 1000px) 100vw, 300px"
Grid (2 columns)(max-width: 600px) 100vw, 50vwProduct gridssizes="(max-width: 600px) 100vw, 50vw"
Fixed width800pxFixed-size content areasizes="800px"
Complex layoutMultiple breakpointsResponsive layoutssizes="(max-width: 600px) 100vw, (max-width: 1000px) 50vw, 800px"

Creating Multiple Image Sizes

Multiple image sizes generation
Figure 2: Creating multiple sizes for responsive images

Now had new problem: needed to create 4 versions of every image.

Manual approach (terrible):

Open image in Photoshop.

Resize to 400px. Save.

Resize to 800px. Save.

Resize to 1200px. Save.

Resize to 2000px. Save.

For 50 product images: would take all day.

Automated approach (better):

Used ImageMagick:

mogrify -path ./400/ -resize 400x *.jpg
mogrify -path ./800/ -resize 800x *.jpg
mogrify -path ./1200/ -resize 1200x *.jpg
mogrify -path ./2000/ -resize 2000x *.jpg

Processed all 50 images in 2 minutes.

The Sizes Attribute Confusion

srcset tells browser what sizes are available.

sizes tells browser how wide image will be displayed.

Got this wrong first time:

<img srcset="image-400.jpg 400w, image-800.jpg 800w">

No sizes attribute.

Browser didn't know which to use. Sometimes picked wrong one.

Fixed it:

<img
  srcset="image-400.jpg 400w, image-800.jpg 800w"
  sizes="(max-width: 600px) 400px, 800px">

Now browser knows:

"On small screens (under 600px), image displays at 400px, so download 400w version.

On larger screens, image displays at 800px, so download 800w version."

Retina Display Complication

iPhone has retina display.

2× pixel density.

400px wide image needs 800px image file to look sharp.

My solution:

Created extra large versions for retina.

Screen width 400px: serve 800px image (for 2× displays)

Screen width 800px: serve 1600px image (for 2× displays)

<img
  srcset="image-800.jpg 800w,
          image-1600.jpg 1600w"
  sizes="(max-width: 600px) 400px, 800px">

Browser automatically picks higher resolution for retina displays.

The Art Direction Problem

Product photo: horizontal (landscape).

Worked great on desktop.

On phone: too wide. Important details too small.

What I needed:

Desktop: landscape crop showing full product

Mobile: portrait crop focusing on product detail

Can't do this with srcset alone. Same crop, different sizes.

Solution: <picture> element

<picture>
  <source media="(max-width: 600px)"
          srcset="product-mobile-portrait.jpg">
  <source media="(min-width: 601px)"
          srcset="product-desktop-landscape.jpg">
  <img src="product-desktop-landscape.jpg" alt="Product">
</picture>

Different crop for different screen sizes.

Picture Element vs Srcset: When to Use Which

FeatureSrcsetPicture + SrcsetMy Recommendation
Use CaseSame image, different sizesDifferent crops/formats per deviceSrcset for 90% of cases
ComplexitySimpleComplexStart with srcset
Browser SelectionAutomaticManual controlPicture when you need control
Art DirectionNot possibleYesPicture for art direction
Format SwitchingNoYes (WebP fallback)Picture for WebP + JPEG
File Management3-4 versions6-8+ versionsSrcset is easier
Code MaintenanceEasyHarderSrcset is simpler
PerformanceExcellentExcellentBoth perform well

Decision Tree:

ScenarioSolutionApproach
Same image, different sizesUse srcsetMultiple widths (400w, 800w)
Different crop for mobileUse <picture>Media query for mobile crop
WebP with JPEG fallbackUse <picture>Type attribute for WebP
Portrait on mobile, landscape on desktopUse <picture>Different aspect ratios
Retina displaysUse srcset with 2× sizes800w + 1600w versions

Real Examples:

TaskWrong ApproachRight ApproachWhy
Product photopicture with 4 sourcesimg srcset with 3 sizesOverkill, same crop
Hero bannerSingle 4K imageimg srcset 3-4 sizesSaves bandwidth
Logo with detailsimg srcset same croppicture mobile close-upMobile needs zoomed crop
Modern formatSeparate WebP pagespicture WebP + JPEGAutomatic fallback

Real Example: Before and After

Before (single image):

<img src="product-4k.jpg" alt="Product">

Desktop: downloads 3.2MB, displays 1200px wide ✓

Phone: downloads 3.2MB, displays 375px wide ✗

After (responsive):

<img
  src="product-1200.jpg"
  srcset="product-400.jpg 400w,
          product-800.jpg 800w,
          product-1200.jpg 1200w,
          product-2000.jpg 2000w"
  sizes="(max-width: 600px) 400px,
         (max-width: 1000px) 800px,
         1200px"
  alt="Product">

Desktop: downloads 1.2MB (1200px image), displays 1200px wide ✓

Phone: downloads 180KB (400px image), displays 375px wide ✓

Savings:

Desktop: 63% less bandwidth

Mobile: 94% less bandwidth

When Srcset Got Weird

Product image on homepage.

Desktop: displays at 600px wide (part of grid)

Mobile: displays at 375px wide (full width)

Tablet: displays at 800px wide (landscape mode)

My srcset:

<img srcset="product-600.jpg 600w, product-800.jpg 800w">

Tablet downloaded 800px image. Correct.

But desktop also downloaded 800px image.

Displayed it at 600px.

Wasted bandwidth.

The problem:

Didn't have 600px version in srcset. Browser chose next size up.

Fixed:

Added exact sizes I needed:

<img srcset="product-400.jpg 400w,
             product-600.jpg 600w,
             product-800.jpg 800w">

Now each screen size gets exactly what it needs.

The Fallback for Old Browsers

Old browsers don't support srcset.

Solution: src attribute

<img
  src="product-800.jpg"
  srcset="product-400.jpg 400w, product-1200.jpg 1200w">

Modern browsers: use srcset.

Old browsers: ignore srcset, use src.

src acts as fallback.

Use medium-sized image for fallback. Compromise between quality and size.

Lazy Loading + Srcset

Can combine lazy loading with responsive images:

<img
  src="product-800.jpg"
  srcset="product-400.jpg 400w,
          product-800.jpg 800w,
          product-1200.jpg 1200w"
  sizes="(max-width: 600px) 400px, 800px"
  loading="lazy"
  alt="Product">

Gets benefits of both:

  • Responsive images (right size for screen)
  • Lazy loading (only loads when needed)

Perfect for image galleries.

File Naming Convention

Managing multiple versions got messy fast.

Bad naming:

product1.jpg
product1_small.jpg
product1_2.jpg
product1_large.jpg

Which is which? No idea.

Good naming:

product-leather-wallet-400w.jpg
product-leather-wallet-800w.jpg
product-leather-wallet-1200w.jpg
product-leather-wallet-2000w.jpg

Width in filename. Instantly clear.

File Naming Convention Best Practices

Naming StyleExamplePros / ConsRating / When to Use
Width suffixproduct-wallet-400w.jpgClear size, sortable / Longer namesBest / Always recommended
Descriptive sizeproduct-wallet-small.jpgHuman-readable / AmbiguousPoor / Avoid
Number suffixproduct-wallet-1.jpgShort / No size infoTerrible / Never use
Resolution marker[email protected]Standard for retina / Only 2 sizesOK / Simple retina only
Dimension bothproduct-wallet-800x600.jpgExact dimensions / VerboseGood / Aspect ratio matters
Mixed folder400/product-wallet.jpgClean names / Harder to manageOK / Large projects

My Recommended Naming System:

Image TypeNaming PatternExampleWhy
Product photos[category]-[product]-[width]w.[ext]shoes-nike-air-max-800w.jpgSEO-friendly + clear size
Hero images[page]-hero-[width]w.[ext]homepage-hero-1200w.jpgPurpose + size clear
Thumbnails[category]-[item]-thumb-[width]w.[ext]blog-post-123-thumb-400w.jpgExplicit thumbnail marker
Art direction[base]-[device]-[width]w.[ext]hero-mobile-600w.jpg, hero-desktop-1200w.jpgDevice-specific crops

Bandwidth Savings Reality

50 product pages. Each page: 8 product images.

Before responsive images:

Average page load: 28MB (all images at 4K)

Mobile users complained about slow loading.

After responsive images:

Desktop: 8MB per page

Mobile: 2MB per page

Tablet: 4MB per page

Result:

Mobile users: 93% less data

Complaints stopped.

Load time: 8 seconds → 2 seconds on mobile.

The WebP Complication

WebP images are smaller than JPEG.

Can combine with responsive images:

<picture>
  <source type="image/webp"
          srcset="product-400.webp 400w,
                  product-800.webp 800w">
  <img src="product-800.jpg"
       srcset="product-400.jpg 400w,
               product-800.jpg 800w"
       alt="Product">
</picture>

Browser supports WebP: uses WebP versions.

Browser doesn't support WebP: falls back to JPEG.

Even more optimization.

But also more files to manage.

Automated Solution I Use Now

Manually creating 4 versions of each image is tedious.

Current workflow:

  1. Upload original high-res image
  2. Build script automatically creates all versions
  3. Script outputs srcset HTML

Script (simplified):

# Create versions
convert input.jpg -resize 400x output-400.jpg
convert input.jpg -resize 800x output-800.jpg
convert input.jpg -resize 1200x output-1200.jpg

# Generate HTML
echo '<img src="output-800.jpg"
      srcset="output-400.jpg 400w,
              output-800.jpg 800w,
              output-1200.jpg 1200w"
      sizes="(max-width: 600px) 400px, 800px"
      alt="Description">'

Upload one image. Get responsive version automatically.

Testing Responsive Images

How to verify it works:

Chrome DevTools → Network tab

Resize browser window.

Refresh page.

Check which image file downloaded.

Small window: should download small image.

Large window: should download large image.

My test:

Desktop (1920px): downloads product-1200.jpg ✓

Resize to 600px: refresh, downloads product-400.jpg ✓

Works.

Common Mistakes I Made

Mistake 1: Too many sizes

Created versions: 200w, 400w, 600w, 800w, 1000w, 1200w, 1600w, 2000w

Eight versions per image.

Overkill. Most barely used.

Better:

3-4 versions covers most cases: 400w, 800w, 1200w, 2000w (for 4K displays).

Mistake 2: Sizes don't match actual display

sizes="800px" but image actually displays at 600px.

Browser downloads larger than needed.

Measure actual display width. Use that in sizes.

Mistake 3: Forgot alt text

Easy to forget when focused on srcset complexity.

Alt text still required. Always.

Common Responsive Image Mistakes & Fixes

MistakeWhat Happens / ImpactHow to FixFrequency
Too many sizes8+ versions / Storage bloatUse 3-4 sizes: 400w, 800w, 1200w, 2000wCommon
Sizes mismatchsizes="800px" displays 600px / Downloads largerMeasure actual width in DevToolsVery common
Missing sizesOnly srcset / Browser guesses wrongAlways add sizes attributeCommon
Forgot fallbackNo src / Breaks old browsersAdd src with medium sizeRare
Wrong aspect ratioMobile gets desktop crop / Poor UXUse <picture> for different cropsCommon
Missing alt textAccessibility issue / SEO failureAdd descriptive alt textCommon
No WebP fallbackMissing 30% savings / SlowerUse <picture> WebP + JPEGRare
Retina ignoredBlurry on iPhone / Poor qualityProvide 2× image sizesCommon
Not testingDoesn't work / Wastes effortTest with DevTools Network tabVery common
Lazy load conflictImages don't load / BrokenTest lazy + srcset integrationRare

Most Critical Fixes (Priority Order):

  1. Always add sizes attribute (90% of issues)
  2. Test with DevTools Network tab (verify correct size loads)
  3. Use 3-4 sizes max (don't over-engineer)
  4. Measure actual display width (don't guess)
  5. Include src fallback (old browser support)

Bottom Line

Responsive images aren't optional anymore.

60% of web traffic is mobile.

Serving 4K images to phones wastes data and makes site slow.

Minimum viable implementation:

<img
  src="image-800.jpg"
  srcset="image-400.jpg 400w,
          image-800.jpg 800w,
          image-1200.jpg 1200w"
  sizes="(max-width: 600px) 400px, 800px"
  alt="Description">

Three image sizes.

Appropriate size for mobile, tablet, desktop.

My results:

Page load time on mobile: 8s → 2s

Bandwidth usage: -93% on mobile

User complaints: stopped

Takes time to set up.

But once you have automated process, it's just part of workflow.

Your mobile users will thank you.

Complete Responsive Images Implementation Workflow

Loading diagram...

Step-by-Step Time Breakdown:

StepTime per ImageTime for 50 ImagesCan Automate?
1. Analyze usage1 min1 hour (bulk plan)Partial
2. Create versions10 min manual / 10 sec auto8 hours / 8 minYes
3. Write HTML3 min manual / instant auto2.5 hours / instantYes
4. Add attributes2 min1.5 hoursPartial
5. Test5 min4 hoursManual
6. Optimize2 min1.5 hoursYes
Total23 min manual / 12 min auto19 hours / 10 hours50% automatable

ROI Timeline:

  • Setup: 2-3 days for automation workflow
  • Per-image: 12 min automated vs 23 min manual
  • Payback: After 109 images (~2 weeks for most sites)
  • Ongoing: 48% time savings forever

Your Responsive Images Implementation Checklist

PhaseTaskRequired / TimeDifficulty / ImpactNotes
📋 PlanningDefine target screen sizesYes / 15 minEasy / CriticalMobile, tablet, desktop minimums
Determine how many versionsYes / 10 minEasy / High3-4 sizes recommended
Check if art direction neededOptional / 15 minMedium / MediumDifferent crops? Use <picture>
🛠️ SetupInstall image processing toolYes / 30 minMedium / CriticalImageMagick, Sharp, or online
Create automation scriptOptional / 2 hoursHard / HighSaves massive time
Define naming conventionYes / 10 minEasy / HighUse -[width]w suffix
🖼️ GenerationResize all images (automated)Yes / 10 minEasy / CriticalBatch process
Compress each versionYes / 20 minEasy / HighAim <200KB each
Create WebP versionsOptional / 15 minEasy / Medium30% extra savings
💻 CodeAdd srcset attributeYes / 3 min/imgEasy / CriticalList all sizes
Add sizes attributeYes / 2 min/imgMedium / CriticalMatch actual display
Add src fallbackYes / 30 sec/imgEasy / HighFor old browsers
Add alt textYes / 1 min/imgEasy / CriticalSEO + accessibility
Add lazy loadingOptional / 10 sec/imgEasy / MediumBelow-fold images
🧪 TestingTest in DevToolsYes / 5 min/imgMedium / CriticalVerify correct size loads
Test on real iPhoneYes / 3 minEasy / HighCheck retina display
Test on real AndroidOptional / 3 minEasy / MediumDifferent pixel densities
Check page load speedYes / 5 minEasy / HighPageSpeed Insights
MonitorTrack bandwidth savingsOptional / 10 min/weekEasy / LowAnalytics
Monitor load timesYes / 5 min/weekEasy / MediumStay under 2 seconds
Check mobile bounce rateOptional / 5 min/weekEasy / HighShould improve 20-40%

Quick Start (Minimum Viable):

  1. Create 3 sizes: 400w, 800w, 1200w (30 min for 50 images)
  2. Add srcset + sizes + src + alt (5 min per image)
  3. Test in DevTools (5 min per image)
  4. Total: ~10 min per image, 93% bandwidth savings

Full Implementation (Recommended):

  1. Everything above PLUS:
  2. WebP versions
  3. Lazy loading
  4. Real device testing
  5. Total: ~15 min per image, 95% bandwidth savings

Data Sources

This article is based on global best practices and standards:

Note: Tool availability, features, and best practices may vary by region. Always verify compliance with local regulations before using any service.


Related Reading

Explore more image optimization articles to improve your website performance:

Advertisement
Next Reading

Related guides worth indexing with this article

See all articles