Skip to content
Leverage AI

Free guide · from the Reel

Type one topic, get a finished 50-second video from Claude Code

Give Claude Code one topic, "why the sky is blue", and it hands back a finished 51-second vertical video. It writes a 143-word script and records the voice with ElevenLabs. Then it draws ten animated scenes in code, timed to the spoken words, adds word-by-word captions and renders a 1080x1920 MP4. No video model is involved: every frame is HTML and SVG that Claude wrote, and HyperFrames (Apache-2.0) turns it into video on your own computer. Our explainer took 10 minutes 46 seconds. The only cost was 1,596 ElevenLabs voice credits, on our ElevenLabs account (the free plan's 10,000 a month would cover it), plus normal Claude Code use.

  • About 11 minutes
  • 6 steps
  • ElevenLabs voice, no video model

Published · Updated

Start building
Watch it with sound:
the sky video
(51 s). The second example is a short story made from a second one-line topic:
the lighthouse story
(55 s).

The whole flow is one Claude skill, make-video, in two files you can copy from this page: SKILL.md (the instructions) and voice.py (makes the ElevenLabs voice and checks it).

What you need

  • Claude Code, with a Claude plan or API billing. We used a paid plan and Claude Opus 5.5.
  • Node.js 22 or newer (we had v22.23.4) and FFmpeg (we had 6.1.1).
  • Python 3 (we had 3.12.3).
  • An ElevenLabs account and API key. The free plan's 10,000 credits a month covers tests. For videos you will publish commercially, make the voice on a paid plan: on 31 October 2026 the pricing page listed the Commercial License from Starter (30,000 credits a month) up, and not on Free.
  • Recommended: faster-whisper (MIT), so voice.py can listen to each take and check it says the script word for word. Install it with pip install faster-whisper. It downloads the Whisper "small" model on first use. Without it, voice.py still checks pauses and pace, but it reports "words not checked" and you have to listen to the voice yourself.

Check what you have:

bash
node --version
ffmpeg -version
python3 --version

The steps

Step 1: Save the skill

A Claude Code skill is a folder with a SKILL.md in it. Personal skills live in ~/.claude/skills/<name>/ and work in every project (Claude Code docs). Make the folder:

bash
mkdir -p ~/.claude/skills/make-video

Save the two files below into it as ~/.claude/skills/make-video/SKILL.md and ~/.claude/skills/make-video/voice.py.

SKILL.md:

markdown
---
name: make-video
description: Turn one topic into a finished 45-60 second vertical video (1080x1920) with a written script, an ElevenLabs voice, word-timed captions and animated scenes built in HTML with HyperFrames. No video or image model. Use when someone asks for a short video from one topic.
argument-hint: <topic, e.g. "why the sky is blue" or "a short story about a lighthouse keeper's last night">
---

# make-video

Topic: $ARGUMENTS

Make the whole video without stopping to ask questions. The person gave you one line; every choice after that is yours. Work in a new folder named after the topic (kebab-case, max 4 words) inside the current folder.

## 0. Setup check (stop with a clear message if one fails)

- `node --version` is 22 or newer, `ffmpeg -version` works, `python3 --version` works.
- `ELEVENLABS_API_KEY` is set in the environment (`test -n "$ELEVENLABS_API_KEY" && echo set`). Never print the key or write it into a file.
- `voice.py` sits next to this SKILL.md. Copy it into the project.
- Make the project: `npx -y hyperframes@0.8.221 init <folder> --resolution portrait --non-interactive`. Then read the HyperFrames skills it installs (`hyperframes`, `hyperframes-cli`, and from `hyperframes/references/`: `types/vertical.md`, `captions.md`, `motion.md`, `format.md`) before writing any scene.

## 1. Script (write `script.txt` and `plan.md`)

- Decide the kind: an explainer (answers one question) or a short story (one named character who wants something, one problem, and an ending that settles it).
- 125-150 words, which ElevenLabs reads in 45-55 s. Plain spoken sentences, mostly under 14 words. Write numbers as words ("four hundred", not "400") so the transcript check can match them.
- The first sentence is the hook and has to work on its own: a question or a surprising claim for an explainer, a person in trouble for a story.
- Explainer: use only facts you are sure of and that a school textbook would agree with. No exact figures you can't stand behind. End by answering the opening question in one line.
- Story: original characters and setting, one location, one problem, and a last line that shows how it ended. No famous characters or existing plots.
- `plan.md`: the script split into 7-10 beats (one beat = one sentence or two short ones) and, for each beat, the picture: what is on screen and what moves. Every beat gets its own picture.

## 2. Voice (before any scene, so scenes are timed to the real words)

```bash
python3 voice.py script.txt -o assets/voice.mp3 --direction "<tag>"
```

- Tag: one short tag for the whole read. Explainer: `[warm, curious, clear]`. Story: `[calm, storytelling, intimate]`. Pick your own words if the topic needs it.
- It makes 2 takes. If faster-whisper is installed, it keeps the one whose Whisper transcript matches the script best; without it, `match` is null, nothing is checked word for word, and you must listen to the take yourself. It writes `assets/voice.mp3`, `assets/words.json` (every word with start and end) and prints a JSON line.
- If the only problem is "words not checked", don't make new takes: keep going, and at the end tell the person to listen to the voice before posting.
- If `ok` is false for another reason: read `problems` in the printed line and `heard` (what Whisper wrote down) in `assets/voice.json`. A misheard name or word: first copy the untouched script to `captions.txt` (only once), then respell the word in `script.txt` for the voice, keeping the same number of words (one spoken word for one caption word), and run voice.py again for 2 new takes. At most 2 rounds (4 more takes), then keep the best take and say so at the end.
- Never speed up, slow down or stretch the audio. If the voice runs over 58 s, cut words and make new takes.
- Video length = `speechEndSeconds` + 1.0 s.

## 3. Scenes (HyperFrames, 1080x1920, 30 fps)

- From `words.json`, set each beat's start to the start time of its first word (the first beat starts at 0). One sub-composition per beat in `compositions/`, each mounted in `index.html`.
- Everything is drawn in HTML, CSS and inline SVG, animated with GSAP: shapes, diagrams, icons, characters made of simple shapes, big words. No image files, no stock, no image or video models, no web fonts from the network (use the bundled fonts).
- Look: one palette of 4-5 colours for the whole video, chosen for the topic; full-bleed colour or gradient grounds, never plain white or black; a few layers per frame (ground, texture or pattern, subject). The subject is big: it fills at least a third of the frame.
- Motion: something moves in every second. Each beat enters with a different move (scale-in, slide, wipe, draw-on stroke, morph, camera push). Put each key action on the word it illustrates, using the word times.
- Frame 0 is the thumbnail: the hook picture and the hook words are already on screen at 0.0 s.
- Keep words and the subject inside y 168-1330 px (the platform's buttons cover the rest); nothing under the right edge's last 140 px.

## 4. Captions

- One captions sub-composition (`data-track-kind="captions"`) built from `assets/words.json` as `captions.md` describes: groups of 2-4 words, one group visible at a time, the spoken word highlighted in the accent colour, white text with a dark outline or a dark pill behind it.
- Place the groups with their centre around y 1150-1250 px, under the scene's subject, fitted with `fitTextFontSize` (max width 900, base 84, min 52).
- Captions show the script's spelling, even where you respelled a word for the voice: if `captions.txt` exists, take the n-th caption word from it and the n-th word's times from `words.json`.

## 5. Check and look

- Add the voice: one `<audio id="voice" src="assets/voice.mp3">` clip in `index.html` from 0 for the whole length.
- `npx hyperframes lint`, then `npx hyperframes check --json`: fix every error; fix contrast and caption overlaps.
- `npx hyperframes snapshot --at <the middle of every beat, plus 0.0 and the last second>` and look at every PNG. Fix: empty or mostly empty frames, a subject smaller than a third of the frame, text cut off, captions over the subject, a beat that looks like the one before it.

## 6. Render and finish

```bash
npx hyperframes render --quality delivery --output renders/raw.mp4
ffmpeg -y -i renders/raw.mp4 -af loudnorm=I=-14:TP=-1.5:LRA=11 -c:v copy -c:a aac -b:a 192k renders/<folder>.mp4
```

- Confirm with `ffprobe` that it is 1080x1920 and its length matches the root `data-duration`.
- Grab 6 frames from the final file (`ffmpeg -ss <t> -i renders/<folder>.mp4 -frames:v 1 ...`) and look at them: captions present, matching the voice at that moment.
- Tell the person: the path to the video, its length, the script, the kept take and its match % (or that it was not transcript-checked), ElevenLabs credits used, and anything you could not fix.
- Don't leave a preview server or render running.

voice.py:

python
#!/usr/bin/env python3
"""Voice a whole short-video script with ElevenLabs: 2 takes, keep the one whose transcript matches best.

    export ELEVENLABS_API_KEY="paste-your-key-here"
    python3 voice.py script.txt -o assets/voice.mp3 [--voice RXtWW6etvimS8QJ5nhVk]
        [--direction "[warm, curious, clear]"] [--takes 2] [--reuse]

The direction tag goes in front of the script (ElevenLabs v4 reads it as a direction, not as words,
but bills its characters). Audio is never sped up or stretched.
For each take: Whisper (faster-whisper, if installed) writes down what it hears; match % = script words heard
in order, divided by the longer of the script and the transcript, so missing words and extra words both lower it. We also flag a pause over 1.2 s, a pace outside 2.2-3.6 words/s
and a start or end cut mid-sound. The kept take: best match, then fewest problems, then closest to 2.8 words/s.
Every take on disk for this output that was made from the same script text (saved next to it as voice-takeN.txt)
is compared, earlier runs too, so a better earlier take is never lost and a take of an older script never wins.
Without faster-whisper nothing checks the words: ok is false with the problem "words not checked", and you must listen.
Writes the kept take to -o, every take next to it (voice-take1.mp3 ...), and assets/words.json:
[{"id": "w0", "text": "Why", "start": 0.12, "end": 0.31}, ...] from ElevenLabs' own timing, for captions.
Prints one JSON line with the summary. Exit 1 if the best take matches under 97 % or has problems."""
import argparse, base64, difflib, json, math, os, re, shutil, subprocess, sys, urllib.error, urllib.request

p = argparse.ArgumentParser()
p.add_argument("script", help="a text file with the narration, plain sentences")
p.add_argument("-o", "--out", required=True)
p.add_argument("--voice", default="RXtWW6etvimS8QJ5nhVk", help="ElevenLabs voice ID (default: Fiona)")
p.add_argument("--direction", default="[warm, curious, clear]")
p.add_argument("--model", default="eleven_v4")
p.add_argument("--takes", type=int, default=2)
p.add_argument("--reuse", action="store_true", help="pick again from takes already on disk, no credits")
a = p.parse_args()

text = " ".join(open(a.script).read().split())
KEY = os.environ.get("ELEVENLABS_API_KEY")
if not KEY and not a.reuse:
    sys.exit("ELEVENLABS_API_KEY is not set. Make a key at elevenlabs.io (Developers > API keys), then run\n"
             '  export ELEVENLABS_API_KEY="paste-your-key-here"\nin this terminal. Never write the key into a project file.')


def tts(path):
    body = {"text": f"{a.direction} {text}".strip(), "model_id": a.model,
            "voice_settings": {"stability": 0.5, "similarity_boost": 0.75}}
    req = urllib.request.Request(
        f"https://api.elevenlabs.io/v1/text-to-speech/{a.voice}/with-timestamps?output_format=mp3_44100_128",
        data=json.dumps(body).encode(), headers={"xi-api-key": KEY, "Content-Type": "application/json"})
    try:
        with urllib.request.urlopen(req, timeout=300) as r:
            data, cost = json.load(r), r.headers.get("character-cost")
    except urllib.error.HTTPError as e:
        sys.exit(f"ElevenLabs said {e.code}: {e.read().decode()[:300]}")
    open(path, "wb").write(base64.b64decode(data["audio_base64"]))
    al = data.get("alignment") or data["normalized_alignment"]
    words, cur, inside = [], None, False          # characters -> words, skipping the [direction] tag
    for c, t0, t1 in zip(al["characters"], al["character_start_times_seconds"], al["character_end_times_seconds"]):
        if c in "[]": inside = c == "["; cur = None; continue
        if inside or c.isspace(): cur = None; continue
        if cur is None: cur = [c, t0, t1]; words.append(cur)
        else: cur[0] += c; cur[2] = t1
    words = [{"id": f"w{i}", "text": w, "start": round(s, 3), "end": round(e, 3)} for i, (w, s, e) in enumerate(words)]
    json.dump(words, open(path[:-4] + ".words.json", "w"))
    open(path[:-4] + ".txt", "w").write(text)       # which script this take says
    return int(cost or len(body["text"]))


def pcm(path):
    raw = subprocess.run(["ffmpeg", "-v", "error", "-i", path, "-ac", "1", "-ar", "16000", "-f", "s16le", "-"],
                         capture_output=True, check=True).stdout
    return [int.from_bytes(raw[i:i + 2], "little", signed=True) / 32768 for i in range(0, len(raw) - 1, 2)]


def norm(t):                                   # "colour" = "color", "sky's" = "skys"
    t = re.sub(r"our\b", "or", t.lower().replace("-", " ").replace("'", ""))
    return re.sub(r"[^a-z0-9 ]", " ", t).split()


try:
    from faster_whisper import WhisperModel
    import numpy as np
    whisper = WhisperModel("small", device="cpu", compute_type="int8")
except ImportError:
    whisper = None
    print("note: faster-whisper is not installed, so takes are not transcribed (pip install faster-whisper)", file=sys.stderr)

stem = a.out.rsplit(".", 1)[0]
folder = os.path.dirname(a.out) or "."
os.makedirs(folder, exist_ok=True)
every = sorted(int(m.group(1)) for f in os.listdir(folder)
               if (m := re.fullmatch(re.escape(os.path.basename(stem)) + r"-take(\d+)\.mp3", f)))
def same_script(k):
    try: return " ".join(open(f"{stem}-take{k}.txt").read().split()) == text
    except FileNotFoundError: return False
have = [k for k in every if same_script(k)]  # only takes of this exact script
new = [] if a.reuse else list(range(max(every, default=0) + 1, max(every, default=0) + a.takes + 1))
takes, billed = [], 0
for k in have + new:                           # judge every take, old and new
    path = f"{stem}-take{k}.mp3"
    if k in new:
        billed += tts(path)
    x = pcm(path)
    db = [10 * math.log10(sum(v * v for v in x[i:i + 160]) / 160 + 1e-12) for i in range(0, len(x) - 160, 160)]
    loud = [i for i, d in enumerate(db) if d > -40]
    first, last = loud[0] / 100, (loud[-1] + 1) / 100
    longest, run = 0, 0
    for d in db[loud[0]:loud[-1]]:
        run = run + 1 if d <= -40 else 0
        longest = max(longest, run)
    wps = len(norm(text)) / max(last - first, 0.1)
    problems = []
    if longest > 120: problems.append(f"pause of {longest / 100:.1f} s")
    if db[0] > -45 or db[-1] > -45: problems.append("starts or ends mid-sound")
    if not 2.2 <= wps <= 3.6: problems.append(f"pace {wps:.1f} words/s")
    heard, match = None, None
    if not whisper: problems.append("words not checked (faster-whisper not installed): listen before using")
    if whisper:
        segs = whisper.transcribe(np.array(x, dtype=np.float32), language="en", condition_on_previous_text=False)[0]
        heard = " ".join(s.text.strip() for s in segs)
        sm = difflib.SequenceMatcher(None, norm(text), norm(heard), autojunk=False)
        match = round(100 * sum(b.size for b in sm.get_matching_blocks()) / max(len(norm(text)), len(norm(heard))), 1)
    dur = len(x) / 16000
    takes.append({"take": path, "durationSeconds": round(dur, 2), "speechEndSeconds": round(last, 2),
                  "wordsPerSecond": round(wps, 2), "match": match, "heard": heard, "problems": problems})
    print(f"take {k}: {dur:.1f} s, {wps:.2f} words/s, match {match} %, " + ("; ".join(problems) or "no problems"), file=sys.stderr)

if not takes: sys.exit(f"no takes of this script found next to {a.out}")
best = min(takes, key=lambda t: (-(t["match"] or 0), len(t["problems"]), abs(t["wordsPerSecond"] - 2.8)))
shutil.copy(best["take"], a.out)
shutil.copy(best["take"][:-4] + ".words.json", os.path.join(folder, "words.json"))
ok = not best["problems"] and (best["match"] is None or best["match"] >= 97)
summary = {"ok": ok, "file": a.out, "words": os.path.join(folder, "words.json"), "kept": best["take"],
           "durationSeconds": best["durationSeconds"], "speechEndSeconds": best["speechEndSeconds"],
           "match": best["match"], "wordsChecked": whisper is not None, "problems": best["problems"], "creditsBilled": billed,
           "takes": [{k: t[k] for k in ("take", "match", "wordsPerSecond", "problems")} for t in takes]}
json.dump({**summary, "heard": best["heard"], "voice": a.voice, "model": a.model, "direction": a.direction},
          open(stem + ".json", "w"), indent=1)
print(json.dumps(summary))
sys.exit(0 if ok else 1)

voice.py makes two takes of the whole script. With faster-whisper installed, it has Whisper write down each take and keeps the one closest to the script; missing and extra words both count against a take. It also compares earlier takes of the same script, and ignores takes of an older version. The skill pins HyperFrames 0.8.221, the version we used, and the ElevenLabs model eleven_v4. Its default voice is "Fiona" from the ElevenLabs Voice Library (ID RXtWW6etvimS8QJ5nhVk). To use another voice, change the --voice default in voice.py to that voice's ID.

Step 2: Put your ElevenLabs key in the terminal

In ElevenLabs, go to Developers, then API keys, and make a key. In the terminal you will start Claude Code from:

bash
export ELEVENLABS_API_KEY="paste-your-key-here"

Claude Code started from this terminal can use the key. Don't paste it into a project file or into the chat.

Step 3: Ask for the video

Make an empty folder and open Claude Code in it:

bash
mkdir videos && cd videos
claude

Type this, with your topic at the end:

text
Read ~/.claude/skills/make-video/SKILL.md and follow it exactly. The topic is: why the sky is blue

Claude works through the skill without asking you anything. Its first hyperframes init installs the HyperFrames skills for AI agents (on our machine they went to ~/.agents/skills) and says to start a new agent session so they load. If Claude can't find them, open a new Claude Code session and type the line again. For our explainer, the steps took this long:

Expect 10 to 12 minutes. Claude makes the project, writes and voices the script, builds the scenes, fixes what lint and check find, then renders.

It finished at 10 minutes 46 seconds with the path to the video, the script, the kept take and the credits used.

Step 4: Watch it and read the script

Watch the whole video with sound. For an explainer, read script.txt and check every claim yourself before you post. We checked our sky script against NASA's explanation: white light is a mix of colours, air scatters short blue waves more than long red ones, and sunsets turn red because the light crosses more air. NASA's page doesn't cover the violet line (less violet in sunlight, eyes less sensitive to it), so that's the one to check in a second source.

Look for empty frames too. Ours has one: at about 10 seconds the screen is blank except for the caption "Each colour travels", for under a second, until the wave draws in. We left it, but you can fix one like it with a note (step 6).

Step 5: Try a different kind of topic

The same request works for a story. We ran:

text
Read ~/.claude/skills/make-video/SKILL.md and follow it exactly. The topic is: a short story about a lighthouse keeper's last night before the light goes automatic

Claude wrote a 149-word story about Walt, a keeper who saves a fishing boat on the night before a machine replaces him, and drew it in a darker palette: a striped lighthouse, a storm, a jammed lens turned by hand. Its first voice take matched the script 97.3 % and was kept. It finished in 12 minutes 13 seconds, plus the 3 minute 45 second note round from step 5.

Step 6: Send a note if a scene is weak

Type what you see and when, in plain words, in the same project. Our story video (step 5) had two weak spots, so we sent this note. Claude edited the scenes, kept the voice and rendered again in 3 minutes 45 seconds:

text
Two fixes, keep everything else. 1) Around 5-8 s the word NINETY spills outside its little circle; make it fit. 2) From 8 to 12 s (polishes the lens, winds the clock, watches the sea) the icons are small in the top corner and the middle of the screen is empty. Make each action big in the middle of the frame while it's said. Then render again.

Ask for picture changes only, so the voice stays as it is and costs no new credits. If you change the words, Claude has to make new takes.

The result

Sky explainerLighthouse story
Topicwhy the sky is bluea short story about a lighthouse keeper's last night before the light goes automatic
Video
the sky video
the lighthouse story
Length51.2 s55.1 s
Size, format1080x1920, 30 fps, H.264 + AAC1080x1920, 30 fps, H.264 + AAC
Loudness-14.3 LUFS, peak -0.9 dB-14.5 LUFS, peak -1.1 dB
ElevenLabs credits1,5961,674
Time to finished file10 min 46 s12 min 13 s, then 3 min 45 s for one note
Short muted clips from both:
lens
,
sunset
,
rainbow
,
forty years
.

If something looks off

  • "ELEVENLABS_API_KEY is not set": run the export line from step 2 in the terminal, then start Claude Code again from that same terminal.
  • Credits used are about twice the script's length: that's normal, because both takes are billed, plus the short direction tag at the start of each.
  • voice.py reports a match under 97 %: open assets/voice.json and read heard, which is what Whisper wrote down. Usually Whisper wrote a number as digits or a word in American spelling. voice.py already treats "colour" and "color" as the same; the skill tells Claude to write numbers as words. For a real mishearing, the skill tells Claude to respell the word for the voice and make two new takes.
  • Lint error gsap_non_transform_motion: an animation changes a layout property such as letter spacing. Ask Claude to animate it with movement, scale, opacity or blur instead. Our story's "TOMORROW" title hit this.
  • Check warning line_unread: a line is on screen for less than 2 seconds. Ask for it to appear earlier. Both our videos hit this on their last line.
  • A thing in the wrong place, such as a boat sitting on a hill: the skill has Claude look at a snapshot of every beat, and that's how ours was found and fixed. If one slips through, describe it with its time, as in step 5.
  • Text spills out of its shape, or the middle of the screen is empty while small icons sit in a corner: send a note like the one in step 6.

Credits and licences

  • Claude Code by Anthropic: wrote the scripts, the scenes and the fixes. Paid plan or API billing.
  • HyperFrames 0.8.221 by HeyGen: Apache-2.0. Renders the HTML to MP4 locally.
  • GSAP 3.14.2: Webflow's Standard "No Charge" licence, which covers commercial use. Loaded by the HyperFrames page from jsDelivr.
  • FFmpeg: LGPL/GPL. Loudness and checks.
  • faster-whisper 1.2.1: MIT. The Whisper "small" model is MIT too. Optional transcript check.
  • Fonts in our videos: Inter and Playfair Display, both SIL Open Font License 1.1.
  • Voice: ElevenLabs, model Eleven v4, voice "Fiona". Paid service; free plan for tests, paid plan for commercial use.
  • SKILL.md and voice.py on this page: written by us for this guide, free to copy and change.

How we tested

We built and ran everything on a Linux server on 31 October 2026, with the voices made on our ElevenLabs account. Claude Code was 2.1.316 with Claude Opus 5.5. Each video ran in a fresh Claude Code agent session given the request from step 3. Claude Code also offers personal skills as slash commands (skills docs), but we used the full request.

TopicScript wordsTakes (match)Fixes Claude made on its ownNotes from usTotal time
Why the sky is blue143100 %, 100 %3 (contrast, short last line, overlapping animation)010 min 46 s
Lighthouse keeper's last night14997.3 %, 96.6 %5 (lint error, short last line, boat position, title wrap, missing sea)116 min

Before the two videos we tested voice.py on one sentence. Whisper wrote "color" for "colour" and the match came out at 90.9 %, so we made the comparison ignore British and American spelling. Match scores in the table come from the voice.py on this page, run on the same takes with --reuse (no new credits). Renders took 50 to 64 seconds each on a CPU with no GPU.

Questions people ask

Is this an AI video generator like Sora or Veo?

No. No video or image model is involved. Claude Code writes the script and then draws every scene itself in HTML and SVG, animated with GSAP. HyperFrames records that page into an MP4 on your computer. You get diagrams, shapes, simple characters and big words, not filmed or photoreal footage.

What does one video cost?

The render is free. The voice used 1,596 ElevenLabs credits for our 51-second explainer and 1,674 for our 55-second story, because both voice takes are billed. The free ElevenLabs plan gives 10,000 credits a month, enough for about six videos, but it doesn't include a commercial licence. Claude Code needs a Claude plan or API billing; we used a paid plan.

Can I post the videos on a monetised channel?

Use a paid ElevenLabs plan for that. On 31 October 2026 the pricing page listed the Commercial License from the Starter plan up, and the free plan without it. HyperFrames (Apache-2.0) and GSAP's no-charge licence both allow commercial use.

How long does it take?

Our explainer took 10 minutes 46 seconds from the request to a finished file, and our story took 12 minutes 13 seconds. We sent the story one note afterwards, which took another 3 minutes 45 seconds. You don't need to watch while it works.

Will the facts be right?

The skill tells Claude to use only facts a school textbook would agree with and to leave out exact figures it can't stand behind. Our sky script matched NASA's explanation on every point NASA covers, but read every script before you post it. Stories can contain facts too (how a lighthouse works, a place, a date), so check those the same way.

Read it on your computer

Keep this guide for later.

We'll send you this page's link and the other free guides.

This guide's link

We email you this guide's link now, then one short AI news email a week. Unsubscribe any time.

Get every pro guideEvery pro guide in one $49 payment, as of October 2026.Follow on Instagram@leverageai_io, where our Reels and code words are.
Text to Video AI with Claude Code: One Topic, One Short | Leverage AI