ToRun MCP ve Remotion ile seslendirilmiş hareketli anlatım videoları
Son güncelleme: 11 Kas 2026
Neler yapabilirsiniz
Claude Code, opencode veya Cursor gibi bilgisayarınızda komut çalıştıran bir kodlama ajanı, ToRun ile kısa, seslendirilmiş bir anlatım videosu hazırlayabilir. Ajan senaryoyu yazar, sahne görsellerini, seslendirmeyi ve arka plan müziğini ToRun'dan ister ve videoyu bilgisayarınızda, React bileşenlerini videoya dönüştüren bir kütüphane olan Remotion ile oluşturur.
Nelere ihtiyacınız var
- ToRun MCP sunucusuna bağlı, bir klasörde komut çalıştırıp dosya yazabilen bir kodlama aracı: opencode, Claude Code veya Cursor.
- Node.js 18 veya üstü. İlk oluşturma, Remotion'ın oluşturmada kullandığı tarayıcıyı indirir.
- Ücretli bir planın payı ya da cüzdan bakiyesi: API çağrıları ücretsiz payı hiçbir zaman kullanmaz. Bkz. çağrılar nasıl ödenir.
Remotion'ın kendi lisansı vardır: bireyler ve çok küçük şirketler için ücretsizdir, daha büyük şirketlerin bir şirket lisansına ihtiyacı vardır. İş için kullanmadan önce remotion.dev/license sayfasına bakın.
Başlangıç projesi
Dört dosya. Ajanınızdan bunları boş bir klasörde oluşturmasını isteyin ya da kendiniz kopyalayın, ardından orada npm install çalıştırın. Üretilen dosyalar package.json dosyasının yanındaki public klasörüne konur.
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 videonun tamamını tutar. Her sahnede ekranda gösterilen bir caption, seslendirme metni say, görsel istemi prompt, görselinin ve seslendirmesinin dosya adları ve seconds cinsinden süresi bulunur:
{
"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 sahneleri art arda oynatır. Her görsel yavaşça yakınlaşır, alt yazı yukarı doğru belirir, sahnenin seslendirmesi onunla birlikte başlar ve müzik videonun tamamı boyunca alçak sesle çalar:
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)}
/>
);
Ajan videoyu nasıl hazırlar
Senaryo. Ajan sahneleri
video.jsondosyasına yazar: sahne başına bir fikir, birkaç kelimelik bir alt yazı, söylenecek bir ya da iki cümle. Herhangi bir şey üretmeden önce senaryoyu size göstermesini isteyin.Bakiye. Hesabın ne kadar harcayabileceğini görmek için
account_balancearacını çağırır. Bu çağrı ücretsizdir.Görseller. Her sahne için sahnenin
promptdeğeri vesize1792x1024ileimage_generatearacını çağırır ve görselipublic/scene-1.png,public/scene-2.pnggibi adlarla kaydeder. Bir görsel bekleme süresi içinde hazır olmazsa araç bir iş kimliği döndürür ve ajan görsel hazır olana kadarjob_statusaracına sorar.Seslendirme. Her sahne için sahnenin
saymetni ve bir sesle, örneğinnova,tts_generatearacını çağırır ve sesipublic/scene-1.mp3gibi adlarla kaydeder.Zamanlama. Her sahnenin
secondsdeğerini seslendirmesinin süresi artı yaklaşık yarım saniye olarak ayarlar.tts_generatebu süreyi ses dosyasından ölçülmüş olarakduration_secondsalanında döndürür. Yanıttaduration_estimated: truede yazıyorsa süre yalnızca metinden tahmin edilebilmiştir, bu yüzden ajan dosyayı ölçer. Bir dosyayı kendiniz kontrol etmek için:npx remotion ffprobe -v error -show_entries format=duration -of csv=p=0 public/scene-1.mp3Müzik (isteğe bağlı). "calm instrumental background, soft piano" gibi bir istem,
falseolarak ayarlanmışwith_vocalsve videonun süresine yakın birduration_secondsilemusic_generatearacını çağırır. Araç bir iş kimliği döndürür; ajan parça hazır olana kadarjob_statusaracına sorar ve onupublic/music.mp3olarak kaydeder. Müzik istemiyorsanızvideo.jsondosyasındanmusicsatırını silin.Önizleme ve oluşturma.
npm run studiotarayıcınızda bir önizleme açar.npm run renderout/explainer.mp4dosyasını yazar.
Bir aracın döndürdüğü her dosya bağlantısı oturum açmadan indirilir. Soru işaretinden sonraki kısım da dahil olmak üzere bağlantının tamamını koruyun:
curl -L -o public/scene-1.png "<the image link>"
Ajanınıza sorun
torun.ai/docs/mcp-motion-explainers adresindeki Remotion başlangıç projesiyle uygulamamızın eşitlemesinin nasıl çalıştığını anlatan 30 saniyelik bir video hazırla. Dört sahne olsun. Önce video.json dosyasındaki senaryoyu bana göster. Ardından her sahne için içinde yazı olmayan bir görsel üret, her sahneyi nova sesiyle seslendir, alçak sesli enstrümantal müzik ekle, her sahneyi seslendirmesine göre zamanla ve videoyu oluştur.
Dikey bir video için video.json dosyasında width 1080 ve height 1920, görseller için de size 1024x1792 isteyin.
Maliyeti ne kadar
image_generate,tts_generatevemusic_generateücretli çağrılardır ve uygulamadaki fiyatlarla Runs olarak ücretlendirilir. Dört sahneli ve müzikli bir video dört görsel, dört seslendirme ve bir parçadır.account_balancevejob_statusücretsizdir.- Oluşturma bilgisayarınızda yapılır. Alt yazıları, zamanlamayı ya da hareketi değiştirip yeniden oluşturmak ToRun'da hiçbir ücret doğurmaz; bir görseli ya da seslendirmeyi yeniden üretmek yeni bir ücretli çağrıdır.
- Tek bir üretim anahtarın eşiğinden daha pahalıya mal olacaksa araç önce fiyatı söyler ve ajanınız çağrıyı çalıştırmadan önce size sorar.
- Ajanın ürettiği her şey ToRun kütüphanenize de kaydedilir.
İpuçları
- İçinde yazı olmayan görseller isteyin. Sözcükleri alt yazı taşır ve görsel modelleri yazıyı sık sık yanlış yazar.
- Her sahneyi bir fikir ve bir ya da iki cümlelik seslendirmeyle sınırlayın.
- Bir ses seçin ve videonun tamamında onu kullanın.
- Seslendirme net kalsın diye müziğin ses düzeyini
0.1ile0.15arasında tutun.