Guide
Packaging & final delivery: ICO, ICNS, mobile, WebP & favicons
Assemble production-ready ICO and ICNS containers, mobile app icons for iOS and Android, WebP exports, and a release pipeline that regenerates everything from one source.
Updated
- #ico
- #icns
- #packaging
- #ios
- #android
- #webp
- #release-pipeline
- #icon-delivery
Getting an icon out of a design tool and into a shipping product are two different jobs. The design step ends with a polished vector or high-resolution raster. The packaging step turns that source into every container, size variant, and platform-specific artifact that the Windows installer, macOS app bundle, iOS asset catalog, Android mipmap resource, and web delivery pipeline each need.
This guide covers the full delivery side: what goes inside each container, how mobile platforms consume icon assets, how WebP fits into web delivery, and how to structure a release pipeline so that "update the icon" is a one-command operation rather than a multi-hour manual re-export.
ICO: assembling the Windows icon container
ICO is not a single image — it is a container that holds multiple raster images at different sizes so the operating system can pick the best one for the rendering context. A 16 × 16 entry is shown in the Windows taskbar; a 256 × 256 entry is shown in a large-icon File Explorer view or on a high-DPI desktop.
The maximum raster dimension inside an ICO file is 256 × 256 pixels. There is no path to larger — the ICO format's header uses a single byte to encode width and height, and 256 is the value that byte represents when set to zero by convention. If you need a larger icon on Windows (for example, a Store tile or a hero image), that is a separate asset outside the ICO container.
ICO size sets
There are two distinct ICO jobs with different size requirements:
| Context | Sizes to include | Bit depth |
|---|---|---|
| Web favicon only | 16 × 16, 32 × 32 | 32-bit RGBA |
| Windows app icon (EXE, installer, shell) | 16, 24, 32, 48, 64, 128, 256 | 32-bit RGBA; PNG-compressed at 128 and 256 |
The larger entries — typically 128 × 128 and 256 × 256 — should be stored as PNG-compressed data within the ICO container rather than uncompressed BMP bitmaps. PNG compression reduces a 256 × 256 RGBA entry from several hundred kilobytes to roughly 10–30 KB depending on icon complexity. Every Windows version since Vista handles PNG-compressed ICO entries correctly.
The smallest entries — 16 × 16 and 24 × 24 — deserve hand-tuned pixel art or at minimum a carefully pixel-snapped downscale. Anti-aliased strokes from a scaled-down 256 px master look muddy at 16 px on standard-DPI monitors. If you produce the full-size entries automatically but hand the small sizes to a pixel-art pass, your ICO will look sharp everywhere.
ICO alpha: straight vs premultiplied
Export all ICO layers with straight (non-premultiplied) alpha. Some compositors use premultiplied alpha internally, and if the ICO entry is stored as premultiplied, the Windows shell multiplies the alpha a second time — producing darkened, smeared halos around semi-transparent edges at small sizes. This is the most common cause of "blurry dark fringe" bug reports on Windows icons.
Verify the compositing at 16 × 16 against both a white and a dark background before shipping. The halo, if present, is invisible against white but very obvious on the dark Windows taskbar.
ICNS: assembling the macOS icon container
ICNS is Apple's icon container for macOS app bundles. It follows the same multi-resolution principle as ICO but extends the size set to 1024 × 1024 and pairs every point size with a Retina (@2x) pixel-doubled variant.
ICNS size set
A complete ICNS file contains ten images — five point sizes at two pixel densities each:
| Point size | @1x pixels | @2x pixels | Filename in .iconset |
|---|---|---|---|
| 16 pt | 16 × 16 | 32 × 32 | icon_16x16.png / icon_16x16@2x.png |
| 32 pt | 32 × 32 | 64 × 64 | icon_32x32.png / icon_32x32@2x.png |
| 128 pt | 128 × 128 | 256 × 256 | icon_128x128.png / icon_128x128@2x.png |
| 256 pt | 256 × 256 | 512 × 512 | icon_256x256.png / icon_256x256@2x.png |
| 512 pt | 512 × 512 | 1024 × 1024 | icon_512x512.png / icon_512x512@2x.png |
The 1024 × 1024 entry (512 pt at @2x) is the largest and is required for Mac App Store submission. Finder on Apple silicon Macs uses it for large-icon views and for Quick Look previews. Do not skip it.
Current versions of macOS apply rounded corners automatically in Finder, the Dock, and Launchpad. Export a square icon and let the OS handle the radius. Baking rounded corners into the artwork causes double-rounding on modern macOS — a visibly uneven radius that looks wrong next to every other app icon on the system.
Building ICNS on and off macOS
The macOS command-line path is iconutil:
# Organize your PNGs into an .iconset folder first, then:
iconutil -c icns MyApp.iconset
iconutil reads the standard filename conventions (icon_16x16.png, icon_16x16@2x.png, etc.) and writes a valid ICNS file. It requires macOS and is not available on Windows or Linux.
For cross-platform pipelines — CI running on Linux, a Windows-based team — tools that handle ICNS packaging without iconutil are the practical alternative, since you cannot reasonably require a macOS runner just to assemble an icon container.
Mobile app icons: iOS and Android
Both iOS and Android have moved toward simpler workflows for supplying icon assets, but the underlying requirements are distinct enough to deserve separate treatment.
iOS: the asset catalog model
iOS app icons live in an Xcode asset catalog — an AppIcon.appiconset folder containing one or more PNG files and a Contents.json manifest that maps each file to its role and density.
Since Xcode 14 (and continuing in Xcode 15 and 16), a project targeting iOS 16 or later can use a single 1024 × 1024 PNG and let Xcode generate all smaller sizes automatically. The 1024 × 1024 source must use an opaque background — no transparency — because iOS applies its own shaped mask to app icons and a transparent source will show a black fill behind the icon shape.
The Contents.json for a single-size configuration looks like this:
{
"images": [
{
"filename": "icon-1024.png",
"idiom": "universal",
"platform": "ios",
"size": "1024x1024"
}
],
"info": {
"author": "xcode",
"version": 1
}
}
For projects that still target older iOS versions or need to supply every size explicitly, Xcode expects PNGs at these point sizes and pixel densities:
| Use | @1x | @2x | @3x |
|---|---|---|---|
| Home screen (iPhone) | — | 120 × 120 | 180 × 180 |
| Home screen (iPad) | 76 × 76 | 152 × 152 | — |
| iPad Pro home screen | — | 167 × 167 | — |
| Spotlight (iPhone) | — | 80 × 80 | 120 × 120 |
| Settings (iPhone) | — | 58 × 58 | 87 × 87 |
| Notification (iPhone) | — | 40 × 40 | 60 × 60 |
| App Store marketing | — | 1024 × 1024 | — |
Note that the App Store marketing icon (1024 × 1024) is always required regardless of which other sizes you provide.
Starting with iOS 18 (Xcode 16), Apple allows apps to supply alternate dark-mode and tinted icon variants. If you opt in, you provide separate image sets for each appearance — same folder structure, additional idiom entries in Contents.json. See the themeable icons guide for the design considerations; the asset catalog is where those variants land in the build.
Android: mipmap folders and adaptive icons
Android distributes app icons across density-bucketed resource folders (mipmap-mdpi through mipmap-xxxhdpi). Adaptive icons, introduced in Android 8.0, separate the icon into a foreground layer and a background layer so the system launcher can apply any mask shape — circle, squircle, rounded square — without clipping meaningful artwork.
The key measurement for adaptive icons is 108 dp total canvas size, which is larger than the 48 dp icon display size to give the system room to animate and reshape the layers. Keep all meaningful content within the central 66 dp circle (the "safe zone"); the outer ring is decorative background that will be cropped by most launchers.
Pixel dimensions by density bucket:
| Density | mipmap folder | Launcher icon px | Adaptive icon canvas px |
|---|---|---|---|
| mdpi (1×) | mipmap-mdpi |
48 × 48 | 108 × 108 |
| hdpi (1.5×) | mipmap-hdpi |
72 × 72 | 162 × 162 |
| xhdpi (2×) | mipmap-xhdpi |
96 × 96 | 216 × 216 |
| xxhdpi (3×) | mipmap-xxhdpi |
144 × 144 | 324 × 324 |
| xxxhdpi (4×) | mipmap-xxxhdpi |
192 × 192 | 432 × 432 |
The adaptive icon XML lives in res/mipmap-anydpi-v26/ and references the foreground and background drawables:
<!-- res/mipmap-anydpi-v26/ic_launcher.xml -->
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
<!-- Optional monochromatic layer for Android 13+ themed icons -->
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
</adaptive-icon>
Vector drawables are the preferred format for adaptive icon layers — they scale to any density without a raster grid of files. If your foreground artwork is too complex for a vector, supply the raster PNGs at all five density levels.
WebP for web delivery
WebP belongs in the delivery step for web-bound assets, not in the source or packaging step for platform containers. Neither ICO nor ICNS accepts WebP entries; iOS asset catalogs do not accept WebP; Android accepts WebP for drawables and image assets but not for the launcher icon via the standard mipmap pipeline.
Where WebP is the right answer:
- PWA Open Graph images (the 1200 × 630 preview image attached to social shares and link unfurls)
- In-app imagery served through a web view or a web-based product shell
- Promotional screenshots, feature graphics, and store listing imagery served from a web backend
- Raster assets within web pages (
<picture>element with a PNG fallback for tooling that cannot handle WebP)
A practical delivery pattern for web raster assets:
<picture>
<!-- WebP: smaller payload for modern browsers -->
<source
type="image/webp"
srcset="/assets/app-preview-800.webp 1x, /assets/app-preview-1600.webp 2x">
<!-- PNG fallback: for browsers/tools that do not handle WebP -->
<img
src="/assets/app-preview-800.png"
srcset="/assets/app-preview-800.png 1x, /assets/app-preview-1600.png 2x"
width="800" height="500"
alt="Application screenshot showing the main dashboard">
</picture>
For a deeper treatment of format selection decisions — when SVG, PNG, WebP, ICO, and ICNS each win — see the companion guide SVG vs PNG vs ICO vs ICNS vs WebP.
A release pipeline that regenerates everything
The worst icon problem is not a bad source file. It is discovering, after shipping, that the Windows ICO was regenerated but the ICNS was not, or that the maskable PWA icon is three design iterations behind the rest. The solution is a pipeline — not a checklist — that treats every downstream artifact as derived output.
What the pipeline must do
- Accept one input: the canonical master (a clean, optimized SVG or a 1024 × 1024 PNG).
- Produce every platform artifact from that input in a single pass.
- Version the outputs alongside the source so diffs are traceable.
- Fail loudly if any step produces a file with unexpected dimensions or invalid metadata.
Representative pipeline steps
A shell script or Makefile target that wraps generation commands gives you a single entry point:
#!/usr/bin/env bash
# generate-icons.sh — run after any change to assets/icon-master.png
set -euo pipefail
SRC="assets/icon-master.png"
OUT="dist/icons"
mkdir -p "$OUT"
echo "==> Favicon family"
# SVG is authored separately — copy it from source
cp assets/icon-master.svg "$OUT/favicon.svg"
# ICO: 16+32 for favicon, full set for Windows
# (Invoke your ICO packager here, e.g. via CLI or build tool)
ico-builder --input "$SRC" --sizes 16,32 --output "$OUT/favicon.ico"
ico-builder --input "$SRC" --sizes 16,24,32,48,64,128,256 \
--png-compress 128,256 --output "$OUT/app.ico"
echo "==> Apple touch icon"
convert "$SRC" -resize 180x180 "$OUT/apple-touch-icon.png"
echo "==> PWA manifest icons"
convert "$SRC" -resize 192x192 "$OUT/icon-192.png"
convert "$SRC" -resize 512x512 "$OUT/icon-512.png"
# Maskable: add 10% padding on all sides, fill with brand color
convert "$SRC" -resize 410x410 -gravity center \
-background "#2563eb" -extent 512x512 "$OUT/icon-maskable.png"
echo "==> macOS ICNS"
# Build .iconset folder then call iconutil (macOS only)
# Or use cross-platform ICNS packager for CI
icns-builder --input "$SRC" --output "$OUT/AppIcon.icns"
echo "==> iOS 1024px source"
convert "$SRC" -resize 1024x1024 "$OUT/ios-icon-1024.png"
echo "==> Android mipmap PNGs"
for density in 48 72 96 144 192; do
convert "$SRC" -resize "${density}x${density}" \
"$OUT/android/mipmap-${density}.png"
done
echo "==> Done. Outputs in $OUT"
This uses ImageMagick's convert for raster steps, which is a common dependency in Linux CI. Replace the ICO and ICNS steps with whichever packager your pipeline uses — the point is a single entry point that produces everything.
CI integration
Run the pipeline on every push that touches the source artwork. The CI check should:
- Verify that all expected output files exist and are non-empty.
- Confirm pixel dimensions of key outputs (a 180 × 180 apple-touch icon at 181 pixels is a broken pipeline, not a small rounding error).
- Compare the ICO file's reported entry list against the expected size set.
- Fail the build if any output is older than the source, which catches the case where a step silently skips due to an error swallowed by the shell.
Pre-ship verification
A final manual verification pass is worth the ten minutes:
| Surface | What to check |
|---|---|
| Browser tab (light mode) | SVG favicon renders, not broken placeholder |
| Browser tab (dark mode) | SVG dark-mode variant activates via prefers-color-scheme |
| iOS home screen (test device or Simulator) | Touch icon visible, no black background bleed |
| Android home screen (emulator) | Adaptive icon masks correctly to circle and squircle |
| Windows File Explorer (large icons view) | 256 × 256 ICO entry renders without artifacts |
| Windows taskbar | 16 × 16 ICO entry is legible, not a blurred blob |
| Lighthouse PWA audit | No installability errors; all manifest icon entries resolve |
| Chrome DevTools > Application > Manifest | Icons all show green, sizes match entries |
For the web-side favicon and manifest wiring — the <link> tags and site.webmanifest JSON — see favicon & app-icon families from a single source. For questions about which format is correct at each stage of the pipeline, the format decision guide at SVG vs PNG vs ICO vs ICNS vs WebP covers the full decision matrix.
Packaging is where good icon work either survives or falls apart. The containers are mechanical — ICO assembles layers, ICNS assembles layers — but the care around pixel hinting at 16 px, safe-zone discipline for maskable icons, and a pipeline that makes re-generation effortless are the difference between an icon family that ships once and one that ships correctly every time.