PWA manifest icons: any, maskable and monochrome from AI images
Make the three PWA icon purposes with the Sume image API: transparent, full-bleed maskable and one-color monochrome masters, plus the cost of each.
A web app manifest icon can declare one of three purposes, and each one wants a different picture: any is a normal icon, maskable is designed with a safe zone so the OS can crop it, and monochrome uses the alpha channel as a mask for a solid fill. So one AI render is not enough; you need three masters. On Sume that is three square calls to POST /v1/images on openai/gpt-image-2.5 at medium quality, an estimated $0.0165 each (about $0.05 for the set).
This post covers what MDN says each purpose means, how to ask for each master, the one Sume parameter that decides whether you get transparency, and a short script that writes the files.
What the three purposes ask of the picture
The facts below come from MDN's manifest icons reference (read 2026-10-03). Sizes are yours to choose; MDN's example lists several, and the browser picks the best fit.
| purpose | What MDN says | What the master needs |
|---|---|---|
| any | Can be used in any context (the default) | The mark on a transparent or plain ground |
| maskable | Designed with a safe zone so it survives masking | Full-bleed background, mark kept small and centered |
| monochrome | Used as a monochrome icon with solid fill; alpha is the mask | One flat shape on transparent, no gradients |
The Sume parameter that decides transparency
The Image API docs list background: auto | transparent | opaque as a ChatGPT Image 2.5 parameter. Other catalog rows do not take it, and a request that sets a parameter the model does not list is rejected with 400 unsupported_parameter. So the transparent and monochrome masters belong on openai/gpt-image-2.5 with background: "transparent" and output_format: "png", and the maskable master uses background: "opaque".
The monochrome purpose uses only the alpha channel, so that master needs real transparency. Ask for it with the background field rather than in the prompt, and open the PNG to confirm it has an alpha channel before you ship it. The Image API docs also point to Image 1.0 with transparency: true for transparent stills, so use that route if the check fails.
Prompts that fit each purpose
Keep the subject one simple mark. Icons shrink hard, and detail turns to mush.
- any: 'a bold geometric fox head, flat vector style, centered, generous padding'
- maskable: 'fills the whole square with a solid orange background, the same fox head small in the exact center'. Check the result against a circle and a rounded-square mask before you ship it; the mark must stay inside the middle.
- monochrome: 'a solid black fox head silhouette only, no shading, no outline, no background'
A script that writes all three
The script follows the response rule in the docs: 200 is the image body with data[].url, and 202 is a job envelope you poll instead. It needs requests and Pillow, and SUME_API_KEY in the environment.
import os, requests
from io import BytesIO
from PIL import Image
def make(prompt, background):
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", "prompt": prompt,
"aspect_ratio": "1:1", "quality": "medium",
"background": background, "output_format": "png"},
timeout=90)
if r.status_code != 200:
raise SystemExit(f"{r.status_code}: poll {r.json()['data']['status_url']}")
raw = requests.get(r.json()["data"][0]["url"], timeout=60).content
return Image.open(BytesIO(raw)).convert("RGBA")
mark = "a bold geometric fox head, flat vector style"
masters = [
("any", "transparent", mark + ", centered, generous padding"),
("maskable", "opaque", mark + " small in the exact center on a solid orange background filling the square"),
("monochrome", "transparent", "a solid black silhouette of " + mark + ", no shading, no background"),
]
for name, bg, prompt in masters:
img = make(prompt, bg)
for size in (192, 512):
img.resize((size, size)).save(f"icon-{name}-{size}.png")
Cost and what to check
Prices below are the repo's estimate for a 1024x1024 GPT Image 2.5 output times the 1.25 house margin (read 2026-10-03). The endpoint record from GET /v1/images/models/{id}/endpoints and each response's usage.cost are what you are actually billed; a failed generation is not billed.
- Medium is enough for an icon you will shrink to 512 px or less; high is the default if you omit
quality. - Run the three masters again when the brand mark changes; a manifest points at files, not at jobs.
- Resize with Pillow, not with another model call.
| quality | Sume price per image | List estimate |
|---|---|---|
| Low | $0.0074 | $0.0059 |
| Medium | $0.0165 | $0.0132 |
| High (default) | $0.0659 | $0.0527 |
| XHigh | $0.1171 | $0.0937 |
| Max | $0.2635 | $0.2108 |
What Sume does and does not do
Sume returns the image files. It does not write your manifest.webmanifest, validate maskable safe zones, or know which sizes your install flow needs; that stays in your build.
Sources
Related posts
More in Developers
- Pydantic AI ToolCallJudge: check a Sume paid call before it runs
Pydantic AI 2.53.0 adds ToolCallJudge to assess tool calls before execution. Pair it with Sume's dry_run and max_spend_usd on paid calls.
- Pydantic AI cancel_and_resume vs Sume agent run cancel
Pydantic AI 2.54.0 shows cancel_and_resume. Sume Agent Completions have a different cancel: a run is cancelled by id and resumes only as a new run.
- Pydantic model to Sume output_schema: extra forbid, no defaults
Turn a Pydantic v2 model into a valid Sume Format output_schema: extra=forbid, nullable instead of defaults, and the SumeMediaFile reference. Tested.
- Unit test Sume webhook signature checks in pytest (Python)
A pytest file for HMAC webhook verification: tamper, rotation header, stale timestamp and empty secret, written against Sume's sume-v1 scheme.
Written by Sume