> ## Documentation Index
> Fetch the complete documentation index at: https://docs.videodb.io/llms.txt
> Use this file to discover all available pages before exploring further.

# AI Storyteller for Kids

> Generate complete animated educational videos for kids with AI scripts, voiceovers, and captions

<a href="https://colab.research.google.com/github/video-db/videodb-cookbook/blob/main/editor/creative/ai_storyteller_for_kids.ipynb" target="_blank">
  <img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab" noZoom />
</a>

## The Idea

Ever wished you could create fun, educational videos for kids with just a single topic? Tell the system "explain the solar system" and get back a complete animated video with:

* Kid-friendly voiceover narration
* Fun background music
* Loopable animated backgrounds
* Timed subject videos (cartoons of planets, stars, etc.)
* Animated captions that kids can follow along

This workflow takes a topic and magically transforms it into a complete learning video. All powered by **VideoDB's Editor SDK** — pure automation magic.

## Setup

### Install Dependencies

```bash theme={null}
pip install videodb
```

### Connect to VideoDB

```python theme={null}
import os
import math
import videodb
from videodb import play_stream
from videodb.editor import (
    Timeline, Track, Clip,
    VideoAsset, AudioAsset, TextAsset, CaptionAsset,
    Fit, Position, Offset, Transition,
    Font, Border,
    CaptionAnimation, CaptionAlignment, CaptionBorderStyle,
    FontStyling, Positioning, BorderAndShadow,
)

# Connect to VideoDB
api_key = "your_api_key"
conn = videodb.connect(api_key=api_key)
coll = conn.get_collection()
```

## Implementation

### Step 1: Generate Script and Title

```python theme={null}
import json

# Define the learning topic
topic = "Explain the solar system"

script_prompt = f"""You are a friendly children's educational content writer.

Write a fun, simple explanation about: "{topic}"

Requirements:
- Target audience: 5-year-old children
- Length: 250 words
- Use simple words and short sentences
- Make it fun and engaging

Return a JSON with:
- "title": A fun, catchy title for the video (max 5 words)
- "script": The complete narration script

Example format:
{{"title": "The Amazing Sun!", "script": "Hey friends! Today we're going to learn about..."}}
"""

script_response = coll.generate_text(
    prompt=script_prompt,
    model_name="pro",
    response_type="json"
)

# Handle nested output structure
if 'output' in script_response and isinstance(script_response['output'], dict):
    video_title = script_response["output"]["title"]
    video_script = script_response["output"]["script"]
else:
    video_title = script_response["title"]
    video_script = script_response["script"]
```

### Step 2: Generate Background Prompts

```python theme={null}
bg_prompt = f"""Based on this children's educational video topic: "{topic}"

Create prompts for background media.

Return JSON with:
- "background_video_prompt": Detailed prompt for generating background video
- "music_prompt": Prompt for generating background music

Example: {{"background_video_prompt": "...", "music_prompt": "..."}}
"""

bg_response = coll.generate_text(
    prompt=bg_prompt,
    model_name="pro",
    response_type="json"
)

# Handle nested output structure
if isinstance(bg_response, dict) and "output" in bg_response:
    background_video_prompt = bg_response["output"]["background_video_prompt"]
    music_prompt = bg_response["output"]["music_prompt"]
else:
    background_video_prompt = bg_response["background_video_prompt"]
    music_prompt = bg_response["music_prompt"]
```

### Step 3: Generate All Assets

```python theme={null}
# Generate voiceover from script
voiceover = coll.generate_voice(
    text=video_script,
    voice_name="Default"
)

# Generate background music (loopable)
bg_music = coll.generate_music(
    prompt=music_prompt,
    duration=10
)

# Generate background video (loopable)
bg_video = coll.generate_video(
    prompt=background_video_prompt,
    duration=5
)
```

### Step 4: Generate Timed Transcript

```python theme={null}
# Generate and fetch timestamped transcript
print("📝 Generating transcript with timestamps...")

voiceover.generate_transcript()
transcript = voiceover.get_transcript(segmenter=videodb.Segmenter.time, length=5)

import base64

def format_srt_time(seconds):
    total_ms = int(round(float(seconds) * 1000))
    hours, remainder = divmod(total_ms, 3_600_000)
    minutes, remainder = divmod(remainder, 60_000)
    secs, milliseconds = divmod(remainder, 1_000)
    return f"{hours:02}:{minutes:02}:{secs:02},{milliseconds:03}"

srt = "\n".join(
    f"{number}\n"
    f"{format_srt_time(segment['start'])} --> {format_srt_time(segment['end'])}\n"
    f"{segment['text']}\n"
    for number, segment in enumerate(transcript, start=1)
)
srt_src = base64.b64encode(srt.encode("utf-8")).decode("utf-8")

# Calculate total video duration (intro + voiceover + outro)
voiceover_length_float = float(voiceover.length)
intro_duration = 5
outro_duration = 5
total_duration = intro_duration + voiceover_length_float + outro_duration

print(f"✅ Transcript ready! {len(transcript)} segments")
print(f"\n⏱️ Video breakdown:")
print(f"   - Intro: {intro_duration}s")
print(f"   - Voiceover: {voiceover_length_float}s")
print(f"   - Outro: {outro_duration}s")
print(f"   - Total: {total_duration}s")

import json
print(json.dumps(transcript, indent=2))
```

### Step 5: Configure Reusable Captions

```python theme={null}
# Configure reusable captions from the timed transcript
caption_asset = CaptionAsset(
    src=srt_src,
    animation=CaptionAnimation.karaoke,
    primary_color="&H00FFFFFF",
    secondary_color="&H0000FFFF",
    back_color="&H80000000",
    position=Positioning(
        alignment=CaptionAlignment.bottom_center,
        margin_l=20,
        margin_r=20,
        margin_v=30,
    ),
    font=FontStyling(
        name="Clear Sans",
        size=52,
        bold=True,
    ),
    border=BorderAndShadow(
        style=CaptionBorderStyle.outline_and_shadow,
        outline=4,
        outline_color="&H00000000",
        shadow=2,
    ),
)

print("✅ Reusable caption style ready!")
```

### Step 6: Build the Complete Video

```python theme={null}
# Use the actual length of each background asset for looping
timeline = Timeline(conn)
timeline.background = "#0a0a1a"
timeline.resolution = "1280x720"

bg_video_actual_length = bg_video.length
video_loops = math.ceil(total_duration / bg_video_actual_length)
music_loops = math.ceil(total_duration / 10)

# Track 1: Looped background video
bg_video_track = Track()
for i in range(video_loops):
    start = i * bg_video_actual_length
    remaining = total_duration - start
    dur = min(bg_video_actual_length, remaining)

    if dur <= 0:
        break

    bg_video_track.add_clip(
        start=start,
        clip=Clip(
            asset=VideoAsset(id=bg_video.id, volume=0),
            duration=dur,
            fit=Fit.cover,
            opacity=0.7,
        ),
    )

# Track 2: Looped background music
bg_music_track = Track()
for i in range(music_loops):
    start = i * 10
    remaining = total_duration - start
    dur = min(10, remaining)
    bg_music_track.add_clip(
        start=start,
        clip=Clip(asset=AudioAsset(id=bg_music.id, volume=0.5), duration=dur),
    )

# Track 3: Voiceover and captions (start after intro)
vo_track = Track()
vo_track.add_clip(
    start=intro_duration,
    clip=Clip(
        asset=AudioAsset(id=voiceover.id, volume=1.0),
        duration=voiceover_length_float,
    ),
)
vo_track.add_clip(
    start=intro_duration,
    clip=Clip(asset=caption_asset, duration=voiceover_length_float),
)

# Track 4: Title card
title_track = Track()
title_track.add_clip(
    start=0,
    clip=Clip(
        asset=TextAsset(
            text=video_title,
            font=Font(family="Quicksand", size=96, color="#FFD700"),
            border=Border(color="#000000", width=6),
        ),
        duration=intro_duration,
        position=Position.center,
        transition=Transition(in_="fade", out="fade", duration=0.8),
    ),
)

# Track 5: Outro card
outro_track = Track()
outro_track.add_clip(
    start=intro_duration + voiceover_length_float,
    clip=Clip(
        asset=TextAsset(
            text="Happy Learning!",
            font=Font(family="Quicksand", size=88, color="#90EE90"),
            border=Border(color="#000000", width=6),
        ),
        duration=outro_duration,
        position=Position.center,
        transition=Transition(in_="fade", out="fade", duration=0.8),
    ),
)

timeline.add_track(bg_video_track)
timeline.add_track(bg_music_track)
timeline.add_track(vo_track)
timeline.add_track(title_track)
timeline.add_track(outro_track)

print("✅ Timeline built!")
stream = timeline.generate_stream()
play_stream(stream)
```

### Step 7: Generate Subject Video Prompts

```python theme={null}
# Group the timed transcript into pairs for a visual subject prompt per pair
transcript_pairs = []
for i in range(0, len(transcript), 2):
    pair = {
        "start": float(transcript[i]["start"]),
        "end": float(transcript[i + 1]["end"]) if i + 1 < len(transcript) else float(transcript[i]["end"]),
        "text": transcript[i]["text"] + " " + (transcript[i + 1]["text"] if i + 1 < len(transcript) else ""),
    }
    transcript_pairs.append(pair)

subject_prompt = f"""You are creating visual prompts for a children's educational video about: "{topic}"

For each PAIR of narration lines below, create ONE video generation prompt that captures the essence of both lines.

Transcript pairs:
{json.dumps(transcript_pairs, indent=2)}

Requirements for each video prompt:
- Style: Cartoonish, whimsical, colorful, kid-friendly
- NO text in the videos
- NO complex actions
- Focus on a SINGLE subject that represents both lines
- Think fun and simple — like a storybook illustration come to life
- Slight motion is okay (spinning, floating, twinkling, bouncing)

Return a JSON object with a "videos" array containing ONE item per PAIR, in the SAME ORDER:
{{
    "videos": [
        {{"subject": "<what it shows>", "prompt": "<video generation prompt for pair 1>"}},
        {{"subject": "<what it shows>", "prompt": "<video generation prompt for pair 2>"}},
        ...
    ]
}}

You MUST return exactly {len(transcript_pairs)} items, one for each pair.
"""

subject_response = coll.generate_text(
    prompt=subject_prompt,
    model_name="pro",
    response_type="json",
)

video_prompts = subject_response["output"]["videos"]
if not isinstance(video_prompts, list) or len(video_prompts) != len(transcript_pairs):
    raise ValueError("Expected exactly one video prompt per transcript pair.")

subject_moments = []
for i, pair in enumerate(transcript_pairs):
    prompt_data = video_prompts[i]
    subject_moments.append(
        {
            "start": pair["start"],
            "end": pair["end"],
            "text": pair["text"],
            "subject": prompt_data.get("subject", f"Scene {i + 1}"),
            "prompt": prompt_data["prompt"],
        }
    )

print(f"✅ Generated {len(subject_moments)} video prompts for {len(transcript)} transcript lines!")
```

### Step 8: Generate Subject Videos

```python theme={null}
subject_videos = []

for moment in subject_moments:
    subject_video = coll.generate_video(
        prompt=moment["prompt"],
        duration=5,
    )
    subject_videos.append(
        {
            "video": subject_video,
            "start": moment["start"],
            "end": moment["end"],
            "subject": moment["subject"],
        }
    )

print(f"🎉 All {len(subject_videos)} subject videos generated!")
```

### Step 9: Render the Final Video

```python theme={null}
final_timeline = Timeline(conn)
final_timeline.background = "#0a0a1a"
final_timeline.resolution = "1280x720"

# Track 1: Looped background video
bg_video_track = Track()
for i in range(math.ceil(total_duration / bg_video.length)):
    start = i * bg_video.length
    dur = min(bg_video.length, total_duration - start)
    if dur <= 0:
        break
    bg_video_track.add_clip(
        start=start,
        clip=Clip(
            asset=VideoAsset(id=bg_video.id, volume=0),
            duration=dur,
            fit=Fit.cover,
            opacity=0.7,
        ),
    )

# Track 2: Looped background music
bg_music_track = Track()
for i in range(math.ceil(total_duration / 10)):
    start = i * 10
    dur = min(10, total_duration - start)
    bg_music_track.add_clip(
        start=start,
        clip=Clip(asset=AudioAsset(id=bg_music.id, volume=0.5), duration=dur),
    )

# Track 3: Voiceover and captions
vo_track = Track()
vo_track.add_clip(
    start=intro_duration,
    clip=Clip(
        asset=AudioAsset(id=voiceover.id, volume=1.0),
        duration=voiceover_length_float,
    ),
)
vo_track.add_clip(
    start=intro_duration,
    clip=Clip(asset=caption_asset, duration=voiceover_length_float),
)

# Track 4: Subject videos mapped to their paired transcript timestamps
subject_track = Track()
sorted_subjects = sorted(subject_videos, key=lambda item: float(item["start"]))

for i, subject in enumerate(sorted_subjects):
    clip_start = intro_duration + subject["start"]
    clip_duration = max(1, subject["end"] - subject["start"])

    if i < len(sorted_subjects) - 1:
        next_start = intro_duration + sorted_subjects[i + 1]["start"]
        clip_duration = min(clip_duration, next_start - clip_start)

    voiceover_end = intro_duration + voiceover_length_float
    clip_duration = min(clip_duration, voiceover_end - clip_start)
    if clip_duration <= 0:
        continue

    subject_video_length = subject["video"].length
    for loop in range(math.ceil(clip_duration / subject_video_length)):
        loop_start = clip_start + (loop * subject_video_length)
        loop_duration = min(subject_video_length, clip_duration - (loop * subject_video_length))
        if loop_duration <= 0:
            break
        subject_track.add_clip(
            start=loop_start,
            clip=Clip(
                asset=VideoAsset(id=subject["video"].id, volume=0),
                duration=loop_duration,
                fit=Fit.contain,
                scale=0.75,
                position=Position.top,
                offset=Offset(x=0, y=0.05),
                opacity=0.95,
            ),
        )

# Track 5: Title card
title_track = Track()
title_track.add_clip(
    start=0,
    clip=Clip(
        asset=TextAsset(
            text=video_title,
            font=Font(family="Quicksand", size=96, color="#FFD700"),
            border=Border(color="#000000", width=6),
        ),
        duration=intro_duration,
        position=Position.center,
        transition=Transition(in_="fade", out="fade", duration=0.8),
    ),
)

# Track 6: Outro card
outro_track = Track()
outro_track.add_clip(
    start=intro_duration + voiceover_length_float,
    clip=Clip(
        asset=TextAsset(
            text="Happy Learning!",
            font=Font(family="Quicksand", size=88, color="#90EE90"),
            border=Border(color="#000000", width=6),
        ),
        duration=outro_duration,
        position=Position.center,
        transition=Transition(in_="fade", out="fade", duration=0.8),
    ),
)

final_timeline.add_track(bg_video_track)
final_timeline.add_track(bg_music_track)
final_timeline.add_track(vo_track)
final_timeline.add_track(subject_track)
final_timeline.add_track(title_track)
final_timeline.add_track(outro_track)

final_stream = final_timeline.generate_stream()
play_stream(final_stream)
```

## What You Get

A complete educational video with:

* Kid-friendly AI-written script
* Natural voiceover narration (kids' voice)
* Loopable animated background
* Timed subject videos (cartoon illustrations)
* Synchronized animated captions
* Upbeat background music
* Professional intro and outro cards

Here's the final educational video:

<iframe className="w-full aspect-video rounded-xl" src="https://console.videodb.io/player?url=https://d1zudc7ewmc6ey.cloudfront.net/v1/9c1816f6-8375-4834-a60a-cfd4a4889735.m3u8" title="AI Storyteller Educational Video for Kids" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen />

## Perfect For

* **Home Learning** - Parents teaching kids at home
* **Educational Channels** - YouTube Kids content creators
* **School Supplements** - Teachers augmenting classroom lessons
* **Language Learning** - Kids' language learning videos
* **Skill Development** - Tutorial videos for children

## What Topics Can You Cover?

* The Solar System
* Ocean Animals
* Ancient Civilizations
* How Photosynthesis Works
* The Water Cycle
* Math Basics
* History Stories
* Science Experiments
* Geography
* And anything else!

## The Result

What used to take weeks of scriptwriting, voiceover recording, animation, and editing now takes minutes.

Teachers and parents can create engaging, professional educational content instantly. Kids get to learn with animated characters, fun music, and engaging visuals. Everyone wins.

**Pure learning magic — powered by VideoDB.**

<Card icon="notebook" title="Explore the Full Notebook" href="https://colab.research.google.com/github/video-db/videodb-cookbook/blob/main/editor/creative/ai_storyteller_for_kids.ipynb">
  Open the complete implementation with advanced subject video generation, caption styling, and content customization.
</Card>

## Related Tutorials

<CardGroup cols={2}>
  <Card title="Faceless Video Creator" icon="video" href="/examples-and-tutorials/content-factory/faceless-video-creator">
    Generate complete videos from scripts
  </Card>

  <Card title="Text-to-Video" icon="sparkles" href="/examples-and-tutorials/content-factory/text-prompts">
    Create video directly from text prompts
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.