---
title: Make a launch video from your product URL and screenshots
description: A launch video, also called a product demo or promo video, from your product page and screenshots: voiceover, pushes into the UI, music and captions.
---
You get a launch video, also called a product promo or SaaS demo video, made from your product page, your screenshots and your screen recordings. The video has a voiceover written from your page's copy, slow pushes into the parts of the UI that matter, music and captions. A screen can sit inside a tilted phone or laptop frame.

Use invideo for this when you want the launch video as an editable timeline: you change a line of the script, a screenshot or the music after the first cut, and your agent does it in your chat.

This is ordinary marketing work on your own product. The agent makes the video. You publish it and you post it. The agent never publishes it.

## Quickest path

Set up your agent first with [invideo for AI agents](/agents). Then give it this prompt.

```text
Make a 45 second launch video for https://example.com. Capture the
landing page and the pricing page, use my screenshots in ./shots and
the screen recording ./demo.mp4. Write a short voiceover from the
page's copy, push into each key feature, add calm upbeat music and
captions. Tell me the price before you generate the voice.
```

In a chat app, you drag your screenshots and recordings into the project in invideo, and you paste the page captures the agent makes. In a terminal, you name the folder and the agent uploads it.

## What you need

Bring what shows the product best. Real screens beat mockups.

- **The product URL.** The agent opens it with its own browser to read the copy and take screenshots.
- **Screenshots and screen recordings.** PNG, JPG, MP4 or MOV, at the size you captured them.
- **A device frame, if you want one.** A PNG of a phone or a laptop with a transparent screen.
- **The shape.** 16:9 for a site or YouTube, 1:1 or 9:16 for social.
- **The voice and the mood.** A voice you like, and a music mood such as calm or upbeat.
- **The length.** 30, 45 or 60 seconds is common for a launch.

## Steps

The agent works on a branch and tells you what changed at each step.

<Steps>
<Step title="Capture the page outside invideo">
invideo does not capture web pages. The agent opens the URL with its own browser or screenshot tool, saves full-width screenshots, and reads the headline, the features and the call to action. Expected result: a folder of page images and a list of the page's key lines.
</Step>
<Step title="Bring the media in">
The agent makes a project with `new_project` and a 1920 by 1080 timeline with `new_timeline`. It creates a branch with `timeline_history`. It uploads each screenshot and recording with `start_upload` and `add_media`. Expected result: a sequence URL per file.
</Step>
<Step title="Write the script and make the voice">
The agent writes a short script from the page's copy, one line per feature, and shows it to you first. It runs `find_models` with the job in words, picks one, and reads its inputs with `get_model`. A good pick is `elevenlabs_text_to_speech.elevenlabs_tts_v4`, with a voice from `search_library` in `elevenlabs_text_to_speech_voices`. It calls `generate` with `dry_run: true`, the script as `prompt` and the `voice_id`, and tells you the price. Then it calls `generate` with `max_credits` set to that price and an `idempotency_key`. Expected result: a sequence URL with the voiceover. The agent reads it with `read` and never calls `generate` again to check.
</Step>
<Step title="Lay out the screens and push into the UI">
The agent places each screen with `place_media` in the order of the script. On a still, it adds the `dynamic_zoom` effect with `add_effect`, then sets the end point and the end zoom with `set_effect_parameter` to push into the feature. For a custom move, it adds a lane with `add_keyframe_lane` on `zoom_x`, `zoom_y`, `position_x` or `position_y` and fills it with `add_keyframes`. Expected result: each screen moves toward the part the voice talks about.
</Step>
<Step title="Put a screen inside a device frame">
For a flat frame, the agent places the device PNG on a track above the screen and sizes the screen with `set_zoom` and `set_position`. For a tilted frame, it needs a corner pin. It reads `timeline_catalog` with `guides` `custom_shader`, writes a GLSL shader with the four screen corners as `vec2` parameters, and adds it with `add_custom_effect`, `dry_run` first. Expected result: the recording sits inside the tilted screen.
</Step>
<Step title="Add music and captions">
The agent finds a track with `search_library` in `stock_music` and adds it with `add_media`. It sets the music under the voice with `set_audio_gain`. It prices `audio.transcript` on the voiceover's sequence URL with `dry_run`, starts it with `max_credits` set to the price, and calls again after `retry_after_s` for the word times. Then it adds captions with `add_captions`. Expected result: music under the voice, and captions that follow each word.
</Step>
<Step title="Check and export">
The agent runs `capture` at each push and each caption, and `check_timeline`. It looks for text off the edge and screens that look soft. Then it runs `export` with `format` video. Expected result: an export card with status `completed`, and the MP4 in its files. A terminal agent saves it with `invideo notebook show`.
</Step>
</Steps>

The same job from a terminal:

```bash
invideo project new --title "Launch video"
invideo agent register <project-url> --name <name>
export INVIDEO_AS=<name>
invideo edit new <project-url> --name "Launch 45s"
invideo asset upload <edit-url> ./shots/*.png ./demo.mp4
invideo library search <project-url> \
  --library elevenlabs_text_to_speech_voices --query "warm narrator"
invideo gen models <project-url> --query "voiceover from a script"
invideo gen model <project-url> \
  --model elevenlabs_text_to_speech.elevenlabs_tts_v4
invideo gen run <page-url> \
  --model elevenlabs_text_to_speech.elevenlabs_tts_v4 \
  --dry-run -i prompt=@script.txt -i voice_id=<voice-id>
invideo gen run <page-url> \
  --model elevenlabs_text_to_speech.elevenlabs_tts_v4 \
  --max-credits <price> --idempotency-key launch-voice-v1 \
  -i prompt=@script.txt -i voice_id=<voice-id>
invideo media audio.transcript <sequence-url> --dry-run
invideo media audio.transcript <sequence-url> --max-credits <price>
invideo edit catalog <edit-url> --guides custom_shader --effects dynamic_zoom
invideo edit show <edit-url> --json
invideo branch new <edit-url> --name launch-v1 --token <state_token>
invideo edit show <branch-url> --json
invideo edit change <branch-url> --timeline T1 --token <state_token> \
  --operations @ops.json
invideo edit capture <branch-url> --at 2,8,15,30 --out ./frames
invideo edit check <branch-url>
invideo edit export <branch-url> --format video --quality 1080 --json
invideo status <card-url> --wait
invideo notebook show <card-url> --out ./out
```

Where the values come from:

- `<project-url>` comes from `invideo project new`, and `<edit-url>` from `invideo edit new`. `<name>` is the name you choose in `invideo agent register`. `<page-url>` is a notebook page from `invideo notebook show <project-url>`.
- `<voice-id>` comes from the voice search. Each `<price>` is the dry run right before it.
- `<sequence-url>` is the voice's sequence URL, which the paid voice run returns.
- `<state_token>` comes from `invideo edit show <edit-url>`, and on the branch from `invideo edit show <branch-url>`; the main token is stale there. `<branch-url>` is the `.branch_url` that `invideo branch new` returns.
- `<card-url>` is `.sequences[0]` of what the export returns. `invideo status` waits and does not download; `invideo notebook show` saves the file as `./out/export.mp4`.
- The file `./ops.json` holds a bare list of operations: the `operations` list of the MCP call below.

The key MCP call places a screenshot, starts the push and puts the corner pin on the recording. `<branch-url>` is the branch's URL from `timeline_history`, and `<state_token>` comes from `get_timeline`. `<sequence-url>` is the URL `add_media` returns for the screenshot. `c1` is the id the placed screenshot gets, and `c4` is the recording, from `get_timeline`. `<glsl-source>` is the shader the agent writes from the guide.

```json
{
  "url": "<branch-url>",
  "timeline_id": "T1",
  "expected_state_token": "<state_token>",
  "operations": [
    { "op": "place_media", "asset": "<sequence-url>",
      "track": "V1", "at_f": 0, "source_start_seconds": 0,
      "source_end_seconds": 5, "mode": "append", "linked_audio": false },
    { "op": "add_effect", "clip": "c1", "effect": "dynamic_zoom" },
    { "op": "add_custom_effect", "clip": "c4", "name": "Corner pin",
      "source": "<glsl-source>",
      "params": [
        { "name": "tl", "label": "Top left", "type": "vec2",
          "default": [0.18, 0.12] },
        { "name": "tr", "label": "Top right", "type": "vec2",
          "default": [0.82, 0.16] },
        { "name": "br", "label": "Bottom right", "type": "vec2",
          "default": [0.80, 0.86] },
        { "name": "bl", "label": "Bottom left", "type": "vec2",
          "default": [0.20, 0.90] }
      ] }
  ]
}
```

The agent sets the end point and end zoom of `dynamic_zoom` in a second call, with the effect's id from `get_timeline`. You can drag the four corner controls in the effects panel after.

## Cost and time

Edits and exports are free. Generation and transcripts cost credits. Paid stock costs a small credit charge, or one of your workspace's iStock licenses. Your agent runs a dry run first and tells you the price before it spends anything.

- **Free:** uploads, layout, the zoom effect, keyframes, the corner pin shader, captions you place, and the export.
- **Costs credits:** the voiceover model, the transcript of the voice, and stock music.

A launch video takes under an hour, most of it on the script and your review. See [Credits and what they cost](/account/credits) for prices.

## If something goes wrong

Most problems are a soft screenshot or a corner pin that sits off the screen.

| Symptom | Fix |
| --- | --- |
| `get_model` or `generate` refuses the model as not found | Models retire. Run `find_models` with the job in words, use the best match, and tell the person which model you used. |
| Exit code 6, or `status` is `held` | A safety review, not a refusal. Wait, then run `invideo status <url> --wait` again. Do not resend. If it ends `failed`, change the input the review flagged. |
| An upload is refused | Read the `message`, fix what it names, and upload once more. |
| A screenshot looks soft after the push | Capture it at a larger size, or push less with a lower end zoom. |
| `invalid_custom_effect` | Fix each error the refusal lists by line and column. Send again with `dry_run` first. |
| The pinned screen does not line up with the frame | Change the corner values with `set_effect_parameter`, and `capture` again. |
| `set_keyframes` refused for zoom | It takes `opacity` and `rotation` only. Use `add_keyframe_lane` and `add_keyframes`. |
| The voice reads a product name wrong | Spell it as it sounds in the script, and generate that line again. |
| CLI exit 3 on the branch | The token is from main. Run `invideo edit show <branch-url>` and use its `state_token`. |
| The `./out` folder is empty after `status` | `status` waits and does not download. Run `invideo notebook show <card-url> --out ./out`. |
| CLI exit 5 | A credit limit. Ask the person before the agent spends more. |
| Avoid: the agent asks invideo to capture the URL | Instead: it uses its own browser or screenshot tool, then `add_media`. |
| Avoid: saying done without looking | Instead: `capture` frames at every push and caption. |

## Prompts to try

Each prompt is a real variation of the job.

```text
Make a 30 second vertical teaser for my app from the screen recording
./onboarding.mov inside the phone frame ./iphone.png. Tilt the phone,
no voice, bold captions with the three key lines from my homepage.
```

```text
Turn my changelog page https://example.com/changelog into a 60 second
"what's new" video. One screenshot per feature, a push into each,
a calm voice and soft music.
```

```text
Make the launch video for https://example.com in 16:9 and 1:1. Same
script and voice. Put each shape on its own branch so I can compare.
```

## When to use something else

invideo does not capture web pages or record your screen. Use the agent's browser tool, your OS screenshot tool, or a screen recorder, then bring the files in.

For a 3D device that turns in space, use a 3D tool and bring the render in as a clip. The corner pin here is a flat warp of the screen.

## Related

- [Resize one master ad for every placement, with safe zones](/how-to/marketing/every-placement-from-one-master)
- [Test hooks on one ad, with branches](/how-to/marketing/test-hooks-on-branches)
- [Make an App Store preview video at Apple's sizes](/how-to/marketing/app-store-preview)
- [Turn release notes or a changelog into a video every week](/how-to/training/release-notes-to-video)
- [Check your video before you say it is done](/how-to/check-your-video)