name: image-portrait
version: 1.0.5
description: |
Identity-consistent portrait generation from a reference photo. Covers professional headshots, dating photos, style transfers, themed portraits, photo series, avatars, ID photos.
Use when generating styled portraits from a reference photo (e.g. professional headshot, anime avatar, cyberpunk portrait, travel photo, dating profile photo, ID photo).
metadata:
starchild:
emoji: "📸"
skillKey: image-portrait
user-invocable: true
disable-model-invocation: false
image-portrait
Use this skill for all identity-consistent portrait generation requests on Starchild.
Covers: professional headshots, dating/social photos, artistic style transfers, themed/holiday portraits, photo series, digital avatars, children/family photos, ID/passport photos.
Core principle: call the provided script. Do not re-implement proxy/billing plumbing.
1. Quick start — single portrait (most common)
⚠️ Execution context — read this first.
The code blocks below are Python, not shell commands. Starchild's bash tool
runs /bin/bash -c, which cannot parse exec(open(...)) — pasting them directly
into a bash command will fail with syntax error near unexpected token 'open'.
Also, exec(open(...)) inside python3 -c fails with NameError: __file__
because the script uses __file__ for path resolution.
Use python3 - <<'EOF' with from exports import when calling via the bash tool:
bash
python3 - <<'EOF'
import sys
sys.path.insert(0, "skills/image-portrait")
from exports import generate_portrait
result = generate_portrait(
image_path="path/to/user/photo.jpg",
style="professional",
)
print(result)
EOF
The heredoc (<<'EOF') preserves all quotes and newlines — no escaping needed.
python
exec(open('skills/image-portrait/generate_portrait.py').read())
result = generate_portrait(
image_path="path/to/user/photo.jpg",
style="professional",
)
# result -> {"success": True, "images": [{"local_path": "output/images/..."}], ...}
The script reads the local file, base64-encodes it, and sends it to fal.ai as a data URI — no manual URL publishing needed.
2. Quick start — public URL
python
exec(open('skills/image-portrait/generate_portrait.py').read())
result = generate_portrait(
face_image_url="https://example.com/photo.jpg",
style="anime",
)
3. Quick start — text-to-image (no reference photo)
python
exec(open('skills/image-portrait/generate_portrait.py').read())
result = generate_portrait(
prompt="a young woman in cyberpunk armor, neon city background, rain",
model="nanopro",
)
When no image_path or face_image_url is provided, the script uses the text-to-image endpoint (no /edit suffix).
Delivering the result to the user — IMPORTANT
Never hand the user the raw fal.media URL. fal serves files with restrictive CSP headers. The only reliable delivery path is the already-downloaded local file:
Use each image's local_path (e.g. output/images/xxx.png) — the script always downloads on success.
Tell the user the files are saved to output/images/ and viewable in the workspace file panel.
On Web channel, embed inline so the user can preview in chat:
markdown

On Telegram / WeChat: send via send_to_telegram(file_path="output/images/...", message_type="image") or send_to_wechat(file_path="output/images/...", message_type="image").
4. Parameters
Parameter
Required
Default
Description
image_path
no
—
Local workspace file path to the user's face photo
face_image_url
no
—
Public HTTPS URL of the user's face photo
style
no
"professional"
Preset style key (see §5)
scene
no
None
Custom scene description (appended to style prompt)
prompt
no
None
Fully custom prompt — overrides style+scene when set
Default for all requests. Balanced speed and quality.
GPT Image 2
gpt
~150s
Best
When user explicitly asks for "highest quality" or "best quality". Complex scenes.
Decision rules:
Default: always use nanopro unless the user explicitly requests otherwise.
Use nano2 when: user wants fastest results, is iterating on styles, generating many images, or says "quick", "draft", "fast".
Use gpt when: user says "highest quality", "best quality", "premium", or the scene is very complex with many specific details.
python
# Default (fast)
result = generate_portrait(image_path="photo.jpg", style="anime")
# High quality (user requested)
result = generate_portrait(image_path="photo.jpg", style="anime", model="gpt")
7. Custom scene examples
python
# Style + custom scene
result = generate_portrait(
image_path="uploads/my_photo.jpg",
style="professional",
scene="in a modern office with city skyline view",
)
# Custom scene only (defaults to professional style base)
result = generate_portrait(
image_path="uploads/my_photo.jpg",
scene="standing on a beach at sunset, golden hour lighting",
)
# Fully custom prompt (overrides everything)
result = generate_portrait(
image_path="uploads/my_photo.jpg",
prompt="portrait of a person as a medieval knight, full plate armor, castle background, dramatic lighting, oil painting style",
)
# Different aspect ratio
result = generate_portrait(
image_path="uploads/my_photo.jpg",
style="cyberpunk",
aspect_ratio="9:16",
)
# Multiple images
result = generate_portrait(
image_path="uploads/my_photo.jpg",
style="dating_cafe",
count=4,
)
8. Prompt engineering best practices
When the user's request doesn't match any preset style, or when you need to construct a custom prompt, follow these guidelines (derived from reference skills: ai-headshot-generation, ai-avatar-generation, style-transfer, portrait-enhancement, character-design-sheet, avatar-portrait, nano-banana-pro, pet-portrait-generation).
Automatic likeness preservation
When a reference image is provided (edit mode), the script automatically prepends a likeness preservation instruction to every prompt. This ensures the generated portrait preserves the subject's facial identity. You do NOT need to add likeness instructions manually — the script handles it.
Exception: avatar styles (avatar_3d, avatar_gaming, avatar_vtuber) skip the likeness prefix because stylization takes priority over photographic likeness.
The 7-element prompt structure
Every effective portrait prompt should include these elements (from nano-banana-pro skill):
✅ "neutral calm expression with mouth closed" (ID photo)
✅ "passionate expression, energetic" (musician)
Example: building a custom prompt
User: "I want a photo of me as a wizard in a magical forest"
python
result = generate_portrait(
image_path="uploads/photo.jpg",
prompt=(
"fantasy wizard portrait, wearing mystical purple robes with glowing runes, "
"ancient wooden staff with crystal orb, wise powerful expression, "
"enchanted forest background with bioluminescent plants and floating particles, "
"dramatic magical lighting with ethereal glow, "
"high fantasy art style, detailed digital painting quality"
),
)
# Note: likeness prefix is auto-added because image_path is provided
Example: pixel art avatar (from avatar-portrait skill)
User: "Make me a retro pixel art avatar"
python
result = generate_portrait(
image_path="uploads/photo.jpg",
prompt=(
"retro 16-bit pixel art portrait, visible pixels with clean lines, "
"rich colors, consistent shading, stylized but maintains individual features, "
"warm sunset cityscape background in matching pixel art style, "
"head and shoulders, square format"
),
)
9. Photo series
Generate a coordinated set of themed portraits in one call. Pass a custom list of styles/scenes — the agent assembles the list based on the user's request.
python
exec(open('skills/image-portrait/generate_portrait.py').read())
result = generate_series(
image_path="uploads/my_photo.jpg",
series=[
{"style": "professional"},
{"style": "casual", "scene": "at a rooftop bar, sunset"},
{"style": "anime"},
{"prompt": "portrait as a superhero, cape flowing, city skyline"},
],
)
# result -> {"success": True, "images": [...4 images...], "series": "custom"}
Each item in the list is a dict with optional keys:
style — any style key from §7 (e.g. "professional", "anime", "cyberpunk")
scene — override the scene description (combined with the style template)
This skill's core contract is identity preservation: whenever a reference photo is provided, a likeness prefix ("preserve the subject's exact facial features…") is prepended to every prompt (except the 3 avatar_* styles). This means:
User wants to drastically change the face/identity or fully re-imagine the person (e.g. "make me look like a different person", heavy character redesign) → route to image-create (text-to-image) instead. The likeness prefix will fight the stylization and iterations won't converge.
User wants strong stylization but still recognizable → stay here; use anime / avatar_3d etc.
User wants to edit a non-person photo → image-edit.
If a request keeps failing to move away from the reference photo's look after 2+ iterations, that's the likeness contract working as designed — switch skills rather than re-prompting.
11. Provided scripts
File
Purpose
generate_portrait.py
Core script: submit → poll → download. Handles local files (base64) and URLs, all styles, custom scenes, three models (nano2/nanopro/gpt).
exports.py
Re-exports generate_portrait, generate_series, STYLE_PROMPTS for programmatic use by other skills.
_cost_track.py
Cost tracking helper — records per-call costs via sc-proxy headers.
12. Local testing
Set FAL_KEY env var to call fal.ai directly (bypasses sc-proxy):