Animoidut selitysvideot kertojanäänellä ToRun MCP:n ja Remotionin avulla
Viimeksi päivitetty 11 marrask. 2026
Mitä voit tehdä
Koodausagentti, joka ajaa komentoja tietokoneellasi, kuten Claude Code, opencode tai Cursor, voi tehdä ToRunilla lyhyen selitysvideon, jossa on kertojanääni. Agentti kirjoittaa käsikirjoituksen, pyytää ToRunilta kohtausten kuvat, kertojanäänen ja taustamusiikin ja renderöi videon tietokoneellasi Remotionilla, kirjastolla, joka muuttaa React-komponentit videoksi.
Mitä tarvitset
- ToRunin MCP-palvelimeen yhdistetyn koodaustyökalun, joka voi ajaa komentoja ja kirjoittaa tiedostoja kansioon: opencode, Claude Code tai Cursor.
- Node.js 18:n tai uudemman. Ensimmäinen renderöinti lataa selaimen, jolla Remotion renderöi.
- Maksullisen tilauksen kiintiön tai lompakon saldon: API-kutsut eivät koskaan käytä ilmaista kiintiötä. Katso miten kutsut maksetaan.
Remotionilla on oma lisenssinsä: se on ilmainen yksityishenkilöille ja hyvin pienille yrityksille, ja suuremmat yritykset tarvitsevat yrityslisenssin. Tarkista remotion.dev/license ennen kuin käytät sitä työssä.
Aloitusprojekti
Neljä tiedostoa. Pyydä agenttia luomaan ne tyhjään kansioon tai kopioi ne itse ja aja sitten siellä npm install. Luodut tiedostot menevät package.json-tiedoston viereen kansioon public.
package.json:
{
"name": "motion-explainer",
"private": true,
"scripts": {
"studio": "remotion studio src/index.ts",
"render": "remotion render src/index.ts Explainer out/explainer.mp4"
},
"dependencies": {
"@remotion/cli": "4.0.539",
"react": "18.3.1",
"react-dom": "18.3.1",
"remotion": "4.0.539"
}
}
video.json sisältää koko videon. Jokaisella kohtauksella on näytöllä näkyvä caption, kertojanäänen teksti say, kuvan prompt, sen kuvan ja kertojanäänen tiedostonimet sekä sen pituus kentässä seconds:
{
"width": 1920,
"height": 1080,
"fps": 30,
"music": { "file": "music.mp3", "volume": 0.12 },
"scenes": [
{
"caption": "Every team has a backlog.",
"say": "Every team has a backlog, and it only grows.",
"prompt": "flat illustration of a wall covered in sticky notes, soft morning light, no text",
"image": "scene-1.png",
"narration": "scene-1.mp3",
"seconds": 5.5
},
{
"caption": "Sort it by what users feel.",
"say": "Sort it by what your users feel, not by what is loudest.",
"prompt": "flat illustration of hands arranging cards into three columns on a desk, no text",
"image": "scene-2.png",
"narration": "scene-2.mp3",
"seconds": 6
},
{
"caption": "Ship the top one this week.",
"say": "Then ship the top one this week.",
"prompt": "flat illustration of a small paper boat leaving a harbour at sunrise, no text",
"image": "scene-3.png",
"narration": "scene-3.mp3",
"seconds": 5
}
]
}
src/index.ts:
import {registerRoot} from 'remotion';
import {Root} from './Root';
registerRoot(Root);
src/Root.tsx toistaa kohtaukset peräkkäin. Jokainen kuva lähenee hitaasti, teksti nousee alhaalta, kohtauksen kertojanääni alkaa samaan aikaan, ja musiikki soi hiljaa koko videon ajan:
import React from 'react';
import {AbsoluteFill, Composition, Html5Audio, Img, interpolate, Series, staticFile, useCurrentFrame} from 'remotion';
import data from '../video.json';
// "say" and "prompt" are the script: the narration text and the image prompt. Rendering reads the rest.
type Scene = {image: string; narration?: string; seconds: number; caption?: string; say?: string; prompt?: string};
type Video = {width: number; height: number; fps: number; music?: {file: string; volume: number}; scenes: Scene[]};
const video = data as Video;
const clamp = {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'} as const;
const framesOf = (scene: Scene) => Math.round(scene.seconds * video.fps);
// One scene: the image slowly zooms in, the caption rises in, the narration plays from the scene's start.
const SceneView: React.FC<{scene: Scene; frames: number}> = ({scene, frames}) => {
const frame = useCurrentFrame();
const fade = interpolate(frame, [0, 10, frames - 10, frames], [0, 1, 1, 0], clamp);
const zoom = interpolate(frame, [0, frames], [1, 1.08]);
const rise = interpolate(frame, [8, 26], [40, 0], clamp);
return (
<AbsoluteFill style={{backgroundColor: 'black', opacity: fade}}>
<Img src={staticFile(scene.image)} style={{width: '100%', height: '100%', objectFit: 'cover', transform: `scale(${zoom})`}} />
{scene.caption ? (
<div
style={{
position: 'absolute', left: '6%', right: '6%', bottom: '8%', transform: `translateY(${rise}px)`,
color: 'white', fontFamily: 'Arial, sans-serif', fontSize: video.height / 18, fontWeight: 700,
textShadow: '0 2px 16px rgba(0,0,0,0.7)',
}}
>
{scene.caption}
</div>
) : null}
{scene.narration ? <Html5Audio src={staticFile(scene.narration)} /> : null}
</AbsoluteFill>
);
};
const Explainer: React.FC = () => (
<AbsoluteFill style={{backgroundColor: 'black'}}>
<Series>
{video.scenes.map((scene, i) => (
<Series.Sequence key={i} durationInFrames={framesOf(scene)}>
<SceneView scene={scene} frames={framesOf(scene)} />
</Series.Sequence>
))}
</Series>
{video.music ? <Html5Audio src={staticFile(video.music.file)} volume={video.music.volume} loop /> : null}
</AbsoluteFill>
);
export const Root: React.FC = () => (
<Composition
id="Explainer"
component={Explainer}
width={video.width}
height={video.height}
fps={video.fps}
durationInFrames={video.scenes.reduce((sum, scene) => sum + framesOf(scene), 0)}
/>
);
Miten agentti tekee videon
Käsikirjoitus. Agentti kirjoittaa kohtaukset tiedostoon
video.json: yksi ajatus kohtausta kohden, muutaman sanan teksti, yksi tai kaksi lausetta sanottavaksi. Pyydä sitä näyttämään käsikirjoitus ennen kuin se luo mitään.Saldo. Se kutsuu
account_balance-työkalua nähdäkseen, paljonko tili voi käyttää. Tämä kutsu on maksuton.Kuvat. Jokaiselle kohtaukselle se kutsuu
image_generate-työkalua kohtauksenprompt-arvolla jasize-arvolla1792x1024ja tallentaa kuvan nimelläpublic/scene-1.png,public/scene-2.pngja niin edelleen. Jos kuva ei ole valmis odotusajan kuluessa, työkalu palauttaa työn tunnisteen, ja agentti kysyyjob_status-työkalulta, kunnes kuva on valmis.Kertojanääni. Jokaiselle kohtaukselle se kutsuu
tts_generate-työkalua kohtauksensay-tekstillä ja äänellä, esimerkiksinova, ja tallentaa äänen nimelläpublic/scene-1.mp3ja niin edelleen.Ajoitus. Se asettaa kunkin kohtauksen
seconds-arvoksi sen kertojanäänen pituuden plus noin puoli sekuntia.tts_generatepalauttaa tämän pituuden arvonaduration_seconds, mitattuna äänitiedostosta. Jos vastauksessa lukee myösduration_estimated: true, pituus voitiin vain arvioida tekstistä, joten agentti mittaa tiedoston. Tarkistaaksesi tiedoston itse:npx remotion ffprobe -v error -show_entries format=duration -of csv=p=0 public/scene-1.mp3Musiikki (valinnainen). Se kutsuu
music_generate-työkalua kehotteella, kuten ”calm instrumental background, soft piano”,with_vocals-arvollafalseja videon pituutta lähellä olevalladuration_seconds-arvolla. Työkalu palauttaa työn tunnisteen; agentti kysyyjob_status-työkalulta, kunnes kappale on valmis, ja tallentaa sen nimelläpublic/music.mp3. Jos et halua musiikkia, poistamusic-rivi tiedostostavideo.json.Esikatselu ja renderöinti.
npm run studioavaa esikatselun selaimeesi.npm run renderkirjoittaa tiedostonout/explainer.mp4.
Jokainen työkalun palauttama tiedostolinkki latautuu ilman kirjautumista. Säilytä koko linkki, myös kysymysmerkin jälkeinen osa:
curl -L -o public/scene-1.png "<the image link>"
Pyydä agenttiasi
Tee 30 sekunnin selitysvideo siitä, miten sovelluksemme synkronointi toimii, Remotionin aloitusprojektilla osoitteesta torun.ai/docs/mcp-motion-explainers. Neljä kohtausta. Näytä ensin käsikirjoitus tiedostossa video.json. Luo sitten jokaiselle kohtaukselle yksi kuva ilman tekstiä, lisää jokaiseen kohtaukseen kertojanääni äänellä nova, lisää hiljainen instrumentaalimusiikki, sovita kunkin kohtauksen pituus sen kertojanääneen ja renderöi.
Pystyvideota varten pyydä video.json-tiedostoon width 1080 ja height 1920 sekä kuvia size-arvolla 1024x1792.
Mitä se maksaa
image_generate,tts_generatejamusic_generateovat maksullisia kutsuja, jotka veloitetaan Runs-yksiköinä samoilla hinnoilla kuin sovelluksessa. Neljän kohtauksen video musiikilla tarkoittaa neljää kuvaa, neljää kertojanääntä ja yhtä kappaletta.account_balancejajob_statusovat maksuttomia.- Renderöinti tapahtuu tietokoneellasi. Tekstien, ajoituksen tai liikkeen muuttaminen ja uudelleenrenderöinti ei maksa ToRunissa mitään; kuvan tai kertojanäänen luominen uudelleen on uusi maksullinen kutsu.
- Kun yksittäinen luonti maksaisi enemmän kuin avaimen kynnys, työkalu vastaa ensin hinnalla, ja agenttisi kysyy sinulta ennen kuin se suorittaa kutsun.
- Kaikki, mitä agentti luo, tallentuu myös ToRun-kirjastoosi.
Vinkkejä
- Pyydä kuvia ilman tekstiä. Sanat kulkevat tekstityksessä, ja kuvamallit kirjoittavat tekstin usein väärin.
- Rajaa jokainen kohtaus yhteen ajatukseen ja yhteen tai kahteen lauseeseen kertojanääntä.
- Valitse yksi ääni ja pidä se koko videon ajan.
- Pidä musiikin äänenvoimakkuus välillä
0.1–0.15, jotta kertojanääni pysyy selkeänä.