DEV Community

Cover image for à 720 or 600à 600: What Each Size Does in pack_product.py and Why

à 720 or 600à 600: What Each Size Does in pack_product.py and Why

I spent an evening staring at a marketplace listing that looked fine on my laptop and terrible in the store grid. The cover I had uploaded was a square crop of a wide image, so the title text was half gone. The thumbnail in search results was blurry because the platform upscaled it. That was the moment I stopped treating cover images as an afterthought and wrote the sizing logic properly into pack_product.py. Here is what the two sizes actually do and why both exist.

What pack_product.py produces

The script takes a product folder, a title, a subtitle, and an asset count, and writes three things: the zip of files, a cover, and a thumbnail. The command looks like this:

python pack_product.py my-product "My Product" "A short subtitle" 9
Enter fullscreen mode Exit fullscreen mode

The cover is rendered at 1280×720. The thumbnail is rendered at 600×600. They are not two exports of the same layout. They are two different layouts, drawn separately, because they get used in different places with different aspect requirements.

Why the cover is 1280×720

The 16:9 ratio matches how product pages and listing previews display wide imagery. A 1280×720 canvas gives me room for a title on one or two lines, a subtitle underneath, and a small count badge like "9 assets" in a corner. At 720 pixels of height, text set at 40 to 48 points in Pillow stays crisp after marketplace compression. Below that, JPEG artifacts start eating the edges of letterforms, and a cover with fuzzy text reads as low effort, which kills conversion before anyone opens the zip.

The wide format also matches how I show products on my own pages. One render, two uses. When I tried square covers early on, wide slots cropped them and I lost the title every time.

Why the thumbnail is 600×600

Search grids and category pages are square. That is just how most storefronts lay things out. A 600×600 thumbnail is drawn as its own composition: the title centered, larger relative to the canvas, no subtitle, because at thumbnail size the subtitle is unreadable noise. I tested 300×300 first and it looked soft on retina screens. 600×600 holds up after platform-side downscaling and stays a small file, which matters when you are packaging 39 products and watching disk usage.

The key decision is that the thumbnail is not a resize of the cover. Resizing a 1280×720 image into a square means either cropping the sides, which cuts text, or letterboxing, which looks like a placeholder. Drawing the square separately takes maybe twenty extra lines of Pillow code and removes the whole class of problems.

How it stays cheap on a tiny server

The whole pipeline runs on 1 vCPU and 2 GB RAM with no GPU, so image work has rules. One process at a time, never parallel image jobs on a single core. gc.collect() after each product. Pillow rendering two images at these sizes uses well under 100 MB of peak RSS, which I check with resource.getrusage(RUSAGE_SELF).ru_maxrss rather than guessing. The heavy trick is rendering text at the final resolution directly instead of rendering large and scaling down, which would spike memory for no visual gain.

Output goes to a _system folder next to where you run the script, or wherever --dir or the PACK_ROOT environment variable points. That keeps generated covers out of your packs/ folder so the folder layout stays clean: assets/ for raw output, output/ for ready-to-submit files, packs/ for finished zips and covers.

The part that actually sold something

The first paid sale in this project was for the Digital Asset Factory itself, for $2.80. Small number, real signal. The listing used exactly this pairing: a 1280×720 cover for the product page and a 600×600 thumbnail for the grid. I cannot prove the covers caused the sale, but I can prove the earlier listings with cropped square covers got zero. Presentation is the only variable I changed.

The numbers behind the whole operation: 1,860 assets made, 1,077 ready or submitted, 39 products packaged, 856 MB of output, all on that same 1 vCPU, 2 GB box. Packaging was the boring step that made the difference between a folder of files and things a stranger could buy.

What I would tell my past self

Pick your two render sizes once, hardcode them, and draw each one as its own layout. Do not crop, do not upscale, do not reuse one image for both slots. Text at 600 pixels wide needs to be big and short. Text at 1280 pixels wide can breathe. And test on the actual marketplace, not your file manager, because the grid view is where buyers first see your work.

pack_product.py is one of four scripts in the kit I built from this work. The others handle the parts that stalled me even more: make_metadata.py writes the exact Adobe 5-column, Vecteezy 4-column, and Dreamstime 15-column CSV headers so uploads do not bounce, upload_tracker.py prevents double-posting re-runs, and md2pdf.py turns a Markdown doc into a styled PDF with reportlab. Two third-party packages total, Pillow and reportlab, everything else is standard library. It all ran on the tiny server described above.

If you want the same scripts instead of rebuilding the sizing and metadata logic from scratch, the full system is here:

Get digital asset factory


Building digital products? Free guides & kits for digital product sellers.

Top comments (0)