Skip to content

Instantly share code, notes, and snippets.

@jimmywarting
Created December 21, 2025 16:49
Show Gist options
  • Select an option

  • Save jimmywarting/af3059bb03503a4a56a7481702ff66d8 to your computer and use it in GitHub Desktop.

Select an option

Save jimmywarting/af3059bb03503a4a56a7481702ff66d8 to your computer and use it in GitHub Desktop.
download-favicons.js

Download Favicons Browser / Crawler Snippet

Description

This browser snippet allows you to download all favicons from a web page into a structured folder using the File System Access API.
It automatically generates smart filenames based on the type of favicon (apple-touch-icon, mask-icon, favicon) and the actual image dimensions.
It also creates a simple example.html referencing all the saved icons with correct rel, sizes, and mask-color attributes.


Features

  • Prompts the user to select a folder where all favicons will be saved.
  • Creates a main folder named <host> icons and an icons/ subfolder.
  • Supports all common favicon types:
    • favicon.ico
    • favicon.png in different sizes
    • apple-touch-icon
    • mask-icon (including color attribute)
  • Determines image dimensions using createImageBitmap for raster images (PNG, ICO).
  • For SVGs, falls back to sizes attribute or defaults to auto.
  • Generates example.html that includes proper <link> tags for all saved icons.

Usage

  1. Open the web page in your browser.
  2. Open DevToolsConsole.
  3. Paste the snippet from download-favicons.js.
  4. Press Enter.
  5. A folder picker will appear — choose a folder to store the favicons.
  6. The script will:
    • Create a <host> icons folder.
    • Create an icons/ subfolder with all downloaded favicons.
    • Create example.html referencing all icons.

Notes

  • SVGs cannot be decoded by createImageBitmap, so the script reads sizes if available.
  • If multiple <link> elements point to the same file, the script will download them separately (no deduplication).
  • Works only in browsers that support the File System Access API (Chrome, Edge, Opera, and some Chromium-based browsers).
  • The script will log all saved filenames and errors in the console.

Example folder structure


<host> icons/
├── icons/
│   ├── apple-touch-icon-180x180.png
│   ├── favicon-32x32.png
│   ├── mask-icon-auto x auto.svg
│   └── ...
└── example.html

(async function saveFaviconsSmartNames() {
try {
// Ask user to pick a root folder
const _ = await window.showDirectoryPicker({ id: 'origin-favicon', mode: 'readwrite' })
// Create a main folder named "<host> icons"
const rootHandle = await _.getDirectoryHandle(`${location.host} icons`, { create: true })
// Create an "icons" folder inside the main folder
const iconsHandle = await rootHandle.getDirectoryHandle('icons', { create: true })
// Collect all favicon-related link elements from the DOM
const linkElements = [...document.querySelectorAll('link[rel*="icon"], link[rel="mask-icon"], link[rel="apple-touch-icon"]')]
// Include default favicon.ico if missing
const origin = window.location.origin
const faviconDefault = origin + '/favicon.ico'
if (!linkElements.some(l => l.href === faviconDefault)) {
linkElements.push({ href: faviconDefault, rel: 'icon' })
}
const savedFiles = []
for (const link of linkElements) {
try {
const url = link.href
const response = await fetch(url)
if (!response.ok) throw new Error(`Failed to fetch: ${response.status}`)
const blob = await response.blob()
let width = 0, height = 0
// Only use createImageBitmap for raster images
if (blob.type.startsWith('image/') && blob.type !== 'image/svg+xml') {
const bitmap = await createImageBitmap(blob)
width = bitmap.width
height = bitmap.height
} else if (link.sizes && link.sizes.value) {
// fallback to sizes attribute for SVGs
const sizesMatch = link.sizes.value.match(/(\d+)x(\d+)/)
if (sizesMatch) {
width = parseInt(sizesMatch[1], 10)
height = parseInt(sizesMatch[2], 10)
}
}
const rel = link.rel || 'icon'
let filename = ''
if (rel.includes('apple-touch-icon')) {
filename = `apple-touch-icon-${width || 'auto'}x${height || 'auto'}.png`
} else if (rel.includes('mask-icon')) {
filename = `mask-icon-${width || 'auto'}x${height || 'auto'}.svg`
} else {
filename = `favicon-${width || 'auto'}x${height || 'auto'}${blob.type === 'image/png' ? '.png' : '.ico'}`
}
const fileHandle = await iconsHandle.getFileHandle(filename, { create: true })
const writable = await fileHandle.createWritable()
await writable.write(blob)
await writable.close()
savedFiles.push({ filename, rel, width, height, color: link.color || '' })
console.log('Saved:', filename)
} catch (e) {
console.error('Error saving', link.href, e)
}
}
// Generate example.html with rel, sizes and color attributes
const htmlContent = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Favicons Example</title>
${savedFiles.map(f => {
const sizesAttr = f.width && f.height ? ` sizes="${f.width}x${f.height}"` : ''
const colorAttr = f.color ? ` color="${f.color}"` : ''
return `<link rel="${f.rel}" href="icons/${f.filename}"${sizesAttr}${colorAttr}>`
}).join('\n ')}
</head>
<body>
<h1>Favicons from ${origin}</h1>
</body>
</html>
`.trim()
const htmlHandle = await rootHandle.getFileHandle('example.html', { create: true })
const writableHTML = await htmlHandle.createWritable()
await writableHTML.write(htmlContent)
await writableHTML.close()
console.log('All favicons saved with smart names, colors, and example.html created!')
} catch (err) {
console.error('Cancelled or File System Access not supported', err)
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment