İçeriğe geç

Programatik Video Otomasyon Mimarisi: JavaScript ve Python ile Kendi YouTube Render Motorunuzu Kurmak

PixiJS, GSAP, Whisper, Matter.js, Rapier ve FFmpeg ile kare kontrollü video üretim mimarisi. Ses senkronizasyonu, Manim entegrasyonu ve gerçek 10 dakikalık VPS render ölçümü; kaynak kod ve MP4 ile.

· TankDev Mühendislik

Bir anlatıcı “çarpışma” dediği anda ekrandaki parçacıklar dağılacak. İlk denemede her şey doğru görünüyor. Sunucu yoğunlaşınca ses ilerliyor, görüntü geride kalıyor. Aynı sahneyi yeniden render edince parçacıklar bu kez başka yere düşüyor. Videoyu üretmiş olmakla, aynı girdiden denetlenebilir bir video üretim hattı kurmak arasındaki mesafe burada açılıyor.

Kendi YouTube render motorunuzu kurmak için tarayıcıda çalışan bir animasyonu ekran kaydına almak zorunda değilsiniz. Sahneyi veriyle tanımlar, sesi zaman damgalarına çevirir, her kareyi belirli bir medya zamanında üretir ve FFmpeg ile kodlarsınız. JavaScript görsel sahneyi ve zaman çizelgesini yönetirken Python ses analizi, içerik hazırlama veya Manim sahnelerini üstlenebilir.

Bu rehberin ana senaryosu, konuşmaya bağlı vurgular ve fizik olayları içeren bir anlatım videosudur. PixiJS, Three.js, Paper.js ve Manim’i görevlerine göre konumlandıracağız. Senkronizasyonun merkezinde tek bir medya saati, sabit fizik adımları ve açık event kimlikleri olacak. Son bölümde TankDev VPS’inde gerçekten ürettiğimiz 10 dakikalık sentetik test videosunun süre ve kaynak verilerini paylaşacağız.

1. Headless rendering: ekransız çalışmak, tarayıcısız çalışmak değildir

Puppeteer kullanan Node.js worker’ı gerçek bir Chromium süreci açar. DOM, Canvas, WebGL, font yükleme ve shader derleme hâlâ vardır; yalnızca kullanıcıya pencere gösterilmez. Python/Manim tarafı Cairo veya OpenGL renderer kullanabilir. Dolayısıyla “sunucuda render” tek bir teknoloji değil, farklı renderer süreçlerini ortak bir çıktı sözleşmesine bağlama biçimidir.

Gerçek zamanlı önizleme, mümkün olduğunca akıcı görünmeye çalışır. Çevrimdışı render ise 18.000 kare istenmişse 18.000 doğru kare üretmelidir; bunun duvar saatinde 4 veya 40 dakika sürmesi videonun zamanını değiştirmez. Bu ayrım yapılmadığında setTimeout, requestAnimationFrame, ses oynatıcısı ve fizik motoru birbirinden bağımsız saatler gibi davranır.

text
For a 10-minute video at 30 FPS:
N = duration_seconds * FPS = 600 * 30 = 18,000 frames
t(frame) = frame_index / FPS

Frame 900 always represents t = 30 seconds.
Its time does not depend on when the worker reached frame 900.
Wall-clock render throughput and output FPS are separate metrics.

Binlerce video ölçeğinde sorun yalnız çizim hızı değildir. Eksik asset, değişen font, kopan browser süreci, dolan geçici disk, tekrar kuyruğa giren iş ve bozuk MP4 için de karar gerekir. Render job’ı; girdileri hash ile sabitlenmiş, kaynak sınırı olan ve sonucu doğrulanmadan tamamlandı sayılmayan bir iş olmalıdır. Bu yüzden içerik üretimi, render ve yayın ayrı durum makineleri olarak tasarlanır.

2. Görsel katman: PixiJS, Three.js, Paper.js ve Manim nasıl seçilir?

“WebGL çok yüksek, Canvas orta performans” sıralaması bir benchmark değildir. Sahne karmaşıklığı, draw call, çözünürlük, blur/shadow filtreleri, texture boyutu ve ekran yakalama maliyeti sonucu değiştirir. Sunucuda WebGL bulunması fiziksel GPU kullanıldığını da kanıtlamaz; yazılımsal renderer devrede olabilir. Aşağıdaki tablo motorları iş yüküne göre ayırıyor.

Render motoru seçim matrisi
MotorTemel görevBackendÖnerilen sahneÖlçülecek darboğaz
PixiJS2D scene graph, sprite, parçacık ve efektWebGL/WebGPU; backend ve donanım doğrulanmalıÇok sayıda 2D öğe, altyazı/vurgu ve dinamik sahnelerDraw call, fill-rate, texture ve readback
Three.js3D geometri, ışık, kamera ve uzamsal sahneWebGLRenderer: WebGL 2; ayrıca WebGPU yolu varÜrün animasyonu, 3D veri ve kamera hareketiGeometri, material, ışık/gölge ve shader maliyeti
Paper.jsVektör geometri, path ve Bézier işlemleriCanvas çizimi; SVG import/exportAnimasyonlu çizgi, plan ve vektör dönüşümleriPath sayısı, geometri hesabı ve rasterizasyon
Manim CommunityMatematik, algoritma ve açıklayıcı sahnelerPython; Cairo veya OpenGL rendererFormül dönüşümleri, eğitim ve şema anlatımıSahne karmaşıklığı, metin/LaTeX ve seçilen renderer

PixiJS’in Application API’si async init ve manuel render kontrolünü sağlar. Three.js’in WebGLRenderer’ı 3D sahneler için WebGL 2 kullanır. Paper.js’in kendi açıklamasında ana backend Canvas’tır; SVG, ayrı bir import/export yeteneğidir. Manim’de renderer seçimi açık yapılır. Bunları tek videoda birleştirmek mümkündür; aynı runtime veya aynı scene graph içinde yaşamaları gerekmez.

Bu senaryoda PixiJS görsel katman, Matter.js fizik katmanı, GSAP zamana bağlı görsel özellikler için kullanılıyor. Matematik sahnesi gerektiğinde Manim ayrı bir artifact üretip kompozisyona katılır. Three.js veya Rapier’i sırf listeyi tamamlamak için çalıştırmıyoruz; o motorlar için burada karşılaştırmalı hız iddiası yok.

3. Sesin ara temsili: Whisper kelimelerini timeline verisine çevirmek

Önce kullanılacak son voiceover dosyası belirlenir. Metin aynı kalsa bile TTS modelinin başka sürümü farklı duraklama ve süre üretebilir. Zaman damgaları düzenlenmiş son sese ait olmalı; ses hash’i, sample rate, kanal sayısı ve duration manifestte tutulmalıdır. Ses sonradan kırpılır veya hızlandırılırsa timestamp dosyası da aynı dönüşümden geçirilir ya da yeniden üretilir.

Açık kaynak Whisper’da word_timestamps=True, kelime başlangıç ve bitiş tahminlerini segmentlerde döndürür. Kaynak kodu, bunun attention ve dynamic time warping temelli olduğunu açıklar. Değerler saniyedir. start × 1000 işlemi birim dönüşümüdür; tahmini sınırı milisaniye kesinliğine taşımaz. Bilinen metni sese daha sıkı bağlamak gereken işlerde forced alignment ve insan kontrolü ayrıca değerlendirilir.

python
import json, math, hashlib
from pathlib import Path
import whisper

audio_path = Path("voiceover-final.wav")
model = whisper.load_model("small")  # Pin package + model artifact in production.
result = model.transcribe(
    str(audio_path), language="tr", word_timestamps=True, fp16=False
)

words = []
for segment in result["segments"]:
    for raw in segment.get("words", []):
        start, end = float(raw["start"]), float(raw["end"])
        if not (math.isfinite(start) and math.isfinite(end) and 0 <= start <= end):
            raise ValueError("Invalid ASR timestamp")
        text = raw["word"].strip()
        if not text:
            continue
        words.append({"id": f"w{len(words):06d}", "text": text,
                      "startSec": start, "endSec": end,
                      "startMs": round(start * 1000), "endMs": round(end * 1000)})

manifest = {"schemaVersion": 1,
            "audioSha256": hashlib.sha256(audio_path.read_bytes()).hexdigest(),
            "timingSource": "whisper-word-estimate", "words": words}
Path("words.json").write_text(json.dumps(manifest, ensure_ascii=False, indent=2))

Kod entegrasyon örneğidir; aşağıdaki render deneyinde Whisper inference çalıştırmadık. Gerçek veri girişinde ayrıca duration üst sınırı, sıralama, beklenmeyen overlap ve izinli event eşlemeleri doğrulanmalıdır. Yuvarlanmış startMs yerine yüksek çözünürlüklü startSec asıl zaman olarak korunur; aksi halde her aşamada yeniden yuvarlama birikir.

Kelimeyi metniyle kimliklendirmeyiz. “Render, sonra yeniden render” cümlesindeki iki render farklı olaydır. Kelime ID’si ve sahne ID’si kararlı tutulur; normalization yalnız eşleme için kullanılır. Modelin ürettiği kelime veya talimattan doğrudan JavaScript fonksiyon adı çalıştırılmaz. Örneğin normalize edilmiş “çarpışma” kelimesi, önceden tanımlı burst olayıyla eşleştirilir.

json
{
  "schemaVersion": 1,
  "audioSha256": "sha256-of-final-audio",
  "fps": {"num": 30, "den": 1},
  "physicsHz": 120,
  "seed": 123456,
  "events": [
    {"id": "scene-01:w000017:burst", "type": "burst", "atSec": 1.237}
  ]
}

4. En kritik katman: kare zamanı, fizik tick’i ve GSAP seek

Çevrimdışı export’ta ana saat kare indeksidir: t = k / fps. 30 FPS’te bir kare yaklaşık 33,33 ms’dir. 1,237 saniyede başlayan bir vurgu en erken ceil(1,237 × 30) = 38. karede, yani 1,2667 saniyede görünür. Bu örnekte yalnız kareye oturtma farkı yaklaşık 29,7 ms’dir; ASR hatası, ses codec gecikmesi ve ekran oynatım gecikmesi bu hesabın dışındadır.

Fizik motorunu 120 Hz, videoyu 30 FPS çalıştırırsak kare başına dört fizik adımı alırız. Render worker yavaşlasa da deltaTime büyütülmez. Bu sabit adım collision çözümünü ve event sırasını kontrol edilebilir tutar. 29,97 FPS gibi rasyonel hızlarda num/den zaman tabanı kullanılır; “her karede sabit dört adım” varsayımı geçerli değildir. Accumulator, örnekleme/interpolasyon ve event sınırı aynı zaman tabanında tanımlanmalıdır.

GSAP timeline’ı paused olarak kurup mutlak zamana taşırız. seek API’sinde ikinci parametre suppressEvents’tir ve varsayılanı true’dur. Bu, export sırasında .call() callback’lerine güvenip fizik olayı başlatmayı kırılgan yapar. Callback’leri açmak da geriye seek veya replay sırasında aynı fizik etkisini tekrar uygulayabilir. Bu nedenle GSAP görsel property’leri örnekler; fizik olayları ayrı, ileri yönlü bir event ledger’dan tüketilir.

javascript
// Reference core: fps divides physicsHz; frames must be requested in order.
const physicsHz = 120;
const timeline = gsap.timeline({ paused: true });
const pulse = { alpha: 1 };
for (const word of words) {
  timeline.fromTo(pulse, { alpha: 0.35 },
    { alpha: 1, duration: 0.25, ease: "none", immediateRender: false },
    word.startSec);
}
gsap.ticker.sleep();

const events = words.map(w => ({ ...w,
  tick: Math.ceil(w.startSec * physicsHz - 1e-9)
})).sort((a,b) => a.tick - b.tick || a.id.localeCompare(b.id));
let tick = 0, nextEvent = 0, lastFrame = -1;

function consumeEvents() {
  while (nextEvent < events.length && events[nextEvent].tick <= tick) {
    const e = events[nextEvent++];
    applySeededBurst(e.id); // Stable RNG, predetermined bodies, no wall clock.
  }
}
function renderFrame(k) {
  if (k !== lastFrame + 1) throw new Error("Reset/replay required for seeking");
  const targetTick = k * physicsHz / fps;
  while (tick < targetTick) {
    consumeEvents();
    Matter.Engine.update(engine, 1000 / physicsHz);
    tick++;
  }
  consumeEvents();
  timeline.seek(k / fps, true); // Visual sampling, callbacks suppressed.
  gsap.ticker.sleep();
  visualLayer.alpha = pulse.alpha;
  syncSpritesFromBodies();
  app.renderer.render(app.stage);
  lastFrame = k;
}

GSAP ile fizik motoruna aynı objenin x/y alanlarını aynı anda yazdırmayız. Fizik gövdesinin konumu bir child container/sprite’a, kamera hareketi veya opacity gibi GSAP özellikleri üst katmana ait olabilir. “Kim bu property’nin sahibi?” sorusu burada da önemlidir. Timeline’ı 10. saniyeden 3. saniyeye çekmek fizik dünyasını geri sarmaz; motor state’i checkpoint’ten yüklenmeli veya aynı seed ve olaylarla yeniden simüle edilmelidir.

PixiJS tarafında autoStart:false ve ayrı ticker kullanır, otomatik render döngüsünü durdururuz. Three.js için de export döngüsünü uygulama çağırır; gerçek zamanlı önizlemenin animation loop’u export saatini belirlemez. Asset ve fontların hazır olduğuna dair açık bariyer gerekir. “Sayfa açıldıktan sonra iki saniye bekle” yaklaşımı, farklı makinede eksik font veya texture yakalayabilir.

5. Tone.js: gerçek zamanlı ses saati ile offline ses buffer’ı

Tarayıcıdaki önizlemede Web Audio saati ses olayları için referans olabilir. Tone callback’inin verdiği time değeri audio node’una aktarılır; görsel güncelleme aynı ses zamanına uygun draw mekanizmasıyla eşlenir. UI callback’i içinde Date.now okuyup ses çalmak aynı doğruluğu vermez. Etkileşimli tarayıcıda audio context başlatmak kullanıcı hareketi gerektirebilir; bunu headless export döngüsüne sahte tıklama ekleyerek çözmeye çalışmayız.

Export’ta en temiz seçenek, son voiceover’ı dosya olarak tutmak ve efektleri de offline buffer’a üretmektir. Tone.Offline bu callback içindeki node’ları OfflineAudioContext üzerinden üretir. GSAP seek’in audio context’i ilerletmesini beklemeyiz. Görsel event ve ses olayı aynı manifestteki mutlak saniyeden türetilir, sonra FFmpeg bunları zaman damgalarıyla birleştirir.

javascript
const effects = await Tone.Offline(() => {
  const synth = new Tone.Synth().toDestination();
  for (const event of events) {
    synth.triggerAttackRelease("A4", 0.05, event.atSec);
  }
}, durationSec, 1, 48000);

// effects is an audio buffer, not a WAV file by itself.
// Encode PCM/WAV explicitly, or pass samples through a controlled encoder.
// Keep narration and effects in one agreed sample rate/channel layout.

48 kHz seste bir örnek yaklaşık 20,83 mikrosaniyedir. Bu çözünürlük Whisper’ın kelime tahminini daha doğru yapmaz; yalnızca audio event’in hangi sample’a yerleşeceğini belirler. Saat farklarını saklamamak gerekir: t=0’dan önceki sessizlik, TTS’nin başındaki padding, sonradan eklenen intro ve müzik fade’i manifestte açık offset olmalıdır.

Uzun videonun tamamını stereo Float32 buffer’da tutmak pahalıdır. 600 saniye × 48.000 sample/s × 2 kanal × 4 byte yaklaşık 230,4 MB eder; buna browser, texture ve encoder belleği eklenir. Uzun ses için chunk’lı üretim veya dosya tabanlı mix daha uygun olabilir. Chunk sınırlarında delay/reverb kuyruğu ve overlap yönetilmezse ses sürekliliği bozulabilir.

6. Matter.js ve Rapier: fizik performansı kadar tekrar üretilebilirlik

Fizik motorunu gereksinime göre seçmek
MotorBoyut / runtimeEntegrasyonGüçlü aday olduğu durumÖlçüm ve garanti sınırı
Matter.jsJavaScript, 2DEngine.update ile açık adım; Canvas/Pixi’den bağımsız2D kutu, parçacık, collision ve hızlı prototipGövde/constraint sayısını ölç; sabit delta tek başına cross-platform bitwise garanti değildir
RapierWASM tabanlı 2D ve 3D paketleriAsync WASM hazırlığı, world/collider ve farklı API3D veya daha kapsamlı rigid-body gereksinimiWASM otomatik hız üstünlüğü değildir; sürüm/başlangıç/input koşulları sabitlenmeli

Matter.js’in Engine.update çağrısına delta milisaniye olarak verilir. Runner’ın duvar saatine bağlı döngüsü yerine export step’ini uygulama yönetir. Gövde sayısı kadar collision pair sayısı, solver iteration, sleeping, constraint ve scene ölçeği de önemlidir. Aynı görünüm için daha az fizik gövdesi, bazen motor değiştirmekten daha büyük kazanç sağlar.

Rapier’ın JavaScript determinism belgesi aynı sürüm, aynı başlangıç koşulları ve girdilerle platformlar arası deterministik simülasyonu tanımlar. Bu koşulları uygulamanın da koruması gerekir. Kontrolsüz random, değişen asset geometrisi veya başlangıç verisini platforma bağlı hesaplamak zinciri bozabilir. Fizik sonucu deterministik olsa bile farklı GPU/font/codec zincirinde aynı MP4 byte’larını elde etmek ayrı bir gereksinimdir.

7. Puppeteer → FFmpeg: kare kaybetmeden MP4 üretmek

Reference worker her kare için önce renderFrame(k) çağırır, sonra tam boyutta PNG alır ve FFmpeg’in image2pipe girişine yollar. PNG aktarımı kayıpsız ara temsildir, fakat encode/readback maliyeti vardır. Screenshot yolunu otomatik olarak en hızlı yöntem saymayız; raw RGBA, doğrudan frame extraction veya başka encoder yolları aynı sahnede ayrıca kıyaslanabilir. Ölçtüğümüz yol burada açıkça Puppeteer PNG screenshot’tır.

javascript
const encoder = spawn("ffmpeg", [
  "-hide_banner", "-loglevel", "error", "-y",
  "-f", "image2pipe", "-framerate", String(fps), "-i", "pipe:0",
  "-an", "-c:v", "libx264", "-preset", "veryfast", "-crf", "23",
  "-threads", "1", "-pix_fmt", "yuv420p", "-movflags", "+faststart",
  "render.pending.mp4"
], { stdio: ["pipe", "ignore", "pipe"] });

for (let k = 0; k < frameCount; k++) {
  await page.evaluate(k => window.renderFrame(k), k);
  const png = await page.screenshot({
    type: "png", clip: { x: 0, y: 0, width, height }, captureBeyondViewport: false
  });
  if (!encoder.stdin.write(png)) await once(encoder.stdin, "drain");
}
encoder.stdin.end();
// Await exit, fail on nonzero status, then ffprobe + decode validation.
// Kill child processes on cancellation/error; publish only verified artifacts.

stdin.write false döndüğünde drain beklemek backpressure’dır. Bunu atlayıp binlerce PNG’yi RAM’e biriktirmek throughput optimizasyonu değildir. Üretici/encoder dengesi, maksimum in-flight kare ve iptal davranışı belirlenir. FFmpeg nonzero exit, browser context kaybı veya son kare eksikliği job’ı başarısız yapmalıdır; yalnız .mp4 uzantılı dosya bulunması yeterli değildir.

FFmpeg’de giriş -framerate karelerin medya zamanını tanımlar; çıkışa rastgele -r eklemek yanlış zaman tabanını sonradan onarmanın yolu değildir. CRF kalite/bitrate davranışını, preset encode eforunu etkiler. GPU encoder kullanmak browser çizimi veya CPU readback darboğazını otomatik çözmez. FFmpeg dokümantasyonu ve kurulu binary’nin help/encoder seçenekleri birlikte okunmalıdır.

bash
ffmpeg -i render.pending.mp4 -i voiceover-final.wav   -map 0:v:0 -map 1:a:0 -c:v copy -c:a aac -b:a 128k -ar 48000   -t 600 -movflags +faststart final.pending.mp4

ffprobe -v error -show_streams -show_format -of json final.pending.mp4
ffmpeg -v error -i final.pending.mp4 -f null -

Buradaki 600 saniye örnek bir sözleşmedir; ses süresi farklıysa körlemesine -t ile kesmek veya -shortest ile videoyu kısaltmak yerine ürünün trim/pad politikasını uygularız. Finalde frame count, rasyonel FPS, çözünürlük, video/audio süreleri, kanal ve sample rate kontrol edilir. AAC gibi codec’lerin priming/padding etkileri container metadata’sıyla değerlendirilir; her stream duration alanının birebir aynı olacağı varsayılmaz.

8. Manim ile JavaScript sahnelerini aynı projede birleştirmek

Python ile JavaScript arasında canlı scene graph paylaşmak yerine artifact sözleşmesi kurarız. Manim matematik sahnesini alpha taşıyan kare dizisi veya uygun ara formatta üretir. JavaScript sahnesi de kendi katmanını verir. Kompozisyon aşamasında çözünürlük, FPS, başlangıç PTS’i, süre ve alpha yorumu eşleştirilir. Karmaşık formül dönüşümünü Manim’e bırakıp UI/parçacık katmanını PixiJS’de tutmak çoğu zaman daha anlaşılır bir sınırdır.

bash
# Manim Community CLI: verify flags against the pinned version.
manim render --renderer=cairo --resolution=1920,1080 --fps=30   --format=png-sequence --transparent math_scene.py FormulaScene

# Composite compatible intermediates; overlay disappears after its own end.
ffmpeg -i js-base.mp4 -i math-alpha.mov   -filter_complex "[1:v]setpts=PTS-STARTPTS,format=rgba[math];[0:v][math]overlay=eof_action=pass:repeatlast=0[v]"   -map "[v]" -map 0:a? -c:v libx264 -crf 18 -pix_fmt yuv420p composite.mp4

PNG dizisi ile MOV burada iki alternatif ara yoldur; ilk komut ikinci komutun MOV dosyasını üretmez. Güncel Manim CLI belgesi --format=png ile yalnız son karenin, --format=png-sequence ile tüm karelerin yazıldığını ayırır. Alpha MOV kullanacaksanız uygun codec/pixel format ve süre sözleşmesi ayrıca seçilmelidir. Bir ara formatın FFmpeg tarafından okunması, Chromium VideoTexture tarafından da okunacağı anlamına gelmez.

Premultiplied/straight alpha karışıklığı kenarlarda koyu hale, farklı renk uzayı ise kompozitte tutarsızlığa yol açabilir. Düşük çözünürlüklü bir proof sahnesinde kenar, font ve renk kontrolü yapılır. Sadece dosyaların açılması kabul kriteri değildir. Bu yazının ölçüm koşusunda Manim, Three.js, Paper.js veya Rapier çalıştırılmadı; onların render süreleri için sonuç çıkarmıyoruz.

9. Üretim hattı: kuyruk, manifest, doğrulama ve yayın

mermaid
flowchart LR
    A["Approved script"] --> B["TTS / final voiceover"]
    B --> C["Whisper word estimates"]
    C --> D["Validated timing manifest + asset hashes"]
    D --> Q["Render queue / bounded workers"]
    Q --> J["Headless Chromium: Pixi / Three / Paper + GSAP"]
    Q --> M["Python / Manim artifact"]
    J --> F["Ordered frames + FFmpeg encoding"]
    M --> F
    B --> X["Audio mix and mux"]
    F --> X
    X --> V["ffprobe + decode + sync checks"]
    V --> O["Immutable artifact / review"]
    O --> P["Separate publish job"]

Manifest; içerik/asset hash’leri, ses ve timing sürümü, renderer/browser paketleri, fontlar, seed, çözünürlük, rasyonel FPS ve encoder ayarlarını taşır. Render kimliği bu doğrulanmış girdilerden türetilir. Aynı kimlikle tekrar denenen iş aynı hedef artifact kaydına bağlanır; başarısız .pending dosyası hazır video diye yayınlanmaz. Yüksek hacimde işler küçük batch’ler halinde kabul edilir, tenant ve eşzamanlı browser sınırı korunur.

Fiziği bağımsız chunk’lara bölmek için de state gerekir. Her chunk’ı seed ile sıfırdan başlatmak önceki çarpışmaları kaybettirir. Tam state snapshot’ı, gerekli preroll veya başlangıçtan deterministik replay seçilir. Stateless bir sahnenin analitik konum hesabı ile stateful fizik sahnesi aynı dağıtım stratejisine sahip değildir. Chunk birleştirmede kare ve audio sample sınırları ayrıca uzlaştırılır.

Render worker’a dışarıdan keyfi JavaScript, Python veya FFmpeg shell komutu verilmez. İzinli template ve veri manifesti kullanılır; asset indirmeleri allowlist, boyut ve tür kontrollerinden geçer. Ayrı OS kimliği, kaynak/time limitleri ve ağ izolasyonu gerekir. BrowserContext tek başına güçlü işletim sistemi izolasyonu değildir. Untrusted içerikte browser sandbox’ını kapatmak standart çözüm olamaz.

TankDev’in Otonom AI Video Stüdyosu yaklaşımında video üretimi; içerik, medya üretimi, render ve işletim akışının birlikte tasarlanmasıdır. Uzun işlerin queue/retry tarafını asenkron worker rehberimiz, yayın gibi dış etkilerin tekrarını ise idempotent API/outbox blueprint’imiz tamamlar. Render’ın bitmesi, dış platforma yüklemenin güvenle yeniden denenebilir olduğu anlamına gelmez.

10. TankDev render laboratuvarı: gerçekten üretilmiş 10 dakikalık MP4

5 Kasım 2026 Türkiye saatiyle kendi VPS’imizde bir 6 saniyelik pilot ve ardından tek bir 600 saniyelik render çalıştırdık. Bu, anlatımlı bir müşteri videosu değil; 64 fizik gövdesi, sentetik kelime olayları ve 880 Hz kısa ses darbeleri içeren kontrollü bir fixture. Whisper, TTS, Manim, Three.js, Paper.js, Rapier, dış API ve YouTube yüklemesi bu ölçümde çalıştırılmadı. Kaynak kodu, ham kare sürelerini ve MP4’ü aşağıda paylaşıyoruz.

Ölçüm koşulları — kapasite taahhüdü değildir
ParametreDeğer
Host2 logical CPU; 8.326.615.040 byte RAM
Worker sınırıCPUQuota=75%: 0,75 CPU çekirdeği bütçesi; MemoryMax=1536 MiB
Görüntü640 × 360, 10 FPS, 600 saniye, 6.000 kare
Sahne64 Matter.js gövdesi, 120 Hz fizik, seed=123456
Grafik backendWebGL 2 / ANGLE SwiftShader; yazılımsal, dedicated GPU yok
PaketlerNode 22.23.2; Chrome 142.0.7444.175; Puppeteer 24.31.0; PixiJS 8.14.0; GSAP 3.13.0; Matter.js 0.20.0; Tone.js 15.1.22
EncoderFFmpeg 6.1.1; libx264, veryfast, CRF 23, threads=1, yuv420p
Tek uzun koşunun sonucu
MetrikÖlçülen değer
Video render duvar süresi447,333 saniye
Capture döngüsü446,289 saniye; 13,444 üretilen kare/saniye
Kare döngüsü p50 / p9582,389 ms / 100,163 ms; 6.000 kare, nearest-rank
Cgroup CPU tüketimi334,362 CPU-saniye
100 ms aralıkla örneklenen cgroup RAM zirvesi400,684 MiB
Unit başlangıcından beri kernel memory.peak420,078 MiB; pilotu da kapsayan kümülatif sayaç
Ses mux süresi2,915 saniye; video render süresine dahil değil
Son MP411.426.242 byte; 600,000 saniye; 6.000 H.264 kare; AAC mono 48 kHz

447,333 saniyelik sayaç; yeni sayfanın açılması, sahne kurulumu, 6.000 screenshot’un encode pipe’a aktarılması ve encoder’ın bitmesini içerir. Chrome sürecinin ilk açılması ve paket kurulumu dışarıdadır; browser pilot sonrasında tekrar kullanıldı. Sonraki sentetik WAV üretimi, ses mux’u ve tam decode doğrulaması da bu sayaca dahil değildir. Kare p95’i, simülasyon + screenshot + pipe beklemesinin tek koşu içindeki dağılımıdır; farklı render işlerinin p95 tamamlanma süresi değildir.

ffprobe, son dosyada 600,000 saniye, 6.000 video karesi ve 10/1 FPS doğruladı; FFmpeg ile dosyanın tamamı hatasız decode edildi. Aynı build içinde başlangıçtan tekrar simüle edilen fizik durumu eşleşti; geriye seek reddedildi. Ayrı Tone.Offline smoke testinde 48.000 sample üretildi ve 0,5 saniyede planlanan sesin ilk sıfır dışı sample’ı 24.001 oldu. Bunlar; tarayıcılar arası bit-identical video, insan konuşmasına hizalama doğruluğu veya algısal A/V kalite testi anlamına gelmez.

Bu düşük çözünürlüklü tek koşuyu 1080p/30 FPS üretim kapasitesi diye okumayın. 1920×1080×30, 640×360×10’a göre saniyede 27 kat piksel içerir; süre veya RAM’in tam 27 kat olacağını söyleyemeyiz. Shader, ekran okuma, PNG sıkıştırma ve codec farklı darboğazlara dönüşebilir. Kapasite için hedef sahnelerle tekrarlar, concurrency artışları ve üretim SLO’su ayrıca ölçülür.

Test worker’ı ayrı dinamik OS kullanıcısıyla, private network, salt okunur sistem, süre ve RAM/CPU sınırları altında çalıştı. Bu yalnız kendi yerel fixture’ımızdı. Bu deneyde Chrome sandbox’ı kapalıydı; indirilen kaynak bu gerçeği saklamaz. Dışarıdan alınan sahne veya script’leri bu ayarla çalıştırmak için örnek sayılmamalıdır. Untrusted render için browser sandbox’ı ve uygun container/VM izolasyonu ayrıca tasarlanmalıdır.

11. Render maliyeti: sayaç ile muhasebe varsayımını ayırmak

VPS’in işletme tarafından bildirilen bedeli 100 USD/ay. 30 günlük ay varsayımında, tek işin 447,333 saniyelik video render süresine sunucunun tamamını ayıran zaman payı 100 × 447,333 / 2.592.000 ≈ 0,01726 USD olur. Bu, sağlayıcının iş başına kestiği fatura veya ölçülmüş marjinal maliyet değildir. Worker’ın CPU kotası yüzde 75 olduğu için tutarı otomatik yüzde 75 ile çarpmak da doğru değildir; sabit faturada bellek, disk ve boşta bekleme de vardır.

text
Fixed-host allocation per successful published video
  = monthly_host_bill * assigned_fraction / successful_videos_per_month

Example assumptions, NOT measured production volumes:
  $100 * 1.0 / 1,000 successful videos = $0.10/video (host only)
  $100 * 1.0 /   100 successful videos = $1.00/video (host only)

Total cost adds TTS/ASR/model use, storage, egress,
failed attempts, monitoring, maintenance, and review.
Neither example establishes that the host can sustain that volume.

Asıl işletim metriği başarılı ve kabul edilmiş video başına maliyettir. Retry sayısı, queue wait, render real-time factor, CPU-saniye/video, RAM zirvesi, geçersiz çıktı oranı ve yayın tekrarları birlikte izlenir. Kuyrukta bir saat bekleyen işin 7,5 dakikada render edilmesi kullanıcıya 7,5 dakikalık teslimat değildir. Üretim kabulünü yalnız process exit code’una bağlamak yerine frame/sample sayısı, asset/font hazır oluşu, clipping, renk profili ve örneklenmiş insan kalite kontrolüyle tamamlayın.

12. Kopyalanabilir kaynaklar ve üretime geçiş

Test sonucu JSON, ham kare ölçümleri, çalıştırılabilir referans kaynak paketi ve gerçek 10 dakikalık test MP4’ü aynı deneyin parçalarıdır. Paket; kaynakları, dependency lockfile’ını ve çalıştırma koşullarını içerir. Makalenin Markdown sürümü karar tablolarını, Mermaid kaynağı ise mimari akışı kopyalamak içindir. Kaynağı kullanırken sürüm ve test sınırlarını da taşıyın.

TankDev’de bu hattı tek bir animasyon demosu olarak ele almıyoruz. İçerik sözleşmesi, ses/görsel varlıkların sahipliği, frame clock, worker sınırları ve teslimat durumu birlikte tasarlanır. Bir sonraki adım, hedef video tipiniz için kabul örneklerini ve kapasite bütçesini belirlemektir. Video otomasyon sistemi kapsamını inceleyebilir veya projenizin üretim koşullarını bizimle paylaşabilirsiniz. Render motorunun değeri, kaç kütüphane kullandığında değil; aynı işi hangi koşullarda doğru tamamlayabildiğini gösterebilmesindedir.

Sık Sorulan Sorular

01Programatik video render motoru nedir?

Sahne verisini, medya saatini ve asset’leri alıp kareleri otomatik üreten; sesi birleştirip doğrulanmış video artifact’ı çıkaran bir iş hattıdır. Gerçek zamanlı ekran kaydı yerine her kareyi tanımlı medya zamanında üretir.

02Headless rendering için tarayıcı gerekir mi?

Puppeteer kullanıyorsanız gerçek Chromium çalışır; yalnız görünür pencere yoktur. Python/Manim farklı renderer’larla tarayıcı gerektirmeden çalışabilir. Headless sözcüğü tüm render yollarının browserless olduğu anlamına gelmez.

03Whisper kelime zamanlarını milisaniye doğruluğunda verir mi?

word_timestamps tahmini kelime başlangıç/bitişlerini saniye olarak sağlar. Binle çarpmak gösterim birimini değiştirir, doğruluğu artırmaz. Kritik vurgu için gerçek ses üzerinde hizalama kontrolü ve gerekirse forced alignment gerekir.

04GSAP callback’leriyle fizik tetiklemek neden sorun olabilir?

Seek, replay ve callback suppression olayları atlayabilir veya yineleyebilir. Sabit fizik tick’i, kararlı event kimliği ve işlenmiş olay ledger’ı kullanın. Geriye gitmek için fizik state’i geri yüklenmeli veya başlangıçtan tekrar simüle edilmelidir.

05Tone.js ile offline ses ve görüntü nasıl eşitlenir?

İkisi de aynı medya başlangıcını ve saniye cinsinden timeline’ı kullanmalıdır. Görüntü k/FPS ile, ses sample/sampleRate ile adreslenir. Tone.Offline sesi ayrı üretir; ses cihazının gerçek zaman saati render döngüsünü yönetmez.

06Matter.js mi Rapier mi seçilmeli?

Basit 2D ve doğrudan JavaScript entegrasyonunda Matter.js uygun olabilir. 3D veya Rapier’in özellikleri ve determinism sözleşmesi gerektiğinde Rapier değerlendirilir. WASM etiketi tek başına hız kanıtı değildir; hedef sahne ölçülmelidir.

07Manim ile JavaScript kütüphaneleri aynı projede nasıl birleştirilir?

Manim çıktısını açık süre, FPS, çözünürlük ve alpha sözleşmesine sahip clip veya kare dizisi olarak üretin. JavaScript sahnesiyle FFmpeg compositing aşamasında birleştirin. İki runtime’ı tek canlı sahne ağacı gibi varsaymayın.

08Programatik video üretiminde FFmpeg optimizasyonu nasıl yapılır?

Önce simülasyon, screenshot, pipe beklemesi ve encode sürelerini ayırın. Preset, CRF, thread ve codec’i hedef kaliteye göre ölçün; stdin backpressure uygulayın. Donanım encoder’ı kullanmak WebGL çizimini otomatik hızlandırmaz.

09Bu deneyde 10 dakikalık video ne kadar sürede üretildi?

640×360 ve 10 FPS’de 6.000 kare, yazılımsal WebGL ile 447,333 saniyede render edildi. Sonraki ses mux’u 2,915 saniyedir. Tek koşudur; Whisper/TTS, paket kurulumu, ilk browser açılışı ve yayın dahil değildir.

100,01726 USD gerçek video maliyeti mi?

Bu, 100 USD/ay sabit sunucu bedelini 30 günlük aya ve tek render’ın duvar süresine bölen muhasebe payıdır. Toplam veya marjinal maliyet değildir; API, depolama, egress, başarısız işler ve işletim emeği ayrıca hesaplanır.

11Aynı seed aynı MP4 byte’larını garanti eder mi?

Hayır. Seed fizik girdilerinin bir parçasıdır. Paket sürümleri, başlangıç state’i, fontlar, renderer, renk yönetimi ve encoder da etkilidir. Aynı build fizik replay testi, platformlar arası bit-identical MP4 garantisi değildir.

12TankDev bu sistemi mevcut içerik akışına bağlayabilir mi?

Desteklenen API ve medya hakları kapsamında içerik hazırlama, ses, render worker’ları, doğrulama ve teslimat entegrasyonu tasarlayıp geliştirebiliriz. Kapsam; hedef sahne, kalite kabulü, hacim, bütçe ve platform yetkileriyle belirlenir.

İlgili notlar

WhatsAppDoğrudan iletişim