DEV Community

Custodia-Admin
Custodia-Admin

Posted on • Originally published at pagebolt.dev

How to Auto-Generate a Narrated Demo Video for Your SaaS Product

How to Auto-Generate a Narrated Demo Video for Your SaaS Product

You're launching a new feature.

Your co-founder spends three hours recording a Loom video. Thirty takes to get it right. Then two more hours editing, adding captions, uploading.

The video goes live. Three days later, you realize there's a bug in the flow. Back to Loom. Another three hours.

By the time you've done this ten times, you've spent 30+ hours recording demo videos—time you could have spent on literally anything else.

There's a better way.


The Problem: Demo Videos Are a Bottleneck

Loom is great for one-off recordings. But for SaaS, you need demo videos for:

  • Landing page (product hero)
  • Feature announcements
  • Help docs (how to use X)
  • Sales decks
  • Changelog entries
  • Customer success emails
  • Tutorial content

That's dozens of videos. Dozens of hours in Loom.

Each video requires:

  1. Recording — "Let me click through this slowly while narrating" (10-30 minutes per take)
  2. Re-recording — "Oops, I said the wrong thing" (5-10 re-takes)
  3. Editing — Trim, crop, add captions, upload (1-2 hours)
  4. Waiting — Upload finishes, video processes, etc. (15-30 minutes)

Total time per video: 3-5 hours. For one feature.

And that's if you get it right on the first try.


The Solution: AI-Narrated Demo Videos in One API Call

What if you could define your demo as code?

Step 1: Navigate to homepage
  Note: "This is our dashboard"
Step 2: Click Features button
  Note: "Click here to see what's new"
Step 3: Fill in demo data
  Note: "You can enter your data here"
Step 4: Submit form
  Note: "One click to get results"
Step 5: Show results page
  Note: "Here's what you get"
Enter fullscreen mode Exit fullscreen mode

Then one API call:

  • Records the video (5 browser steps)
  • Adds AI narration (reads your notes)
  • Syncs narration to each step
  • Renders MP4

Total time: < 5 minutes.

No Loom. No manual recording. No editing. Just a script and an API call.

Here's the complete implementation:

import requests
import json
import time

PAGEBOLT_API_KEY = "YOUR_API_KEY"
BASE_URL = "https://api.pagebolt.dev/v1"

def record_demo_video(title, steps, narration_script, output_file):
    """
    Record a narrated demo video by defining browser steps + narration script.

    Args:
        title: Video title (for logging)
        steps: List of browser automation steps (navigate, click, fill, etc.)
        narration_script: Full narration script with {{N}} markers for each step
        output_file: Where to save the MP4
    """

    print(f"🎬 Recording demo: {title}")
    print(f"📝 Steps: {len(steps)}")
    print(f"🎙️  Narration length: {len(narration_script)} chars\n")

    # Build the video recording request
    payload = {
        "steps": steps,
        "audioGuide": {
            "enabled": True,
            "script": narration_script,
            "provider": "azure",
            "voice": "ava",
            "speed": 1.0
        },
        "format": "mp4",
        "pace": "normal",  # normal, slow, dramatic, cinematic
        "cursor": {
            "visible": True,
            "style": "highlight",
            "persist": True
        },
        "frame": {
            "enabled": True,
            "style": "macos"
        },
        "background": {
            "enabled": True,
            "type": "gradient",
            "gradient": "ocean"
        }
    }

    # Send to PageBolt
    headers = {
        "Authorization": f"Bearer {PAGEBOLT_API_KEY}",
        "Content-Type": "application/json"
    }

    print("📡 Sending to PageBolt...\n")

    response = requests.post(
        f"{BASE_URL}/record-video",
        json=payload,
        headers=headers
    )

    if response.status_code != 200:
        print(f"❌ Error: {response.status_code}")
        print(response.text)
        return False

    # Save the video
    with open(output_file, 'wb') as f:
        f.write(response.content)

    print(f"✅ Video saved: {output_file}")
    print(f"📊 File size: {len(response.content) / (1024*1024):.1f} MB\n")

    return True


# Example 1: Product Tour (5 steps, 60 seconds)
def product_tour_demo():
    steps = [
        {
            "action": "navigate",
            "url": "https://example-saas.com"
        },
        {
            "action": "click",
            "selector": "[data-testid='features-btn']",
            "note": "Click to explore features"
        },
        {
            "action": "scroll",
            "y": 500,
            "note": "See what's new this month"
        },
        {
            "action": "click",
            "selector": "[data-testid='pricing-btn']",
            "note": "Check out our pricing"
        },
        {
            "action": "click",
            "selector": "[data-testid='signup-btn']",
            "note": "Ready to get started?"
        }
    ]

    script = """Welcome to our platform. {{0}} This is where you manage everything. {{1}}
    We just launched new features this month. {{2}} Check out our flexible pricing plans. {{3}}
    Sign up in minutes and start using it today. {{4}}"""

    record_demo_video(
        "Product Tour",
        steps,
        script,
        "product-tour.mp4"
    )


# Example 2: Feature Demo (4 steps, 45 seconds)
def feature_demo():
    """Demo of a specific feature: bulk data import"""

    steps = [
        {
            "action": "navigate",
            "url": "https://example-saas.com/dashboard"
        },
        {
            "action": "click",
            "selector": "[data-testid='import-btn']",
            "note": "Click Import to get started"
        },
        {
            "action": "fill",
            "selector": "[type='file']",
            "value": "data.csv",
            "note": "Upload your CSV file"
        },
        {
            "action": "click",
            "selector": "[data-testid='confirm-import']",
            "note": "Confirm to import your data"
        }
    ]

    script = """Here's how to import your data in three clicks. {{0}} Click the Import button. {{1}}
    Upload your CSV file. {{2}} And you're done! Your data is imported and ready to use. {{3}}"""

    record_demo_video(
        "Bulk Import Feature",
        steps,
        script,
        "bulk-import-demo.mp4"
    )


# Example 3: Customer Success (6 steps, complex workflow)
def customer_success_demo():
    """Demo for customer support: how to resolve a refund request"""

    steps = [
        {
            "action": "navigate",
            "url": "https://example-saas.com/admin/refunds"
        },
        {
            "action": "click",
            "selector": "[data-testid='filter-pending']",
            "note": "Filter for pending refunds"
        },
        {
            "action": "click",
            "selector": "[data-testid='refund-row-1']",
            "note": "Select a refund request"
        },
        {
            "action": "scroll",
            "y": 300,
            "note": "Review the customer details"
        },
        {
            "action": "click",
            "selector": "[data-testid='approve-refund']",
            "note": "Click Approve to process"
        },
        {
            "action": "wait",
            "ms": 2000,
            "live": True,
            "note": "Processing..."
        }
    ]

    script = """Let me walk you through processing a refund. {{0}} First, filter your refunds. {{1}}
    Select the one you want to process. {{2}} Review the customer information. {{3}}
    Click Approve and it's done. {{4}} The system will process it immediately. {{5}}"""

    record_demo_video(
        "Refund Processing Workflow",
        steps,
        script,
        "refund-processing.mp4"
    )


# Example 4: Interactive Tutorial (5 steps, educational)
def tutorial_demo():
    """Tutorial: how to set up integrations"""

    steps = [
        {
            "action": "navigate",
            "url": "https://example-saas.com/settings/integrations"
        },
        {
            "action": "click",
            "selector": "[data-integration='slack']",
            "note": "Click the Slack integration"
        },
        {
            "action": "click",
            "selector": "[data-testid='connect-btn']",
            "note": "Click Connect"
        },
        {
            "action": "wait",
            "ms": 3000,
            "live": True,
            "note": "Authorizing with Slack..."
        },
        {
            "action": "click",
            "selector": "[data-testid='close-modal']",
            "note": "You're connected! Close this dialog"
        }
    ]

    script = """Setting up integrations is easy. {{0}} Go to Settings > Integrations. {{1}}
    Click the service you want to connect. {{2}} Click Connect. {{3}} Authorize it with your account. {{4}}
    That's it! Your integration is now live."""

    record_demo_video(
        "Integration Setup Tutorial",
        steps,
        script,
        "integration-tutorial.mp4"
    )


# Run all demos
if __name__ == "__main__":
    print("=" * 60)
    print("🎬 PageBolt Demo Video Generator")
    print("=" * 60 + "\n")

    product_tour_demo()
    print("\n" + "-" * 60 + "\n")

    feature_demo()
    print("\n" + "-" * 60 + "\n")

    customer_success_demo()
    print("\n" + "-" * 60 + "\n")

    tutorial_demo()

    print("\n" + "=" * 60)
    print("✅ All demos recorded!")
    print("=" * 60)
Enter fullscreen mode Exit fullscreen mode

What this code does:

  1. Defines browser steps (navigate, click, fill, scroll, wait)
  2. Adds a note to each step (what to say while this happens)
  3. Writes a narration script with {{N}} markers synced to each step
  4. Sends to PageBolt's record_video endpoint
  5. Gets back an MP4 with AI voice + cursor highlights + browser frame

The narration script syncs automatically:

  • {{0}} fires during step 0
  • {{1}} fires during step 1
  • And so on...

The voice reads each section while that step executes. No manual audio editing needed.


Real-World Example: See It In Action

Here's what the output looks like:

Video: https://streamable.com/do0vc5

What you see in the video:

  • macOS browser frame
  • Ocean gradient background
  • Cursor highlights on each click
  • AI voice narrating in real-time
  • Perfect lip-sync between narration and action
  • Professional, polished output

Total time to create this video: 5 minutes (define steps + script, hit send).

Time it would take in Loom: 2-3 hours (record, re-record, edit, export).


Use Cases & ROI

Use Case 1: Landing Page Hero

Record a 30-second product demo that auto-plays on your homepage.

Old workflow: Hire video editor, $300-500
New workflow: API call, $0.10

Savings: $300+ per video

Use Case 2: Feature Announcement

New feature ships. You announce it with a 2-minute demo video.

Old workflow: 3 hours recording + editing
New workflow: 5 minutes (write steps + script)

Savings: 2.75 hours per announcement

Use Case 3: Help Docs

10 help docs, each with a 1-minute demo video.

Old workflow: 30+ hours (3 hours per video)
New workflow: 50 minutes (5 minutes per video)

Savings: 25+ hours

Use Case 4: Sales Decks

Sales team uses 5 different demo videos in pitches.

Old workflow: 15 hours creating + updating
New workflow: 25 minutes creating + instant updates

Savings: 14.5 hours, plus instant updates when product changes


Production Tips

1. Write your script first — Narration should be conversational, not robotic:

❌ Bad: "This is the feature. Click the button."
✅ Good: "Here's the cool part. Just click here and watch."
Enter fullscreen mode Exit fullscreen mode

2. Keep pacing natural — Default pace: "normal" works for most demos. Use "slow" for tutorials, "dramatic" for product launches.

3. Add waits strategically — Don't let the video race ahead:

{
    "action": "click",
    "selector": "[data-testid='submit']",
    "note": "Click submit"
},
{
    "action": "wait",
    "ms": 2000,  # Let the page load before next action
    "live": True,  # Show actual page rendering
    "note": "Processing your request..."
}
Enter fullscreen mode Exit fullscreen mode

4. Test with different viewports — Desktop (1920x1080) vs. tablet (768x1024) render differently. Record both if your product is responsive.

5. Update when product changes — Change one line of code, re-record the video. Instant updates. No manual editing.


Cost & Scale

Plan Videos/Month 30-sec Video 2-min Video 5-min Video
Free Limited ✅ Yes (1) ❌ No ❌ No
Starter 10 ✅ Yes ✅ Yes (3-4) ⚠️ Limited
Growth 100 ✅ Yes ✅ Yes (20+) ✅ Yes (10+)
Scale Unlimited ✅ Yes ✅ Yes ✅ Yes

A 2-minute demo = ~8 API calls (at 15 fps, ~120 frames per video).

One demo video per week = 4 videos/month = 32 API calls = Starter plan ($29/month).

One demo video per day = 30 videos/month = 240 API calls = Growth plan ($99/month).


The Differentiator

Every other screenshot/video API requires you to build the narration layer yourself. You upload pre-recorded audio. You sync timing manually. You edit afterward.

PageBolt's audioGuide with script + step notes does it in one call.

That's it. That's the product.

One API parameter. One narration script. One click. Professional demo video.


Ready to stop spending hours in Loom?

Record your first narrated demo in 5 minutes. Free tier lets you test. Growth plan covers a library of 20+ demo videos per month.

Get started free →

Top comments (0)