What this tool does
A progressive web app is installable only if a specific set of files exists, and almost none of them are hard to find one at a time: an icon at every size Chrome will ask for, a maskable variant Android can crop, three Apple touch icons Safari reads, two Windows tiles, a favicon, a manifest, and the head tags that make a browser notice any of it. Collecting that by hand means resizing one square nine times and hoping the filenames match what the manifest points at. This studio takes a single master image and returns the whole bundle, with the manifest and the link tags generated from the same filenames as the images.
How it works
The manifest is where most of the real work is, and the two fields people get wrong are scope and start URL. Scope is matched as a URL prefix, not treated as a folder name, which is why a scope of /app quietly swallows /application while leaving out /app/dashboard. The start URL has to sit inside the scope, or the installed copy launches somewhere the browser considers a different application entirely and loses its storage and its session. Those two values are checked against each other before anything is written, and a scope missing its trailing slash is flagged with the corrected form.
Icons are resampled from the master image with the canvas API, and the interesting one is the maskable icon. Android crops an adaptive icon by whatever mask the launcher applies — a circle, a squircle, a rounded square — so everything that matters has to sit inside a circle covering roughly 80% of the canvas. On a 512 pixel icon that is a 410 pixel safe zone. The logo is inset to fit inside that circle and the background is filled in behind it, so whatever the launcher cuts away is still solid colour rather than transparency.
Every platform reads a different subset of this bundle and ignores the rest. iOS never reads the manifest for icons at all; it takes only the apple-touch-icon links, applies its own corner rounding, and composites transparency against black rather than against your page, which is why the Apple icons are flattened onto the background colour instead of shipping with an alpha channel. Windows ignores both the manifest and the link tags and reads browserconfig.xml, which belongs to a different subsystem and fixes the grey-screenshot tile problem on its own.
The archive is assembled in the browser and written without a compression library. It is store-only rather than deflated, which is the honest choice for this particular payload: the files are PNGs, which are already deflated, so running the algorithm over them again costs CPU and usually saves nothing. Nothing is uploaded anywhere. The manifest, the browserconfig tile file, the HTML head tags and the Next.js metadata object are all produced from the same filename helpers the images are written with, so the references cannot drift away from the files that were actually generated.
Worked example
A client wants a React app installable on both iOS and Android, starting from one 1024 × 1024 PNG logo.
- The logo is resampled across the Android ladder from 48 to 512, with a separate 512 maskable icon whose logo is inset inside the 410 pixel safe circle
- Three Apple touch icons are produced at 180, 167 and 152, each flattened onto the background colour because a transparent iOS icon ships as a black square
- Two Windows tiles and a browserconfig.xml are added so pinning the site to the start menu uses an icon rather than a screenshot
- favicon.ico is packed with 16, 32 and 48 pixel images, and manifest.webmanifest is written with start_url and scope both at / so the two agree
- The head tags are pasted into the document head and the bundle is unzipped into the public directory
A bundle covering every icon size, both splash sets, the manifest and the tile XML, where every src the manifest declares points at a file that is actually in the zip.
Accuracy and limitations
- A manifest does not make an app work offline. That requires a service worker, and the tag this tool prints only registers one at /sw.js if you have written it yourself.
- iOS draws its own splash screens from the launch image and the operating system, not from the manifest. The generated splash PNGs are for Android and for any builder that asks for them by filename.
- Icons are resampled from your master image, so scaling past the master's own pixel count produces soft edges. 512 × 512 is the useful floor; 1024 × 1024 is better.
- Nothing here adds HTTPS, a service worker or a cache. Browsers will also suppress the install prompt entirely for a site not served over a secure context.
Frequently asked questions
- Do I still need a service worker if I have a manifest?
- Yes. The manifest makes a page installable; it does nothing for offline use, caching or updates. Everything a home-screen icon promises about launching into a working app comes from a service worker. The head tags printed here register one at /sw.js, which you still have to write and ship.
- Why is my Android icon getting its corners cut off?
- Because an icon declared with purpose "any" is treated as a full-bleed square and cropped by the launcher's own mask, which is usually a circle or a squircle. The fix is the maskable variant, where meaningful content sits inside a circle covering about 80% of the canvas. This tool generates it at 512 with the logo inset into a 410 pixel safe zone.
- What is the difference between start_url and scope?
- start_url is where the installed app opens; scope is the URL prefix the app is allowed to control. If start_url falls outside scope, the installed copy launches somewhere the browser treats as a different app, so storage and session are not shared with the tab you installed from. Scope is also a prefix match, so /app includes /app/dashboard and also /application.
- Can I use an SVG as my master image?
- Not here, and the reason is decoding rather than format support. Browser canvas rasterisation of an SVG needs the markup parsed before it can be drawn, which does not fit the same pipeline that decodes your PNG or JPEG once and resamples from pixels. Export your SVG to a 1024 × 1024 PNG first, which is also what every launcher wants to be handed anyway.
- Is my logo uploaded to a server anywhere?
- No. Decoding, resizing, masking and zipping all happen in the browser tab using the canvas API and a small ZIP writer, so an unreleased brand mark or a client's logo never leaves the machine. The manifest, the head tags and the tile XML are string templates produced in the same tab.