Leveraging Image Models to Create 1st Party Product Images

Published: 2025-06-09 · #Node.js · #Express · #Vanilla JS · #Claude Haiku Vision · #Gemini 2.5 Flash Image · #Google Drive API · #Railway · #Multer

Problem

Sales sourced our first beauty chain: five outlets, and a deal that hinged on whether we could get their catalogue online. Most of their products had no barcodes, which meant our existing onboarding pipeline could not identify them. If we could not digitize that inventory, the deal was off.

There was a second catch. Raw in-store photos are not fit to publish. Merchants can already upload their own product images to Fairmart, but we replace them before anything goes live, because the quality is inconsistent. So the job was not just data entry. It was producing a clean, publishable image for every product.

The only conventional path was studio photography plus writing each title by hand, quoted at around 25,000. The client would not fund that. Without a cheaper route, there was no deal.

The constraints were tight. I had one week. Engineering had no bandwidth to take on a build. And the core approach was unproven: we had never tried generating product images at scale, so it was genuinely unclear whether this would work at all.

Approach

The user is an onboarding teammate standing in a store. Their job is to convert a shelf of physical products into rows of listings, each with a clean title and a publishable image, that can be bulk-uploaded into Fairmart.

I scoped the tool to exactly that loop: capture a photo, extract a title, generate a clean image, store the originals for records, and export a file. Claude Haiku Vision reads the product title from the raw photo. Gemini 2.5 Flash Image regenerates that photo as a studio-style shot. Both images go to a Google Drive Shared Drive, and each row exports to CSV or TSV for the team to bulk-upload.

Everything outside that loop was cut: no database, no integration with Fairmart’s systems, no auth, no batch upload, no price or category detection.

Key decisions and tradeoffs

Generate the image instead of cleaning it. I tried cleaning the source photos first. They were never good enough, and there was too much variation between them to handle reliably. The unlock was realizing that fidelity to the original photo did not matter: we replace merchant images at publish time anyway. The only real bars were that the product had to stay accurate and the result had to look publishable. Generation cleared both. Gemini held onto the details that count, like the correct volume on a bottle, and removed things like price stickers, while delivering the studio look. The tradeoff is that generation can drift from the real product and adds a watermark. I accepted that because the bar was “accurate product, clean image,” not “faithful reproduction of a bad photo,” and in testing it held.

Build for validation, not for the product. With one week, the question was not “what is the productized feature.” It was narrower: can image generation reliably produce product images at scale for a customer in a new segment. A database or a proper integration into our existing UI would have answered a question I was not asking yet, and it would have burned the week. So the tool just outputs a file the team uploads, and stores images in Drive for records. The cost of that choice is real: no shared state, no resuming a session, and a manual handoff step. But it kept the experiment cheap and reversible, and a working answer here unlocked a lot: a new customer segment and a differentiated feature.

Ship the urgent mode first, add the second later. Non-barcoded products were the pressing, deal-breaking case, so that flow shipped first and auto-generates a SKU. We still hit barcoded products that our existing pipeline could not identify, so I added a second mode that uses the real barcode as the identifier. The sequencing followed urgency, not completeness.

Architecture

The system is a thin pipeline. The browser captures or uploads a photo and sends it to an Express backend. The backend makes two model calls, one to Claude Vision for the title and one to Gemini for the image, then uploads both images to Drive and appends a row to a CSV log. The frontend builds a table the user can edit, then exports it.

The leverage point is those two API calls. Together they do the work that would otherwise need a photographer and someone writing copy. State is deliberately minimal: localStorage in the browser and a CSV log in Drive, nothing more.

The failure modes shaped two choices. Railway’s filesystem is ephemeral, so anything saved locally disappears on restart. That is why Drive is the system of record, not local disk. Model APIs also overload under load, so calls retry with exponential backoff. The real bottleneck, though, is not the software. Someone still has to physically photograph each item, so throughput is gated by hands, not compute.

Results

The image generation came out at roughly 9 out of 10. It sticks to the prompt, gets product details like volume right, and reliably strips stickers and clutter. It was clearly viable.

We used it to onboard 800 non-barcoded beauty products and get the chain selling online. It replaced the roughly 25,000 photography-and-cataloguing path the client would not pay for. For the merchant, going online added a 12% monthly lift to their overall store revenue. For us, it proved that generated product images work at scale, which is what made the new segment worth pursuing.

What I’d do differently

The tool sped up everything except the slow part. Photography is still manual, so capture is the real throughput ceiling, not the model calls. If I built it again, I would attack capture earlier: batch upload, or pulling several products from one wider photo, so the human spends less time pointing a camera one item at a time.


Tech stack: Node.js Express Vanilla JS Claude Haiku Vision Gemini 2.5 Flash Image Google Drive API Railway Multer