How to automate favicon generation with the jad local runner
- Step 1Understand why there's no cloud API — Favicon Master uses Canvas rasterisation and is not in the server-safe slug set, so the hosted API rejects it. Automation goes through the local runner (a daemon on your machine), where it's registered as a
runner-builtinfor Pro tier. - Step 2Install and pair the runner — Install
@jadapps/runnerand pair it once with your Pro+ account. It listens on127.0.0.1:9789and exposesGET /v1/health. When it's reachable, the web tool and your scripts can dispatch jobs to it. - Step 3Hash the brand SVG — In your script, hash
public/brand.svgand compare it to a cached hash from the last run. If unchanged, skip — favicons only need rebuilding when the mark changes. If changed, continue. - Step 4POST the job to the runner — Send the SVG as multipart form-data to
http://127.0.0.1:9789/v1/tools/svg-favicon-master/run. There are no options to pass — the field is empty/{}because the tool exposes no settings. The runner returns JSON with the output path of the generated ZIP. - Step 5Unpack into your public folder — Unzip the result into
public/:favicon.ico,icon-*.png, the SVG,manifest.json, andREADME.html. Use the<link>block from the README in your<head>(or your framework's metadata). - Step 6Commit the stable files — Commit
favicon.ico, your.svg, andmanifest.jsonfor reproducibility; the PNGs can be committed or rebuilt on deploy. Re-run the hash check on every build so unchanged brands cost nothing.
How to run Favicon Master programmatically
Favicon Master is browser/runner-only — it is not in the server-safe SVG API set. These are the real execution paths.
| Path | Where it runs | Availability |
|---|---|---|
| Web tool (Canvas) | Your browser tab | Pro tier; the default interactive path |
Local runner runner-builtin | Your machine, via 127.0.0.1:9789 | Pro+ with @jadapps/runner paired — the automation path |
Hosted cloud /api | Not available | Rejected: 'requires browser-side processing (DOM, Canvas…)' |
Runner request shape for svg-favicon-master
POST multipart form-data to the run endpoint. The tool has no options, so the options field is empty or omitted.
| Field | Value | Notes |
|---|---|---|
| Endpoint | POST http://127.0.0.1:9789/v1/tools/svg-favicon-master/run | Default port 9789 (override via NEXT_PUBLIC_RUNNER_URL in the web app) |
files | your brand.svg | A single SVG file part |
options | {} (or omit) | No settings exist — nothing to configure |
| Tier | Pro or higher | Runner returns 429 tier_limit_exceeded if under-tier |
| Response | JSON with outputPath, filename, sizeBytes | The generated ZIP is written to disk on your machine |
Cookbook
The local-runner automation pattern, end to end.
Check the runner is up before dispatching
Hit the health endpoint first so your script fails fast with a clear message if the runner isn't paired/running.
# health check
curl -fsS http://127.0.0.1:9789/v1/health || {
echo 'JAD runner not reachable on :9789 — start/pair it'
exit 1
}Hash-on-change guard
Only regenerate when brand.svg actually changed. Cache the hash between runs.
NEW=$(sha256sum public/brand.svg | cut -d' ' -f1) OLD=$(cat .favicon.hash 2>/dev/null || echo none) if [ "$NEW" = "$OLD" ]; then echo 'brand.svg unchanged — skipping favicon build' exit 0 fi
Dispatch the job to the local runner
POST the SVG as form-data. No options field is needed — the tool has none. The runner writes the ZIP to disk and returns its path.
curl -fsS \
-F 'files=@public/brand.svg;type=image/svg+xml' \
http://127.0.0.1:9789/v1/tools/svg-favicon-master/run \
-o runner-response.json
# runner-response.json → { outputPath, filename, sizeBytes, ... }Unpack into public/ and record the hash
Extract the package next to your other static assets, then persist the hash so the next build can skip.
OUT=$(node -e "process.stdout.write(require('./runner-response.json').outputPath)")
unzip -o "$OUT" -d public/
sha256sum public/brand.svg | cut -d' ' -f1 > .favicon.hash
echo 'favicons regenerated into public/'Wire it into a prebuild hook
Run the script before every build; the hash guard makes it a no-op when the brand is unchanged.
// package.json
{
"scripts": {
"prebuild": "bash scripts/generate-favicons.sh",
"build": "next build"
}
}
// In Next.js App Router, reference the files via metadata:
// icons: { icon: '/favicon.svg', apple: '/icon-180.png' }Edge cases and what actually happens
Calling the hosted cloud API for this tool
RejectedFavicon Master is not in the server-safe SVG slug set, so the hosted engine throws 'is not exposed via the API — it requires browser-side processing (DOM, Canvas…)'. Automate via the local runner or the browser, not a remote endpoint.
Runner not running or not paired
Dispatch failsIf the daemon isn't up on 127.0.0.1:9789, the POST fails to connect. In the web app this silently falls back to the browser Canvas path; in a headless CI script there's no browser to fall back to, so check GET /v1/health first and fail with a clear message.
Account is under Pro tier
429 tier_limit_exceededFavicon Master's runner-builtin requires Pro. A free-tier dispatch returns HTTP 429 with { error: "tier_limit_exceeded", limit, upgrade_url }. Retrying in-browser won't help — the cap is real. Upgrade or run a different tool.
Trying to pass options like background or padding
No-opThe tool has no schema options, so any options payload is ignored — there's no background, padding, or size selection to set. Pre-process the SVG instead (square the viewBox, bake a background rect) before dispatching.
Non-square brand SVG in the pipeline
Distorted outputGeneration stretches a non-square SVG onto each square canvas. Bake an aspect-ratio fix into the pipeline: run svg-viewbox-fixer (or normalise the source) before the favicon step so committed icons aren't squashed.
CI runner on a server with no display
Depends on runner modeThe hosted API can't help, and a pure headless CI box without the JAD runner can't rasterise Canvas. Run the favicon step on a developer machine (or a CI agent with the runner installed), commit the generated files, and let CI consume them as static assets.
SVG references a web font
Glyphs may dropCanvas rasterisation won't reliably load remote fonts, so <text> can disappear in the rendered PNGs. Convert text to paths with svg-font-to-path as an earlier pipeline step so the favicon is self-contained.
Output filename includes the source stem
ExpectedThe package and the SVG <link> use your source filename stem (e.g. brand.svg). If you want /favicon.svg in the markup, name the source favicon.svg or rewrite the link tag in your unpack step. The hash guard keys off the source file regardless.
Frequently asked questions
Is there a hosted favicon API I can POST to in CI?
No public cloud API. Favicon Master rasterises on Canvas and isn't in the server-safe SVG engine set, so the hosted /api rejects it. Automate through the local @jadapps/runner, where it runs as a Pro-tier runner-builtin on your own machine.
What's the runner endpoint and request shape?
POST multipart form-data to http://127.0.0.1:9789/v1/tools/svg-favicon-master/run with your SVG as a files part. There are no options, so the options field is {} or omitted. The response JSON includes outputPath for the generated ZIP on disk.
Should favicons be generated at build time or runtime?
Build time. Favicons change only when the brand changes, so generate once (guarded by a hash check) and serve the static files from CDN. Runtime generation adds latency for no benefit.
How do I skip regeneration when the brand hasn't changed?
Hash brand.svg, compare to a cached hash, and skip if they match. Because the tool has no options, identical input always yields the identical package, so the hash is a reliable cache key.
Does the brand SVG leave my machine?
No. The runner runs locally on 127.0.0.1; the job and its output stay on your machine. The web tool likewise rasterises in your browser. Neither path uploads your brand mark to JAD's servers.
Which tier do I need for the runner path?
Pro or higher. Favicon Master's runner-builtin is registered with tierRequired: pro; an under-tier dispatch returns HTTP 429 with an upgrade URL. Free-tier users get the Pro overlay in the web tool.
How does Next.js App Router consume the output?
Place the generated favicon.ico and PNGs in public/ (or app/) and reference them via the metadata icons export, e.g. icons: { icon: '/favicon.svg', apple: '/icon-180.png' }. A favicon.ico in app/ is auto-served at /favicon.ico.
What if our brand mark isn't square?
Square it before the favicon step — the generator stretches non-square SVGs. Add an aspect-fix stage (e.g. svg-viewbox-fixer to pad the viewBox) so the committed icons stay proportional.
Can I configure which sizes get generated?
No. The size list (16–512, thirteen sizes) and ICO frames (16/32/48) are fixed; the tool has no schema options. If you need a different size set, post-process the PNGs or use svg-app-icon-gen for native app icon dimensions.
Can I batch multiple brand SVGs through the runner?
Dispatch one job per SVG — Favicon Master takes a single source file. Loop your script over each brand mark and unpack each ZIP into its own folder. The per-file hash guard then keeps each brand's rebuild independent.
What happens if the runner is down during CI?
The POST fails to connect. Unlike the web tool, a headless CI script has no browser to fall back to, so guard with a GET /v1/health check and fail with a clear message — or run the favicon step on a machine that has the runner and commit the results.
Do I need the App Icon generator too?
Only if you're shipping native apps. svg-app-icon-gen produces the iOS appiconset and Android mipmap densities (Developer tier). For web favicons, Favicon Master is the right tool. Both can run through the same local runner.
Privacy first
Every JAD SVG tool runs entirely in your browser using the DOM API and Canvas. Your SVG files never leave your device — verified by zero outbound network requests during processing.