Visual
FAVICON GENERATOR
Generate favicons from text, emoji, or an uploaded image. Download PNG in multiple sizes. 100% browser-based.
Preview (256×256)
⬆
Click to upload or drag and drop an image
PNG, JPG, SVG — square images work best
Developer Reference
Core Algorithm & Standalone Script
Standalone, zero-dependency JavaScript implementation powering this tool. Free to inspect, copy, and build upon.
const SIZES = [16, 32, 48, 64, 128, 180, 512];
async function createIcoBlob(srcCanvas) {
const icoCanvas = document.createElement('canvas');
icoCanvas.width = 32; icoCanvas.height = 32;
icoCanvas.getContext('2d').drawImage(srcCanvas, 0, 0, 32, 32);
const pngBlob = await new Promise(resolve => icoCanvas.toBlob(resolve, 'image/png'));
const pngBuffer = await pngBlob.arrayBuffer();
const pngSize = pngBuffer.byteLength;
const icoBuffer = new ArrayBuffer(6 + 16 + pngSize);
const view = new DataView(icoBuffer);
// Header
view.setUint16(0, 0, true);
view.setUint16(2, 1, true); // ICO=1
view.setUint16(4, 1, true); // Count=1
// Entry
view.setUint8(6, 32); // Width
view.setUint8(7, 32); // Height
view.setUint8(8, 0); // Palette
view.setUint8(9, 0); // Reserved
view.setUint16(10, 1, true); // Planes
view.setUint16(12, 32, true); // BPP
view.setUint32(14, pngSize, true); // Size
view.setUint32(18, 22, true); // Offset
const icoArray = new Uint8Array(icoBuffer);
icoArray.set(new Uint8Array(pngBuffer), 22);
return new Blob([icoBuffer], {type: 'image/x-icon'});
}
function switchTab(id, el) {
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active'));
el.classList.add('active');
document.getElementById('panel-' + id).classList.add('active');
}
function renderText() {
const text = document.getElementById('faviconText').value || '?';
const bg = document.getElementById('bgColor').value;
const fg = document.getElementById('textColor').value;
const sizePct = parseInt(document.getElementById('fontSize').value) / 100;
const canvas = document.getElementById('textCanvas');
const ctx = canvas.getContext('2d');
const s = 256;
ctx.clearRect(0, 0, s, s);
ctx.fillStyle = bg;
ctx.fillRect(0, 0, s, s);
const fontSize = Math.round(s * sizePct);
ctx.font = `${fontSize}px serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = fg;
ctx.fillText(text, s/2, s/2);
}
function loadImage(file) {
if (!file) return;
const reader = new FileReader();
reader.onload = e => {
const img = new Image();
img.onload = () => {
const src = document.getElementById('sourceCanvas');
src.width = img.width; src.height = img.height;
src.getContext('2d').drawImage(img, 0, 0);
const canvas = document.getElementById('imgCanvas');
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, 256, 256);
const min = Math.min(img.width, img.height);
const sx = (img.width - min) / 2, sy = (img.height - min) / 2;
ctx.drawImage(img, sx, sy, min, min, 0, 0, 256, 256);
document.getElementById('imgPreviewWrap').style.display = '';
};
img.src = e.target.result;
};
reader.readAsDataURL(file);
}
// Drag-and-drop
const dz = document.getElementById('dropZone');
dz.addEventListener('dragover', e => { e.preventDefault(); dz.classList.add('dragover'); });
dz.addEventListener('dragleave', () => dz.classList.remove('dragover'));
dz.addEventListener('drop', e => { e.preventDefault(); dz.classList.remove('dragover'); loadImage(e.dataTransfer.files[0]); });
function getSourceCanvas(mode) {
return mode === 'text' ? document.getElementById('textCanvas') : document.getElementById('imgCanvas');
}
function generateSizes(mode) {
const src = getSourceCanvas(mode);
const grid = document.getElementById('sizeGrid');
grid.innerHTML = SIZES.map(sz => {
return `<div class="size-item">
<div class="size-label">${sz}×${sz} (PNG)</div>
<button class="size-dl" onclick="downloadSize(${sz}, '${mode}')">download</button>
</div>`;
}).join('');
// Add ICO option
const icoDiv = document.createElement('div');
icoDiv.className = 'size-item';
icoDiv.innerHTML = `
<div class="size-label">32×32 (ICO)</div>
<button class="size-dl" onclick="downloadIco('${mode}')">download</button>
`;
grid.prepend(icoDiv);
const snippet = `<link rel="icon" href="/favicon.ico" sizes="any">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">`;
document.getElementById('htmlSnippet').textContent = snippet;
document.getElementById('sizesSection').style.display = '';
// Save reference
document.getElementById('sizesSection').dataset.mode = mode;
}
function downloadSize(sz, mode) {
const src = getSourceCanvas(mode);
const canvas = document.createElement('canvas');
canvas.width = sz; canvas.height = sz;
canvas.getContext('2d').drawImage(src, 0, 0, sz, sz);
canvas.toBlob(blob => {
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
const name = sz === 180 ? 'apple-touch-icon' : `favicon-${sz}x${sz}`;
a.download = `${name}.png`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(a.href);
});
}
async function downloadIco(mode) {
const src = getSourceCanvas(mode);
const blob = await createIcoBlob(src);
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'favicon.ico';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(a.href);
}
async function downloadAll() {
const mode = document.getElementById('sizesSection').dataset.mode;
const src = getSourceCanvas(mode);
const zip = new JSZip();
// Add ICO
const icoBlob = await createIcoBlob(src);
zip.file('favicon.ico', icoBlob);
const promises = SIZES.map(sz => {
return new Promise(resolve => {
const canvas = document.createElement('canvas');
canvas.width = sz; canvas.height = sz;
const ctx = canvas.getContext('2d');
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = 'high';
ctx.drawImage(src, 0, 0, sz, sz);
canvas.toBlob(blob => {
const name = sz === 180 ? 'apple-touch-icon' : `favicon-${sz}x${sz}`;
zip.file(`${name}.png`, blob);
resolve();
}, 'image/png');
});
});
try {
await Promise.all(promises);
const content = await zip.generateAsync({type: "blob"});
const a = document.createElement('a');
a.href = URL.createObjectURL(content);
a.download = `favicons.zip`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(a.href);
} catch (err) {
console.error("Zip generation failed", err);
}
}
function copySnippet(btn) {
const el = document.getElementById('htmlSnippet');
const text = el.textContent;
navigator.clipboard.writeText(text).catch(() => {});
btn.textContent = 'copied!'; btn.classList.add('copied');
setTimeout(() => { btn.textContent = 'copy'; btn.classList.remove('copied'); }, 2000);
}
renderText();