Soft drop shadow under an AI product cutout with Pillow
Give a transparent product PNG from Sume a soft drop shadow: copy the alpha, blur it, offset it, tint it, then composite. Parameters for subtle and heavy looks.

Take the cutout's alpha channel, blur it with GaussianBlur, offset it down and to one side, fill it with a dark color at partial opacity, and put the product on top. For a 1024 pixel product, a blur radius of 20, an offset of (0, 24), and 35 percent opacity give a soft studio shadow, with no extra model call.
Where the cutout comes from
ChatGPT Image 2.5 on Sume accepts background: "transparent", which returns a product with no backdrop. Other models do not accept that parameter and return 400 unsupported_parameter, so for those you remove the background in a separate step. Either way, you end up with an RGBA image whose alpha channel is the product's silhouette.
A silhouette is exactly what a shadow needs: the shape of the object, in one channel.
Build the shadow
The shadow layer is solid dark color whose alpha is the blurred silhouette times an opacity. Pad the canvas first so the blur is not clipped at the edges.
import os, io, requests
from PIL import Image
H = {"Authorization": "Bearer " + os.environ["SUME_API_KEY"]}
def gen(**body):
r = requests.post("https://api.sume.com/v1/images", json=body, headers=H, timeout=60)
r.raise_for_status()
if r.status_code == 202:
raise SystemExit("queued, read /v1/jobs/{id}/result: " + r.text)
return r.json()
def fetch(u):
return Image.open(io.BytesIO(requests.get(u, timeout=60).content))
from PIL import ImageFilter
out = gen(model="openai/gpt-image-2.5", background="transparent", output_format="png",
prompt="Single white sneaker, three-quarter view, studio product photo")
item = fetch(out["data"][0]["url"]).convert("RGBA")
m = 80
canvas = Image.new("RGBA", (item.width + 2 * m, item.height + 2 * m), (255, 255, 255, 255))
a = Image.new("L", canvas.size, 0)
a.paste(item.getchannel("A"), (m, m))
a = a.filter(ImageFilter.GaussianBlur(20))
a = a.point(lambda v: int(v * 0.35))
shadow = Image.new("RGBA", canvas.size, (20, 20, 30, 0))
shadow.putalpha(a)
canvas.alpha_composite(shadow, (0, 24))
canvas.alpha_composite(item, (m, m))
canvas.convert("RGB").save("sneaker_shadow.jpg", quality=92)
print("cost", out["usage"]["cost"])Shadow presets
Light direction decides offset, and softness decides blur. Keep the same preset across a catalog so shelves look consistent.
| Look | Blur radius | Offset | Opacity |
|---|---|---|---|
| Subtle contact | 8 | (0, 8) | 0.45 |
| Soft studio | 20 | (0, 24) | 0.35 |
| Heavy floating | 40 | (30, 60) | 0.25 |
Common mistakes
- Forgetting to pad the canvas. The blurred shadow gets cut off at the border.
- A pure black shadow looks pasted on. Use a dark tint close to the backdrop hue.
- Blurring the RGB instead of the alpha leaves color fringes. Blur only the alpha.
- A shadow that points a different way in each shot of a set breaks the look. Fix the offset in one place.
Finish and deliver
Keep the PNG with alpha as the master, and export the white-backdrop JPEG as in the code only for listings. Derive widths from one master using the srcset recipe, and avoid repeated JPEG saves while you iterate, as in PNG between passes. The transparent PNG note covers when to trust the model's alpha.
Sources
Related posts
More in Media tools
- soundtrack_fade_exceeds_output: a bed fade longer than a short clip
Sume rejects soundtrack.fade_out_seconds longer than audio.duration_seconds. The fade also caps at 10 s. For a 6 s Short, use a 1 to 2 s fade.
- Split a voiceover into per-scene WAV files with Timeline ranges
One narration take, one Timeline audio split call: send up to 20 ranges and get a wav per scene plus offsets for $0.01, instead of a TTS take per scene.
- Stitching AI video clips in Sume Timeline: the rules that return 400
Timeline 1.0 joins model clips into one MP4: first slot at 0, import first, fades up to 1 s, at most 8 chained fades. Error codes, cost, and a curl request.
- Sume BGM catalog: which tracks need a credit line and which do not
Sume's own BGM loops need no third-party credit. The licensed km tracks are CC BY 4.0 and need the Kevin MacLeod credit on every public export.
Written by Sume