This is a profile picture service that generates AI avatars deterministically from a user seed, so the same ID always gets the same image. You configure a fallback chain: check for uploaded photo, check cache, call gen-ai to create an illustrated or photoreal avatar, or drop back to initials on a colored background if the API is down. The smart move here is treating it as infrastructure, not decoration. Pre-warm the cache in batch for existing users, version your prompt template so you can restyle later without breaking URLs, and always serve that SVG initials fallback so rate limits never break your UI. Picsart built this as a Gravatar alternative that doesn't leak email hashes and stays on your CDN.
npx -y skills add PicsArt/gen-ai-skills --skill dev-avatar-service --agent claude-codeInstalls into .claude/skills of the current project.
A service endpoint that returns a branded avatar for any user: photoreal, illustrated, or pixel, deterministic from a seed (user ID / email hash / initials). Built around a fallback hierarchy — real photo → upload → AI from initials → static default — with caching by seed so you pay once per user.
Not for: deepfaking real people from a single photo (wrong tool + wrong ethics), or stylizing an already-great uploaded photo — let users keep their own.
Before building, ask (one message, 5 questions):
Skip if the user hands over a detailed brief.
Decide the fallback hierarchy first. This is the whole service:
1. user.photo_url → return as-is
2. cache.get(seed) → return stored AI avatar URL
3. gen-ai generate → store URL + return
4. initials on brand color → SVG fallback (no credits)
5. static /default.png → last resort
Estimate + validate model.
gen-ai models info recraftv4
gen-ai pricing recraftv4
gen-ai generate -m recraftv4 -p "illustrated avatar, seed abc123" \
--aspect-ratio 1:1 --dry-run --debug
Deterministic seed → prompt. Hash the seed into stable attribute picks. Never inject Math.random() or timestamps.
SEED="u_482c91"
# Pick palette + mood from the seed, not from RNG
HUE=$(printf "%s" "$SEED" | shasum | cut -c1-2) # 0-255 from hash
gen-ai generate -m recraftv4 \
-p "illustrated profile avatar, round frame, abstract humanoid silhouette, HSL hue ${HUE}, friendly neutral expression, flat vector, centered composition, studio background" \
--aspect-ratio 1:1 \
--negative-prompt "text, watermark, letters, realistic face, specific person" \
--save-to-drive --drive-folder avatars \
--json --no-input | jq -r '.url'
Cache the URL (not the binary) keyed on the seed. Any store: Redis, KV, Postgres column, S3 + stable filename.
Initials fallback (zero credits). When gen-ai is down, rate-limited, or over budget — return an SVG built from the user's initials on a hue derived from the same seed. Users never see a broken image.
const initials = name.split(' ').map(p => p[0]).slice(0, 2).join('').toUpperCase();
const hue = parseInt(sha1(seed).slice(0, 2), 16);
const svg = `<svg viewBox="0 0 64 64"><rect width="64" height="64" fill="hsl(${hue} 70% 45%)"/><text x="32" y="40" text-anchor="middle" font-size="28" fill="#fff" font-family="system-ui">${initials}</text></svg>`;
Wire the endpoint. /avatar/:seed → check photo → check cache → gen-ai → initials. 302 to CDN URL on success; inline SVG on fallback.
Pre-warm the cache in batch for existing users so your prod rollout doesn't hammer gen-ai live:
gen-ai batch run avatars-backfill.json -c 4 -o ./avatars-out
Backfill 500 existing users:
{
"defaults": {
"model": "recraftv4",
"aspectRatio": "1:1",
"negativePrompt": "text, watermark, letters, realistic face, specific person"
},
"jobs": [
{ "id": "u_482c91", "prompt": "illustrated profile avatar, round frame, flat vector, hue 42, friendly neutral, studio background" },
{ "id": "u_91ac3b", "prompt": "illustrated profile avatar, round frame, flat vector, hue 128, calm expression, studio background" },
{ "id": "u_7de119", "prompt": "illustrated profile avatar, round frame, flat vector, hue 201, warm smile, studio background" }
]
}
Output lands at ./avatars-out/<user-id>.webp — upload to your CDN under the same key. results.json becomes the seed → URL lookup table.
| Sub-task | Model | Why |
|---|---|---|
| Illustrated / vector / flat avatars | recraftv4 | Clean, consistent, cheap — ideal default |
| Photoreal portrait (synthetic, generic person) | flux-2-pro | Best faces; use with generic descriptors only |
| Pixel-art avatars | recraftv4 with pixel-style prompt | Lowest credits, stylistic control |
| Stylize an uploaded real photo (opt-in) | flux-kontext-pro + -i photo.jpg | Edit model preserves identity |
| Cheap draft / style exploration | gemini-3.1-flash-image | Lowest per-call cost |
| Identity-locked variants across sizes | ideogram-character + -i master.png | Same face across multiple avatars |
Verify live IDs with gen-ai models --mode image.
V=2 in the cache key when you restyle; old avatars age out, new ones regenerate on demand.text, watermark, letters, realistic face of specific person — avatars must not resemble real celebrities or render accidental copy.--save-to-drive --drive-folder avatars gives a stable CDN URL with no extra infra./avatar/:random can burn your credit budget in minutes.| Pitfall | Fix |
|---|---|
| Regenerated avatar looks different on every refresh | Non-deterministic prompt — hash the seed, derive attributes, no RNG/timestamps |
| Cache never invalidates after restyle | Version the prompt template (V=3 in the cache key) |
| AI renders real-looking faces resembling celebrities | Add negativePrompt: "realistic face of specific person, celebrity, known individual" |
| Text or watermarks appear in the avatar | Add negativePrompt: "text, watermark, letters, logos" |
| Bot traffic burns credit budget | Rate-limit per IP; allow only authed users to hit the generator; fallback to initials for anons |
| Gen-ai 429s cause broken avatar UI | SVG initials fallback must be the last step in the chain |
| Batch backfill half-fails | gen-ai batch resume <out> retries only failures |
| Different avatar style across the app | Commit the prompt template; never let callers pass raw prompts |
Run gen-ai whoami to confirm authentication, then re-run the failed command with --debug.
| Task | Credits | Time |
|---|---|---|
| 1 avatar (Recraft V4, illustrated) | 1-2 | ~4-8s |
| 1 avatar (Flux 2 Pro, photoreal) | 3-5 | ~10-15s |
| 1 avatar (Gemini 3.1 Flash, draft) | 1 | ~3-5s |
| Initials SVG fallback | 0 | <1ms |
| Cache read (KV/Redis hit) | 0 | ~2-5ms |
| Batch of 500 at concurrency 4 | ~750-1000 | ~15-20 min |
In steady state, expect >99% cache hit rate — near-zero ongoing cost.
gen-ai-use.md — CLI reference, auth, scripting with --json --no-input / jqgen-ai-batch.md — manifest shapes, batch resume, rate-limit strategy, Cloudflare Worker patterngen-ai-workflows.md — Workflow 6 (headshot studio) for stylizing uploaded photosdev-og-image-service — same caching + determinism patterns for OG cardssickn33/antigravity-awesome-skills
moizibnyousaf/ai-agent-skills
github/awesome-copilot