The batch image page I'd thrown together offered "save to folder" in Chrome and simply didn't in Firefox. So I checked which browsers really let a page write back into a folder the user picked, then measured what the fallback costs. Short answer: per MDN's compat data (2026-10-15), only Chrome and Edge 86+ and Chrome Android 132+ have showDirectoryPicker(). Everyone else gets downloads, and the way you build that download decided whether the browser held about 850 MiB more at the end. My numbers come from a 16 GB Mac running desktop Chromium 149 unless noted: 300 synthetic 3024×4032 JPEGs, each decoded, drawn and re-encoded at JPEG quality 0.8. MiB means 1024×1024 bytes.
Only Chromium browsers write into a picked folder
The red cells are the whole story: Firefox, Safari and Safari on iOS have no showDirectoryPicker(). Picking a folder to read works everywhere through webkitdirectory. Data: MDN browser-compat-data 8.1.6 (2026-10-15).
createWritable() does show up for Firefox 111+ and Safari 26+, but with no folder picker the only file handles those browsers hand out are in the origin private file system (OPFS), the site's own sandbox. That makes OPFS good scratch space, which the rest of this post leans on, while the results still leave as a download. In a Firefox 151 engine build and a WebKit 26.5 engine build (not release Firefox or Safari), showDirectoryPicker was undefined in both.
Three ways out when downloads are all you have
One download per file is the easy one, but <a download> carries a file name and no folders, and I haven't seen what a release browser asks before 300 downloads in a row. A single ZIP keeps the paths. The usual way keeps every result Blob in an array, then reads them back to compute CRCs and glue the archive together. The third way writes each result into OPFS as soon as it's encoded and keeps only its name, size and CRC32. At the end it builds the ZIP Blob from the OPFS File objects plus headers, so the image bytes are never read back into the page.
The staged ZIP added nothing at the end
I ran both ZIP routes twice on 300 images and actually triggered the download. Every run produced the same 448,912,751-byte ZIP, and all of them passed a CRC check.
| Fallback | Highest during the loop | Building the ZIP | Highest while building and downloading |
|---|---|---|---|
| Results kept in memory, zipped at the end | 796 / 574 MiB | 998 / 978 ms | 1,376 / 1,376 MiB |
| Results staged in OPFS, zipped from files | 592 / 538 MiB | 38 / 36 ms | 516 / 524 MiB |
The in-memory route sat at 1,373–1,376 MiB for the whole time the download was being written (about 17 s in the first run) and only dropped to around 493 MiB after. The staged route crept up during the loop as well, which writing to OPFS seems to do and I haven't tracked down why. Building and downloading added nothing on top. Caveats: memory was sampled about every 1.3–2.1 s while other jobs ran on the machine, so peaks may be low, and I'm not quoting download times. One extra in-memory run lost the browser before the download started, cause unknown, so it's left out.
WebKit kept the per-file route alive
With 6 images, the same OPFS code built a valid ZIP in the Firefox 151 engine. WebKit 26.5 with a throwaway profile threw UnknownError on the first OPFS write, and with a persistent profile it worked. So checking that navigator.storage.getDirectory exists isn't enough. My page now does a tiny test write first and drops to per-file downloads if that throws. Whether a Safari private window behaves like the throwaway profile, I haven't tested. Side note that surprised me: those 6 photos at "quality 0.8" came out 8,863,217 bytes in Chromium, 10,493,685 in Firefox and 14,495,252 in WebKit.
The ladder I'm keeping
When I just need a folder done I use ImgIng (https://imging.ai/), and from its interface it draws the same line: in Chromium its batch panel can save into a new folder, while the Firefox and WebKit builds only offered confirming each download. My page now goes like this: showDirectoryPicker present, write each result into a new folder; OPFS test write succeeds, stage and hand over one ZIP; otherwise one download per file. In the Firefox engine build that means one ZIP instead of a pile of downloads; release Firefox and Safari are still on my list. What happens when OPFS runs out of quota on a nearly full disk is the next thing I want to try.
Top comments (0)