Create coherent video story sequences with AI-generated images and narrated audio. Combines image-generation and elevenlabs skills to produce complete video stories with visual and narrative...
This skill creates coherent video story sequences by combining AI-generated images with narrated audio. Acts as a story director and visual coordinator, maintaining perfect consistency across characters, visual style, lighting, and narrative tone throughout all scenes. Produces complete MP4 videos with synchronized images and character voiceovers.
This skill should be invoked when the user asks to:
Global Style Lock:
Character Lock:
Multi-Turn Image Generation:
Character Voices:
Emotion Tags:
Narrative Structure:
Automated Pipeline:
STYLE_LOCK:
- Aspect ratio: 1080Ć1080 (square)
- Camera: 50mm lens, eye-level perspective
- Lighting: soft three-point lighting, warm key light (4500K)
- Color palette: #0B5FFF, #FFB703, #FB8500, #023047, #8ECAE6
- Materials: matte finish, no film grain or heavy bloom
- Background: subtle gradient, clean composition
- Style: semi-realistic cartoon with clear lines and gentle shading
- Post: crisp focus, no vignette or text artifacts
NEGATIVE_LOCK:
no text errors, no misspellings, no watermarks, no stickers,
no extra characters, no visual noise, no drastic lighting changes
Customization: Users can override with custom style locks, but defaults ensure consistency.
From ElevenLabs Voices:
Narrators:
JBFqnCBsd6RMkjVDRZzb)21m00Tcm4TlvDq8ikWAM)Character Voices:
TxGEqnHWrfWFTfGW9XjX)21m00Tcm4TlvDq8ikWAM)EXAVITQu4vr4xnSDxMaL)pNInz6obpgDQGcFmaJgB)XrExE9yKIg1WjnnlVkGX)ErXwobaYiN019PkySvjV)Assignment Logic:
Collect necessary information from the user:
Required:
Optional (prompt if missing):
Example Prompts:
"What's your story about?"
"How many scenes would you like? (Default: 1 title + 5 story scenes)"
"Describe your main character(s): name, appearance, personality"
"Any specific visual style preferences? (Default: semi-realistic cartoon)"
For each character in the story, create a character profile:
Character Profile Template:
character = {
"name": "Character Name",
"species": "human/animal/creature",
"description": "brief description",
"colors": {
"primary": "#HEX",
"secondary": "#HEX"
},
"outfit": "clothing description",
"features": ["distinctive trait 1", "trait 2", "trait 3"],
"personality": "personality description",
"voice_id": "elevenlabs-voice-id",
"voice_name": "ElevenLabs voice name"
}
Example:
pyter_python = {
"name": "Pyter Python",
"species": "friendly snake mascot",
"description": "A cheerful coding mentor snake",
"colors": {
"body": "#0B5FFF", # Blue
"belly": "#FFB703" # Yellow
},
"outfit": "tiny white lab coat with circular Ļ logo",
"features": ["large brown eyes", "rounded head", "cheerful smile"],
"personality": "enthusiastic, helpful, curious",
"voice_id": "TxGEqnHWrfWFTfGW9XjX",
"voice_name": "Josh"
}
Voice Assignment:
Create scene-by-scene outline:
Scene 0 (Title Scene):
Scenes 1-N (Story Scenes):
Example Scene Plan:
scene_plan = [
{
"number": 0,
"type": "title",
"visual_description": "Pyter Python with laptop, 'Pyter's Coding Adventure' text overlay",
"characters": ["Pyter Python"],
"narrative": "[cheerful] Join Pyter Python on an exciting coding adventure!",
"speaker": "Narrator",
"voice_id": "JBFqnCBsd6RMkjVDRZzb"
},
{
"number": 1,
"type": "story",
"visual_description": "Pyter at desk looking at computer screen showing error message, confused expression",
"characters": ["Pyter Python"],
"narrative": "[confused] Hmm... what does this error message mean? [pause] I thought my code was perfect!",
"speaker": "Pyter Python",
"voice_id": "TxGEqnHWrfWFTfGW9XjX"
},
# ... more scenes
]
Prepare Global Style Lock:
STYLE_LOCK = """
Aspect ratio: 1080Ć1080 (square)
Camera: 50mm lens, eye-level perspective
Lighting: soft three-point lighting, warm key light (4500K)
Color palette: #0B5FFF, #FFB703, #FB8500, #023047, #8ECAE6
Materials: matte finish, no film grain or heavy bloom
Background: subtle gradient, clean composition
Style: semi-realistic cartoon with clear lines and gentle shading
Post: crisp focus, no vignette or text artifacts
"""
NEGATIVE_LOCK = """
no text errors, no misspellings, no watermarks, no stickers,
no extra characters, no visual noise, no drastic lighting changes
"""
Build Character Lock for Each Scene:
def build_character_lock(characters_in_scene):
lock = ""
for character in characters_in_scene:
lock += f"""
Character: {character['name']}
Species: {character['species']}
Colors: body {character['colors']['primary']}, secondary {character['colors']['secondary']}
Outfit: {character['outfit']}
Key features: {', '.join(character['features'])}
"""
return lock
Generate images using multi-turn generation for consistency:
Implementation:
from pathlib import Path
import json
# Initialize tracking
previous_image_id = None
image_files = []
# Generate each scene
for scene in scene_plan:
print(f"Generating Scene {scene['number']}: {scene['visual_description']}")
# Build character lock for this scene
character_lock = build_character_lock(
[char_profiles[name] for name in scene['characters']]
)
# Build complete image prompt
image_prompt = f"""
{STYLE_LOCK}
{character_lock}
Scene Description:
{scene['visual_description']}
{NEGATIVE_LOCK}
"""
# Add reference to previous scene if not first scene
if previous_image_id:
image_prompt += f"\nReference previous scene for consistency: {previous_image_id}"
# Generate image using image-generation skill
# (This would invoke the image-generation skill)
# For implementation, use appropriate model (DALL-E 3 or Gemini Pro)
result = generate_image(
prompt=image_prompt,
model="dall-e-3", # or gemini-3-pro-image-preview
size="1024x1024",
reference_image=previous_image_id
)
# Save image
filename = f"scene-{scene['number']:02d}.png"
save_image(result, filename)
image_files.append(filename)
# Track for next scene reference
previous_image_id = result['image_id']
print(f" ā Saved: {filename}")
Key Points:
Create voice narration for each scene:
Implementation:
from elevenlabs.client import ElevenLabs
client = ElevenLabs(api_key=os.environ['ELEVENLABS_API_KEY'])
audio_files = []
for scene in scene_plan:
print(f"Generating audio for Scene {scene['number']}")
# Prepare dialogue input
dialogue_input = {
"text": scene['narrative'],
"name": scene['speaker'],
"voice_id": scene['voice_id']
}
# Generate audio using text_to_dialogue
audio = client.text_to_dialogue.convert(
inputs=[dialogue_input]
)
# Save audio file
filename = f"scene-{scene['number']:02d}.mp3"
with open(filename, 'wb') as f:
for chunk in audio:
f.write(chunk)
audio_files.append(filename)
print(f" ā Saved: {filename}")
Narrative Guidelines:
[excited], [thoughtful], [confused], [pause][sound effect: door creaking]Combine all scene audio into single track:
Implementation:
import subprocess
# Build ffmpeg concat command
concat_filter = "concat=n={}:v=0:a=1[out]".format(len(audio_files))
inputs = []
for audio_file in audio_files:
inputs.extend(['-i', audio_file])
cmd = ['ffmpeg', '-y'] + inputs + [
'-filter_complex', concat_filter,
'-map', '[out]',
'full_audio.mp3'
]
subprocess.run(cmd, check=True)
print("ā Audio concatenated: full_audio.mp3")
Use the included assemble_video.sh script:
Implementation:
import subprocess
from pathlib import Path
# Prepare command
script_path = Path(__file__).parent / "scripts" / "assemble_video.sh"
cmd = [str(script_path), "full_audio.mp3"] + image_files
# Run assembly
subprocess.run(cmd, check=True)
# Output will be full_audio.mp4
print("ā Video created: full_audio.mp4")
Script Details:
Provide user with:
<story-name>.mp4Example Output:
ā Video Story Created: pyter-coding-adventure.mp4
Scenes:
0. Title: "Pyter's Coding Adventure" (20s)
1. Pyter encounters an error (18s)
2. Pyter realizes the mistake (17s)
3. Pyter fixes the code (19s)
4. Code runs successfully (16s)
5. Pyter celebrates (15s)
Total Duration: 1:45
Resolution: 1080Ć1080
Characters: Pyter Python (voiced by Josh)
Files generated:
- pyter-coding-adventure.mp4 (final video)
- scene-00.png through scene-05.png (images)
- scene-00.mp3 through scene-05.mp3 (audio)
- full_audio.mp3 (concatenated audio)
Narrators:
JBFqnCBsd6RMkjVDRZzb21m00Tcm4TlvDq8ikWAMYoung Characters:
TxGEqnHWrfWFTfGW9XjXEXAVITQu4vr4xnSDxMaLErXwobaYiN019PkySvjVMF3mGyEYCl7XYWbV9V6OAdult Characters:
pNInz6obpgDQGcFmaJgBAZnzlk1XvdvUeBnXmlldXrExE9yKIg1WjnnlVkGXAssignment Strategy:
def assign_voice(character):
"""Auto-assign voice based on character attributes"""
# Check for explicit assignment
if 'voice_preference' in character:
return get_voice_id(character['voice_preference'])
# Auto-assign based on attributes
age = character.get('age', 'young')
gender = character.get('gender', 'male')
if age == 'young':
if gender == 'male':
return 'TxGEqnHWrfWFTfGW9XjX' # Josh
else:
return '21m00Tcm4TlvDq8ikWAM' # Rachel
else: # adult
if gender == 'male':
return 'pNInz6obpgDQGcFmaJgB' # Adam
else:
return 'XrExE9yKIg1WjnnlVkGX' # Matilda
User Request: "Create a short story about a coding snake fixing his first bug"
Step 1: Character Definition
pyter = {
"name": "Pyter Python",
"species": "friendly snake",
"colors": {"body": "#0B5FFF", "belly": "#FFB703"},
"outfit": "white lab coat with Ļ logo",
"features": ["large brown eyes", "rounded head", "cheerful smile"],
"personality": "enthusiastic learner",
"voice_id": "TxGEqnHWrfWFTfGW9XjX" # Josh
}
Step 2: Scene Plan
scenes = [
{
"number": 0,
"visual": "Pyter with laptop, title 'Pyter's First Bug'",
"narrative": "[cheerful] Today, Pyter Python will fix his very first coding bug!",
"speaker": "Narrator",
"voice_id": "JBFqnCBsd6RMkjVDRZzb"
},
{
"number": 1,
"visual": "Pyter staring at screen with red error message",
"narrative": "[confused] Wait... why isn't my code working? [pause] The computer says there's a syntax error!",
"speaker": "Pyter",
"voice_id": "TxGEqnHWrfWFTfGW9XjX"
},
{
"number": 2,
"visual": "Pyter reading a Python book, thoughtful",
"narrative": "[thoughtful] Let me check the Python book... [pause] Oh! I need to look at line 5 carefully.",
"speaker": "Pyter",
"voice_id": "TxGEqnHWrfWFTfGW9XjX"
},
{
"number": 3,
"visual": "Close-up of Pyter pointing at screen, realization",
"narrative": "[excited] I found it! I forgot to close the parentheses! [pause] That's the bug!",
"speaker": "Pyter",
"voice_id": "TxGEqnHWrfWFTfGW9XjX"
},
{
"number": 4,
"visual": "Screen showing 'Success!' with green checkmark",
"narrative": "[proud] I fixed it! My code is running perfectly now!",
"speaker": "Pyter",
"voice_id": "TxGEqnHWrfWFTfGW9XjX"
},
{
"number": 5,
"visual": "Pyter celebrating, confetti in background",
"narrative": "[warm] And that's how Pyter learned that every programmer makes mistakes... and that's okay!",
"speaker": "Narrator",
"voice_id": "JBFqnCBsd6RMkjVDRZzb"
}
]
Step 3: Generate (using process described above)
Output: pyters-first-bug.mp4 with 6 scenes, ~90 seconds total
Skills:
image-generation - For creating consistent visual sceneselevenlabs - For character voice narrationPython Packages:
pip install elevenlabs pillow
System:
API Keys:
File Permissions:
assemble_video.shKeep it Simple:
Character Consistency:
Pacing:
Use Style Locks:
Character Locks:
Multi-Turn References:
Narrative Guidelines:
Voice Selection:
Audio Testing:
File Organization:
Quality Settings:
Testing:
Problem: Character looks different across scenes
Solutions:
Problem: Voice doesn't match character
Solutions:
Problem: Concatenated audio has gaps
Solutions:
Problem: Script fails with "file not found"
Solutions:
Problem: Images different sizes in video
Solutions:
Scene Count:
Character Complexity:
Visual Changes:
Audio Length:
Processing Time:
image-generation - Required for visual generationelevenlabs - Required for voice narrationpython-plotting - For visualizing story analyticsscientific-writing - For writing narrative scriptsimage-generation/SKILL.mdelevenlabs/SKILL.mdreferences/style-locks.mdreferences/narrative-design.mdreferences/video-assembly.mdexamples/example-stories.md