Deep Dive: Favicon Generation & Reconciliation
Favicons might seem like a small detail, but when you're managing thousands of dynamically generated projects, asset management becomes a serious engineering challenge. You want your users to have custom branding instantly, but you also need to ensure those assets persist correctly across multiple project redeployments.
Here is how BYOB handles favicon generation and reconciliation to ensure speed, stability, and brand consistency.
TLDR
- Favicons are generated dynamically when a project is initialized.
- Reconciliation ensures these assets are not overwritten during continuous deployment syncs.
- Recent speedups in our
services.pybackend drastically reduced asset sync times.
The Challenge of Dynamic Assets
In a standard web app, a favicon is a static file committed to the repository (e.g., favicon.png in the static folder). However, in BYOB, projects are generated dynamically from user prompts. We need to create a custom, relevant favicon and inject it into the project repository instantly.
The challenge arises when the user updates their project. If the system regenerates assets too aggressively, it risks overwriting a custom favicon the user has already set up. Conversely, if the system never regenerates, projects with stale or missing assets go unbranded.
How Generation Works
BYOB generates favicons using a dedicated favicon_generator module. The generator extracts color tokens from the project's app.css — pulling the primary and accent colors from the design system — and renders an SVG favicon featuring the project's initials against a matching gradient background.
From that SVG, the system uses CairoSVG and Pillow to rasterize:
- A 512×512 PNG (
favicon.png) - A multi-resolution ICO file (
favicon.ico) containing 16×16, 32×32, and 48×48 variants
The canonical favicon link is written into app.html — the single source of truth for favicon references. The system explicitly avoids mutating +layout.svelte.
Reconciliation Logic
To prevent overwriting user customizations, the reconciliation engine runs a detection pass before any generation:
- It reads the current
app.htmland+layout.sveltefrom the project workspace. - It checks whether either file contains a custom favicon link — one that references a non-default asset path.
- If a custom link is detected, the engine returns
skipped: truewith reasonuser_custom_link_in_html, protecting the user's branding entirely. - If no custom link is found, it checks whether existing favicon files (SVG, PNG, ICO, JPG, JPEG, WEBP) are present and valid.
- Only when generation is actually needed — missing assets, invalid SVG, or a force-regeneration flag — does it produce new files.
This approach means: if you upload your own favicon and reference it in app.html, BYOB will never overwrite it. The system respects user intent.
Concurrency & Performance
Favicon generation is guarded by two layers of concurrency control:
- A per-project lock ensures that only one favicon operation runs per project at a time, preventing race conditions during simultaneous updates.
- A global semaphore (configurable via
BYOB_FAVICON_MAX_CONCURRENT, defaulting to 4) limits total parallel generations across the platform, protecting system resources during batch operations.
Generation is triggered asynchronously in the background — it doesn't block the project initialization or deployment flow.
Summary
It's the small details that make a platform feel magical. By combining intelligent detection with deferred async generation and strict concurrency control, BYOB ensures that your project always looks the part — and that your custom branding is always respected.