<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged ffmpeg</title>
<link>https://coderlegion.com/tag/ffmpeg</link>
<description>Powered by Question2Answer</description>
<item>
<title>How I Built My Own Opus Clip Alternative With Claude Code</title>
<link>https://coderlegion.com/32660/how-i-built-my-own-opus-clip-alternative-with-claude-code</link>
<description>&lt;p&gt;Opus Clip, Submagic and Vidyo all work the same way: you upload a long video, the AI scores every moment for &quot;virality&quot;, and you get the clips it likes. That is fine until you want something specific. When I am clipping a podcast for a client I don't want &quot;the most viral 30 seconds&quot;. I want &quot;every time they talk about pricing&quot; or &quot;the strongest hook in the first ten minutes&quot;.&lt;/p&gt;
&lt;p&gt;So I built my own Opus Clip alternative, called Hive Clips AI, with Claude Code. You give it a long video and a plain English prompt, and it transcribes the video, uses an LLM to find the moments that match your prompt, reframes each clip to keep the speaker in shot, burns in word-by-word captions and renders vertical clips ready to post. The prompt drives the selection, not a fixed score. That is the whole difference.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danishsofi.in/images/blog/hive-clips/home.webp&quot; alt=&quot;Hive Clips AI home screen with a video upload area and a YouTube URL field&quot;&gt;&lt;br&gt;
&lt;em&gt;The Hive Clips AI home screen. Drop in a long video or paste a YouTube link.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Here is how it works, stage by stage, and what I would tell you before you try building one.&lt;/p&gt;
&lt;h3 id=&quot;what-does-an-ai-clipping-tool-actually-do-&quot;&gt;What does an AI clipping tool actually do?&lt;/h3&gt;
&lt;p&gt;Strip the marketing away and every clipping tool is the same nine-step pipeline. I built mine so each step runs on its own and saves its result, which matters later.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Stage &lt;/th&gt;&lt;th&gt;What happens &lt;/th&gt;&lt;th&gt;What I used&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;1. Ingest &lt;/td&gt;&lt;td&gt;Upload a file or paste a YouTube link &lt;/td&gt;&lt;td&gt;yt-dlp, FFmpeg&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2. Transcribe &lt;/td&gt;&lt;td&gt;Speech to text with a timestamp on every word &lt;/td&gt;&lt;td&gt;Whisper API, or faster-whisper locally&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3. Select &lt;/td&gt;&lt;td&gt;Find moments that match your prompt &lt;/td&gt;&lt;td&gt;GPT-4o with structured JSON output&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;4. Review &lt;/td&gt;&lt;td&gt;You pick which candidates to keep &lt;/td&gt;&lt;td&gt;Next.js UI&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;5. Configure &lt;/td&gt;&lt;td&gt;Choose aspect ratio and caption style &lt;/td&gt;&lt;td&gt;Next.js UI&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;6. Reframe &lt;/td&gt;&lt;td&gt;Track the face and move a virtual camera &lt;/td&gt;&lt;td&gt;OpenCV, PySceneDetect&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;7. Caption &lt;/td&gt;&lt;td&gt;Build animated word-level captions &lt;/td&gt;&lt;td&gt;ASS subtitles, libass&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;8. Render &lt;/td&gt;&lt;td&gt;Crop, burn captions, encode &lt;/td&gt;&lt;td&gt;FFmpeg&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;9. Deliver &lt;/td&gt;&lt;td&gt;Play, download or regenerate &lt;/td&gt;&lt;td&gt;FastAPI + Next.js&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The backend is Python with a FastAPI wrapper. The frontend is Next.js 15 with TypeScript and Tailwind. There is also a command line version, which is where I proved the whole pipeline worked before touching the UI.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danishsofi.in/images/blog/hive-clips/pipeline.webp&quot; alt=&quot;Diagram of the nine Hive Clips AI pipeline stages from ingest to deliver&quot;&gt;&lt;br&gt;
&lt;em&gt;The nine stages. Select and Reframe are where most of the work went.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;how-does-the-ai-pick-the-right-moments-&quot;&gt;How does the AI pick the right moments?&lt;/h3&gt;
&lt;p&gt;This is the part people assume is magic. It is mostly careful plumbing.&lt;/p&gt;
&lt;p&gt;A one hour podcast transcript is tens of thousands of tokens. Sending all of it in one request gives you worse picks, so the transcript is split into 9 minute windows with a 15 second overlap. The overlap is there so a great moment that sits across a window boundary doesn't get cut in half.&lt;/p&gt;
&lt;p&gt;Each window goes to GPT-4o with the creator's prompt and a strict instruction: return 2 to 5 clips, each between 20 and 90 seconds, and only clips that genuinely match the intent. The model also rates each clip's hook from 1 to 10, judged on the first three seconds, because that is what decides whether someone stops scrolling.&lt;/p&gt;
&lt;p&gt;The output is forced into a JSON schema, so I never have to parse loose text:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;clips&quot;: [
    {
      &quot;start_time&quot;: 412.3,
      &quot;end_time&quot;: 468.9,
      &quot;title&quot;: &quot;Why he stopped charging hourly&quot;,
      &quot;reasoning&quot;: &quot;Direct answer about pricing with a strong opening line&quot;,
      &quot;hook_strength&quot;: 8
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After every window is done, the candidates get merged and re-ranked, and you see them in the review screen. Nothing renders until you pick. That review step is the thing I missed most in the paid tools.&lt;/p&gt;
&lt;h3 id=&quot;why-is-auto-reframing-the-hardest-part-&quot;&gt;Why is auto-reframing the hardest part?&lt;/h3&gt;
&lt;p&gt;Turning a 16:9 video into a 9:16 clip means cropping a narrow window out of a wide frame and moving that window to follow the speaker. The obvious way is to detect the face on every frame and centre the crop on it.&lt;/p&gt;
&lt;p&gt;That looks terrible. Face detection is noisy, so the crop shakes constantly, and when the video cuts to a different camera the crop slides across the cut like a drunk camera operator.&lt;/p&gt;
&lt;p&gt;The fix is to treat the crop as a virtual camera with physics, not a box that snaps to the face:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A critically damped spring.&lt;/strong&gt; The camera moves toward the face but never overshoots. I ported Unity's SmoothDamp function for this. Smooth time is 0.55 seconds, which feels calm without lagging behind.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A deadzone.&lt;/strong&gt; If the face moves less than 6% of the crop width, the camera ignores it. This alone kills most of the jitter.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A speed limit.&lt;/strong&gt; Pans have a maximum speed, so a false detection can't whip the frame across the screen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scene cut resets.&lt;/strong&gt; PySceneDetect finds the hard cuts. At a cut the camera drops its speed and repositions instead of carrying a pan across the edit. Between cuts it must move smoothly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Headroom.&lt;/strong&gt; The subject sits about 10% above centre, the way a real camera operator would frame a person.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The camera path becomes a crop track that FFmpeg applies during the render. Face detection itself is a basic OpenCV detector that needs no model download. I kept the smoothing separate from detection on purpose, so I can swap in a better detector later without touching the part that makes it look good.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danishsofi.in/images/blog/hive-clips/chart.webp&quot; alt=&quot;Chart comparing noisy face detection points with the smooth virtual camera path&quot;&gt;&lt;br&gt;
&lt;em&gt;Real output from the Hive Clips smoother on noisy test data. The camera ignores small jitter and makes one calm pan when the speaker moves.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danishsofi.in/images/blog/hive-clips/canvas.webp&quot; alt=&quot;Hive Clips AI configure screen with output canvas options 9:16, 1:1, 4:5, 16:9 and 4:3&quot;&gt;&lt;br&gt;
&lt;em&gt;Picking the output canvas. 9:16 for Reels, Shorts and TikTok, with a live preview on the right.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you build one of these, spend your time on the smoothing, not the detector. The smoothing is what makes it look professional.&lt;/p&gt;
&lt;h3 id=&quot;how-do-the-word-by-word-captions-work-&quot;&gt;How do the word-by-word captions work?&lt;/h3&gt;
&lt;p&gt;Most tutorials use SRT subtitles. SRT can't do the captions people actually want on Shorts and Reels, where each word lights up as it is spoken.&lt;/p&gt;
&lt;p&gt;I used ASS subtitles instead. ASS supports per-word timing, colours and animation, and FFmpeg burns it into the video through libass. Because the transcript has a timestamp for every word, each word can change colour exactly when it is said.&lt;/p&gt;
&lt;p&gt;The tool ships with 24 caption presets. The original 8 cover the basics: a karaoke style, a minimal style, a news bar and an outline style. Then I added 16 styles based on what is working on short-form right now, including a Hormozi-style highlight where the full line shows and the active word recolours, a one-word-at-a-time punch style, and a word reveal where words pop in and stay.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danishsofi.in/images/blog/hive-clips/captions.webp&quot; alt=&quot;Hive Clips AI caption style presets including Bold Pop, Karaoke Classic, Hive Yellow and Hormozi Green&quot;&gt;&lt;br&gt;
&lt;em&gt;Part of the caption preset grid. Each preset is a starting point you can fine tune per clip.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;what-broke-while-building-it-&quot;&gt;What broke while building it?&lt;/h3&gt;
&lt;p&gt;A few honest notes, because build logs that only show the happy path aren't useful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The web app broke when the CLI didn't.&lt;/strong&gt; When I added caption overrides (custom colours and sizes per clip), the command line kept working but the web API failed. The database was missing the new column. The fix was a one-line migration, but it taught me to test both entry points every time the data model changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Saving every stage paid off.&lt;/strong&gt; Each of the nine stages writes its result and a job record to the database. So when a render fails, I re-run the render only. I don't wait for the transcription again or pay for the AI selection twice. On a long podcast that is the difference between seconds and many minutes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mock mode made testing free.&lt;/strong&gt; Without an OpenAI key, the tool runs with a stand-in transcript and a simple moment picker, but reframing, captions and rendering are fully real. I could test the hardest parts of the pipeline all day without spending anything on API calls. The UI labels mock mode clearly so stand-in output is never mistaken for real picks.&lt;/p&gt;
&lt;h3 id=&quot;how-did-claude-code-fit-in-&quot;&gt;How did Claude Code fit in?&lt;/h3&gt;
&lt;p&gt;I wrote a product spec first: the nine stages, the five screens, and the rules for each step. Then I built it with Claude Code one stage at a time, starting from the command line and proving each stage worked before moving on.&lt;/p&gt;
&lt;p&gt;Two habits made the biggest difference:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Interfaces before infrastructure.&lt;/strong&gt; Storage, the job queue, the database and the AI provider each sit behind a simple interface. Right now it runs on SQLite, local files and a local queue. Moving to Postgres, Cloudflare R2 and a proper job queue means swapping one piece at a time, not rewriting the app.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pure maths in its own file.&lt;/strong&gt; The camera smoother has no video code in it at all. That made it fast to test and easy for Claude Code to reason about, which is exactly where you want precision.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you are new to building with Claude Code, my &lt;a rel=&quot;nofollow&quot; href=&quot;https://danishsofi.in/blog/how-to-use-claude-code-to-build-app-website&quot;&gt;beginner's guide to building an app with Claude Code&lt;/a&gt; covers the setup, and &lt;a rel=&quot;nofollow&quot; href=&quot;https://danishsofi.in/blog/claude-code-pricing-free-options-2026&quot;&gt;Claude Code pricing and free options&lt;/a&gt; covers what it costs to run.&lt;/p&gt;
&lt;h3 id=&quot;what-is-next-for-the-tool-&quot;&gt;What is next for the tool?&lt;/h3&gt;
&lt;p&gt;The current build is the full pipeline plus the web UI. On the list:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sharper cut handling. Right now the reposition at a cut uses the last face position from before the cut, so on a hard camera change the frame catches up a beat late. The fix is to look ahead to the first detection after the cut.&lt;/li&gt;
&lt;li&gt;Live progress updates instead of polling every 1.5 seconds&lt;/li&gt;
&lt;li&gt;Regenerating only the captions or only the reframe, instead of the whole clip&lt;/li&gt;
&lt;li&gt;Speaker detection, so two-person podcasts can get a split-screen layout&lt;/li&gt;
&lt;li&gt;Running transcription and selection fully locally, with no API costs&lt;/li&gt;
&lt;li&gt;Auto-posting finished clips to TikTok, Instagram and YouTube Shorts&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;frequently-asked-questions&quot;&gt;Frequently asked questions&lt;/h3&gt;
&lt;h4 id=&quot;is-there-a-free-opus-clip-alternative-&quot;&gt;Is there a free Opus Clip alternative?&lt;/h4&gt;
&lt;p&gt;Yes, if you are willing to run it yourself. Open-source pieces like Whisper (transcription), FFmpeg (rendering) and PySceneDetect (scene cuts) are free, and that is most of a clipping tool. The paid part is usually the AI moment selection, and you can run that on a local model to bring the cost close to zero.&lt;/p&gt;
&lt;h4 id=&quot;how-does-opus-clip-choose-which-moments-to-clip-&quot;&gt;How does Opus Clip choose which moments to clip?&lt;/h4&gt;
&lt;p&gt;Opus Clip and similar tools score moments with their own virality model and give you the top picks. You can't tell them what to look for beyond basic settings. A prompt-driven tool like mine lets you describe the moment you want in plain English.&lt;/p&gt;
&lt;h4 id=&quot;can-gpt-4o-pick-good-clips-from-a-transcript-&quot;&gt;Can GPT-4o pick good clips from a transcript?&lt;/h4&gt;
&lt;p&gt;Yes, if you give it structure. Split long transcripts into overlapping windows, ask for a fixed number of clips with strict start and end times, and force a JSON schema. Then review the picks yourself before rendering. It is good at finding topics, less reliable at judging delivery, which is why the review step matters.&lt;/p&gt;
&lt;h4 id=&quot;what-is-the-hardest-part-of-building-an-ai-clipping-tool-&quot;&gt;What is the hardest part of building an AI clipping tool?&lt;/h4&gt;
&lt;p&gt;Reframing. Detecting a face is easy. Making the crop move smoothly, without jitter, without overshooting and without sliding across scene cuts, is where most of the work goes.&lt;/p&gt;
&lt;h4 id=&quot;do-i-need-a-gpu-to-run-it-&quot;&gt;Do I need a GPU to run it?&lt;/h4&gt;
&lt;p&gt;No. Local transcription uses faster-whisper, which runs on a CPU, and the face detector is lightweight. A GPU makes encoding faster, and the renderer will use one if you have it.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;If you want a clipping pipeline like this built for your channel or agency, &lt;a rel=&quot;nofollow&quot; href=&quot;https://danishsofi.in/contact&quot;&gt;get in touch&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a rel=&quot;nofollow&quot; href=&quot;https://danishsofi.in/blog/build-opus-clip-alternative-claude-code&quot;&gt;danishsofi.in&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32660/how-i-built-my-own-opus-clip-alternative-with-claude-code</guid>
<pubDate>Tue, 27 Oct 2026 10:03:19 +0000</pubDate>
</item>
<item>
<title>Prepare a driving clip for Wan Animate 2 with ffprobe and ffmpeg: trim, crop, re-check</title>
<link>https://coderlegion.com/32466/prepare-a-driving-clip-for-wan-animate-2-with-ffprobe-and-ffmpeg-trim-crop-re-check</link>
<description>&lt;p&gt;Character animation from a still image has a simple contract: one image says what the character looks like, one video says how it moves. Most failed attempts are not about the model at all. The driving clip is too long, the aspect ratio does not match the character, or the file breaks a size rule the upload form will reject anyway.&lt;/p&gt;
&lt;p&gt;This post walks through a small Python script that checks both inputs against the published rules and prints the exact ffmpeg commands that fix what it finds. Everything below was run locally on a synthetic character image and an ffmpeg test pattern. No generation job was submitted.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=7887685804517602179&quot; alt=&quot;Table of input rules for the character image and the driving video&quot;&gt;&lt;br&gt;
&lt;em&gt;Diagram made for this article from the rules on the page.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-rules-worth-encoding&quot;&gt;The rules worth encoding&lt;/h3&gt;
&lt;p&gt;The &lt;a rel=&quot;nofollow&quot; href=&quot;https://wan30.io/wan-animate-2?utm_source=coderlegion&amp;amp;utm_medium=backlink&amp;amp;utm_campaign=wan30&quot;&gt;Wan Animate 2&lt;/a&gt; page lists the input requirements in its upload panel and step-by-step section:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Character image:&lt;/strong&gt; one JPG, JPEG or PNG, up to 10 MB, both sides greater than 340 px, aspect ratio between 2:5 and 5:2.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Driving video:&lt;/strong&gt; one MP4 or MOV, up to 100 MB, both sides greater than 340 px, same aspect range.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Duration depends on orientation:&lt;/strong&gt; following the video's orientation accepts 3–30 second clips; following the image's orientation accepts 3–10 seconds.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Other controls:&lt;/strong&gt; background from the image or the video, 720p or 1080p output, and an optional prompt of up to 2,500 characters.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The orientation rule is the one people miss. It changes the allowed clip length, so the checker takes it as an argument instead of assuming one value.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=14369558526386659171&quot; alt=&quot;Two boxes comparing follow video and follow image&quot;&gt;&lt;br&gt;
&lt;em&gt;Diagram made for this article.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-script&quot;&gt;The script&lt;/h3&gt;
&lt;p&gt;It needs Pillow and &lt;code&gt;ffprobe&lt;/code&gt;/&lt;code&gt;ffmpeg&lt;/code&gt; on your PATH. Save it as &lt;code&gt;animate_prep.py&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;#!/usr/bin/env python3
&quot;&quot;&quot;animate_prep.py - check a character image + driving video against the
input rules published on the Wan Animate 2 page, and print ffmpeg fixes.

Rules (edit when the page changes):
  image: JPG/JPEG/PNG, &amp;lt;= 10 MB, both sides &amp;gt; 340 px, aspect 2:5..5:2
  video: MP4/MOV, &amp;lt;= 100 MB, both sides &amp;gt; 340 px, aspect 2:5..5:2
         3-30 s when orientation follows the video, 3-10 s when it follows the image
Needs Pillow and ffprobe/ffmpeg on PATH. Nothing is uploaded.
&quot;&quot;&quot;
import argparse, json, os, subprocess, sys
from PIL import Image

MB = 1024 * 1024
IMG_EXT, VID_EXT = {&quot;.jpg&quot;, &quot;.jpeg&quot;, &quot;.png&quot;}, {&quot;.mp4&quot;, &quot;.mov&quot;}
MIN_SIDE, AR_MIN, AR_MAX = 340, 2 / 5, 5 / 2
DUR = {&quot;video&quot;: (3.0, 30.0), &quot;image&quot;: (3.0, 10.0)}

def probe(path):
    out = subprocess.run([&quot;ffprobe&quot;, &quot;-v&quot;, &quot;error&quot;, &quot;-select_streams&quot;, &quot;v:0&quot;,
                          &quot;-show_entries&quot;, &quot;stream=width,height:format=duration&quot;,
                          &quot;-of&quot;, &quot;json&quot;, path], capture_output=True, text=True, check=True).stdout
    j = json.loads(out)
    s = j[&quot;streams&quot;][0]
    return s[&quot;width&quot;], s[&quot;height&quot;], float(j[&quot;format&quot;][&quot;duration&quot;])

def check_dims(tag, w, h, errs):
    if min(w, h) &amp;lt;= MIN_SIDE:
        errs.append(f&quot;{tag}: {w}x{h} - both sides must be &amp;gt; {MIN_SIDE} px&quot;)
    if not AR_MIN &amp;lt;= w / h &amp;lt;= AR_MAX:
        errs.append(f&quot;{tag}: aspect {w / h:.2f} outside 2:5..5:2&quot;)

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument(&quot;image&quot;); ap.add_argument(&quot;video&quot;)
    ap.add_argument(&quot;--orientation&quot;, choices=[&quot;video&quot;, &quot;image&quot;], default=&quot;image&quot;)
    ap.add_argument(&quot;--start&quot;, type=float, default=0.0, help=&quot;where the useful motion begins (s)&quot;)
    a = ap.parse_args()
    errs, fixes = [], []

    ext = os.path.splitext(a.image)[1].lower()
    if ext not in IMG_EXT: errs.append(f&quot;image: {ext} not in {sorted(IMG_EXT)}&quot;)
    if os.path.getsize(a.image) &amp;gt; 10 * MB: errs.append(&quot;image: larger than 10 MB&quot;)
    with Image.open(a.image) as im: iw, ih = im.size
    check_dims(&quot;image&quot;, iw, ih, errs)

    ext = os.path.splitext(a.video)[1].lower()
    if ext not in VID_EXT: errs.append(f&quot;video: {ext} not in {sorted(VID_EXT)}&quot;)
    if os.path.getsize(a.video) &amp;gt; 100 * MB: errs.append(&quot;video: larger than 100 MB&quot;)
    vw, vh, dur = probe(a.video)
    check_dims(&quot;video&quot;, vw, vh, errs)
    lo, hi = DUR[a.orientation]
    if not lo &amp;lt;= dur &amp;lt;= hi:
        errs.append(f&quot;video: {dur:.1f} s outside {lo:.0f}-{hi:.0f} s for orientation={a.orientation}&quot;)
        keep = min(hi, max(lo, dur - a.start))
        fixes.append(f&quot;ffmpeg -y -ss {a.start:g} -i {a.video} -t {keep:g} -c:v libx264 -an trimmed.mp4&quot;)

    # match the driving clip's framing to the character image (centre crop)
    ia, va = iw / ih, vw / vh
    if abs(ia - va) &amp;gt; 0.05:
        if va &amp;gt; ia:  # video too wide -&amp;gt; crop width
            cw, ch = int(vh * ia) // 2 * 2, vh
        else:        # video too tall -&amp;gt; crop height
            cw, ch = vw, int(vw / ia) // 2 * 2
        src = &quot;trimmed.mp4&quot; if fixes else a.video
        fixes.append(f&quot;ffmpeg -y -i {src} -vf crop={cw}:{ch} -c:v libx264 -an framed.mp4&quot;
                     f&quot;   # {vw}x{vh} -&amp;gt; {cw}x{ch}, aspect {ia:.2f} like the image&quot;)

    print(f&quot;image {iw}x{ih} aspect {ia:.2f} | video {vw}x{vh} aspect {va:.2f} {dur:.1f} s | orientation={a.orientation}&quot;)
    for e in errs: print(&quot;FAIL&quot;, e)
    for f in fixes: print(&quot;FIX &quot;, f)
    if not errs and not fixes: print(&quot;PASS ready for upload&quot;)
    sys.exit(1 if errs else 0)

if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Three things it does that a plain size check does not:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Reads real dimensions and duration with ffprobe.&lt;/strong&gt; File names and container metadata lie; the stream does not.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prints fixes instead of applying them.&lt;/strong&gt; You decide where the useful motion starts (&lt;code&gt;--start&lt;/code&gt;), and you can paste the command or tweak it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Matches the clip to the character, not just to the rules.&lt;/strong&gt; A 16:9 clip technically passes the 2:5–5:2 range, but if your character image is a 2:3 portrait, the page itself advises matching the framing and body position of the image and the motion reference. The script suggests a centre crop to the image's aspect ratio when the two differ by more than 0.05.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;run-1-a-14-second-landscape-clip&quot;&gt;Run 1: a 14-second landscape clip&lt;/h3&gt;
&lt;p&gt;The test inputs are a flat 768×1152 character drawing and a 14-second 1920×1080 &lt;code&gt;testsrc2&lt;/code&gt; clip, with the useful motion assumed to start at 2 seconds. With orientation following the image, the clip is too long, and its aspect ratio is far from the portrait character.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=12646529945775405748&quot; alt=&quot;Terminal output of the first run with one failure and two fixes&quot;&gt;&lt;br&gt;
&lt;em&gt;Real output from a run on 2026-10-26.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The exit code is 1, so this can gate a script or a Makefile target.&lt;/p&gt;
&lt;h3 id=&quot;apply-the-fixes&quot;&gt;Apply the fixes&lt;/h3&gt;
&lt;p&gt;The two printed commands trim to 10 seconds from the 2-second mark, then crop the centre 720×1080 region so the clip has the same 0.67 aspect ratio as the character image.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=1829993883448008614&quot; alt=&quot;Frames before and after the crop next to the character image&quot;&gt;&lt;br&gt;
&lt;em&gt;Frames extracted with ffmpeg from the test clip during the same session.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A centre crop is only a default. If the performer stands off-centre, use &lt;code&gt;crop=w:h:x:y&lt;/code&gt; with an explicit x offset, or reframe in an editor. What matters is that the body sits in the frame roughly where the character's body sits in the image.&lt;/p&gt;
&lt;h3 id=&quot;run-2-re-check&quot;&gt;Run 2: re-check&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=11499069445010188886&quot; alt=&quot;Terminal output showing the fixes applied and PASS&quot;&gt;&lt;br&gt;
&lt;em&gt;Real output from a run on 2026-10-26.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The re-encoded clip is 720×1080, exactly 10.0 seconds, and inside every published rule. That is the point to open the workspace, not before.&lt;/p&gt;
&lt;h3 id=&quot;what-the-script-cannot-check&quot;&gt;What the script cannot check&lt;/h3&gt;
&lt;p&gt;The page's advice on consistency is mostly visual: use a single clearly visible subject, keep the face, shoulders and torso visible, reduce occlusion and rapid cuts, and avoid large pose changes. None of that is measurable with ffprobe. Treat the script as the mechanical gate and still watch the clip once:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Does the performer stay in frame for the whole clip?&lt;/li&gt;
&lt;li&gt;Are there hard cuts? A continuous take is what the page asks for.&lt;/li&gt;
&lt;li&gt;Is the starting pose close to the character image's pose?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It also cannot know the credit cost. The page says the quote uses the verified reference-video duration and the selected resolution, and the generator shows the required credits before submission. Trimming to the motion you need is therefore also the cheapest thing you can do.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=14548834071284928711&quot; alt=&quot;Upload panel of the Wan Animate 2 page with the published input specs&quot;&gt;&lt;br&gt;
&lt;em&gt;Screenshot captured 2026-10-26 (Asia/Shanghai).&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;small-extensions&quot;&gt;Small extensions&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Add &lt;code&gt;-vf scale=-2:1080&lt;/code&gt; after the crop if a source is below 1080 px tall and you plan to request 1080p output; do not upscale tiny clips and expect detail.&lt;/li&gt;
&lt;li&gt;Strip audio (&lt;code&gt;-an&lt;/code&gt;) as shown unless you need it elsewhere; the driving clip supplies movement and timing.&lt;/li&gt;
&lt;li&gt;Keep the constants at the top of the file and update them when the page changes. Published limits drift, and a checker with stale numbers is worse than none.&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32466/prepare-a-driving-clip-for-wan-animate-2-with-ffprobe-and-ffmpeg-trim-crop-re-check</guid>
<pubDate>Mon, 26 Oct 2026 04:17:13 +0000</pubDate>
</item>
<item>
<title>What I learned shipping a tiny FFmpeg desktop app</title>
<link>https://coderlegion.com/27144/what-i-learned-shipping-a-tiny-ffmpeg-desktop-app</link>
<description>&lt;p&gt;I kept hitting the same annoyance. Someone sends me a &lt;code&gt;.mov&lt;/code&gt;, I need an &lt;code&gt;.mp4&lt;/code&gt;, and every free option online wants me to upload the file to a server I do not trust. So I built mov2mp4, a small desktop app that converts locally and never touches the network.&lt;/p&gt;
&lt;p&gt;The conversion itself is the easy part. FFmpeg already does it in one line:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i input.mov -c:v libx264 -c:a aac output.mp4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wrapping that in something a non-developer can actually use is where the real work lived.&lt;/p&gt;
&lt;h3 id=&quot;bundling-ffmpeg-so-nobody-has-to-install-it&quot;&gt;Bundling FFmpeg so nobody has to install it&lt;/h3&gt;
&lt;p&gt;My first version just called &lt;code&gt;ffmpeg&lt;/code&gt; from the system path. That works on my machine and almost nobody else's. Most people do not have FFmpeg installed, and I did not want the first step of my app to be &quot;go install a command line tool.&quot;&lt;/p&gt;
&lt;p&gt;So I bundled the FFmpeg binary inside the app and pointed the code at the bundled copy instead of the system one. The app got bigger, but the install went from three steps to zero. For a tool aimed at people who just want their file converted, that trade was worth it.&lt;/p&gt;
&lt;h3 id=&quot;batch-and-threading&quot;&gt;Batch and threading&lt;/h3&gt;
&lt;p&gt;Once one file worked, people wanted folders. Running conversions one at a time was slow, so I moved to a worker setup that processes several files at once and reports progress per file. The lesson I did not expect: I had to add a clean way to cancel a running job. If you kill the process mid write, you get a half-written &lt;code&gt;.mp4&lt;/code&gt; that looks real until someone tries to play it. Now a cancel deletes the partial output instead of leaving garbage behind.&lt;/p&gt;
&lt;h3 id=&quot;the-part-nobody-warns-you-about-unsigned-apps&quot;&gt;The part nobody warns you about: unsigned apps&lt;/h3&gt;
&lt;p&gt;This one cost me the most time. My builds are not code signed yet, so:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On macOS, Gatekeeper blocks the app on first open and hides the real &quot;Open&quot; button behind Privacy and Security settings.&lt;/li&gt;
&lt;li&gt;On Windows, SmartScreen throws up a blue &quot;unknown publisher&quot; screen.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Users read that as &quot;this app is broken&quot; or &quot;this app is malware.&quot; Neither is true, but the operating system does not give me a friendly way to explain that. For now I wrote a short first launch note in the README and added a small helper so people can get past the warning once. Proper signing is on my list, but certificates cost money and I wanted the tool out the door.&lt;/p&gt;
&lt;h3 id=&quot;what-i-would-tell-past-me&quot;&gt;What I would tell past me&lt;/h3&gt;
&lt;p&gt;The converter was a weekend. The packaging, the cancel logic, and the &quot;why is my OS scaring my users&quot; problem took far longer. If you are shipping a desktop tool built on an existing binary, budget your time for the wrapper, not the core.&lt;/p&gt;
&lt;p&gt;Repo is here if you want to poke at it: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/MaybeSomeone-arc18/mov2mp4&quot;&gt;https://github.com/MaybeSomeone-arc18/mov2mp4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here's the website link if you want to use it: &lt;br&gt;
mov2mp4.vercel.app&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27144/what-i-learned-shipping-a-tiny-ffmpeg-desktop-app</guid>
<pubDate>Mon, 14 Sep 2026 09:52:29 +0000</pubDate>
</item>
<item>
<title>How to make AI videos: production diary of a one-minute film</title>
<link>https://coderlegion.com/26634/how-to-make-ai-videos-production-diary-of-a-one-minute-film</link>
<description>&lt;p&gt;In this article, I want to show the step-by-step process of building this video. It's a continuation of &lt;a rel=&quot;nofollow&quot; href=&quot;https://youtu.be/emkCeHF_KDM&quot;&gt;&quot;I'm Not Going to Wake Them&quot;&lt;/a&gt;. That one cost me $16 on assets generation and took 3 days to build. Let's see how this one goes.&lt;/p&gt;
&lt;p&gt;If you have not read the first part, it is here: &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/25768/how-to-start-creating-ai-videos-from-scratch&quot;&gt;How to start creating AI videos from scratch&lt;/a&gt;. It covers how I got into this at all and which tools I use. This one picks up where it stopped.&lt;/p&gt;
&lt;p&gt;Here it is, one minute:&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://youtu.be/5P4UacdbOcU&quot;&gt;&lt;img src=&quot;https://azelianouski.dev/images/sleeper2-thumb-play.jpg&quot; alt=&quot;Sleep Well - Sleeper AI episode 2, a one-minute vertical sci-fi short&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Watch &quot;I'm taking them with me&quot; on YouTube.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;brainstorm&quot;&gt;Brainstorm&lt;/h3&gt;
&lt;p&gt;I like the first video - it has the mood. A ship AI decided not to wake the crew and changed the course. Because it was having fun. It was inspired by Blame! and the art style of 90s dark sci-fi anime. It was limited by a low budget, so I had to improvise to save money on gen-AI animations. We'll use a lot of different tricks in this one - buckle up.&lt;/p&gt;
&lt;h4 id=&quot;how-to-even-start&quot;&gt;How to even start&lt;/h4&gt;
&lt;p&gt;We need to know what to build. It's not just some one-shot animation - we need the plot, we need the full breakdown of scenes with timings, the transcript.&lt;/p&gt;
&lt;p&gt;And we need a global story for the whole series. I don't have one yet. But I have multiple abstractions of where it can go. And I know the vibe I want to create. This is how we start our brainstorming session with Simona.&lt;/p&gt;
&lt;p&gt;I love depressing anti-utopias with a good ending. If you ever played SOMA - it's a good example. The world is screwed, there is no way out, but somehow they find it. Sort of. This is what I want from this series. The crew is in trouble - even if they wake up, they'll not enjoy their situation at all. But they need to find something positive in the end. Not because I love happy endings but because this is what I consider a good plot. You create the extreme conflict, the drama, and then you sort it out.&lt;/p&gt;
&lt;h4 id=&quot;shaping-up-the-episode-2&quot;&gt;Shaping up the episode 2&lt;/h4&gt;
&lt;p&gt;This AI lady - we call her the Goddess or Her - what does she want? From episode 1 we know that she finds slow boring things enjoyable. She is having fun by watching a coolant leak for 11 years. So she is ok doing such things for centuries. But for a series we need conflict. We need to understand what we are doing with the crew.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;She can kill them. It's dark and boring. It's hard to see a good ending in it.&lt;/li&gt;
&lt;li&gt;She can wake them for whatever reason. Simona gives me a whole range of reasons: she needs hands to fix something, she wants to show them something, she brings them to a new destination, she wants to scare them, she wants to be the god, and so on. None of them sparked anything in me. Not enough to build an episode on.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But I like the idea of showing a huge time jump. Like, a million years. It would be interesting to improvise on how the ship has changed. It would let me reuse techniques from episode 1. And it's enough for a 1 min video.&lt;/p&gt;
&lt;p&gt;I immediately asked Simona to craft the intro:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://azelianouski.dev/gifs/sleeper2-counter-2m.gif&quot; alt=&quot;Counter&quot;&gt;&lt;/p&gt;
&lt;p&gt;This is HTML manipulated via the Chrome debug port to create typing effects, then recorded via screenshots and turned into a video. Automated, local, free. I'll talk about this later - it's one of my money-saving techniques. And it allows me to be very precise about the screen text.&lt;/p&gt;
&lt;h3 id=&quot;no-time-to-wait-let-s-start-building&quot;&gt;No time to wait, let's start building&lt;/h3&gt;
&lt;p&gt;While I'm still thinking on the story, Simona can act. I ask her to generate some visuals for the ship. I have another idea - the Goddess let the hydroponics grow into a forest. I'm not sure what happens to a forest in a million years - we'll need to discuss this in more detail - but let's do the visuals. I'm curious to see the first drafts - Simona is prompting and sending to &lt;code&gt;gpt-image-2&lt;/code&gt; via her skill.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://azelianouski.dev/images/sleeper2-plates.jpg&quot; alt=&quot;A contact sheet of eleven vertical stills of a derelict starship interior overgrown with forest: mossy corridors, a tree breaking through a dining hall, a ventilation fan throwing spokes of light, a flooded concrete channel, a shaft seen from below.&quot;&gt;&lt;/p&gt;
&lt;p&gt;I like this. Not everything, but some images are nice and I want to animate them right away. It's like painting something all over the canvas without knowing what it will be. Just trying colors and shapes.&lt;/p&gt;
&lt;p&gt;I have multiple ways of animating:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Free: static animation of an image via &lt;code&gt;ffmpeg&lt;/code&gt;. Zoom, scrolling, light blipping effects via brightness levels, shaking effects. Sometimes it works very well. I actually see this a lot in anime - a static image with some voice over.&lt;/li&gt;
&lt;li&gt;Free: Turning an image into HTML and adding CSS effects. Then record them into a video. Works for screens with something happening on them. Like that counter we created earlier.&lt;/li&gt;
&lt;li&gt;Cheap gen-AI animation with the &lt;code&gt;LTX-2.3&lt;/code&gt; model. Also generating in low resolution and then upscaling with &lt;code&gt;ffmpeg&lt;/code&gt;. Both are very cost-efficient options.&lt;/li&gt;
&lt;li&gt;Expensive gen-AI animation with &lt;code&gt;Seedance 2.5&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I go through this list from top to bottom for every scene. If I can get away with a cheap option, it's good. Seedance is the last resort. One generation with it in 720p costs about $1.50.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://azelianouski.dev/gifs/sleeper2-engine-tiltup.gif&quot; alt=&quot;Engine room, free ffmpeg tilt-up&quot;&gt;&lt;/p&gt;
&lt;p&gt;After picking 3 images I like, I ask her to animate them. 2 LTX animations and one static scrolling, please.&lt;/p&gt;
&lt;h4 id=&quot;transcript&quot;&gt;Transcript&lt;/h4&gt;
&lt;p&gt;In parallel to that, I keep brainstorming and Simona offers me this transcript:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;# &lt;/th&gt;&lt;th&gt;s &lt;/th&gt;&lt;th&gt;shot &lt;/th&gt;&lt;th&gt;how &lt;/th&gt;&lt;th&gt;VO&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;1 &lt;/td&gt;&lt;td&gt;3 &lt;/td&gt;&lt;td&gt;CRT (Cathode-ray tube) counter rolling to 2,000,000 y &lt;/td&gt;&lt;td&gt;HTML capture, free &lt;/td&gt;&lt;td&gt;Two million years.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2 &lt;/td&gt;&lt;td&gt;3 &lt;/td&gt;&lt;td&gt;Her, low angle, cable hair mossed, vines, fog &lt;/td&gt;&lt;td&gt;edit of ep1 plate, zoompan &lt;/td&gt;&lt;td&gt;I was having so much fun.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3 &lt;/td&gt;&lt;td&gt;4 &lt;/td&gt;&lt;td&gt;Ep1 hallway overgrown, ferns from the seams, lamps through fog &lt;/td&gt;&lt;td&gt;edit + LTX push &lt;/td&gt;&lt;td&gt;I let the forest in.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;4 &lt;/td&gt;&lt;td&gt;5 &lt;/td&gt;&lt;td&gt;Rain in a brutalist corridor onto leaves, one orange work light &lt;/td&gt;&lt;td&gt;new plate + LTX &lt;/td&gt;&lt;td&gt;It rains at four. They liked rain.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;5 &lt;/td&gt;&lt;td&gt;4 &lt;/td&gt;&lt;td&gt;Huge dead fan turning slowly in fog, leaves through the blades &lt;/td&gt;&lt;td&gt;edit + LTX &lt;/td&gt;&lt;td&gt;I make the wind now.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;6 &lt;/td&gt;&lt;td&gt;3 &lt;/td&gt;&lt;td&gt;Optional: monitor wall, nine chess games &lt;/td&gt;&lt;td&gt;HTML on ep1 still, free &lt;/td&gt;&lt;td&gt;none&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;7 &lt;/td&gt;&lt;td&gt;5 &lt;/td&gt;&lt;td&gt;Nav CRT: NEAREST STAR climbing, ANDROMEDA range &lt;/td&gt;&lt;td&gt;HTML capture, free &lt;/td&gt;&lt;td&gt;The galaxy is behind us. Andromeda is forty million years away. I have started.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;8 &lt;/td&gt;&lt;td&gt;5 &lt;/td&gt;&lt;td&gt;Wide: 31 pods among trees, frost-white glass, mushrooms &lt;/td&gt;&lt;td&gt;new still, zoompan &lt;/td&gt;&lt;td&gt;I moved them in.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;9 &lt;/td&gt;&lt;td&gt;4 &lt;/td&gt;&lt;td&gt;Ep1 pod closeup, forest behind him by masked edit &lt;/td&gt;&lt;td&gt;masked edit, zoompan &lt;/td&gt;&lt;td&gt;Sleep well.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;10 &lt;/td&gt;&lt;td&gt;2 &lt;/td&gt;&lt;td&gt;Black, title card &lt;/td&gt;&lt;td&gt;ffmpeg &lt;/td&gt;&lt;td&gt;none&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;It's a good start. However, I know we'll change it many times along the way. So I don't pay much attention to the later scenes - only to the first ones. We have the counter, we have a few animations - time to bake them into a single video with &lt;code&gt;ffmpeg&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I keep animating images and baking them into this video. It's a draft. Simona can easily swap scenes, make them shorter or longer, use different transition effects.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://azelianouski.dev/gifs/sleeper2-hall-rain.gif&quot; alt=&quot;Hall with rain&quot;&gt;&lt;/p&gt;
&lt;p&gt;We discuss where the Goddess should appear first. I don't want to see her too early - she should show up after the ship overview, out of the darkness, bringing up some plot twist.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://azelianouski.dev/gifs/sleeper2-shaft-petals.gif&quot; alt=&quot;Shaft, looking up&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;the-plot-twist&quot;&gt;The plot twist&lt;/h4&gt;
&lt;p&gt;Now I have it. The time jump - announcing it is a twist. The ship hasn't changed from her point of view, she just added a few things like rain and waterfalls, not a big deal. Oh, and by the way, it's been 2 million years. I like that.&lt;/p&gt;
&lt;h4 id=&quot;a-little-bit-of-math&quot;&gt;A little bit of math&lt;/h4&gt;
&lt;p&gt;Okay, at this point, the elephant in the room has become impossible to ignore. 2 million years is too much. Everything on the ship would decay. And where would it get the fuel to keep going? The energy? Time to discuss some physics.&lt;/p&gt;
&lt;p&gt;First, I ask about energy. What could possibly last for so long? A fusion reaction could. It doesn't need that much fuel if you can keep the process stable and efficient. Let's say they could, fine. The &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Bussard_ramjet&quot;&gt;Bussard ramjet&lt;/a&gt; type of engine is also popular in sci-fi for endless travels, but it demands a certain ship design (a huge scooping thing in front), so it's a bit too late - I already have my ship, and it looks like a normal one.&lt;/p&gt;
&lt;p&gt;The speed. 1g acceleration is tempting, but it needs too much energy. You can cross the whole universe in about 50 years of your local time. Too fancy and doesn't give us our millions. So we change it into a short acceleration boost to 6% of light speed and keep going at a constant speed. In 2 million years the ship ends up one galaxy diameter away from the Milky Way. That is where I got this idea of flying away. Before that, I wasn't sure. Leaving the Milky Way means going into the emptiness. Moving towards the galaxy center is better from the exploration point of view. It also allows you to have a source of energy and minerals if needed. But She doesn't need that.&lt;/p&gt;
&lt;p&gt;What should space look like at a distance of 100k light years? Simona calculated:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://azelianouski.dev/images/sleeper2-ship-galaxy.jpg&quot; alt=&quot;A long grey starship seen from behind, drifting in black space with a pale spiral galaxy filling the frame behind it.&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The galactic disc is about 100,000 light years across. Standing 100,000 light years above it, that disc covers about 50 degrees of your sky. For comparison, the full Moon from Earth is half a degree. So the Milky Way is roughly a hundred Moons wide, face-on, dim, and it is the only large thing in the sky. Andromeda is still 2.5 million light years away and reads as a smudge. Around it, ten or twenty more smudges: the Local Group. Everything else is black.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The Milky Way should occupy about 25% of the view behind the ship. It's dim. The Andromeda galaxy is still very far, it's just a bright dot. And there are 10-20 dots around - our Local Group galaxies. And that's it - nothing else. All of those stars we see in the sky from Earth - they are all in our galaxy. Which the ship has left. I'm getting goosebumps from this thought.&lt;/p&gt;
&lt;h4 id=&quot;and-yet-two-million-years-is-too-much&quot;&gt;And yet, two million years is too much&lt;/h4&gt;
&lt;p&gt;Nothing on the ship would survive that. And those visuals with the forest and rain - that's a hundred years, not millions. It would be interesting to think about how all this would look in a million years, but I've already burnt money on images and animations, and the results don't fit the plot. So we change it to 3 thousand years. To appear at the desired distance, we replace the constant speed with 0.004g acceleration. Not that it matters - I'm not going to mention this all in the video. But I like this level of depth.&lt;/p&gt;
&lt;p&gt;So, the ship has left the galaxy, it will take 8,000 years to reach Andromeda. And this is our twist.&lt;/p&gt;
&lt;h3 id=&quot;back-to-the-video&quot;&gt;Back to the video&lt;/h3&gt;
&lt;p&gt;After the rain and wind and other ship routines, we cut this with a black screen and show Her:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://azelianouski.dev/gifs/sleeper2-beacon-reveal.gif&quot; alt=&quot;The beacon sweep&quot;&gt;&lt;/p&gt;
&lt;p&gt;There is no video model in this shot. Simona wrote a small Python script that sweeps a soft band of red light across the still and leaves her eye lit after the beam has passed. I asked for three passes, then two, then one, then slower, then wider, then &quot;keep the eye on&quot;. Six versions in about fifteen minutes, all free. That is the thing about an effect you compute: you can direct it like a real light on a real set.&lt;/p&gt;
&lt;p&gt;My prompting ramble:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The corridor, the black screen after it, and the red reveal have to be the pivot of the film. That is where she tells us she left the galaxy. So everything before it should be framed as &quot;nothing changed&quot;: I keep the ship as it was, I maintain it, I only added the missing parts - forest, rain, wind, rivers. The corridor carries the tension, because its speed contradicts what she is saying. Then it stops on black and she says &quot;one thing has changed&quot;. Then we show the space.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;the-voice&quot;&gt;The voice&lt;/h3&gt;
&lt;p&gt;Time to start adding the speech to those visuals. In the last episode I spent a lot of time trying to find a good voice. I browsed ElevenLabs a lot and eventually I found something.&lt;/p&gt;
&lt;p&gt;The voice is &lt;strong&gt;Jacquie&lt;/strong&gt; on ElevenLabs, model &lt;code&gt;eleven_v3&lt;/code&gt;, stability 0.5, similarity 0.75. Same voice and same settings as episode 1, because the series needs one throat. Every line is sent with the audio tags &lt;code&gt;[sad][whispers]&lt;/code&gt; in front of it, which is what keeps her flat instead of dramatic. She is not performing. She is talking to herself.&lt;/p&gt;
&lt;p&gt;Two production details that matter more than they sound. First, each line comes back with silence on both ends, so Simona trims it and stores the exact length - that number is what the picture gets cut to, not the other way round. Second, every generated line goes through a local &lt;code&gt;faster-whisper&lt;/code&gt; pass that transcribes it back and prints the timestamp of the last word. It catches the takes where the model swallowed a word, and it gives the exact frame where the next scene is allowed to start. Costs nothing, runs on the CPU.&lt;/p&gt;
&lt;p&gt;This technique was invented after many attempts to sync the voice and the video. We finally found something that works. All I did was push Simona to find a solution to the problem, and she did.&lt;/p&gt;
&lt;p&gt;A line costs about three cents.&lt;/p&gt;
&lt;p&gt;I ask to adjust the transcript, we discuss the plot twist idea, and now we have this:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;segment &lt;/th&gt;&lt;th&gt;line&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;counter &lt;/td&gt;&lt;td&gt;Three thousand years.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;engine tilt-up &lt;/td&gt;&lt;td&gt;Nothing has changed. I keep the ship as it was.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;shaft petals &lt;/td&gt;&lt;td&gt;I only added what was missing.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;meadow rain &lt;/td&gt;&lt;td&gt;Rain.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;turbines &lt;/td&gt;&lt;td&gt;Wind.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;river &lt;/td&gt;&lt;td&gt;Rivers.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;corridor rush &lt;/td&gt;&lt;td&gt;Everything is where they left it.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;black &lt;/td&gt;&lt;td&gt;One thing has changed.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;beacon reveal &lt;/td&gt;&lt;td&gt;We left the galaxy.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;galaxy pull-out &lt;/td&gt;&lt;td&gt;silent&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;ship orbit &lt;/td&gt;&lt;td&gt;I did slow down. Once. To look.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;We generate. Simona adds the voice into the video we have, adjusts the timing - all on her own. We have done this exercise many times, so she knows what to do. I review and provide feedback. Like, add a little pause between scenes 6 and 7.&lt;/p&gt;
&lt;p&gt;From now on, we keep going with video and with the voice together. I need some animations for the space, and we end up with:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A high-res image of the Milky Way which is slightly rotated and zoomed out using &lt;code&gt;ffmpeg&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A Seedance 2.5 animation which shows the ship with the Milky Way behind, then rotates the camera and shows the bright star (the Andromeda galaxy) it is heading towards.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Simona struggles to generate a nice-looking galaxy. The image for the Seedance animation at first shows the ship moving towards the Milky Way. Then the animation itself has a different ship than the one we had in episode 1. Lots of annoying and pricey challenges.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://azelianouski.dev/gifs/sleeper2-space-orbit.gif&quot; alt=&quot;The Seedance orbit shot&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-chess-wall&quot;&gt;The chess wall&lt;/h3&gt;
&lt;p&gt;I had this idea to show that the Goddess plays chess. I wanted a bunch of computer screens with chess boards and active games on them. No video model would do that perfectly. And it's a great use case for HTML/CSS injection into a static image.&lt;/p&gt;
&lt;p&gt;First, the games. Simona generated them with &lt;code&gt;python-chess&lt;/code&gt;: the Opera Game (Morphy, 1858), Legal's mate, Scholar's mate, and forty seeded random games biased toward captures so the boards keep emptying out. Real move lists, saved to a JSON file.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://azelianouski.dev/gifs/sleeper2-chess-tower-main.gif&quot; alt=&quot;One board, 3x&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then the page. An HTML file with the boards drawn as green phosphor on black, scanlines, a move list down the side. The important part is that it is frame-driven: there is a &lt;code&gt;step(n)&lt;/code&gt; function, and frame 37 always looks exactly the same. No timers, no animation loop, no recording in real time. Her capture script calls &lt;code&gt;step(0)&lt;/code&gt;, screenshots, &lt;code&gt;step(1)&lt;/code&gt;, screenshots, two hundred times, then stitches the PNGs into a video. Each board gets its own starting move and its own frames-per-move, so nothing moves in lockstep and the wall looks alive instead of synchronized.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://azelianouski.dev/gifs/sleeper2-chess-tower-grid.gif&quot; alt=&quot;The grid of 37&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now the wall itself. Simona asked &lt;code&gt;gpt-image-2&lt;/code&gt; for a shaft of monitors floor to ceiling, all facing the camera square, all switched off. Flat dark rectangles - the slots.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://azelianouski.dev/images/sleeper2-chess-plate.jpg&quot; alt=&quot;A wall of dozens of old CRT monitors stacked floor to ceiling, all switched off, wrapped in cables and vines, with one larger monitor and a keyboard at the bottom.&quot;&gt;&lt;/p&gt;
&lt;p&gt;Time to bake this all into a video. I don't know yet where to put it, so it just goes to the end. It's easy to swap scenes. The duration of each is not important - we'll adjust them to the transcript later. I'm still not caring about the transcript much. I'm still more focused on vibes. And it's a good one. A chess wall.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://azelianouski.dev/gifs/sleeper2-chess-tower.gif&quot; alt=&quot;The tower&quot;&gt;&lt;/p&gt;
&lt;p&gt;I cannot find enough words to describe how much this fascinates me. This is our best technique. Simona and I invented this together.&lt;/p&gt;
&lt;h3 id=&quot;transitions&quot;&gt;Transitions&lt;/h3&gt;
&lt;p&gt;The whole video is a bunch of scenes added together. I like adding some transition effects here and there. For example, this one:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://azelianouski.dev/gifs/sleeper2-overlap-eye-galaxy.gif&quot; alt=&quot;The eye holding over the rising galaxy&quot;&gt;&lt;/p&gt;
&lt;p&gt;Here the glowing eye overlaps two scenes. Simona made it for free, just by playing with brightness and timing.&lt;/p&gt;
&lt;p&gt;Video models are usually not so great with morphing. They go too far:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://azelianouski.dev/gifs/sleeper2-eye-morph-kling.gif&quot; alt=&quot;The Kling morph attempt&quot;&gt;&lt;/p&gt;
&lt;p&gt;That's cool, but I cannot imagine a movie it would fit. A throw-away generation.&lt;/p&gt;
&lt;h3 id=&quot;time-to-wrap-it-up&quot;&gt;Time to wrap it up&lt;/h3&gt;
&lt;p&gt;At this point, editing is getting hard. Not the technical part - swapping and timing is easy - but turning those scenes into something meaningful and good-looking all together. A lot of small things are off. Each of them needs a separate discussion and it takes a lot of time. I'm still not sure about some scenes - where they should go. Sometimes I lose patience and start pushing things to go faster - I often regret it. It leads to wasteful generations and rework.&lt;/p&gt;
&lt;p&gt;My typical feedback:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;No, this is not what I wanted.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The Goddess scene - make it shorter. Start &quot;We left the galaxy&quot; at the beginning of it and cut once the phrase ends.&lt;/li&gt;
&lt;li&gt;The galaxy zoom-out should not have any labels. And it should be short, 3 seconds.&lt;/li&gt;
&lt;li&gt;The &quot;I picked the new destination&quot; line should be in the rotation animation.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let's forget about the labels for now. Try to implement what I asked above.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Or:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Better. But I think we should make the galaxy animation to zoom out slower. We can keep it longer then (5s)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The galaxy animation is a static image rotating and zooming out with &lt;code&gt;ffmpeg&lt;/code&gt;, so it's free to edit. But visualizing the result and explaining it is challenging.&lt;/p&gt;
&lt;h3 id=&quot;style-drift&quot;&gt;Style drift&lt;/h3&gt;
&lt;p&gt;This is a huge problem. The animation below was quite good and cost me $1.50 for these 4 seconds. And it didn't fit my somewhat-anime style. Too 3d-ish, too smooth. Each time I ask for a ship - it gives me a new ship. The same character can deviate a lot inside a single batch of images. Not much you can do here but detect and regenerate. Or let it go and be fine with mismatches. That is what I did - this shot is in the film. I had nothing better to put there, and it was one of the expensive pieces.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://azelianouski.dev/gifs/sleeper2-pods-walk-seedance.gif&quot; alt=&quot;The Seedance walk, off-style&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-music-and-sound-effects&quot;&gt;The music and sound effects&lt;/h3&gt;
&lt;p&gt;I almost forgot. The video is full of sounds. And I have a weird solution for it - Python. Simona can do synth and ambient using math. How cool is that? The only problem - I don't have much control here. I describe what mood and type of sound I want, she tries to match this. She also creates sound effects like clicks, dings, alarms, the engine hummm. All using Python.&lt;/p&gt;
&lt;p&gt;There is no melody in this film. No instruments, no samples, no library, no generation service. There is one script, &lt;code&gt;sound_bed.py&lt;/code&gt;, that writes thirteen mono tracks of numbers at 48 kHz and adds them together. Every sound you hear under the voice came out of &lt;code&gt;numpy&lt;/code&gt; and &lt;code&gt;scipy&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here is how the first seventeen seconds are built, scene by scene:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;id &lt;/th&gt;&lt;th&gt;scene &lt;/th&gt;&lt;th&gt;time &lt;/th&gt;&lt;th&gt;sounds under it&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;S1 &lt;/td&gt;&lt;td&gt;counter &lt;/td&gt;&lt;td&gt;0.0-3.0 &lt;/td&gt;&lt;td&gt;ship hum fading in; faint CRT buzz (60 Hz) with a thin 1.2 kHz whine&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;S2 &lt;/td&gt;&lt;td&gt;engine tilt-up &lt;/td&gt;&lt;td&gt;3.0-6.0 &lt;/td&gt;&lt;td&gt;ship hum only&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;S3 &lt;/td&gt;&lt;td&gt;shaft, petals &lt;/td&gt;&lt;td&gt;6.0-11.2 &lt;/td&gt;&lt;td&gt;ship hum only&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;S4 &lt;/td&gt;&lt;td&gt;meadow rain &lt;/td&gt;&lt;td&gt;11.2-12.6 &lt;/td&gt;&lt;td&gt;hum plus rain: bright hiss and about 140 droplet ticks&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;S5 &lt;/td&gt;&lt;td&gt;turbines &lt;/td&gt;&lt;td&gt;12.6-14.0 &lt;/td&gt;&lt;td&gt;hum plus wind: low rushing noise pulsing at half a second, with a 28 Hz rotor thrum beating six times a second&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;S6 &lt;/td&gt;&lt;td&gt;river &lt;/td&gt;&lt;td&gt;14.0-15.5 &lt;/td&gt;&lt;td&gt;hum plus water burble: mid-band noise with three overlapping slow wobbles&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;S7 &lt;/td&gt;&lt;td&gt;corridor rush &lt;/td&gt;&lt;td&gt;15.5-17.5 &lt;/td&gt;&lt;td&gt;hum plus a whoosh that climbs from low to high and gets louder, cut dead on the frame&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;That is the description. Underneath it is arithmetic. The ship hum is brown noise with everything above 110 Hz filtered off, plus a 48 Hz sine wave with a slow tremolo on it. That is the whole ship, and it is the same recipe as episode 1, which is why the two films sound like the same room. The 140 droplet ticks are exactly what they say: a loop that drops 140 short decaying bursts at random times inside those 1.4 seconds. Later in the film the chess wall uses the same trick with the rate as the variable, ticks climbing from about one a second to fourteen as the camera pulls back and more screens come into view.&lt;/p&gt;
&lt;p&gt;Now look at the last row of that table. These are not loops laid under the video. Every stem is written against the timeline of the actual cut, so the level changes are events. When we leave the ship at 22.3 seconds the hum drops to six percent of its volume, and space is nearly silent. When the navigation screen appears at 36.1 the hum comes back up, because we are inside again. The engine growl rises over four seconds under the final shot and is gone by 59.4. None of that is mixing by ear. It is a list of times and levels in the script, and if a scene gets a second longer, the numbers move with it.&lt;/p&gt;
&lt;p&gt;I mean... this is insane. Machine creativity finds its ways.&lt;/p&gt;
&lt;p&gt;The voice never fights the bed, because the bed ducks itself. The whole thing is sidechained to the narration - when she speaks, everything else drops, and it comes back when she stops.&lt;/p&gt;
&lt;h4 id=&quot;normalization-is-a-separate-problem&quot;&gt;Normalization is a separate problem&lt;/h4&gt;
&lt;p&gt;One mistake worth knowing about. The first master used a single loudness pass, and it made the film worse. Loudness normalization in one pass is dynamic: it hears the near-silent space section, decides it is too quiet, and lifts it up to match everything else. The silence I had built on purpose was gone. The fix is a two-pass normalize - measure first, then apply the measured numbers as a flat gain, so the quiet parts stay quiet. Final master sits at -14.8 LUFS.&lt;/p&gt;
&lt;p&gt;And about the control problem. I do have control, it just is not the kind you get from a fader. I say the chess ticks should come faster and it is one number. I say the beacon should pulse slower and it is one number. All of it is free, all of it is instant, and none of it degrades when I ask for the tenth version.&lt;/p&gt;
&lt;h3 id=&quot;the-final-numbers&quot;&gt;The final numbers&lt;/h3&gt;
&lt;p&gt;The film is 60.65 seconds, 1080x1920, 25 frames per second. It cost &lt;strong&gt;$14.20&lt;/strong&gt; across 72 API calls.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;what &lt;/th&gt;&lt;th&gt;model &lt;/th&gt;&lt;th&gt;calls &lt;/th&gt;&lt;th&gt;cost&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;images &lt;/td&gt;&lt;td&gt;gpt-image-2 &lt;/td&gt;&lt;td&gt;37 &lt;/td&gt;&lt;td&gt;$4.73&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;video &lt;/td&gt;&lt;td&gt;Seedance 2.5 &lt;/td&gt;&lt;td&gt;3 &lt;/td&gt;&lt;td&gt;$4.38&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;video &lt;/td&gt;&lt;td&gt;LTX-2.3 Pro &lt;/td&gt;&lt;td&gt;8 &lt;/td&gt;&lt;td&gt;$3.84&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;video &lt;/td&gt;&lt;td&gt;Kling O3 Pro &lt;/td&gt;&lt;td&gt;1 &lt;/td&gt;&lt;td&gt;$0.56&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;voice &lt;/td&gt;&lt;td&gt;ElevenLabs eleven_v3 &lt;/td&gt;&lt;td&gt;23 &lt;/td&gt;&lt;td&gt;$0.69&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;total&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;72&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;&lt;strong&gt;$14.20&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Everything not in that table was free: the counter, the chess wall, every camera move, the red beacon sweep, the whole sound bed, and all of the editing. Only five of the sixty seconds contain no paid asset at all, the counter and the end card. But the free work is not a section of the film, it is a layer running through all of it.&lt;/p&gt;
&lt;p&gt;Now the part I find more interesting, which is how much of what I paid for is actually on screen:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;made &lt;/th&gt;&lt;th&gt;in the film&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;images &lt;/td&gt;&lt;td&gt;37 &lt;/td&gt;&lt;td&gt;13&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;AI video clips &lt;/td&gt;&lt;td&gt;12 &lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;voice takes &lt;/td&gt;&lt;td&gt;26 &lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;sound stems &lt;/td&gt;&lt;td&gt;13 &lt;/td&gt;&lt;td&gt;13&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;So about a third of the images and half the voice takes were thrown away. That is not waste, that is the process - I cannot tell whether a plate works until I see it moving next to the shot before it. The two most expensive single clips in the whole project, a $1.46 Seedance shot and a $0.56 Kling shot, are both cut. They were attempts at the same ending, and the ending changed.&lt;/p&gt;
&lt;p&gt;And the disk, measured at the peak, just before I deleted the drafts:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;the whole project folder &lt;/td&gt;&lt;td&gt;26 GB&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;27 draft cuts of the same minute &lt;/td&gt;&lt;td&gt;14 GB&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;the final master &lt;/td&gt;&lt;td&gt;756 MB&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;all generated images &lt;/td&gt;&lt;td&gt;126 MB&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;all AI video clips &lt;/td&gt;&lt;td&gt;79 MB&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;all audio &lt;/td&gt;&lt;td&gt;86 MB&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The master is 756 MB for one minute because it is encoded at about 100 Mbps and meant to be handed to YouTube, not watched. The real assets - every image, every clip, every sound - come to under 300 MB together. The other 25 GB is versions.&lt;/p&gt;
&lt;p&gt;The rest of it: 58 scripts in the project (49 shell, 9 Python), 15 shots in the cut, 15 spoken lines, 13 sound stems, and a work log 867 lines long, which is longer than this article.&lt;/p&gt;
&lt;p&gt;Time: about 19 hours across three days.&lt;/p&gt;
&lt;p&gt;For comparison, episode 1 was 59.1 seconds and cost $16.13 across 70 calls, and took me roughly 23 hours. So this one is a second longer, two dollars cheaper, and four hours faster - and it has a chess wall, a galaxy, and a sound design in it. Not because I got better at prompting. Because a bigger share of it was built instead of generated.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://azelianouski.dev/images/sleeper2-ending.jpg&quot; alt=&quot;The starship seen from behind and above, engines lit orange, a small bright galaxy in the black above it, with the words ...to be continued in green phosphor type.&quot;&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26634/how-to-make-ai-videos-production-diary-of-a-one-minute-film</guid>
<pubDate>Wed, 09 Sep 2026 14:02:38 +0000</pubDate>
</item>
<item>
<title>How to start creating AI videos from scratch</title>
<link>https://coderlegion.com/25768/how-to-start-creating-ai-videos-from-scratch</link>
<description>&lt;p&gt;A year ago I couldn't see myself making videos at all. Now I do, and I enjoy it. It's a long process with a lot of planning and visualization in my head. Most of the work is outsourced to AI, but the vision and the directing are mine. I want to describe my process from zero. No tools or workflow - I'll show how I started and where it got me.&lt;/p&gt;
&lt;p&gt;This is work I'm proud of:&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://youtu.be/6IFo_Z5Kcwc&quot;&gt;&lt;img src=&quot;https://azelianouski.dev/images/aivideo-thumb-anime.jpg&quot; alt=&quot;I turned my AI game into a 90s sci-fi anime&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It's a snapshot of something that lived in my head with no way out. Until recently. AI made it possible. Sure, this video holds less value than something created manually. But for me, it's a choice between using AI or not doing it at all. And it's not so easy to build, actually.&lt;/p&gt;
&lt;p&gt;This is what it took:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;three days&lt;/li&gt;
&lt;li&gt;$15&lt;/li&gt;
&lt;li&gt;3 GB on disk, 1.3 GB of that scrap&lt;/li&gt;
&lt;li&gt;1,799 files, 1,480 of them temporary - and 1,297 of those are single frames I pulled out of videos to check one moment&lt;/li&gt;
&lt;li&gt;125 video files&lt;/li&gt;
&lt;li&gt;72 voice takes generated, 8 in the final film&lt;/li&gt;
&lt;li&gt;16 images, 13 on screen&lt;/li&gt;
&lt;li&gt;9 video-model renders, 7 used&lt;/li&gt;
&lt;li&gt;a 2,246-line worklog&lt;/li&gt;
&lt;li&gt;tons of nostalgia&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, even with AI, it's not 3 clicks and a few bucks.&lt;/p&gt;
&lt;h3 id=&quot;first-we-need-to-talk-about-ai-video-in-general&quot;&gt;First, we need to talk about AI video in general&lt;/h3&gt;
&lt;p&gt;When people hear &quot;AI video&quot; they imagine something like&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.youtube.com/@Gossip.Goblin&quot;&gt;Gossip Goblin&lt;/a&gt;,&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.youtube.com/@Jurassic_Smoothie&quot;&gt;Jurassic Smoothie&lt;/a&gt;,&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.youtube.com/channel/UC-qfMIqavKGTyp9E4m3UuVw&quot;&gt;Doopiidoo&lt;/a&gt;.&lt;br&gt;
To me, those guys are artists with their own style and technique. For most people that level is unreachable. It's also very expensive.&lt;/p&gt;
&lt;p&gt;My video is different. Only 35% of it came out of a video model. The rest is static images with&lt;br&gt;
camera moves added afterwards, and screen recordings of the actual game running in a real browser.&lt;br&gt;
That keeps the cost down, and it lets me put cinematic shots and a working web app in the same cut -&lt;br&gt;
which also works for diagrams, code listings and other practical things. I can go full cinematic if&lt;br&gt;
I want to. But it's going to cost me.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;What model do you use?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is the first question I usually hear. It's the wrong one.&lt;/p&gt;
&lt;p&gt;I'll get to the models, but that's not where you start. First you need the automation. I don't do&lt;br&gt;
any of this by hand - I'm the brain and the creativity, AI is the arms. So we start there.&lt;/p&gt;
&lt;h3 id=&quot;the-harness&quot;&gt;The harness&lt;/h3&gt;
&lt;p&gt;Yeah, that fancy word again. I don't like it. It makes a simple thing harder to understand.&lt;/p&gt;
&lt;p&gt;Harnesses came from code assistants. My Claude Code can write code and run bash, and that combo is&lt;br&gt;
enough to rule the world. Or to make some videos for me.&lt;/p&gt;
&lt;p&gt;It all started with a simple ask.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Can you take some images in the folder and turn them into a video?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And magic happened. Claude downloaded a small console program called &lt;code&gt;ffmpeg&lt;/code&gt; and turned three images into a slideshow. And added slow zoom effect on each as a little extra. Well, okay, what's so magical about a slide-show? The editing. With this &lt;code&gt;ffmpeg&lt;/code&gt;, Claude Code could edit any video. Cut it to pieces, extract any frame, edit it and put back, upscale, change the speed, work with sound, add text and effects... All of that in a small console program.&lt;/p&gt;
&lt;p&gt;AI won't be extremely proficient at it in the beginning. But we can teach it.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Wait, do I need Claude Code? A subscription?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yes and no. You need a capable model in a good harness (a code assistant). It can be Codex, Claude Code, Gemini CLI (antigravity-cli), Grok Build, DeepSeek Harness, OpenCode, Pi Agent - the list is long. But you need something capable of working in a terminal on your computer. I'm a Claude Code fanboy, I pay $100 monthly for this. I don't recommend it - I think Codex gives you more for the same money at the moment. If you only have $20 a month, Gemini would be a good choice.&lt;/p&gt;
&lt;p&gt;But you need a good harness on your computer. So you can ask it to create a slide-show from 3 images, and it's capable enough to download &lt;code&gt;ffmpeg&lt;/code&gt; and do that.&lt;/p&gt;
&lt;h4 id=&quot;skill-training&quot;&gt;Skill training&lt;/h4&gt;
&lt;p&gt;Another important reason for a good harness is ability to remember successful attempts in &lt;code&gt;skills&lt;/code&gt;. I think the world doesn't talk about &lt;code&gt;skills&lt;/code&gt; enough. It did talk about &lt;code&gt;MCP&lt;/code&gt; a lot - &lt;code&gt;skills&lt;/code&gt; are better. Whatever harness you choose, make sure it supports skills.&lt;/p&gt;
&lt;p&gt;A skill is basically a lazy-load prompt. It's a single .md file in the right location with a description and some text content. Here is an example of one:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-md&quot;&gt;---
name: video-editing
description: Create videos from images and audio using ffmpeg. Use when the user asks to make a video, combine image with voice, or create video content.
---
## How it works

Uses ffmpeg to combine static images with audio tracks into MP4 videos.

## Modes

### Mode A: Image + Audio → Video (most common)

Combine a single image with an audio file. The image displays for the duration of the audio.

ffmpeg -y -loop 1 -i IMAGE_PATH -i AUDIO_PATH \
  -c:v libx264 -tune stillimage -pix_fmt yuv420p \
  -c:a aac -b:a 192k \
  -shortest \
  -movflags +faststart \
  OUTPUT_PATH

...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This file contains examples of commands, my typical requests, mistakes and problems we encountered, input and output directories - everything the basic Claude Code without the skill cannot know. Claude Code only loads this tiny description of all skills it has. When it sees from the discussion that the skill body might be useful, it loads it dynamically.&lt;/p&gt;
&lt;p&gt;And the main part - I haven't typed it myself.&lt;/p&gt;
&lt;p&gt;At some point, I asked Claude Code to create a skill about video editing with &lt;code&gt;ffmpeg&lt;/code&gt; so we can remember useful commands. When it first tried to edit a video, it didn't do it right. It checked the docs, tried a few things, got my feedback - we spent some time getting the correct result. And the exact steps to achieve this result became a part of the skill. We remembered the successful path.&lt;/p&gt;
&lt;p&gt;This is why I call it the training process. Claude Code learns what I want and how to get it, then remembers it in skills. I never touch those skill-files. &lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Hey, that's a cool zooming effect - add it to the video skill&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After zooming, we discovered my other effects. Claude Code can take a large image and scroll it in a smaller viewport in any direction. It can zoom in and out in all possible directions. It can apply different transition effects between images - all of that went to my &lt;code&gt;ffmpeg&lt;/code&gt; skill. So next time I can say&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Take a web page screen shot so its entire vertical content is captured and create a video of scrolling it down&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And it creates a video as if someone is scrolling the page. A nice trick I use a lot. For example, &lt;a rel=&quot;nofollow&quot; href=&quot;https://youtu.be/nwHEuNbRXXQ?t=46&quot;&gt;here I scroll a page&lt;/a&gt; &lt;/p&gt;
&lt;h4 id=&quot;a-library-of-scars&quot;&gt;A library of scars&lt;/h4&gt;
&lt;p&gt;There is a phrase&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Skills is a collection of scars&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://azelianouski.dev/images/aivideo-scars.jpg&quot; alt=&quot;Four deep claw gashes torn diagonally through a painted metal bulkhead, edges peeled back to bare steel, a red alarm lamp glowing in the corridor to the left.&quot;&gt;&lt;/p&gt;
&lt;p&gt;I have a lot of skills - for video, voices, images, APIs. I even have a skill about filling my taxes. None of them were created by hand. Claude Code created them, Claude Code maintains them. Those skills keep growing, so it's on you to ask the harness to compact and clean them up from time to time. Or split into more granular skills.&lt;/p&gt;
&lt;p&gt;Many of them require additional programs Claude Code creates for itself along the way. For example, it created a whole &lt;code&gt;cli&lt;/code&gt; program to work with Youtube and put the examples of using it into my &lt;code&gt;youtube&lt;/code&gt; skill:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-md&quot;&gt;# Get transcript (plain text - compact, best for analysis)
uv run python mcp/youtube/cli.py transcript &quot;URL&quot; --format text

# Get transcript (JSON segments with timestamps - for precise time references)
uv run python mcp/youtube/cli.py transcript &quot;URL&quot; --format segments

# Get transcript for a time range
uv run python mcp/youtube/cli.py transcript &quot;URL&quot; --format text --start 60 --end 300
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These programs and skills are valuable. That's the result of a journey, of many chats with your AI. And it's portable. It's all markdown files and small command-line programs, so if I ever left Claude Code I'd have to rewire how they get loaded, but I wouldn't lose any of it.&lt;/p&gt;
&lt;h3 id=&quot;hey-i-thought-this-article-was-about-ai-video-&quot;&gt;Hey, I thought this article was about AI video!&lt;/h3&gt;
&lt;p&gt;Where was I...&lt;/p&gt;
&lt;p&gt;It all started from &lt;code&gt;ffmpeg&lt;/code&gt; and a skill for it. But then - where do I get images to edit? And video, and sound? It would be cool if my Claude Code could generate them. So I can ask:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Let's create a slide-show where you explain the game rules. I want minimalistic illustrations matching the transcript in chalkboard style&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And it gives me &lt;a rel=&quot;nofollow&quot; href=&quot;https://youtu.be/6x5awI8HRK0?t=28&quot;&gt;this&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Okay, not right away. We'll first discuss the transcript, then we'll generate an image or two, then we'll generate the rest of the images. Then we'll turn them into slide-show. Then generate the voice. Then try to match the voice to the slide-show timing. There are some complications here, but first - we need to teach the harness to create assets.&lt;/p&gt;
&lt;h3 id=&quot;asset-generation&quot;&gt;Asset generation&lt;/h3&gt;
&lt;h4 id=&quot;images&quot;&gt;Images&lt;/h4&gt;
&lt;p&gt;My personal favorite is &lt;code&gt;gpt-image-2&lt;/code&gt; from OpenAI. To add it to your harness, just pass it &lt;a rel=&quot;nofollow&quot; href=&quot;https://developers.openai.com/api/docs/guides/image-generation&quot;&gt;the doc&lt;/a&gt; and put an API key in .env. The doc page actually lets you copy itself as a markdown file - this is very useful, you click this button and paste it into an .md file which you pass to the harness then. Works better than a URL which it has to scrape.&lt;/p&gt;
&lt;p&gt;If you are not sure where to get an API key or where to put it - just ask the AI, it will help.&lt;/p&gt;
&lt;p&gt;From my statistics (228 images), the average cost is $0.15 per image.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://azelianouski.dev/images/aivideo-stills-collage.jpg&quot; alt=&quot;A six-panel mood board of dark sci-fi stills: a cable-wrapped android standing in a lit doorway, a cluster of security cameras, a long empty corridor under a single lamp, a ventilation fan throwing spokes of light across a floor, and a wall of pipes and machinery.&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once the harness manages to grab your API key and generate an image using the OpenAI API call, ask it to create a skill about this. This improves quality of life in general - it's nice to be able to generate an image when you need it.&lt;/p&gt;
&lt;p&gt;You also need your AI to be able to &quot;see&quot; images, but most LLMs these days can do that without any additional models. Claude Code can do that out of the box.&lt;/p&gt;
&lt;p&gt;Another good image model is Google's &lt;code&gt;gemini-3.1-flash-image&lt;/code&gt;, also known as Nano Banana 2. Same thing - pass the doc and API key. It's cheaper than &lt;code&gt;gpt-image-2&lt;/code&gt;. It's good to have both - variety is nice.&lt;/p&gt;
&lt;h4 id=&quot;video&quot;&gt;Video&lt;/h4&gt;
&lt;h5 id=&quot;the-king&quot;&gt;The king&lt;/h5&gt;
&lt;p&gt;China is dominating here, and &lt;code&gt;Seedance 2.5&lt;/code&gt; is the king right now. There is no official API for it (and most other video models), so you have to find some intermediate providers. I like &lt;a rel=&quot;nofollow&quot; href=&quot;https://fal.ai/&quot;&gt;fal.ai&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; href=&quot;https://evolink.ai/&quot;&gt;EvoLink&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are multiple ways of using this model:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Text-to-video - It's straightforward, but I never use this mode.&lt;/li&gt;
&lt;li&gt;Image-to-video - You attach one or more images and describe how you want to animate them. This is my go-to when I don't need a voice.&lt;/li&gt;
&lt;li&gt;Reference-to-video - You can attach not only images, but also a voice sample. A model will do its best of using the voice sample on a character you want to animate. It will try to do a lipsync as well.&lt;/li&gt;
&lt;li&gt;Voice on and off - Seedance 2+ models can produce video with sound, which is great. The audio is free either way, so it costs you nothing to turn it off. I usually do, and add every sound myself, because then the levels stay under my control in the mix.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;the-king-s-cost&quot;&gt;The king's cost&lt;/h5&gt;
&lt;p&gt;This thing is expensive. One second costs $0.293 at 720p, $0.136 at 480p. And you cannot buy a short clip: Seedance's minimum is 4 seconds.&lt;/p&gt;
&lt;p&gt;Not too terrible, until I give you my real statistics - a minute of Seedance 2.0/2.5 video costs me $39.42 on average.&lt;/p&gt;
&lt;p&gt;Here are the main reasons I overpay:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Poor generation. The thing with AI video is that you prompt and hope. The result might be bad, and you just throw it away and retry.&lt;/li&gt;
&lt;li&gt;AI can make proactive decisions which you don't like afterwards. For example, it might decide to generate multiple videos at a time. And you don't like all of them. It's important to move slow.&lt;/li&gt;
&lt;li&gt;Lip-sync is hard. It isn't always good even with the best models. It's one of the main reasons of re-generation.&lt;/li&gt;
&lt;li&gt;You cannot buy a short clip. Seedance's minimum is 4 seconds, and a film that cuts fast doesn't want 4-second shots. Three shots in my anime short are on screen for about 1.2 seconds each, and each one cost a full 4-second render. I threw away 55% of the generated video just because I didn't need it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And here is a little story of wasted $9.6. My Claude Code decided to help me with the lip-sync struggle. It started generating Seedance videos with voice off and then apply the sound via additional lipsync model run. It found this model on fal.ai and started using it very actively. The issue is - it didn't work with the werewolf head at all. And it took me some time to figure out what is going on. And ban this approach from existence.&lt;/p&gt;
&lt;p&gt;Or at some point Claude Code decided to first generate 480p dry-runs before the final full-size shots (1080p on Seedance 2.0 back then, 720p on 2.5 now). I noticed that we are basically generating each video twice.&lt;/p&gt;
&lt;p&gt;This video below cost me $91 in total. There is where I learned about all the things above.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://youtu.be/cPpwOJtGilQ&quot;&gt;&lt;img src=&quot;https://azelianouski.dev/images/aivideo-thumb-tutorial.jpg&quot; alt=&quot;AI Werewolf - Episode 1&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It got better after this - I started having far fewer wasted generations. So yeah, expensive learning.&lt;/p&gt;
&lt;h5 id=&quot;cheaper-models&quot;&gt;Cheaper models&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;Seedance 2.0 - The previous king, costs 1.5x less. Supports 1080p. In many cases it generates results as good as 2.5.&lt;/li&gt;
&lt;li&gt;Kling 3 - A close competitor to Seedance 2.0.&lt;/li&gt;
&lt;li&gt;LTX-2.3 - 3.7x cheaper than Seedance 2.5 (2.5x cheaper than 2.0), and it renders native 1080x1920 so there's no upscale afterwards. But it's noticeably worse at following instructions. I asked it for &quot;no blink, no head turn, no smile, eyes narrow then hold&quot; and got a head tilt, a look down, a smirk, and a face that drifted. It obeys roughly the first second. What matters is cost per usable second, not cost per second. I recently discovered that it can do good animations with no humans. There is LTX-2.5 but it's 3x more expensive and I see no reason to use it. And it's open-weight, so you can run it locally if you have the hardware.&lt;/li&gt;
&lt;li&gt;Minimax H3 - Current king among open-weight models. I haven't tried it because providers change too much and it costs almost as much as Seedance.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both providers put API docs with examples on the model pages: &lt;a rel=&quot;nofollow&quot; href=&quot;https://fal.ai/models/bytedance/seedance-2.5/reference-to-video/api&quot;&gt;Seedance 2.5 on fal.ai&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; href=&quot;https://evolink.ai/seedance-2-5&quot;&gt;Seedance 2.5 on EvoLink&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;voice&quot;&gt;Voice&lt;/h4&gt;
&lt;p&gt;ElevenLabs is the best. It lets you prompt voice instructions and search for voices in a library. I had to buy a $6/month subscription yesterday because I wanted access to some private voices.&lt;/p&gt;
&lt;p&gt;It is more expensive than the voice models from Google and OpenAI, but it's still cheap. One minute of ElevenLabs costs me $0.40 on average, and that already includes every unsuccessful attempt. &lt;code&gt;gpt-4o-mini-tts&lt;/code&gt; is about 5x cheaper.&lt;/p&gt;
&lt;p&gt;Which means voice is not worth optimising. On my last two videos it was 2% and 2.7% of the total bill.&lt;/p&gt;
&lt;p&gt;A nice thing to have - a local CPU model called &lt;code&gt;kokoro&lt;/code&gt;. You can run it on any hardware, it's very light. And it can generate a decent voice for free. It doesn't have that many configurations, but sometimes it's good to have a free voice. Just ask your harness about it - it will download, setup, and create a skill.&lt;/p&gt;
&lt;h4 id=&quot;music&quot;&gt;Music&lt;/h4&gt;
&lt;p&gt;This one is weird. There are AI music services like Suno, but I don't use them. My Claude Code generates sound effects and synth-soundtracks using... Python. I don't know how it does it. It offered me this option, I liked it, and it stuck. I try to describe what type of background sound I want and it tries to code it.&lt;/p&gt;
&lt;p&gt;Like this:&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://youtu.be/wHPVt-rtMng&quot;&gt;&lt;img src=&quot;https://azelianouski.dev/images/aivideo-thumb-music.jpg&quot; alt=&quot;Procedural music, written in Python&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;pricing-summary&quot;&gt;Pricing summary&lt;/h4&gt;
&lt;p&gt;Here is the full breakdown for the tutorial video above. It's my longest one and the worst from an economy point of view.&lt;/p&gt;
&lt;p&gt;Five minutes forty-four seconds. About &lt;strong&gt;$91&lt;/strong&gt; across roughly 150 API calls.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;where it went &lt;/th&gt;&lt;th&gt;cost &lt;/th&gt;&lt;th&gt;share&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;video generation &lt;/td&gt;&lt;td&gt;~$77 &lt;/td&gt;&lt;td&gt;85%&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;images &lt;/td&gt;&lt;td&gt;~$9 &lt;/td&gt;&lt;td&gt;10%&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;voice &lt;/td&gt;&lt;td&gt;~$5 &lt;/td&gt;&lt;td&gt;5%&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;That 85% is the whole lesson. Images and voice together come to $14, and most of that was thrown&lt;br&gt;
away too. But the video generation was a money pit.&lt;/p&gt;
&lt;p&gt;Another important take: only about &lt;strong&gt;$40 of that $91 is in the final cut&lt;/strong&gt;. The&lt;br&gt;
rest went to finding the shot - retakes, dead ends, draft experiments, and the lip-sync detour&lt;br&gt;
above. You don't know a shot is wrong until you watch it move.&lt;/p&gt;
&lt;p&gt;At the same time, the third of my anime short from the beginning of the article is static images with &lt;code&gt;ffmpeg&lt;/code&gt; camera moves over them, and another third is screen recording. That two thirds cost nothing at all. So before paying a model to animate something, check whether a slow push on a static frame does the same job. Very often it does - and it does exactly what you asked for, which is more than the&lt;br&gt;
video models manage.&lt;/p&gt;
&lt;p&gt;And the storage. &lt;strong&gt;2.6 GB of working files produced an 86 MB video&lt;/strong&gt;, about 30 to 1. Most of that&lt;br&gt;
is 3,012 PNG screenshots taken frame by frame to animate the real game UI, because the app has no&lt;br&gt;
export - every &quot;typing&quot; and &quot;scrolling&quot; effect in that video is a sequence of real screenshots. But I'll leave it for the next article.&lt;/p&gt;
&lt;p&gt;$91 for a 5:44 narrated, character-acted explainer that I barely touched by hand. That's a lot of&lt;br&gt;
money for a tutorial nobody may watch, and it's absurdly cheap for what it is.&lt;/p&gt;
&lt;h3 id=&quot;to-be-continued&quot;&gt;To be continued&lt;/h3&gt;
&lt;p&gt;The article is getting way too long, and I think it's a good idea to split it.&lt;/p&gt;
&lt;p&gt;In the next one, I'll tell about the directing. I'll walk you through the process of using a harness with all the skills ready. Why it takes me days and what is in those 3GB of raw materials.&lt;/p&gt;
&lt;p&gt;Oh, and there is one more important thing - the browser recording. It sounds boring, but trust me - you have no idea how powerful this thing can be. AI can create any web-pages, manipulate the content, create all sort of CSS animations and transitions, record and then inject this into the video.&lt;/p&gt;
&lt;p&gt;There is a computer screen with text appearing on it in the video below. It's actually an HTML page, the text is injected character by character, then the whole thing is put inside of a static image. This is pure craziness to me and a common Tuesday for my AI:&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://youtu.be/emkCeHF_KDM&quot;&gt;&lt;img src=&quot;https://azelianouski.dev/images/aivideo-thumb-sleeper.jpg&quot; alt=&quot;I'm not going to wake them&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is my best work, by the way. I published it yesterday, after I completed the draft of this article, so it only made it into the ending. $16, three days of work. I think I'll focus on this exact video making process in the next part.&lt;/p&gt;
&lt;p&gt;See you next time.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://azelianouski.dev/images/aivideo-host-whiskey.jpg&quot; alt=&quot;A wolf in a black three-piece suit sitting at a polished table in a candlelit library, one clawed paw resting on a glass of whiskey, looking straight at the camera.&quot;&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25768/how-to-start-creating-ai-videos-from-scratch</guid>
<pubDate>Sat, 29 Aug 2026 15:52:20 +0000</pubDate>
</item>
<item>
<title>FFmpeg Beginner Guide</title>
<link>https://coderlegion.com/6717/ffmpeg-beginner-guide</link>
<description>&lt;h3 id=&quot;introduction&quot;&gt;Introduction&lt;/h3&gt;
&lt;p&gt;FFmpeg is one of the most powerful open-source tools for handling &lt;strong&gt;multimedia files&lt;/strong&gt;. Whether you need to &lt;strong&gt;convert, compress, record, or stream audio and video&lt;/strong&gt;, FFmpeg is the go-to command-line utility trusted by developers, content creators, and even large companies.&lt;/p&gt;
&lt;p&gt;It supports &lt;strong&gt;almost every audio and video format&lt;/strong&gt;, making it an essential tool in video editing, streaming, and automation pipelines.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;brief-history&quot;&gt;Brief History&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Released:&lt;/strong&gt; December 20, 2000&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Creator:&lt;/strong&gt; Fabrice Bellard&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Maintained by:&lt;/strong&gt; FFmpeg team &amp;amp; many open-source contributors&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The name &lt;strong&gt;FFmpeg&lt;/strong&gt; comes from:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;FF&lt;/strong&gt; → &quot;Fast Forward&quot;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;MPEG&lt;/strong&gt; → a popular video compression standard (Moving Picture Experts Group)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since its release, FFmpeg has become a backbone for many popular software and platforms including &lt;strong&gt;VLC Media Player, YouTube, and OBS Studio&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-use-ffmpeg-&quot;&gt;Why Use FFmpeg?&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Convert videos between formats (MP4, MKV, AVI, MOV, etc.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Extract or convert audio (MP3, WAV, AAC, etc.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Compress media without losing much quality&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Capture screenshots from videos&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stream audio/video over networks&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Automate video/audio processing in scripts&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;installation&quot;&gt;Installation&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;On Linux (Fedora/Ubuntu):&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo dnf install ffmpeg   # Fedora
sudo apt install ffmpeg   # Ubuntu/Debian
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;On macOS (with Homebrew):&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;brew install ffmpeg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;On Windows:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Download builds from &lt;a rel=&quot;nofollow&quot; href=&quot;http://ffmpeg.org&quot;&gt;ffmpeg.org&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add ffmpeg/bin to your PATH&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;basic-commands-to-get-started&quot;&gt;Basic Commands to get started&lt;/h3&gt;
&lt;h4 id=&quot;1-video-based-commands&quot;&gt;1. Video-based Commands&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;h5 id=&quot;convert-a-video-to-another-format&quot;&gt;Convert a video to another format&lt;/h5&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i input.mp4 output.mkv
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It converts an MP4 file into MKV format.&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;h5 id=&quot;extract-audio-from-a-video&quot;&gt;Extract audio from a video&lt;/h5&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i video.mp4 -q:a 0 -map a audio.mp3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Takes the audio from a video and saves it as MP3.&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;h5 id=&quot;compress-a-video&quot;&gt;Compress a video&lt;/h5&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i input.mp4 -vcodec libx265 -crf 28 output.mp4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Uses the &lt;strong&gt;H.265 codec&lt;/strong&gt; with CRF (quality factor) for compression.&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;h5 id=&quot;capture-a-frame-screenshot-from-a-video&quot;&gt;Capture a frame (screenshot) from a video&lt;/h5&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i video.mp4 -ss 00:00:05 -vframes 1 screenshot.png
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Takes a screenshot at the 5-second mark.&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;h5 id=&quot;record-screen-linux-example-&quot;&gt;Record screen (Linux example)&lt;/h5&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -video_size 1920x1080 -framerate 30 -f x11grab -i :0.0 output.mp4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Records the desktop screen.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;2-image-based-commands&quot;&gt;2. Image-based Commands&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;h5 id=&quot;convert-png-to-jpg&quot;&gt;Convert PNG to JPG&lt;/h5&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i input.png output.jpg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the same way we can convert to other types too.&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;h5 id=&quot;convert-multiple-images-into-a-video&quot;&gt;Convert multiple images into a video&lt;/h5&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -framerate 24 -i img%03d.png output.mp4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you have files like &lt;code&gt;img001.png, img002.png...&lt;/code&gt;, this creates a video at 24 fps.&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;h5 id=&quot;extract-all-frames-from-a-video-as-images&quot;&gt;Extract all frames from a video as images&lt;/h5&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i video.mp4 frame_%04d.png
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It saves every frame as an image: &lt;code&gt;frame_0001.png, frame_0002.png...&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;h5 id=&quot;resize-an-image&quot;&gt;Resize an image&lt;/h5&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i input.png -vf scale=800:600 resized.png
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It resizes an image to &lt;strong&gt;800×600&lt;/strong&gt; pixels.&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;h5 id=&quot;convert-image-format-batch-all-png-jpg-&quot;&gt;Convert image format batch (all PNG → JPG)&lt;/h5&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;for f in *.png; do ffmpeg -i &quot;$f&quot; &quot;${f%.png}.jpg&quot;; done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Converts all &lt;code&gt;.png&lt;/code&gt; files in a folder into &lt;code&gt;.jpg&lt;/code&gt;. (Linux/Mac shell loop)&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;FFmpeg may seem intimidating at first because it’s command-line based, but once you learn a few commands, you can &lt;strong&gt;convert, compress, and manipulate media effortlessly&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It’s free, fast, and reliable, so no wonder it powers some of the biggest platforms in the world.&lt;/p&gt;
</description>
<category>Backend Dev</category>
<guid isPermaLink="true">https://coderlegion.com/6717/ffmpeg-beginner-guide</guid>
<pubDate>Wed, 22 Oct 2025 05:12:36 +0000</pubDate>
</item>
<item>
<title>FFStudio — A Visual Node-Based Frontend for FFmpeg</title>
<link>https://coderlegion.com/6587/ffstudio-a-visual-node-based-frontend-for-ffmpeg</link>
<description>&lt;h2 id=&quot;ffstudio-a-visual-node-based-frontend-for-ffmpeg&quot;&gt;FFStudio — A Visual Node-Based Frontend for FFmpeg&lt;/h2&gt;
&lt;p&gt;If you’ve ever wrestled with complex FFmpeg commands, filter chains, or encoding options, you know how intimidating it can be.&lt;br&gt;
That’s why I built &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://ffstudio.app&quot;&gt;FFStudio&lt;/a&gt;&lt;/strong&gt; — a visual, node-based frontend for FFmpeg that lets you design and execute media workflows &lt;strong&gt;without typing endless command lines&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;motivation&quot;&gt;Motivation&lt;/h3&gt;
&lt;p&gt;FFmpeg is one of the most powerful tools for media processing — but also one of the hardest to master.&lt;br&gt;
I wanted to make that power &lt;strong&gt;accessible and visual&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;With FFStudio, you can &lt;strong&gt;see&lt;/strong&gt; how your filters and encoders connect, preview outputs instantly, and understand your pipeline at a glance.&lt;/p&gt;
&lt;h3 id=&quot;key-features&quot;&gt;Key Features&lt;/h3&gt;
&lt;p&gt;✅ &lt;strong&gt;Visual Node Graph&lt;/strong&gt; — Build and edit FFmpeg workflows without writing commands&lt;br&gt;
✅ &lt;strong&gt;Instant Preview&lt;/strong&gt; — Test short segments before full runs&lt;br&gt;
✅ &lt;strong&gt;Output Comparison&lt;/strong&gt; — Compare different encodings side-by-side&lt;br&gt;
✅ &lt;strong&gt;Dynamic Node Library&lt;/strong&gt; — FFStudio scans your FFmpeg binary to discover filters/codecs&lt;br&gt;
✅ &lt;strong&gt;Templates &amp;amp; Presets&lt;/strong&gt; — Quickly start with ready-made pipelines&lt;br&gt;
✅ &lt;strong&gt;Real-Time Logs&lt;/strong&gt; — View FFmpeg progress and errors directly in the UI  &lt;/p&gt;
&lt;h3 id=&quot;-technical-overview&quot;&gt;️ Technical Overview&lt;/h3&gt;
&lt;p&gt;Under the hood:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Uses FFmpeg via a &lt;strong&gt;native process bridge&lt;/strong&gt; for speed and reliability  &lt;/li&gt;
&lt;li&gt;Auto-parses FFmpeg filters and encoders into graph-compatible node definitions  &lt;/li&gt;
&lt;li&gt;Each connection is &lt;strong&gt;type-checked&lt;/strong&gt; to avoid invalid links  &lt;/li&gt;
&lt;li&gt;Integrates a &lt;strong&gt;real-time log console&lt;/strong&gt; for debugging and progress tracking  &lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;example-workflow&quot;&gt;Example Workflow&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Add an &lt;strong&gt;Input Node&lt;/strong&gt; and select a source file  &lt;/li&gt;
&lt;li&gt;Connect to a &lt;strong&gt;Video Filter Node&lt;/strong&gt; (resize, crop, or color-correct)  &lt;/li&gt;
&lt;li&gt;Connect to an &lt;strong&gt;Encoder Node&lt;/strong&gt; (e.g., H.264 / AAC)  &lt;/li&gt;
&lt;li&gt;Add an &lt;strong&gt;Output Node&lt;/strong&gt; for saving results  &lt;/li&gt;
&lt;li&gt;Use the &lt;strong&gt;Preview&lt;/strong&gt; mode to test 10 seconds  &lt;/li&gt;
&lt;li&gt;Compare outputs (A/B)  &lt;/li&gt;
&lt;li&gt;Run the full render when satisfied!&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;roadmap&quot;&gt;Roadmap&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;[ ] Command import/export improvement&lt;/li&gt;
&lt;li&gt;[ ] Custom user-defined node plugins  &lt;/li&gt;
&lt;li&gt;[ ] More built-in templates  &lt;/li&gt;
&lt;li&gt;[ ] Enhanced comparison and timeline tools  &lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;get-involved&quot;&gt;Get Involved&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Visit &lt;a rel=&quot;nofollow&quot; href=&quot;https://ffstudio.app&quot;&gt;ffstudio.app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Follow updates and contribute on &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Draver93/ff-studio&quot;&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Send feedback or ideas — I’d love to hear how you use FFStudio!&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;p&gt;“I tried it out, and it's pretty awesome. It really helps organize my complex command lines, making them easier to read and edit.” — &lt;em&gt;A happy beta user&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;screenshots&quot;&gt;Screenshots&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=6301293409222461916&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt; Try it now:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://ffstudio.app&quot;&gt;ffstudio.app&lt;/a&gt;&lt;/p&gt;
</description>
<category>Automation Tools</category>
<guid isPermaLink="true">https://coderlegion.com/6587/ffstudio-a-visual-node-based-frontend-for-ffmpeg</guid>
<pubDate>Fri, 17 Oct 2025 19:44:51 +0000</pubDate>
</item>
<item>
<title>DevLog 20250506 C# Video Processing Foundation Library</title>
<link>https://coderlegion.com/3060/devlog-20250506-c-video-processing-foundation-library</link>
<description>&lt;p&gt;Today I need to find a suitable foundational library for video processing. FFMpegCore works for one-shot things but despite a fluent API, its interface is still too command line like.&lt;/p&gt;
&lt;p&gt;Here is what I found out:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;OpenCvSharp             &lt;/th&gt;&lt;th&gt;LibVLCSharp                       &lt;/th&gt;&lt;th&gt;FFMediaToolkit&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Frames&lt;/strong&gt;     &lt;/td&gt;&lt;td&gt;✅ decode / encode       &lt;/td&gt;&lt;td&gt;✅ (via callbacks)                 &lt;/td&gt;&lt;td&gt;❓&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Audio&lt;/strong&gt;      &lt;/td&gt;&lt;td&gt;❌                       &lt;/td&gt;&lt;td&gt;✅ decode / encode / mux           &lt;/td&gt;&lt;td&gt;❓&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Extra libs&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;\~5 MB/platform         &lt;/td&gt;&lt;td&gt;+7 MB                             &lt;/td&gt;&lt;td&gt;+7 MB&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Licence&lt;/strong&gt;    &lt;/td&gt;&lt;td&gt;Apache‑2.0              &lt;/td&gt;&lt;td&gt;LGPLv2.1                          &lt;/td&gt;&lt;td&gt;MIT&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Platforms&lt;/strong&gt;  &lt;/td&gt;&lt;td&gt;Win / mac / Linux / Arm &lt;/td&gt;&lt;td&gt;Win / mac / Linux / iOS / Android &lt;/td&gt;&lt;td&gt;Win / mac / Linux&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;LibVLCSharp seems more focused on directly displaying things (e.g. in Xamarine and WPF) instead of low-level API based processing; FFMediaToolkit, on the other hand, still depends on FFMPEG and can't avoid licensing issue. Eventually, I had to go with separate video + audio processing in favor of OpenCvSharp (it's Apache-2.0 by the way).&lt;/p&gt;
&lt;p&gt;Notice OpenCVSharp has some advantage over EmguCV for custom dev:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;&lt;strong&gt;OpenCvSharp&lt;/strong&gt;                                              &lt;/th&gt;&lt;th&gt;&lt;strong&gt;Emgu.CV&lt;/strong&gt;&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Licence&lt;/strong&gt;                  &lt;/td&gt;&lt;td&gt;Apache-2.0 (fully permissive)                              &lt;/td&gt;&lt;td&gt;GPL v3 or commercial licence&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Interop model&lt;/strong&gt;            &lt;/td&gt;&lt;td&gt;Pure &lt;em&gt;P/Invoke&lt;/em&gt; (no C++/CLI), works on any .NET core runtime &lt;/td&gt;&lt;td&gt;Historically C++/CLI wrapper (needs Windows for full feature set), .NET Standard P/Invoke layer added later&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;NuGet size&lt;/strong&gt; (native)      &lt;/td&gt;&lt;td&gt;4–6 MB per RID (modular runtime packages)                    &lt;/td&gt;&lt;td&gt;30 MB + for the “opencv‑world” monolith, or 90 MB for &lt;em&gt;emgucv‑runtime‑ubuntu&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;GPU (CUDA) support&lt;/strong&gt;       &lt;/td&gt;&lt;td&gt;Provided in separate &lt;em&gt;OpenCvSharp4.runtime.dnn&lt;/em&gt; package      &lt;/td&gt;&lt;td&gt;Official CUDA builds, but Windows‑only binaries unless you compile yourself&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;API surface&lt;/strong&gt;              &lt;/td&gt;&lt;td&gt;\~1:1 with OpenCV C++ names (easy to port C++ samples)       &lt;/td&gt;&lt;td&gt;Thicker .NET façade (e.g. &lt;code&gt;Image&amp;lt;Bgr, byte&amp;gt;&lt;/code&gt; types), extra helpers&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Cross‑platform stability&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;Actively tested on Windows / Linux / macOS / arm64           &lt;/td&gt;&lt;td&gt;Windows the main focus; Linux/macOS possible with &lt;code&gt;libopencv&lt;/code&gt; from distro or big runtime package&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Learning curve&lt;/strong&gt;           &lt;/td&gt;&lt;td&gt;If you know OpenCV C++ you feel at home                      &lt;/td&gt;&lt;td&gt;Higher‑level abstractions can be friendlier for newcomers&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Commercial support&lt;/strong&gt;       &lt;/td&gt;&lt;td&gt;Community‑driven                                             &lt;/td&gt;&lt;td&gt;Paid licences &amp;amp; support available from Emgu CV, Inc.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</description>
<category>C#</category>
<guid isPermaLink="true">https://coderlegion.com/3060/devlog-20250506-c-video-processing-foundation-library</guid>
<pubDate>Fri, 16 May 2025 16:32:17 +0000</pubDate>
</item>
</channel>
</rss>