---
title: Make sleep, rain and meditation videos for a faceless channel, in bulk
description: Long sleep, rain and meditation videos made in bulk: a looped scene, a calm sound bed, one timeline per variant, exported in parts.
---
You get long sleep videos, rain sounds, ambient and meditation videos, also called relaxing or study-with-me videos, for a faceless channel. Each one is a calm scene that loops without a jump, with rain, waves or soft music under it. You make one, then your agent makes the variants: rain on a window, ocean at night, a forest at dawn.

Use invideo for this when you want many long videos from one setup: the agent loops the scene, joins the sound bed, copies the timeline for each variant, and exports each one in parts.

## Quickest path

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

```text
Make three long sleep videos for my channel: rain on a window, ocean
waves at night, and a quiet forest. Loop a calm stock clip for each,
put matching ambient sound under it, fade in and out, and make one
timeline per video. Show me the loop seam before you export.
```

In a chat app, you name the scenes and the length in the chat. In a terminal, you can also give the agent a folder of your own clips.

## What you need

Pick a few calm scenes first. The agent repeats the setup for each one.

- **The scenes.** Your own clips, stock footage, or a shot to generate. Slow, steady motion loops best: rain, waves, fire, clouds.
- **The sound.** Rain, waves, wind, soft piano, or a drone. From stock music, generated, or your own file if you have the rights.
- **The length.** How long each video runs, in hours or minutes.
- **The title text.** Optional. A short line like "Rain for sleep" for the first seconds.
- **The shape.** 16:9 for YouTube.

## Steps

Your agent edits on a branch and tells you what changed at each step.

<Steps>
<Step title="Pick the scene">
The agent searches `stock_video` with `search_library`, for example "rain on window night". It looks at the first and last moments of each result with `media` and `video.frames`, to find a clip whose ends look alike. For a scene stock does not have, it runs `find_models` with the scene in words. A good pick is `seedance_2_5.frames`. Your agent can search for others with `find_models`. The agent reads the pick with `get_model`, and passes one still as both `first_frame` and `last_frame`, with `generate_audio` false, so the clip loops cleanly. It calls `generate` with `dry_run: true` and tells you the price. For a price of 1 credit or more it waits for your yes. Then it calls `generate` with `max_credits` set to that price and an `idempotency_key`, and reads the result with `read`. Expected result: a sequence URL per scene, in the project.
</Step>
<Step title="Loop the scene to the full length">
The agent runs `media` with `video.loop` on the clip's sequence URL and sets `target_duration` to the length you asked for, in seconds. The dry run shows its price before anything starts. Then the agent runs it with `max_credits` and an `idempotency_key`. Expected result: a new, long sequence beside the clip.
</Step>
<Step title="Make the sound bed">
The agent searches `stock_music` with `search_library`, for example "rain ambience". You pick from the previews. For rain, waves or wind that stock does not have, it runs `find_models` with the sound in words. A good pick is `elevenlabs_sfx`, read with `get_model`, with `loop` true and the longest `duration` it allows. For a soft piano or a drone, a good pick is `elevenlabs_music_v2_5_instrumental`. A music model is a poor fit for rain. Each gets `generate` with `dry_run: true` first, with the price told to you. For a price of 1 credit or more it waits for your yes. Then it calls `generate` with `max_credits` set to that price and an `idempotency_key`, and reads the sequence URL with `read`. Then it joins copies into one long file with `media` and `audio.concat`, the sequence URLs in `audio_array`. Expected result: one sound file as long as the video.
</Step>
<Step title="Build the first timeline">
The agent makes a 16:9 timeline with `new_timeline` and a branch with `timeline_history`. It places the long loop with `insert_media` and the sound bed on an audio track. It fades the picture in and out with `add_edge_transition` and type `fade`, and the sound with `set_audio_fades`. Expected result: one full video on the timeline.
</Step>
<Step title="Make one timeline per variant">
For each other scene, the agent copies the timeline with `duplicate_timeline`. On the copy, it swaps the picture and the sound with `place_media` mode `replace`, and changes the title with `set_text`. Expected result: one timeline per video, each with its own `timeline_id`.
</Step>
<Step title="Check the seam and the sound">
The agent runs `capture` a moment before and a moment after each loop seam, and `check_timeline`. It runs `audio.content` on the sound bed to find any silent gap at a join, and `audio.loudness` to keep it soft and even. Expected result: no jump at the seam and no gap in the sound.
</Step>
<Step title="Export in parts and join them">
For a very long video, the agent exports it in parts with `export`, each with its own `start` and `end` in seconds. Then it joins the parts with `media` and `video.stitch`, the parts' sequence URLs in `video_array`. Expected result: one long MP4.
</Step>
</Steps>

The same job from a terminal:

```bash
invideo project new --title "Sleep channel, batch 1"
invideo agent register <project-url> --name <name>
export INVIDEO_AS=<name>
invideo edit new <project-url> --name "Rain for sleep"
invideo library search <project-url> --library stock_video \
  --query "rain on window night"
invideo asset add <edit-url> --stock <id>
invideo media video.frames <sequence-url> --at 0,9.5 --out ./seam
invideo media video.loop <sequence-url> --target-duration <seconds> \
  --dry-run
invideo media video.loop <sequence-url> --target-duration <seconds> \
  --max-credits <price> --idempotency-key rain-loop-1
invideo gen models <project-url> --query "steady rain sound bed"
invideo gen model <project-url> --model elevenlabs_sfx
invideo gen run <page-url> --model elevenlabs_sfx --dry-run \
  -i prompt="steady soft rain on a window" -i duration=30 -i loop=true
invideo gen run <page-url> --model elevenlabs_sfx --max-credits <price> \
  --idempotency-key rain-1 -i prompt="steady soft rain on a window" \
  -i duration=30 -i loop=true
invideo media audio.concat <rain-url> \
  --audio-array <rain-url>,<rain-url>,<rain-url>
invideo edit show <edit-url> --json
invideo branch new <edit-url> --name rain-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 <seam-1>,<seam-2> --out ./frames
invideo edit check <branch-url>
invideo edit export <branch-url> --format video --quality 1080 \
  --start 0 --end <part-end> --json
invideo status <part-1> --wait
invideo media video.stitch <part-1> --video-array <part-1>,<part-2>
invideo status <stitch-url> --wait
invideo notebook show <stitch-url> --out ./out
```

Where each value comes from:

- `<name>` is the name you choose in `invideo agent register`. `INVIDEO_AS` passes it to every later command.
- `<project-url>` comes from `invideo project new`, and `<edit-url>` from `invideo edit new`.
- `<id>` is a stock result's id from `invideo library search`. `<sequence-url>` is the `url` that `invideo asset add` returns for it, a `…/notebook/seq-N.M` URL. `video.loop` refuses a bin `--asset`.
- `<seconds>` is the length you asked for, in seconds.
- `<page-url>` is a notebook page of the project, listed by `invideo notebook show <project-url>`. `<price>` is each dry run's `.estimated_credits`.
- `<rain-url>` is the sound's sequence URL, from `invideo notebook show` on the thread URL the paid `gen run` returns.
- `<state_token>` comes from `invideo edit show`: on `<edit-url>` for the branch, then on `<branch-url>` for the change. `<branch-url>` is the `.branch_url` that `invideo branch new` returns.
- `@ops.json` reads a bare JSON list of ops, like `insert_media` for the loop on `V1` and the sound bed on `A2`.
- `<seam-1>` and `<seam-2>` are times, in seconds, a moment before and after a loop seam.
- `<part-end>` is where the part ends, in seconds. The next part starts there.
- `<part-1>` and `<part-2>` are `.sequences[0]` of each part's export result, the export cards. The agent waits on each with `invideo status`.
- `<stitch-url>` is the sequence URL `video.stitch` returns. `invideo status` waits but saves nothing. `invideo notebook show` saves the joined video in `./out`, and the agent renames it.

The key MCP call loops the scene. The agent sends it first with `dry_run: true`, then with `max_credits` set to the price. `<sequence-url>` is the scene's sequence URL, from `add_media` or the `generate` result. `<seconds>` is the length you asked for, as a number.

```json
{
  "url": "<sequence-url>",
  "op": "video.loop",
  "target_duration": "<seconds>",
  "idempotency_key": "rain-window-loop-1"
}
```

The parts join the same way, with `op` set to `video.stitch` and the parts in `video_array`, in order.

## 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.

- **Costs credits:** paid stock clips and music, and any generated scene or sound.
- **Tool ops:** `video.frames`, `video.loop`, `video.reverse`, `video.stitch`, `audio.concat`, `audio.content`, `audio.loudness`. The dry run shows its price before anything starts.
- **Free:** the edits, the checks and every export.

A long export takes a while to render. Exporting in parts keeps each job shorter, and the agent can run a failed part again by itself. See [Credits and what they cost](/account/credits) for prices.

## If something goes wrong

Most problems come from a loop that jumps, or a sound bed with a click at the join.

| 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. |
| `invideo status --wait` ends and the folder is empty | `status` does not download. Run `invideo notebook show <url> --out <dir>` on the card or the stitched sequence. |
| `video.loop` or `video.stitch` refused with `code: "not_found"` | The call named a media bin asset. Pass the file's sequence URL, from `add_media`, `generate` or the export card. |
| The picture jumps at each loop | Pick a clip whose ends look alike, or loop a forward-then-backward clip made with `video.reverse` and `video.stitch`. |
| A click or a gap at a sound join | Set `gap_duration` to 0 on `audio.concat`, or crossfade the copies on the timeline with `apply_audio_crossfade`. |
| The sound is too loud for sleep | Lower it with `set_audio_gain`, then check with `audio.loudness`. |
| A long export fails | Export it in shorter parts with `start` and `end`, then join them with `video.stitch`. |
| `video.stitch` does not join the parts | It joins videos of the same aspect ratio. Export every part from the same timeline. |
| CLI exit 7 | Nothing ran. Run the same command again. |
| Avoid: calling `export` again to check on it | Instead: `read` the export card, or run `invideo status` with `--wait`. |
| Avoid: one huge export for every variant at once | Instead: run it in parts, a few timelines at a time. |

## Prompts to try

Each prompt is a real variation of the job.

```text
Make a long fireplace video with crackling fire sound, no music, no
title, and a slow fade in. Then make a version with soft piano.
```

```text
Generate a slow drifting nebula scene, loop it for a long meditation
video, and put a deep, calm drone under it.
```

```text
Make one timeline per row of ./scenes.csv, each with its scene, its
sound and its title. Export them in parts, a few at a time.
```

## When to use something else

invideo does not upload or schedule videos on YouTube. Export the MP4 and upload it in YouTube Studio. Ambient videos that repeat the same clip can count as repetitive content under YouTube's rules. Read those rules in YouTube Studio before you build a channel on them.

For a live stream of a loop that never stops, use a streaming tool like OBS Studio with the exported file.

## Related

- [Make a quote video, nature loop or welcome video](/how-to/creators/quote-and-nature-loop-videos)
- [Make a faceless YouTube video yourself, with AI help](/how-to/creators/faceless-youtube-video)
- [Add music and sound effects that clear rights](/how-to/creators/music-and-sound-effects)
- [Turn a CSV into many videos with a script](/how-to/developers/csv-to-videos)
- [Know the cost before you spend](/how-to/cost-before-you-spend)