Mirror-floor reflection under an AI product cutout, built in Pillow

Flip a transparent product PNG from Sume, fade it with a gradient mask, and stack it under the original for a glossy floor reflection. Code and fade settings.

5 min readSume
All posts

Flip the cutout vertically with ImageOps.flip, multiply its alpha by a vertical gradient that goes from about 40 percent to 0, and paste it directly under the product on a dark or white backdrop. That gives a glossy-floor reflection from one model call, and you control the strength with a single number.

Why build it instead of prompting for it

A prompt that asks for a reflective floor makes the model invent the whole scene, and the reflection often disagrees with the object: wrong angle, wrong cut-off, a different product. Building it from the cutout guarantees the reflection is a true mirror of what is above it.

The source is a transparent PNG. ChatGPT Image 2.5 on Sume is the model that lists background in its supported parameters, so request transparent there.

The code

Make the gradient as a mode L image, then multiply it with the flipped alpha using ImageChops.multiply. The fade height is a fraction of the product height; 0.5 reflects the lower half only.

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 ImageChops, ImageOps
out = gen(model="openai/gpt-image-2.5", background="transparent", output_format="png",
          prompt="Glass perfume bottle with gold cap, front view, studio product photo")
item = fetch(out["data"][0]["url"]).convert("RGBA")
w, h = item.size
refl = ImageOps.flip(item)
fade_h = int(h * 0.5)
grad = Image.new("L", (1, fade_h))
grad.putdata([int(255 * 0.4 * (1 - y / fade_h)) for y in range(fade_h)])
grad = grad.resize((w, fade_h))
mask = Image.new("L", (w, h), 0)
mask.paste(grad, (0, 0))
refl.putalpha(ImageChops.multiply(refl.getchannel("A"), mask))
canvas = Image.new("RGBA", (w, h + fade_h), (12, 12, 16, 255))
canvas.alpha_composite(item, (0, 0))
canvas.alpha_composite(refl.crop((0, 0, w, fade_h)), (0, h))
canvas.convert("RGB").save("bottle_reflection.jpg", quality=92)
print(canvas.size, "cost", out["usage"]["cost"])

Settings that change the feel

Start at 40 percent and a half-height fade, then adjust. Glossy glass and black acrylic take more reflection, and matte cardboard takes almost none.

Reflection presets, values read 2026-10-05
SurfacePeak opacityFade heightBackdrop
Glossy black acrylic0.450.5Near black
Polished white tile0.250.35White
Matte table0.100.2Light gray

What to watch

  • Objects that sit on the floor should touch the reflection. Crop the cutout tight to the product so there is no empty alpha gap between the two.
  • A transparent object such as glass shows the backdrop through it, which a flipped copy does not handle. Keep opacity low.
  • Add a soft shadow too if the product stands on a matte area.
  • Export JPEG only at the end.

Combine with other steps

A reflection goes well with the soft shadow recipe, where the shadow sits just under the contact line. Read the transparent PNG note first if edges look rough, and keep intermediates as PNG per the chain rule.

Sources

Related posts

More in Media tools

All Media tools posts

Written by Sume