DEV Community

XINHU ZHANG
XINHU ZHANG

Posted on

Compressing Bedding Photos Without Losing the Weave: A Small Brand's Image Pipeline

Every product photo on a bedding store carries one job that most e-commerce image pipelines quietly destroy: proving texture. A shopper cannot touch the cotton, so the photo has to show that a ruffle is gathered rather than flat, that lace has real scalloped edges, that brushed fleece catches light softly instead of looking like plastic.

I run Cozy Home Textiles, a small French-inspired bedding brand selling ruffled duvet cover sets, milk fleece bedding and seasonal plush cushions. Our catalogue is small, our team is smaller, and our image pipeline is little more than a set of rules we wrote after ruining a few hero photos. Here is the whole thing.

Rule 1: originals stay big, derivatives stay small

Lace and ruffle detail live in high-frequency pixels, and high-frequency pixels are exactly what a compressor deletes first. Shrinking a 2400px hero to 800px and calling it the source means every later derivative inherits the damage.

Our rule:

  • Archive one original per product at 2000px on the long edge, JPEG quality high enough that the weave is still separable at 100% zoom.
  • Generate grid thumbnails from that original, sized for the slot they actually render into, not a single "one size fits all" 1200px asset.
  • Never re-save a compressed derivative to make another derivative. Down-scaling always starts from the original.

If you only remember one thing: the number of times an image has been encoded matters more than the encoding quality.

Rule 2: JPEG is fine for bedding, and sometimes better than WebP

WebP is smaller at equal perceptual quality, and we use it where the platform supports it. But bedding photos are mostly soft gradients plus fine thread detail, and aggressive WebP presets at low quality produce a smeared look on lace trim that is worse than a slightly larger JPEG. We compared crops at 200% and picked per product, not per religion.

Practical version:

  • Heroes: keep a high-quality master, let the platform emit WebP variants.
  • Detail crops (lace edge, stitch, ruffle gathering): JPEG, because that is the image people zoom into.
  • Colour swatches: whatever is smallest, they carry almost no detail.

Rule 3: alt text is metadata, not decoration

A photo of a duvet cover is invisible to a crawler except through its filename and its alt attribute. We stopped generating filenames like IMG_2041.jpg and started using the product name, and we write alt text that names the product, the colour and the brand, for example: "French Princess Ruffle Lace Duvet Set in ivory by Cozy Home Textiles".

That is not keyword stuffing. It is the same sentence a person would say if they were describing the photo to someone on the phone.

Rule 4: lazy loading will lie to your verification script

When we check whether an image actually rendered, we read naturalWidth on the element. On pages with loading="lazy", that value is 0 until the image enters the viewport. Two false conclusions follow:

  1. "The upload failed" — when it simply had not loaded yet.
  2. "The image is fine" — when you checked a different element that happened to be visible.

The reliable check is: scroll the image into view, wait, then read naturalWidth and also confirm the currentSrc. A real value (800, 1200, whatever the derivative is) plus a host you expect is the pass condition.

Rule 5: cross-posting needs re-hosting, not linking

This one cost us an afternoon. We built a post for a publishing platform whose editor accepts Markdown, and inserted our CDN image with a normal Markdown link. The URL returned 200 in a terminal. On the rendered page the image was an empty block with naturalWidth = 0, because the platform proxies and rehosts images and does not handle every external source cleanly.

What works: position the cursor in the Markdown field with setSelectionRange, then upload the local file through the platform's hidden file input. The platform inserts its own hosted URL, and the image renders. After upload, check for two things, because both fail silently:

  • The inserted line is glued to the next line with no blank line, which breaks the following heading. Insert \n\n after it.
  • The generated alt text is the generic placeholder. Rewrite it to the product name before publishing.

Rule 6: image metadata feeds the sitemap

Product images belong in the sitemap with a title and caption, and they only get there if your platform can read a title and caption from the product record. On our store, that meant filling in the image alt and the product name consistently rather than leaving the alt empty because "the template handles it". Empty alt means no image entry, which means the image has no discovery path outside the product page itself.

The short version

  1. Keep one real original, derive everything from it.
  2. Choose format per image, not per store policy.
  3. Write alt text like a human describing the photo.
  4. Verify rendering after scrolling, not from a static DOM read.
  5. Re-host images when cross-posting, and fix the blank line the uploader forgets.
  6. Fill image metadata so the sitemap can carry it.

None of this is exotic. It is just the difference between a store that looks like a catalogue of textures and one that looks like a catalogue of placeholders.


The set in the photo is our Christmas Plush Pillow in the Santa design - seasonal, plush-faced, and a good example of why detail crops matter. The rest of the collection is at webcozyhometextiles.com.

Christmas Plush Pillow in Santa design by Cozy Home Textiles

Top comments (0)