Build a Magnific to Shopify Image Pipeline in 5 Minutes
- Generate a 4096px hero in Magnific in under a minute
- Resize to four responsive variants with a six-line bash script
- Push every variant to Shopify Files via Admin API in one command
- Reference the assets from Liquid with image_url width filters
I used to spend twelve minutes per product hero. Open Magnific, download, drag into Photoshop, export four sizes, log into Shopify, upload, copy URLs, paste into Liquid. After three products that loop felt like sand in my shoes. So I rebuilt it as a five-minute pipeline with one shell command and one Liquid filter, and now my machine does the boring 30 seconds while I do something else.
Step 1: Generate Assets in Magnific (CLI or Web)
Magnific (formerly Freepik) is where I start every product hero. The Magnific upscaler handles 4096 pixel output at a quality that beats anything I can render locally, and the Mystic generator gives me the source image when I do not have a photograph yet.
For a product hero I run two passes. First, generate at 1024 pixels with Mystic using a tight prompt that locks color, lens, and framing. Second, push the winning frame through the upscaler at 4x. Total Magnific time: about 60 seconds of clicking and 90 seconds of generation. The upscale output is one file: `hero-4096.jpg`, sitting in `~/Downloads`.
If you prefer the API, Magnific exposes the same upscaler over REST. I keep a tiny wrapper that posts the source URL and polls for the result, then writes the binary to disk. The web UI is faster for one-off heroes and the API wins when I am batching ten product photos for a drop.
Two rules I never break. Always upscale at 4096 pixels, never 2048. Storage is free, but a second round trip later because mobile retina needs more pixels is not. And always export as JPEG quality 90, not PNG. Shopify will recompress anyway, and a 4 MB JPEG beats a 14 MB PNG through any CDN.
Naming matters too. I use `product-slug-hero-4096.jpg` from minute one. The slug becomes the Shopify Files filename, the alt text seed, and the Liquid reference key. Three jobs, one string. If you skip naming discipline here, every later step in the pipeline grows a small workaround until the script is uglier than the manual flow you replaced.
By the end of step one I have one big square file on disk and a name I will not have to rename. That is the only artifact step two needs.
Step 2: Pipe Output Through a Local Resize Script
Now the boring 30 seconds. I have one bash script that takes a 4096 source and spits out four variants: 480 mobile, 800 tablet, 1600 desktop, 3200 retina. On macOS I use `sips` because it ships with the OS and never breaks. On Linux or in CI I swap in ImageMagick.
The whole script is six lines.
#!/bin/bash
SRC="$1"
BASE="${SRC%.*}"
for W in 480 800 1600 3200; do
sips -Z $W "$SRC" --out "${BASE}-${W}.jpg" >/dev/null
done
Save it as `~/bin/rx-resize`, make it executable, and run `rx-resize hero-4096.jpg`. Five seconds later the directory has four files. I keep the original as the 4096 variant so I have five total: 480, 800, 1600, 3200, and the 4096 source for hero contexts.
A few things I learned the painful way. Do not use ImageMagick `-resize` with default filtering on JPEGs from Magnific, the output goes soft. Use `-filter Lanczos -resize` if you go that route, or stay with `sips -Z` which gives a cleaner downscale on Apple silicon. Strip EXIF too. Magnific files often carry a 200 KB color profile that adds nothing on the web. `sips` strips it automatically, ImageMagick needs `-strip`.
Compression is the other money line. JPEG quality 82 is the sweet spot for product photography on dark backgrounds like the RAXXO Studios palette. Quality 90 looks identical and weighs 40 percent more. Quality 75 starts to band on smooth gradients. Test once on a hero with strong gradients, lock the number, never touch it again.
The resize step is where the time savings actually live. A manual export of four sizes takes me 4 minutes in Photoshop. The script takes 30 seconds, runs while I drink coffee, and never makes a typo on the dimensions. That is 3.5 minutes saved per product, every product, forever.
Step 3: Push to Shopify Files via the Admin API
This is the step everyone skips and everyone regrets. The Shopify admin uploader is fine for one file. For four files across twenty products it is 80 manual uploads and at least one wrong file in the wrong slot. The Admin API solves it in one shell command.
The dance is three calls. First, `stagedUploadsCreate` returns a presigned S3 URL per file. Second, you PUT the binary to that URL. Third, `fileCreate` registers the upload as a permanent Shopify Files asset and returns a CDN URL. The whole sequence runs in under 8 seconds for four variants on a decent connection.
I keep my upload script in `~/CLAUDE/RAXXOSTUDIOS/raxxo-shop/scripts/shopify-upload`. It reads credentials from `.env` (never settings.json, never hardcoded), accepts a glob pattern, and prints the final CDN URLs as a JSON map. Run it like `rx-shopify-upload "hero-*.jpg"` and it handles the four uploads in parallel.
Three production details that took me a week to figure out. One, the `stagedUploadsCreate` resource must be `IMAGE`, not `FILE`, or Shopify will not recognize the asset in the theme image picker. Two, the S3 PUT needs the exact `Content-Type` Shopify returned in the staged URL parameters, not the one your file actually has. Three, `fileCreate` is async on Shopify's side, so poll the file ID until status is `READY` before treating the URL as live. About 2 to 4 seconds typical.
The CDN URL format is `https://cdn.shopify.com/s/files/.../hero-1600.jpg`. Save those URLs somewhere your Liquid templates can read them, ideally as theme settings or metafields. I write them straight to a metafield on the product so the theme can resolve them with one tag.
Five minutes in, all four variants live on Shopify's CDN, named consistently, accessible from any template.
Step 4: Reference From Liquid With cdn.shop Image Filters
The last step is the cleanest. Shopify's `image_url` filter takes a width parameter and returns the right CDN variant on the fly. Combined with `srcset`, the browser picks the right size for the device pixel ratio.
{% assign hero = product.metafields.custom.hero_image %}
That is the entire frontend. Shopify's CDN serves the matching variant, falls back gracefully, and applies WebP transcoding for browsers that support it. I do not maintain four separate image references, the filter handles selection.
Two Liquid rules I enforce on every theme. Always set explicit `width` and `height` so the browser reserves space and Cumulative Layout Shift stays at zero. Always set `loading="lazy"` except for the first hero above the fold, which gets `loading="eager"` and `fetchpriority="high"`. Lighthouse loves both, and so does Google's ranking model.
For OG and Twitter card images I reference the 1600 variant directly. Social crawlers do not respect srcset, so give them one fixed URL at the right resolution. I add `?v={{ 'now' | date: '%s' }}` during dev to bust cache, then strip it before deploy.
End to end the pipeline now takes 5 minutes of human time and 30 seconds of machine time per product. Compared to my old 12-minute manual loop that is a 60 percent reduction, and the variants are pixel-identical across every product. Consistency turns out to be the real win, not just speed.
Bottom Line
I shipped 17 product pages on raxxo.shop with this pipeline in one weekend. Old workflow would have been 3.4 hours of mouse clicks, new workflow was 85 minutes of waiting on generation while I wrote copy. Same output quality, four times the throughput, zero typos in image dimensions because the script does not have fingers.
If you run a Shopify store and you generate or upscale heroes in Magnific, the four-step pipeline pays for itself on the third product. Local resize, API upload, Liquid filter. No drag and drop, no Photoshop export sheet, no admin uploader.
Want the actual scripts I use on raxxo.shop, including the `.env` setup and the parallel upload variant? Head to raxxo.shop/blogs/lab and check the tutorials cluster for the companion piece on automating Shopify product launches end to end. Same philosophy, ten more steps automated.
Build it once, run it forever.
Back to all articles