DEV Community

Cover image for I Built a Client-Side Favicon Generator (60-Second Tutorial)
bowen Tse
bowen Tse

Posted on Originally published at toolkitloop.blogspot.com

I Built a Client-Side Favicon Generator (60-Second Tutorial)

Your logo looks great at 1200px. At 16x16, it's a smudge.

That's the entire favicon problem in one sentence. Every site needs that tiny tab icon, and the standard workflow for making one is embarrassingly bad: upload your logo to some generator, create an account, wait in a queue, download a watermarked PNG, realize it's a single 32px file with no ICO, no Apple touch icon, no manifest — and start over.

I got tired of that loop, so I built a favicon generator that runs 100% in the browser: type 1–3 initials or pick an emoji, set colors, preview at real tab sizes, and download the full pack (ICO, PNGs, Apple touch icons, PWA manifest). No upload, no signup, no watermark. This post is the 60-second tutorial plus the one canvas technique that makes it all work.

The 60-second workflow

1. Type initials or pick an emoji. Two bold letters ("TK") survive 16px far better than a full wordmark. An emoji works too — it gets rendered into icons at every size, 512px down to 16px.

2. Set background and foreground colors. Contrast is everything at small sizes. Pick a shape (rounded square is the safe default) and keep the default 18% padding so the glyph doesn't touch the icon edges.

3. Check the tiny preview. This is the step most generators skip: look at your design at 64, 48, 32, 24, and 16px side by side. If the 16px version fails, simplify — thicker strokes, fewer characters, more contrast. A favicon that only looks good at 64px fails at its one job.

4. Download the pack. One click gives you the multi-size ICO, PNGs, Apple touch icons, site.webmanifest, the HTML snippet, and install notes for plain HTML, Next.js, and React/Vite.

5. Paste three lines into your <head>:

<link rel="icon" href="/favicon.ico" sizes="any">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
<link rel="manifest" href="/site.webmanifest">
Enter fullscreen mode Exit fullscreen mode

Then hard-refresh — browsers cache favicons aggressively, and a stale cache is the #1 reason "my new favicon isn't showing."

The canvas technique: legible glyphs at every size

The core trick is simple: render once at a large master size, then downscale. Text drawn directly at 16px is at the mercy of the browser's font rasterizer; text drawn at 512px and scaled down keeps clean edges. Here's the essence:

function renderFavicon(text, bg, fg, padding = 0.18) {
  const S = 512; // master size
  const canvas = document.createElement('canvas');
  canvas.width = canvas.height = S;
  const ctx = canvas.getContext('2d');

  // Background: rounded square with padding
  const pad = S * padding;
  ctx.fillStyle = bg;
  ctx.beginPath();
  ctx.roundRect(pad, pad, S - pad * 2, S - pad * 2, S * 0.22);
  ctx.fill();

  // Foreground: centered initials, sized to the padded box
  const box = S - pad * 2;
  ctx.fillStyle = fg;
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.font = `700 ${box * 0.52}px system-ui, sans-serif`;
  ctx.fillText(text, S / 2, S / 2 + box * 0.04);

  return canvas;
}

// Downscale the master to every target size
for (const size of [16, 32, 180, 192, 512]) {
  const c = document.createElement('canvas');
  c.width = c.height = size;
  c.getContext('2d').drawImage(master, 0, 0, size, size);
  const png = c.toDataURL('image/png'); // one per size
}
Enter fullscreen mode Exit fullscreen mode

Three details worth stealing for your own projects:

  • roundRect + padding keeps the glyph optically centered and clear of the edges where launchers clip. The 18% default is a good starting point.
  • The + box * 0.04 vertical nudge compensates for font metrics — most fonts render slightly high when centered by middle baseline. Without it, initials look like they're floating.
  • One master, many downscales beats re-rendering text per size. drawImage downscaling from 512px produces cleaner 16px output than drawing text at 16px directly.

(Emoji favicons use the same pipeline — draw the emoji glyph with fillText at master size and downscale. Canvas handles the rasterization; you handle the sizes.)

ICO vs PNG vs SVG, in 30 seconds

  • ICO is the broadly compatible workhorse — one file bundles multiple sizes, every browser understands it. Your baseline.
  • PNG serves specific sizes: Apple touch icon (180x180), PWA manifest (192/512). Crisp, universal.
  • SVG only when your source is a true, safe vector. Wrapping a raster logo in SVG gains nothing.

The full checklist: 16x16 + 32x32 (tabs, inside the ICO), 180x180 (iOS), 192 + 512 (PWA manifest).

Two gotchas before you ship

Dark mode tabs. A dark mark vanishes on dark browser chrome. Preview on both themes before exporting — a light rounded-square background behind a dark glyph survives either.

Google controls final rendering. Google shows favicons in mobile search results, but on its own schedule. Keep the icon square, stable, and crawlable; updates take time to propagate.

Try it

Disclosure, plainly stated: I built this tool — ToolkitLoop's free favicon generator — because I was tired of the upload-an-account-watermark loop. Everything renders locally in your browser; nothing you type ever leaves the tab.

Sixty seconds, five steps, one download. And now a question for the comments: what's the worst favicon crime you've seen in a browser tab? A stretched logo? A white square on a dark tab? A 404 where the icon should be? I'm collecting specimens for the next iteration — show me your smudges. 👇

Top comments (0)