Chrome extension icons 16/32/48/128 from one Sume master

Chrome's manifest icons are PNG-preferred at 16, 32, 48 and 128 px, and SVG is not supported. Make one 1:1 master on Sume and downsample it in Pillow.

5 min readSume
All posts

Chrome's extension icon page lists four sizes: 16 x 16 for the favicon on extension pages and the context menu, 32 x 32 because Windows often needs it, 48 x 48 for the extensions management page, and 128 x 128 for installation and the Chrome Web Store. PNG is recommended, and SVG is not supported for manifest icons. One square master from Sume, downsampled four times, covers all of them.

Sizes and where they appear

At least one icon is mandatory for the toolbar, and all icons should be square. If none is supplied, Chrome generates a generic toolbar icon from the first letter of the extension name.

Chrome extension manifest icons (read 2026-10-03)
SizeUsed for
16 x 16Favicon on extension pages, context menu icon
32 x 32Windows computers often require this size
48 x 48Extensions management page
128 x 128Installation and the Chrome Web Store

Why not an SVG from the image API

Sume's Image API lists svg among output_format values, but the Chrome page says SVG files are not supported for any manifest icons. Request png; for the Chrome extension manifest SVG is the wrong choice.

One master, four files

Generate at the square ratio, then derive the set. Resize from the master each time rather than chaining 128 to 48 to 32, so every file is one resampling step from the original. A flat, high-contrast mark survives 16 pixels; a detailed illustration will not, so judge the 16 pixel file before approving the master.

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": "bold simple mark of a magnifying glass, flat two-color style, no text", "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))
master = img.convert("RGBA")
for px in (16, 32, 48, 128):
    master.resize((px, px), Image.LANCZOS).save(f"icon-{px}.png", optimize=True)
    print("wrote", f"icon-{px}.png")

Judging the 16 pixel file

The 16 x 16 icon is where generated art fails first. Detail that looks fine at 128 pixels becomes a gray smear at 16, and Lanczos resampling cannot rescue a mark that was too busy. Judge the smallest file first, on both a light and a dark toolbar, and approve the master only if that file is still recognizable. If it is not, generate again with a prompt that asks for one bold shape, thick strokes and two colors, instead of retouching the small file by hand. The page also says all icons should be square to avoid distortion, which is why the request asks for a 1:1 ratio and the script never stretches the image. Chrome shows the first letter of the extension name when no icon is supplied, which is a useful fallback to know about but not a design to ship.

Manifest entry

Register the files under the icons key with the pixel size as the key and the file path as the value, as the page describes: "icons": {"16": "icon-16.png", "32": "icon-32.png", "48": "icon-48.png", "128": "icon-128.png"}. For the 128 size's padding rules in the store listing, see the 128 px padding post.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume