Why Don’t Websites Put All Their Images Into One Giant JPEG? (Nerd-Sniped by My Brain)

Hey there!
I'm an electronics engineer who's dabbled in a bit of everything, including full-stack development and web3 technologies. I love building cool stuff and am always looking to connect with other like-minded professionals. When I'm not tinkering with new projects, you can find me scouring the internet for the latest and greatest in tech.
I had a simple question:
Why do websites load lots of individual images instead of stitching them into one giant image and cropping out the pieces they need?
At first glance, an image atlas sounds great.
Instead of this:
image-1.jpg
image-2.jpg
image-3.jpg
...
image-100.jpg
You create this:
site-atlas.jpg
Then each UI tile crops a specific region from the atlas.
That would mean:
fewer network requests
images arrive together
no staggered popping
maybe better perceived loading
maybe less request overhead
Not a new idea by any means. Games and UI libraries have used sprite sheets and texture atlases forever. The question is: why isn’t this the default for websites?
The experiment
I compared three approaches:
Individual optimized images
14 separate optimized JPG files
rendered as normal
<img>elements
Canvas atlas
one stitched atlas JPG
each tile rendered by cropping from the atlas into
<canvas>
CSS background atlas
one stitched atlas JPG
each tile rendered with
background-image,background-size, andbackground-position
The atlas was regenerated from the same optimized images, so the comparison was more fair.
NOTE: I ran the experiment by hosting it locally. so all the number you see are when you have the application served using a python server running locally.
If you want to poke at it yourself, the experiment is live here: https://daviddodda.com/experiments/img-atlas/
note: make sure you disable cache. try each version a couple of times.
What I measured
I focused on three headline metrics.
1. Transferred
How many bytes were downloaded?
2. Network complete
When did the last required image resource finish downloading?
3. Visible / ready
When was the image grid actually ready to see?
This last one matters because network completion is not the full story. The browser still has to decode images, rasterize, paint, composite, and show pixels.
Results
On a remote machine running Chromium, all files hosted locally, 10 runs each:
| Approach | Transferred | Network avg | Visible avg | Visible median |
|---|---|---|---|---|
| Individual images | 1.6 MB | 34.4 ms | 292.4 ms | 289.1 ms |
| Canvas atlas | 2.3 MB | 12.6 ms | 454.2 ms | 453.7 ms |
| CSS background atlas | 2.3 MB | 12.9 ms | 222.5 ms | 217.5 ms |
The surprising result:
The CSS background atlas was the fastest to visible.
The atlas won the network
The atlas had a clear network advantage:
Individual images: ~34 ms network complete
Atlas: ~13 ms network complete
Well, one larger request has less overhead than many smaller requests.
This effect is especially visible when the server/browser are using less optimal connection behavior. In my test, Chromium reported http/1.0 for the local server, so request overhead was more obvious than it would be under HTTP/2 or HTTP/3.
With modern HTTP/2 and HTTP/3, many individual image requests are less painful because requests can be multiplexed over one connection. But request overhead still exists.
The atlas has dummy pixels
The individual images transferred:
1.6 MB
The regenerated atlas transferred:
2.3 MB
Why?
Because an atlas is a rectangle. Real images have different aspect ratios. When you pack them into one big rectangular sheet, you often create empty space.
In my case:
Individual images total pixels: ~23.8M
Atlas pixels: ~31.2M
That is about 31% extra pixel area.
So even though the atlas used one request, it transferred more data and required the browser to decode a bigger image surface.
Canvas atlas sucks
The canvas atlas looked like it should be fast (thought modern hardware was fast enough). It loaded one atlas image, then cropped each tile into a canvas.
But the results were poor:
Canvas atlas visible avg: ~454 ms
The breakdown showed:
Atlas network complete: ~13 ms
Atlas decode wait after load: ~167 ms
Canvas crop drawing: ~6 ms
Paint/composite wait: ~261 ms
The actual JavaScript canvas drawing was not expensive.
The expensive part was making all those canvas results visible.
That means the bottleneck was not:
ctx.drawImage(...)
It was the browser’s later paint/composite work.
CSS background atlas does not suck
The CSS background atlas used normal DOM elements:
.tile {
background-image: url("./atlas/animals.optimized.jpg");
background-size: ...;
background-position: ...;
}
This was much faster:
CSS atlas visible avg: ~222 ms
The breakdown:
Atlas network complete: ~13 ms
Atlas decode wait after load: ~166 ms
Apply CSS background crops: ~2 ms
Paint/composite wait: ~36 ms
The decode cost was still there. But paint/composite was dramatically better than the canvas version.
So if you are going to do image atlasing in normal web UI, CSS backgrounds may be much better than drawing many cropped canvases.
When atlases do make sense
They are great for:
icons
sprites
emoji sheets
game textures
small repeated UI assets
known fixed-size tile sets
maps or tile-like interfaces
cases where all assets are needed immediately
They are less great for:
photo galleries
blog images
user-generated content
responsive images
content-heavy websites
long scrolling pages
frequently changing assets
now, don't go getting any ideas about rewriting your website's image pipeline to use image atlas. here are some reason why it's a really bad idea.
Why not use an image atlas?
1. You lose lazy loading
With individual images, the browser can load only what is needed:
<img loading="lazy" src="photo-83.jpg" />
With a giant atlas, loading one image means loading everything in that atlas.
That is great if you need everything immediately.
It is terrible if the user only sees 5% of the images.
2. You lose responsive images
The web has powerful responsive image tools:
<img
src="small.jpg"
srcset="small.jpg 480w, medium.jpg 960w, large.jpg 1600w"
sizes="(max-width: 600px) 100vw, 300px"
/>
The browser can choose the right image for the device, viewport, DPR, and network.
With a giant atlas, this becomes much harder. You may need multiple atlases:
atlas-1x.jpg
atlas-2x.jpg
atlas-mobile.jpg
atlas-desktop.jpg
atlas-avif.avif
atlas-webp.webp
The combinatorial complexity gets ugly quickly.
3. You often transfer extra pixels
Atlases require packing. Packing creates waste.
If the images have different shapes, the atlas may contain a lot of empty or unused area.
Even a good packing algorithm cannot always avoid this.
In my test, the atlas had about 31% more pixel area than the individual images.
4. One changed image invalidates the whole atlas
With individual images:
avatar-123.jpg changed
Only that image needs a new URL/cache entry.
With an atlas:
site-atlas.jpg changed
The whole atlas cache is invalidated.
That is bad for websites where content changes often.
5. Prioritization gets worse
Browsers are good at prioritizing resources.
The hero image can be high priority. Below-the-fold images can be lazy. Tiny thumbnails can wait.
With a giant atlas, everything has one priority.
You cannot easily say:
Load the hero image first,
then visible thumbnails,
then below-the-fold images.
The atlas is all-or-nothing.
6. Memory can be worse
A compressed JPG might be 2 MB on the network, but decoded pixels are much larger.
Decoded RGBA memory is roughly:
width × height × 4 bytes
A large atlas can become a huge decoded surface.
In my first broken atlas attempt, the atlas was:
9744 x 25942
That is around:
~1 GB decoded RGBA
Even if the file downloads quickly, that is a lot for the browser to decode, rasterize, and paint.
7. Accessibility and semantics are worse
An <img> has natural semantics:
<img src="rabbit.jpg" alt="Rabbit in grass" />
A CSS background image is decorative by default. If the image is meaningful content, you need to rebuild semantics with ARIA or hidden text.
That is doable, but it is extra work and easier to get wrong.
8. Browser optimizations favor normal images
Browsers have spent decades optimizing:
<img>
<picture>
srcset
lazy loading
decoding
fetch priority
cache behavior
progressive rendering
If you use an atlas, you bypass some of that machinery and take on more responsibility yourself.
Sometimes that is worth it. Often it is not.
What I learned
Every approach has its niche use case (shocker).
My brain nerd-sniped me into exploring and writing about this. It was fun seeing the cute animals load in though.





