Android adaptive icon: 108 dp layers, 66 dp safe zone, from Sume
Adaptive icon layers are 108 x 108 dp with a 66 x 66 dp visible zone and 18 dp margins. Generate a transparent foreground on Sume and scale it inside the zone.
Android's adaptive icon is two required layers, foreground and background, each 108 x 108 dp. The visible area after the device mask is the central 66 x 66 dp, and the 18 dp on every side is reserved for masking and effects. The practical job for an image model is the foreground: a transparent mark that fits inside the 66 dp zone.
The numbers
Android's page gives these dimensions. A monochrome layer is optional, and the page ties it to themed icons from Android 13 (API 33). Layers can be vector drawables, which Android prefers, or bitmaps.
| Item | Value in dp | Share of 108 dp layer |
|---|---|---|
| Layer size | 108 x 108 | 100% |
| Visible zone | 66 x 66 | 61.1% |
| Margin per side | 18 | 16.7% |
| Logo minimum | 48 x 48 | 44.4% |
Pixel budget
A bitmap layer for xxxhdpi, which is 4 pixels per dp, is 432 x 432 pixels with a 264 x 264 pixel visible zone. The density factor is Android's own convention and not on the page quoted here, so confirm it against the density table in the Android docs before you ship. The script below produces the 432 pixel foreground and keeps the mark inside the 264 pixel zone, which is the safe choice for any density because everything scales together.
Generate and place
Request a transparent PNG square from ChatGPT Image 2.5, trim to the visible bounding box, scale it to fit 264 x 264, and center it on a transparent 432 x 432 canvas. Keep the background layer a separate flat color or gradient, because the system moves the layers against each other for parallax and pulse effects, and a foreground that contains its own backdrop defeats that.
import os, io, requests
from PIL import Image
r = requests.post(
"https://api.sume.com/v1/images",
headers={"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"},
json={"model": "openai/gpt-image-2.5", "aspect_ratio": "1:1", "prompt": "single bold leaf-shaped mark, flat color, no text, no backdrop", "background": "transparent", "output_format": "png"},
timeout=90,
)
r.raise_for_status()
if r.status_code != 200:
raise SystemExit("202: read the finished job from /v1/jobs/{id}/result")
img = Image.open(io.BytesIO(requests.get(r.json()["data"][0]["url"], timeout=60).content))
art = img.convert("RGBA")
box = art.getbbox()
if box:
art = art.crop(box)
art.thumbnail((264, 264), Image.LANCZOS)
layer = Image.new("RGBA", (432, 432), (0, 0, 0, 0))
layer.alpha_composite(art, ((432 - art.width) // 2, (432 - art.height) // 2))
layer.save("ic_launcher_foreground.png", optimize=True)Checking the result on a device mask
Android's own page explains why the margin exists: the system applies different mask shapes per device and animates the layers against each other, so the outer 18 dp can be cut away or moved. Preview your foreground inside a circle and a rounded square before you accept it. If any part of the mark touches the edge of the 66 dp zone, shrink it; the page's logo range is 48 to 66 dp, so the mark does not need to fill the whole zone. Avoid shadows baked into the bitmap, since the system can add its own effects and a baked shadow will double up. A flat mark generated with no backdrop is the right input; if the model returns a faint halo around the shape, trim it by thresholding the alpha channel before scaling.
Limits of this approach
A generated bitmap is not a vector drawable, so it will not stay sharp at every size the way Android's preferred vector layers do. Use it for a first foreground and for store art, and redraw the mark as a vector if the app is long-lived. The monochrome layer for themed icons is a solid shape whose alpha is the mask; derive it by filling the foreground's non-transparent pixels with one color rather than asking a model for it. Transparency on Sume is documented for ChatGPT Image 2.5, with Image 1.0 transparency: true named as the other route.
Sources
Related posts
More in Developers
- Animate a picture with Kling 3 via API: Python first-frame request
To animate one picture with Kling 3 on Sume, send it as a first_frame in frame_images to POST /v1/videos with model kling-3. Full Python script included.
- 2026 API client checklist from three open specs, and where Sume stands
Three public specs shape a 2026 API client: RateLimit headers, Idempotency-Key, Standard Webhooks. What each says, and what Sume's API does instead.
- App Store app preview audio: stereo AAC 256 kbps, check the track
Apple wants stereo AAC at 256 kbps and 44.1 or 48 kHz in an app preview. Use Sume audio detach to read channels and rate, and trim to keep the AAC track.
- App Store screenshots reject alpha: flatten a Sume PNG first
App Store Connect screenshots cannot include alpha or transparency. A PNG with an alpha channel from an image API needs flattening; here is a Pillow check.
Written by Sume