Group G — markdown tech-debt cleanup (post-audit 2026-05-02).
- 36 SKILL.md files: added "## When to use" section. Was missing across the
catalog; orchestrator routing by keyword could not auto-dispatch.
- 20 code-implementer agent .md files: added Output Footer block prescribing
RULE 0.16 STATUS-TRUTH MARKER schema in agent's final report. Previously only
code-implementer-rust.md had it; other 27 language/role variants were silent
about the marker, breaking RULE 0.16 §3 status-truth aggregation for non-Rust
batches.
- skills/site-create/: added phase-5-preview.md and phase-6-deploy.md skeleton
files. SKILL.md table-of-contents referenced 7 phases; only 5 existed on disk.
- skills/{ai-animation,rag-pipeline}/skill.md: added migration banner comment
noting they should be SKILL.md (canonical filename). Case-rename via git is a
separate orchestrator task (macOS APFS is case-insensitive; Linux deploy needs
explicit rename).
- 3 deprecated skills (site-builder, competitor-analysis, design-inspiration):
added concrete removed-after dates (was vague "before v2").
- docs/CONVERGENCE-PLAN.md:129: TBD on _blocks/evidence-grading.md duplicate
resolved (file exists, not duplicated).
- docs/DNA-INDEX.md: count edits made then overwritten by auto-encyclopedia-refresh
hook during agent run. The .kei-registry-ignore files in test fixtures (Group F)
are the structural fix; kei-registry walker implementation is the follow-up.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
843 lines
28 KiB
Markdown
843 lines
28 KiB
Markdown
<!-- migrated from skills/ai-animation/skill.md (lowercase legacy filename) on 2026-05-02 -->
|
||
---
|
||
name: ai-animation
|
||
description: Use when creating AI-powered animations and videos — nano-banana keyframes → Kling/Luma/Veo video generation via fal.ai → web-ready output. Full pipeline from concept to scroll-driven animation or video embed. Triggers on "AI animation", "animate image", "AI video", "Kling", "image to video", "анимируй", "сделай видео из картинки".
|
||
arguments:
|
||
- name: pipeline
|
||
description: "Pipeline: keyframe-to-video, image-to-video, text-to-video, video-to-web (default: keyframe-to-video)"
|
||
required: false
|
||
- name: model
|
||
description: "Model: kling, luma, veo, ltx, pixverse (default: auto-select by use case)"
|
||
required: false
|
||
- name: style
|
||
description: "Visual style or reference for generation"
|
||
required: false
|
||
---
|
||
|
||
# AI Animation Pipeline
|
||
|
||
> nano-banana (keyframes) → fal.ai (video gen) → FFmpeg (web-ready) → scroll/embed
|
||
> Prices verified 2026-03-13 [E1-fal.ai API docs]
|
||
|
||
## Pipeline Overview
|
||
|
||
```
|
||
Concept / Prompt
|
||
│
|
||
├─→ [1. Keyframes] nano-banana → reference images (poses, angles, scenes)
|
||
│
|
||
├─→ [2. Animate] fal.ai → Kling / Luma / Veo → MP4 video
|
||
│
|
||
├─→ [3. Process] FFmpeg → frame sequence / optimized video / GIF
|
||
│
|
||
└─→ [4. Integrate] scroll-animation / video embed / motion-design
|
||
```
|
||
|
||
---
|
||
|
||
## Model Selection Matrix [E1-fal.ai docs]
|
||
|
||
| Use Case | Model | Endpoint | Cost/sec | Duration | Why |
|
||
|----------|-------|----------|----------|----------|-----|
|
||
| **Product reveal** | Kling v3 Pro | `fal-ai/kling-video/v3/pro/image-to-video` | $0.112 (no audio) / $0.168 (audio) | 3-15s | Best motion, multi-prompt, elements |
|
||
| **Hero loop** | Luma Ray 2 | `fal-ai/luma-dream-machine/ray-2/image-to-video` | $0.10/sec @540p | 5s, 9s | `loop: true`, start+end keyframes |
|
||
| **Cinematic** | Veo 3 | `fal-ai/veo3` | $0.20 (no audio) / $0.40 (audio) | 4-8s | Best quality + audio, 1080p |
|
||
| **Cinematic (fast)** | Veo 3 Fast | `fal-ai/veo3/fast/image-to-video` | $0.10 (no audio) / $0.15 (audio) | 4-8s | 50% cheaper I2V |
|
||
| **Bulk/cheap** | LTX 2.0 | `fal-ai/ltx-2/text-to-video/fast` | $0.04 @1080p | 6-20s | Cheapest, up to 4K, extend/retake |
|
||
| **Stylized** | PixVerse v4.5 | `fal-ai/pixverse/v4.5/image-to-video` | $0.03-0.08/sec | 5-8s | anime/3d/clay/comic/cyberpunk + camera presets |
|
||
|
||
### Quick Decision
|
||
|
||
- Loop for hero section? → **Luma Ray 2** (loop: true)
|
||
- Animate a product/design comp? → **Kling v3 Pro**
|
||
- Maximum quality, budget OK? → **Veo 3**
|
||
- Need 10+ videos cheap? → **LTX 2.0**
|
||
- Specific art style? → **PixVerse v4.5**
|
||
|
||
---
|
||
|
||
## Step 1: Generate Keyframes (nano-banana)
|
||
|
||
Generate reference images that will be animated into video.
|
||
|
||
### Best Practices for Animation Keyframes
|
||
|
||
```bash
|
||
# Product floating in space — good for Kling rotation
|
||
nano-banana "premium wireless headphones floating on dark background, studio lighting, centered composition, clean edges" -s 2K -a 16:9 -o keyframe-product
|
||
|
||
# Scene for cinematic animation — good for Veo/Kling
|
||
nano-banana "cyberpunk street at golden hour, rain puddles, neon reflections, cinematic composition" -s 2K -a 16:9 -o keyframe-scene
|
||
|
||
# Character pose — good for Kling motion
|
||
nano-banana "3D character mascot robot waving, isometric view, white background, Pixar style" -s 1K -a 1:1 -o keyframe-character
|
||
|
||
# Hero background — good for Luma loop
|
||
nano-banana "abstract flowing liquid metal, iridescent purple and gold, macro photography" -s 2K -a 16:9 -o keyframe-hero
|
||
|
||
# Transparent asset for animation
|
||
nano-banana "floating crystal sphere with inner glow" -t -s 1K -o keyframe-crystal
|
||
```
|
||
|
||
### nano-banana → fal.ai Upload Workflow
|
||
|
||
nano-banana outputs local PNG files. To use as input for fal.ai video models:
|
||
|
||
```python
|
||
import fal_client
|
||
|
||
# Option A: Upload to fal CDN (recommended for large files)
|
||
image_url = fal_client.upload_file("keyframe-product.png") # → https://fal.media/files/...
|
||
|
||
# Option B: Encode as data URI (inline, faster for small files, no CDN hop)
|
||
image_data = fal_client.encode_file("keyframe-product.png")
|
||
|
||
# Then use in any video model
|
||
result = fal_client.run("fal-ai/kling-video/v3/pro/image-to-video", arguments={
|
||
"start_image_url": image_url, # or image_data
|
||
"prompt": "the headphones slowly rotate",
|
||
"duration": "5"
|
||
})
|
||
```
|
||
|
||
**Optimal nano-banana settings per video model:**
|
||
|
||
| Video Model | nano-banana Size | Aspect | Notes |
|
||
|------------|-----------------|--------|-------|
|
||
| Kling v3 Pro | `-s 2K` | `-a 16:9` or `-a 9:16` or `-a 1:1` | Match `aspect_ratio` param |
|
||
| Luma Ray 2 | `-s 2K` | `-a 16:9` or any of 6 ratios | 540p output, 2K input still better |
|
||
| Veo 3 | `-s 2K` | `-a 16:9` or `-a 9:16` only | **Must be 720p+ and <8MB** |
|
||
| LTX 2.0 | `-s 2K` | `-a 16:9` | Flexible, handles most inputs |
|
||
| PixVerse v4.5 | `-s 2K` | match target | Resolution matches input |
|
||
|
||
### Keyframe Prompting Rules
|
||
|
||
1. **Clean edges** — AI video models struggle with busy/cluttered compositions
|
||
2. **Centered subject** — off-center subjects may drift during animation
|
||
3. **Consistent lighting** — dramatic lighting gives video models more to work with
|
||
4. **Simple backgrounds** — solid/gradient backgrounds → cleaner motion
|
||
5. **Describe potential motion** — "floating", "flowing", "spinning" primes the composition
|
||
6. **16:9 for video** — match the target video aspect ratio in the keyframe
|
||
7. **High resolution** — use `-s 2K` minimum, video models downsample better than upscale
|
||
|
||
---
|
||
|
||
## Step 2: Animate via fal.ai
|
||
|
||
### Prerequisites
|
||
|
||
```bash
|
||
pip install fal-client
|
||
export FAL_KEY="your-key"
|
||
```
|
||
|
||
### fal_client SDK Patterns (Python)
|
||
|
||
```python
|
||
import fal_client
|
||
|
||
# SYNC — blocks until done (simplest, for single generations)
|
||
result = fal_client.run("fal-ai/veo3", arguments={...})
|
||
video_url = result["video"]["url"]
|
||
|
||
# ASYNC — non-blocking
|
||
import asyncio
|
||
async def generate():
|
||
result = await fal_client.run_async("fal-ai/veo3", arguments={...})
|
||
return result["video"]["url"]
|
||
|
||
# QUEUE with progress tracking — best for long jobs
|
||
async def generate_with_progress():
|
||
handler = await fal_client.submit_async("fal-ai/veo3", arguments={...})
|
||
logs_index = 0
|
||
async for event in handler.iter_events(with_logs=True):
|
||
if isinstance(event, fal_client.Queued):
|
||
print(f"Queue position: {event.position}")
|
||
elif isinstance(event, (fal_client.InProgress, fal_client.Completed)):
|
||
for log in event.logs[logs_index:]:
|
||
print(log["message"])
|
||
logs_index = len(event.logs)
|
||
result = await handler.get()
|
||
return result["video"]["url"]
|
||
|
||
# FILE UPLOAD — local file → fal CDN URL
|
||
url = fal_client.upload_file("local-image.png") # returns https://fal.media/files/...
|
||
data_uri = fal_client.encode_file("local-image.png") # inline data URI, no upload
|
||
```
|
||
|
||
### REST API Pattern (curl)
|
||
|
||
```bash
|
||
# Sync (blocks until done, timeout risk for long generations)
|
||
curl -X POST "https://fal.run/{endpoint}" \
|
||
-H "Authorization: Key $FAL_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"prompt": "...", ...}'
|
||
|
||
# Queue submit (non-blocking, recommended)
|
||
REQUEST_ID=$(curl -s -X POST "https://queue.fal.run/{endpoint}" \
|
||
-H "Authorization: Key $FAL_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"prompt": "...", ...}' | jq -r '.request_id')
|
||
|
||
# Check status
|
||
curl -s "https://queue.fal.run/{endpoint}/requests/$REQUEST_ID/status" \
|
||
-H "Authorization: Key $FAL_KEY"
|
||
|
||
# Get result
|
||
curl -s "https://queue.fal.run/{endpoint}/requests/$REQUEST_ID" \
|
||
-H "Authorization: Key $FAL_KEY"
|
||
|
||
# Webhook (fire and forget)
|
||
curl -X POST "https://queue.fal.run/{endpoint}" \
|
||
-H "Authorization: Key $FAL_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-H "X-Fal-Webhook-Url: https://your-server.com/webhook" \
|
||
-d '{"prompt": "..."}'
|
||
```
|
||
|
||
### Kling v3 Pro — Image to Video [E1-verified]
|
||
|
||
Best for: product reveals, design comp animation, character motion, multi-shot sequences.
|
||
|
||
```python
|
||
import fal_client
|
||
|
||
result = fal_client.run(
|
||
"fal-ai/kling-video/v3/pro/image-to-video",
|
||
arguments={
|
||
"prompt": "the headphones slowly rotate 360 degrees, studio lighting, smooth motion",
|
||
"start_image_url": "https://...keyframe-product.png",
|
||
"duration": "5", # "3" to "15" seconds (string)
|
||
"aspect_ratio": "16:9", # 16:9, 9:16, 1:1
|
||
"generate_audio": False, # True adds $0.056/sec
|
||
}
|
||
)
|
||
video_url = result["video"]["url"]
|
||
print(f"Video: {video_url}")
|
||
```
|
||
|
||
```javascript
|
||
// Node.js
|
||
import { fal } from "@fal-ai/client";
|
||
|
||
const result = await fal.subscribe("fal-ai/kling-video/v3/pro/image-to-video", {
|
||
input: {
|
||
prompt: "the headphones slowly rotate 360 degrees",
|
||
start_image_url: "https://...keyframe.png",
|
||
duration: "5",
|
||
aspect_ratio: "16:9",
|
||
generate_audio: false,
|
||
},
|
||
logs: true,
|
||
onQueueUpdate: (update) => {
|
||
if (update.status === "IN_PROGRESS") {
|
||
update.logs.map((log) => log.message).forEach(console.log);
|
||
}
|
||
},
|
||
});
|
||
```
|
||
|
||
**Parameters:**
|
||
| Param | Values | Default | Notes |
|
||
|-------|--------|---------|-------|
|
||
| `prompt` | string | — | Describe MOTION, not scene. Mutually exclusive with `multi_prompt` |
|
||
| `start_image_url` | URL | required | Source keyframe (**not** `image_url`) |
|
||
| `end_image_url` | URL | — | End frame for interpolation |
|
||
| `duration` | `"3"`-`"15"` | `"5"` | String, per-second increments |
|
||
| `aspect_ratio` | `16:9`, `9:16`, `1:1` | `16:9` | |
|
||
| `generate_audio` | bool | `true` | Native audio (Chinese/English) |
|
||
| `negative_prompt` | string | `"blur, distort, and low quality"` | |
|
||
| `cfg_scale` | float | `0.5` | Prompt adherence strength |
|
||
| `multi_prompt` | array | — | Multi-shot: `[{"prompt": "...", "duration": "5"}, ...]` |
|
||
| `elements` | array | — | Character/object consistency via reference images |
|
||
|
||
**Cost [E1]:**
|
||
- No audio: $0.112/sec → 5s = $0.56, 10s = $1.12
|
||
- With audio: $0.168/sec → 5s = $0.84, 10s = $1.68
|
||
- With voice control: $0.196/sec
|
||
|
||
**Multi-shot example:**
|
||
```python
|
||
result = fal_client.run("fal-ai/kling-video/v3/pro/image-to-video", arguments={
|
||
"start_image_url": "https://...keyframe.png",
|
||
"multi_prompt": [
|
||
{"prompt": "camera orbits around the product slowly", "duration": "5"},
|
||
{"prompt": "zoom in to the detail panel", "duration": "5"}
|
||
],
|
||
"generate_audio": False,
|
||
})
|
||
```
|
||
|
||
**curl:**
|
||
```bash
|
||
curl -X POST "https://fal.run/fal-ai/kling-video/v3/pro/image-to-video" \
|
||
-H "Authorization: Key $FAL_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"start_image_url": "https://...keyframe.png", "prompt": "rotate slowly", "duration": "5", "generate_audio": false}'
|
||
```
|
||
|
||
**Prompting for motion:**
|
||
- Describe WHAT MOVES: "the camera slowly orbits around the product"
|
||
- NOT the scene: ~~"beautiful product on dark background"~~
|
||
- Be specific: "rotate 90 degrees clockwise" > "rotate"
|
||
- Keep it simple: one primary motion per clip
|
||
|
||
### Luma Ray 2 — Looping Hero Videos [E1-verified]
|
||
|
||
Best for: hero section backgrounds, ambient loops, living textures, start→end interpolation.
|
||
|
||
```python
|
||
result = fal_client.run(
|
||
"fal-ai/luma-dream-machine/ray-2/image-to-video",
|
||
arguments={
|
||
"prompt": "gentle flowing motion, seamless loop",
|
||
"image_url": "https://...keyframe-hero.png",
|
||
"loop": True, # seamless loop!
|
||
"resolution": "540p", # 540p, 720p (2x), 1080p (4x)
|
||
"duration": "5s", # "5s" or "9s"
|
||
"aspect_ratio": "16:9",
|
||
}
|
||
)
|
||
video_url = result["video"]["url"]
|
||
```
|
||
|
||
**Key features:**
|
||
- `loop: True` → seamless loops for `<video loop autoplay>` hero sections
|
||
- `end_image_url` → interpolate between start and end keyframes (морфинг!)
|
||
|
||
**Start→End keyframe example:**
|
||
```python
|
||
result = fal_client.run("fal-ai/luma-dream-machine/ray-2/image-to-video", arguments={
|
||
"prompt": "smooth transformation between the two states",
|
||
"image_url": "https://...keyframe-start.png",
|
||
"end_image_url": "https://...keyframe-end.png",
|
||
"duration": "5s",
|
||
"aspect_ratio": "16:9",
|
||
})
|
||
```
|
||
|
||
**Parameters:**
|
||
| Param | Values | Default | Notes |
|
||
|-------|--------|---------|-------|
|
||
| `prompt` | string | required | Min 3, max 5000 chars |
|
||
| `image_url` | URL | required | Start frame |
|
||
| `end_image_url` | URL | — | End frame (interpolation) |
|
||
| `loop` | bool | `false` | End blended with beginning |
|
||
| `duration` | `"5s"`, `"9s"` | `"5s"` | 9s costs 2x |
|
||
| `resolution` | `"540p"`, `"720p"`, `"1080p"` | `"540p"` | 720p=2x, 1080p=4x cost |
|
||
| `aspect_ratio` | `16:9`, `9:16`, `4:3`, `3:4`, `21:9`, `9:21` | `"16:9"` | |
|
||
|
||
**Cost [E1]:**
|
||
| Resolution | 5s | 9s |
|
||
|------------|----|----|
|
||
| 540p | $0.50 | $1.00 |
|
||
| 720p | $1.00 | $2.00 |
|
||
| 1080p | $2.00 | $4.00 |
|
||
|
||
**curl:**
|
||
```bash
|
||
curl -X POST "https://fal.run/fal-ai/luma-dream-machine/ray-2/image-to-video" \
|
||
-H "Authorization: Key $FAL_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"prompt": "gentle flowing motion, seamless loop", "image_url": "https://...hero.png", "loop": true, "resolution": "540p", "duration": "5s"}'
|
||
```
|
||
|
||
### Veo 3 — Maximum Quality [E1-verified]
|
||
|
||
Best for: cinematic hero videos, with audio. Two variants: standard and fast.
|
||
|
||
```python
|
||
# Standard — best quality
|
||
result = fal_client.run(
|
||
"fal-ai/veo3",
|
||
arguments={
|
||
"prompt": "cinematic reveal of headphones, dramatic lighting, camera dollies in slowly",
|
||
"image_url": "https://...keyframe.png", # optional for text-to-video
|
||
"duration": "8s", # "4s", "6s", "8s"
|
||
"resolution": "1080p", # "720p", "1080p"
|
||
"aspect_ratio": "16:9", # "16:9", "9:16"
|
||
"generate_audio": True, # unique: audio generation
|
||
}
|
||
)
|
||
video_url = result["video"]["url"]
|
||
|
||
# Fast I2V — 50% cheaper for image-to-video
|
||
result = fal_client.run(
|
||
"fal-ai/veo3/fast/image-to-video",
|
||
arguments={
|
||
"prompt": "camera slowly orbits the product",
|
||
"image_url": "https://...keyframe.png",
|
||
"duration": "8s",
|
||
"generate_audio": False,
|
||
}
|
||
)
|
||
```
|
||
|
||
**Unique:** Veo 3 generates **audio** with the video. `safety_tolerance` 1-6 (1=strict, 6=permissive). `auto_fix` auto-rewrites failing prompts. `seed` for reproducibility.
|
||
|
||
**Cost [E1]:**
|
||
| Variant | No audio | With audio |
|
||
|---------|----------|------------|
|
||
| Standard | $0.20/sec | $0.40/sec |
|
||
| Fast I2V | $0.10/sec | $0.15/sec |
|
||
|
||
8s standard with audio = $3.20. 8s fast I2V no audio = $0.80.
|
||
|
||
**Image input constraints (Veo 3):** Max 8MB, must be 720p+, formats: PNG/JPEG/WebP. Aspect must be 16:9 or 9:16.
|
||
|
||
**curl (fast I2V):**
|
||
```bash
|
||
curl -X POST "https://fal.run/fal-ai/veo3/fast/image-to-video" \
|
||
-H "Authorization: Key $FAL_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"prompt": "gentle camera movement, ambient sounds", "image_url": "https://...keyframe.png", "duration": "8s", "generate_audio": true}'
|
||
```
|
||
|
||
### LTX 2.0 Fast — Budget Option [E1-verified]
|
||
|
||
Best for: bulk generation, prototyping, long videos up to 20s, up to 4K.
|
||
|
||
```python
|
||
# Text-to-video
|
||
result = fal_client.run(
|
||
"fal-ai/ltx-2/text-to-video/fast",
|
||
arguments={
|
||
"prompt": "flowing abstract particles, dark background",
|
||
"duration": 6, # 6, 8, 10, 12, 14, 16, 18, 20 seconds
|
||
"resolution": "1080p", # "1080p", "1440p", "2160p"
|
||
"fps": 25, # 25 or 50
|
||
"generate_audio": True,
|
||
}
|
||
)
|
||
|
||
# Image-to-video
|
||
result = fal_client.run(
|
||
"fal-ai/ltx-2/image-to-video/fast",
|
||
arguments={
|
||
"prompt": "gentle camera movement revealing details",
|
||
"image_url": "https://...keyframe.png",
|
||
"duration": 6,
|
||
"resolution": "1080p",
|
||
}
|
||
)
|
||
|
||
# Extend existing video
|
||
result = fal_client.run(
|
||
"fal-ai/ltx-2/extend-video",
|
||
arguments={
|
||
"video_url": "https://...existing.mp4",
|
||
"prompt": "continue the motion smoothly",
|
||
"duration": 5, # max 20s total
|
||
"mode": "end", # "start" or "end"
|
||
}
|
||
)
|
||
```
|
||
|
||
**Cost [E1]:** $0.04/sec @1080p, $0.08/sec @1440p, $0.16/sec @2160p
|
||
6s @1080p = $0.24. **5x cheaper** than Kling (no audio).
|
||
|
||
**Extra endpoints:** `extend-video` (extend existing), `retake-video` (regenerate segment), `audio-to-video` (audio→video).
|
||
|
||
### PixVerse v4.5 — Stylized [E1-verified]
|
||
|
||
Best for: 3D render, cyberpunk, anime, clay, comic styles. Has camera presets.
|
||
|
||
```python
|
||
result = fal_client.run(
|
||
"fal-ai/pixverse/v4.5/image-to-video",
|
||
arguments={
|
||
"prompt": "cyberpunk city comes alive, neon signs flicker",
|
||
"image_url": "https://...keyframe.png",
|
||
"style": "cyberpunk", # anime, 3d_animation, clay, comic, cyberpunk
|
||
"camera_movement": "smooth_zoom_in", # see list below
|
||
"resolution": "720p", # 360p, 540p, 720p, 1080p
|
||
"duration": "5", # "5" or "8" (8s = 2x cost, 1080p = 5s only)
|
||
}
|
||
)
|
||
```
|
||
|
||
**Camera presets:** `horizontal_left`, `horizontal_right`, `vertical_up`, `vertical_down`, `zoom_in`, `zoom_out`, `crane_up`, `quickly_zoom_in`, `quickly_zoom_out`, `smooth_zoom_in`, `camera_rotation`, `robo_arm`, `super_dolly_out`, `whip_pan`, `hitchcock`, `left_follow`, `right_follow`, `pan_left`, `pan_right`, `fix_bg`
|
||
|
||
**Cost [E1]:**
|
||
| Resolution | 5s | 8s (2x) |
|
||
|------------|----|----|
|
||
| 360p/540p | $0.15 | $0.30 |
|
||
| 720p | $0.20 | $0.40 |
|
||
| 1080p | $0.40 | — (5s only) |
|
||
|
||
---
|
||
|
||
## Step 3: Process for Web (FFmpeg)
|
||
|
||
### Download Generated Video
|
||
|
||
```bash
|
||
# Download from fal.ai URL
|
||
curl -o generated.mp4 "VIDEO_URL_FROM_FAL"
|
||
```
|
||
|
||
### Route A: Frame Sequence (for scroll animations)
|
||
|
||
Use when the video will be played via GSAP ScrollTrigger or canvas scroll.
|
||
|
||
```bash
|
||
# Extract frames at 30fps, 1920x1080
|
||
mkdir -p frames/desktop
|
||
ffmpeg -i generated.mp4 -vf "fps=30,scale=1920:1080" frames/desktop/frame_%04d.png
|
||
|
||
# Convert to WebP
|
||
for f in frames/desktop/*.png; do
|
||
cwebp -q 80 "$f" -o "${f%.png}.webp"
|
||
rm "$f"
|
||
done
|
||
|
||
# Mobile version (fewer frames, smaller)
|
||
mkdir -p frames/mobile
|
||
ffmpeg -i generated.mp4 -vf "fps=15,scale=960:540" frames/mobile/frame_%04d.png
|
||
for f in frames/mobile/*.png; do
|
||
cwebp -q 80 "$f" -o "${f%.png}.webp"
|
||
rm "$f"
|
||
done
|
||
```
|
||
|
||
→ Then use `scroll-animation` skill for GSAP ScrollTrigger canvas playback.
|
||
|
||
### Route B: Optimized Video (for autoplay/embed)
|
||
|
||
Use for hero video backgrounds, embedded players.
|
||
|
||
```bash
|
||
# Web-optimized MP4 (H.264 for max compat)
|
||
ffmpeg -i generated.mp4 \
|
||
-c:v libx264 -preset slow -crf 23 \
|
||
-c:a aac -b:a 128k \
|
||
-movflags +faststart \
|
||
-vf "scale=1920:1080" \
|
||
hero-video.mp4
|
||
|
||
# WebM (VP9, smaller, modern browsers)
|
||
ffmpeg -i generated.mp4 \
|
||
-c:v libvpx-vp9 -crf 30 -b:v 2M \
|
||
-c:a libopus -b:a 128k \
|
||
-vf "scale=1920:1080" \
|
||
hero-video.webm
|
||
|
||
# AV1 (smallest, newer browsers)
|
||
ffmpeg -i generated.mp4 \
|
||
-c:v libaom-av1 -crf 30 -b:v 0 \
|
||
-c:a libopus -b:a 128k \
|
||
-vf "scale=1920:1080" \
|
||
hero-video.av1.mp4
|
||
```
|
||
|
||
### Route C: Scrub-Optimized Video (for scroll-driven playback)
|
||
|
||
```bash
|
||
# Every frame is keyframe = instant seeking
|
||
ffmpeg -i generated.mp4 \
|
||
-c:v libx264 -preset slow -crf 23 \
|
||
-g 1 -an -movflags +faststart \
|
||
-vf "scale=1920:1080" \
|
||
scrub-video.mp4
|
||
```
|
||
|
||
### Route D: Looping GIF (for social/email)
|
||
|
||
```bash
|
||
# High quality GIF with palette optimization
|
||
ffmpeg -i generated.mp4 \
|
||
-vf "fps=15,scale=480:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \
|
||
-loop 0 output.gif
|
||
```
|
||
|
||
---
|
||
|
||
## Step 4: Integrate into Web
|
||
|
||
### Hero Video Background
|
||
|
||
```html
|
||
<section class="hero">
|
||
<video autoplay loop muted playsinline class="hero-video">
|
||
<source src="hero-video.av1.mp4" type="video/mp4; codecs=av01.0.05M.08">
|
||
<source src="hero-video.webm" type="video/webm">
|
||
<source src="hero-video.mp4" type="video/mp4">
|
||
</video>
|
||
<div class="hero-content">
|
||
<h1>Product Name</h1>
|
||
</div>
|
||
</section>
|
||
|
||
<style>
|
||
.hero { position: relative; height: 100vh; overflow: hidden; }
|
||
.hero-video {
|
||
position: absolute; inset: 0;
|
||
width: 100%; height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
.hero-content {
|
||
position: relative; z-index: 1;
|
||
display: grid; place-items: center;
|
||
height: 100%;
|
||
}
|
||
</style>
|
||
```
|
||
|
||
### Scroll-Driven Frame Sequence
|
||
|
||
→ Invoke `scroll-animation` skill with the frame sequence from Step 3A.
|
||
|
||
### Reduced Motion Fallback
|
||
|
||
```html
|
||
<picture>
|
||
<source srcset="keyframe-hero.avif" type="image/avif">
|
||
<source srcset="keyframe-hero.webp" type="image/webp">
|
||
<img src="keyframe-hero.jpg" alt="Hero" class="hero-fallback">
|
||
</picture>
|
||
|
||
<style>
|
||
.hero-video { display: block; }
|
||
.hero-fallback { display: none; }
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.hero-video { display: none; }
|
||
.hero-fallback { display: block; }
|
||
}
|
||
</style>
|
||
```
|
||
|
||
---
|
||
|
||
## Cost Calculator [E1-verified]
|
||
|
||
### Per-Video Costs (no audio, cheapest resolution)
|
||
|
||
| Model | 5s | 10s | With keyframe ($0.07) |
|
||
|-------|-----|------|-----------------------|
|
||
| Kling v3 Pro (no audio) | $0.56 | $1.12 | $0.63 / $1.19 |
|
||
| Kling v3 Pro (audio) | $0.84 | $1.68 | $0.91 / $1.75 |
|
||
| Luma Ray 2 @540p | $0.50 | — | $0.57 |
|
||
| Luma Ray 2 @1080p | $2.00 | — | $2.07 |
|
||
| Veo 3 Standard (no audio) | $1.00 | — | $1.07 |
|
||
| Veo 3 Fast I2V (no audio) | $0.50 | — | $0.57 |
|
||
| LTX 2.0 @1080p | $0.20 | $0.40 | $0.27 / $0.47 |
|
||
| PixVerse v4.5 @720p | $0.20 | — | $0.27 |
|
||
|
||
### Landing Page Budget Estimates
|
||
|
||
| Scenario | Videos | Model | Est. Cost |
|
||
|----------|--------|-------|-----------|
|
||
| 1 hero loop @540p | 1 | Luma | ~$0.57 |
|
||
| Product reveal 10s | 1 | Kling no audio | ~$1.19 |
|
||
| Product + hero | 2 | Kling + Luma | ~$1.20 |
|
||
| Full cinematic page | 3-5 | Mixed | ~$2-5 |
|
||
| Bulk content (10 clips) | 10 | LTX 2.0 | ~$2.70 |
|
||
| Max quality (Veo 3 audio) | 3 | Veo 3 Standard | ~$10+ |
|
||
|
||
### Before Running — API Cost Guard
|
||
|
||
1. Check fal.ai dashboard balance
|
||
2. Calculate exact cost: `duration × cost_per_sec + keyframe_cost`
|
||
3. Generate ONE test clip first
|
||
4. Review quality before batch
|
||
5. Tell user total cost before proceeding
|
||
|
||
---
|
||
|
||
## Full Pipeline Examples
|
||
|
||
### Example 1: Apple-Style Product Scroll
|
||
|
||
```bash
|
||
# 1. Keyframe
|
||
nano-banana "premium smart watch floating on dark background, studio rim lighting, centered" -s 2K -a 16:9 -o watch-keyframe
|
||
|
||
# 2. Animate (product rotation)
|
||
python3 -c "
|
||
import fal_client
|
||
result = fal_client.run('fal-ai/kling-video/v3/pro/image-to-video', arguments={
|
||
'prompt': 'the watch slowly rotates 360 degrees, camera orbits smoothly, dramatic lighting',
|
||
'start_image_url': 'file://watch-keyframe.png',
|
||
'duration': '10',
|
||
'aspect_ratio': '16:9',
|
||
'generate_audio': False,
|
||
})
|
||
print(result['video']['url'])
|
||
"
|
||
|
||
# 3. Download & extract frames
|
||
curl -o watch-rotation.mp4 "VIDEO_URL"
|
||
mkdir -p public/frames/watch/desktop public/frames/watch/mobile
|
||
ffmpeg -i watch-rotation.mp4 -vf "fps=30,scale=1920:1080" public/frames/watch/desktop/frame_%04d.png
|
||
ffmpeg -i watch-rotation.mp4 -vf "fps=15,scale=960:540" public/frames/watch/mobile/frame_%04d.png
|
||
|
||
# Convert to WebP
|
||
for dir in public/frames/watch/desktop public/frames/watch/mobile; do
|
||
for f in "$dir"/*.png; do cwebp -q 80 "$f" -o "${f%.png}.webp" && rm "$f"; done
|
||
done
|
||
|
||
# 4. → Use scroll-animation skill for GSAP canvas playback
|
||
```
|
||
|
||
**Cost:** ~$0.07 (keyframe) + $1.12 (Kling 10s no audio) = **$1.19**
|
||
|
||
### Example 2: Hero Background Loop
|
||
|
||
```bash
|
||
# 1. Keyframe
|
||
nano-banana "abstract flowing liquid metal, iridescent, macro photography, dark background" -s 2K -a 16:9 -o hero-bg
|
||
|
||
# 2. Animate (seamless loop)
|
||
python3 -c "
|
||
import fal_client
|
||
result = fal_client.run('fal-ai/luma-dream-machine/ray-2/image-to-video', arguments={
|
||
'prompt': 'gentle flowing motion, metallic liquid ripples, seamless loop',
|
||
'image_url': 'file://hero-bg.png',
|
||
'loop': True,
|
||
'aspect_ratio': '16:9'
|
||
})
|
||
print(result['video']['url'])
|
||
"
|
||
|
||
# 3. Optimize for web autoplay
|
||
curl -o hero-loop-raw.mp4 "VIDEO_URL"
|
||
ffmpeg -i hero-loop-raw.mp4 -c:v libx264 -preset slow -crf 23 -an -movflags +faststart hero-loop.mp4
|
||
ffmpeg -i hero-loop-raw.mp4 -c:v libvpx-vp9 -crf 30 -b:v 2M -an hero-loop.webm
|
||
```
|
||
|
||
**Cost:** ~$0.07 + $0.50 = **$0.57**
|
||
|
||
### Example 3: Bulk Content Videos (Budget)
|
||
|
||
```bash
|
||
# 1. Generate 5 keyframes
|
||
for i in 1 2 3 4 5; do
|
||
nano-banana "feature illustration $i, minimal, clean" -s 1K -a 16:9 -o "feature-$i"
|
||
done
|
||
|
||
# 2. Animate all with LTX (cheapest)
|
||
python3 << 'EOF'
|
||
import fal_client
|
||
prompts = [
|
||
"gentle zoom in, subtle particle effects",
|
||
"slow pan right revealing details",
|
||
"elements float upward gently",
|
||
"soft glow pulses outward",
|
||
"camera slowly pulls back"
|
||
]
|
||
for i, prompt in enumerate(prompts, 1):
|
||
result = fal_client.run("fal-ai/ltx-2/text-to-video/fast", arguments={
|
||
"prompt": prompt,
|
||
"image_url": f"file://feature-{i}.png",
|
||
"duration": 5,
|
||
"aspect_ratio": "16:9"
|
||
})
|
||
print(f"Feature {i}: {result['video']['url']}")
|
||
EOF
|
||
```
|
||
|
||
**Cost:** 5 × ($0.07 + $0.20) = **$1.35** for 5 videos
|
||
|
||
---
|
||
|
||
## Prompting Guide for Video Models
|
||
|
||
### Motion Prompts (DO)
|
||
|
||
- "slowly rotates 360 degrees clockwise"
|
||
- "camera dollies in from medium to close-up"
|
||
- "gentle floating up and down motion"
|
||
- "particles drift outward from center"
|
||
- "liquid flows from left to right"
|
||
- "zoom in on the detail, rack focus"
|
||
|
||
### Motion Prompts (DON'T)
|
||
|
||
- ~~"beautiful product"~~ (describes scene, not motion)
|
||
- ~~"4K cinematic masterpiece"~~ (quality tags don't help)
|
||
- ~~"the scene transforms into a different scene"~~ (too complex)
|
||
- ~~"person walks, talks, and dances"~~ (too many actions)
|
||
|
||
### Rules
|
||
|
||
1. **One motion per clip** — don't combine rotate + zoom + pan
|
||
2. **Describe the camera** — "camera orbits", "dolly in", "static wide"
|
||
3. **Describe speed** — "slowly", "gently", "dramatically fast"
|
||
4. **5s for simple motion**, 10s for complex reveals
|
||
5. **Match prompt to model** — Kling handles product/object motion best, Luma handles ambient/organic best
|
||
|
||
---
|
||
|
||
## Integration with Other Skills
|
||
|
||
| Skill | Role | When |
|
||
|-------|------|------|
|
||
| `nano-banana` | Generate keyframe images | Step 1 |
|
||
| `video-gen` | Process video → frame sequence | Step 3A |
|
||
| `scroll-animation` | GSAP ScrollTrigger playback | Step 4 (scroll) |
|
||
| `motion-design` | Page transitions + micro-interactions | Step 4 (ambient) |
|
||
| `web-assets` | Optimize final video/frames | Step 3 (all routes) |
|
||
| `landing-page` | Full page orchestration | When recipe needs video |
|
||
| `3d-scene` | Three.js alternative to AI video | When real 3D needed |
|
||
|
||
---
|
||
|
||
## nano-banana via fal.ai API (Alternative to CLI)
|
||
|
||
If the nano-banana CLI is not available, use the fal.ai endpoint directly:
|
||
|
||
```python
|
||
import fal_client
|
||
|
||
# Generate keyframe via fal.ai nano-banana (Gemini image gen)
|
||
result = fal_client.run("fal-ai/nano-banana", arguments={
|
||
"prompt": "premium smart watch on dark background, studio rim lighting, centered, clean edges",
|
||
"num_images": 1,
|
||
"aspect_ratio": "16:9", # 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3, 4:5, 5:4, 21:9
|
||
"output_format": "png", # png, jpeg, webp
|
||
"safety_tolerance": 4, # 1 (strict) to 6 (permissive)
|
||
})
|
||
keyframe_url = result["images"][0]["url"]
|
||
# Cost: $0.039 per image
|
||
|
||
# Now chain directly into video generation (no upload needed — already a URL!)
|
||
video_result = fal_client.run("fal-ai/kling-video/v3/pro/image-to-video", arguments={
|
||
"start_image_url": keyframe_url, # direct URL from nano-banana
|
||
"prompt": "watch slowly rotates 360 degrees, studio lighting",
|
||
"duration": "5",
|
||
"generate_audio": False,
|
||
})
|
||
video_url = video_result["video"]["url"]
|
||
```
|
||
|
||
**Key advantage:** nano-banana output is already a fal.ai URL, so it chains directly into any video model without upload. Total cost for keyframe + 5s Kling video: $0.039 + $0.56 = **$0.60**.
|
||
|
||
```bash
|
||
# curl version
|
||
KEYFRAME_URL=$(curl -s -X POST "https://fal.run/fal-ai/nano-banana" \
|
||
-H "Authorization: Key $FAL_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"prompt": "premium watch on dark bg, studio lighting, centered", "aspect_ratio": "16:9"}' \
|
||
| jq -r '.images[0].url')
|
||
|
||
curl -X POST "https://queue.fal.run/fal-ai/kling-video/v3/pro/image-to-video" \
|
||
-H "Authorization: Key $FAL_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d "{\"start_image_url\": \"$KEYFRAME_URL\", \"prompt\": \"watch rotates slowly\", \"duration\": \"5\", \"generate_audio\": false}"
|
||
```
|
||
|
||
---
|
||
|
||
## Workflow Summary
|
||
|
||
1. **Define goal** — what animation, where on page, what triggers it?
|
||
2. **Pick model** — use selection matrix above
|
||
3. **Cost estimate** — calculate and tell user BEFORE generating
|
||
4. **Generate keyframe** — nano-banana with animation-friendly composition
|
||
5. **Animate** — fal.ai with focused motion prompt
|
||
6. **Review** — watch output, iterate prompt if needed (budget 2-3 attempts)
|
||
7. **Process** — choose route: frame sequence / video / scrub / GIF
|
||
8. **Integrate** — embed with scroll-animation, video tag, or motion-design
|
||
9. **Fallback** — static keyframe for prefers-reduced-motion
|