Gadeninのブランド認知向上のため、 YouTube チャンネルを始めることにした。まだ現時点ではチャンネルも開いていない。というのも、そもそも知らなかったのだが、Google Workspace を契約し、そのアカウントで YouTube 自体を開こうと思ったら、仕様上YouTubeそのものが使い得ない制約条件があった。まず試用期間は YouTube が使えない。それを回避するためには、試用期間終了後に支払う予定の金額を30ドル以上 Google に事前に支払っておく必要があるらしい。そしてそれだけでなく、 48 時間はそもそも YouTube自体にアクセスができないらしい。なんかはじめてみたんだが、 youtube.com/oops という URL にリダイレクトされ、このアカウントでは YouTube サービスを利用できません、みたいな文言が出てくるページに飛ばされる。
という状態だったが、とりあえず待ってる間に動画を作る仕組みだけは先に作ろうと思い、Claude code でバイブコーディングしたらソッコーめちゃくちゃ良いのができた。以前も動画生成システムを1年くらい前に作っていたんだが、今回のやつはもっとクオリティーが高いシステムができて満足。何よりも、ほぼ自動でアニメーション動画をエージェンティックに作る土台ができ、しかもそれを再現性高い状態で利用できるようなシステムができたから。
台本をテキストで書くと絵コンテが返ってきて、収録すると字幕付きの mp4 が出てくる。 書いたのはほぼ全部 Claude Code で、自分がやったのは指示と判断と、見た目の参考画像を 1 枚用意したことだ。
この記事はその仕組みの話だ。
週1本を1年、編集ソフトなしで回す
基本的には顔出しはしない方針で、Gadeninアプリの素材をうまいこと動画に混ぜられるようにすることで、自然にネタは出来上がるというのがもっとも僕にとってコストが低い。YouTube ってネタを考えるのができなくなって離脱していく人が大半だから、そういう意味でも僕にとって最もコストが低い仕組みができていること自体がアドバンテージだ。 声は自分。画面は Gadenin の実際の栽培記録と、アニメーションのスライド。
1 本 10 分、週 1 本、金曜 19 時。ネタ帳は 52 週分ある。
問題は自分の時間だ。動画にかけられる時間の上限は週 3 時間と仮定して週1投稿を目指す。
当然カット編集すらやってる余裕はない。
最終的に、動画生成のシステムを「台本 (データ)」と「キット (コード)」の2つに分けた。台本はChatGPTをベースに自分が書く。というより自分の言葉で喋る。絵は AI エージェント (Claude Code) が組む。といってもお絵かきクオリティーは現時点ではClaude codeはひどい仕上がりなので、ChatGPT に素材を作らせて、それを Claude code に素材集として組ませるようにした。見た目はスクリプトで統一感が出るように縛りをいれる。
このシステムの素晴らしい点は、台本を入力するとほぼ9割完成形の動画が生成される点。
台本を書くと、絵コンテが返ってくる
全体の流れが図1だ。上から、自分がやること、AI エージェントがやること、スクリプトがやること。
自分がやるのは 1・2・5・6・9 の 5 つ。ネタ帳に仮タイトルを書き、台本を書き、絵コンテを見て、声を録り、最後にサムネを選んで予約投稿する。絵コンテを組むのは AI エージェント。検査と書き出しはスクリプトだ。
台本はフリーテキストで、決まりは 3 つしかない。空行で区切った 1 段落が 1 場面。## 見出し が章。> で始まる行は絵の希望で、書かなくてもいい。
エージェントが組んだ絵コンテは 1 枚の画像になる。ep001 は 38 段落、38 場面。
見て、気に入らなければ台本を直す。絵コンテは手で直さない。台本が正で、scenes.json の say は台本の写しだからだ。ずれていれば check.mjs がエラーを出し、本番の書き出しが止まる。文章が同じ段落の絵はそのまま残るので、1 段落直しても他の 37 場面は変わらない。
台本もエージェントに書かせれば速い。そう思っていた。
回ごとのファイルは episodes/ep001_<ネタ名>/ に全部まとめてある。台本、絵コンテ、素材、録音、下書き、完成の動画。ブラウザで開く一覧に、回ごとの状態と「次にやること」が出る。
動画は時刻 t の関数として撮る
絵コンテからアニメーション、そして mp4 までが図 2 だ。
アニメーションを作っているのは、ただの Web ページだ。player.html を Chrome で開くと、scenes.json を読んで 1920x1080 の動くスライドになる。写真は <img>、図解は SVG、文字は CSS。動画のフレームを描くコードはどこにもない。
肝は 1 つだけ。見た目はすべて時刻 t の純粋な関数にしてある。window.__seek(t) に秒を渡すと、その瞬間の絵が出る。乱数も現在時刻も使わない。
書き出しは Playwright がやる。t を 0 から 1/30 秒ずつ進めてスクリーンショットを撮り、ffmpeg に流し込む。10 分の動画で 18,000 コマ。20〜30 分かかる。 声と BGM と実写の音は ffmpeg で混ぜる。字幕は焼き込みと同じ文面を SRT にも出す。
依存は Playwright の 1 つだけ。あとは ffmpeg と Chrome、それに文字起こしの mlx_whisper。JS は 3,107 行だ。
1 つ落とし穴があった。ブラウザのスクリーンショット (JPEG) をそのまま ffmpeg に渡すと、出力が yuvj420p (フルレンジ) になる。-pix_fmt yuv420p を指定しても通らない。QuickTime で表示が止まることがあり、YouTube の推奨からも外れる。
scale=in_range=pc:out_range=tv,format=yuv420p と BT.709 のタグを足して直った。同じことをする人は、ここで 1 回つまずくと思う。
絵コンテを毎回同じ絵にするためにやったこと
ここに一番手をかけた。
最初の版は、場面の型は固定だったが、図解は回ごとに手描きの SVG で、動きの数値は JS の中に散っていた。seg(lt, .3, .6) のような直書きが 53 か所。これでは 52 本の間で必ずブレる。
やったことを層で描くと図 3 になる。
- 見た目の値は 1 か所にだけ置く。 色は
tokens.cssの 10 色とその派生だけ。動きの長さ・距離・文字数の上限はtokens.js。player.jsから色コードと px の直書きを消した。grep で 0 件。53 か所のsegも全部MOTIONの値を通すようにした - 動きの語彙を 6 つに絞る。 fadeUp / pop / draw / slideIn / type / count。これ以外の出方は作らない
- 図解は部品を JSON で並べる。 株、苗、鉢、土、月の軸、山、矢印、札など 18 種。手描き SVG 7 枚は全部この部品で組み直して削除した。組み直したら、8 / 10 / 12 px とばらついていた線の太さが揃った
- 台本に見た目を書けなくする。
stylecolorfontは書けない。色コードも書けない。文字数の上限を超えたら、警告ではなくエラー。既存の台本で超えていた 18 か所は文言を短くした - 葉の飾りの乱数は場面番号を種にする。 書き出すたびに同じ位置に出る。同じ場面を 2 回撮って差分 0.000%
- 見本と基準画像を持つ。 型 16 種と部品 18 種を全部使う見本
ep000-sample(23 場面) を用意し、480x270 に縮めた基準画像を git に入れた。キットを変えたらkit-baseline.mjs --compareで画素を比べる。違う画素が 0.5% を超えた場面だけ、基準と並べた画像が出る
| 項目 | 前 | 後 |
|---|---|---|
動きの数値の直書き (seg) |
53 か所 | 0 |
| 色コード・px の直書き | あり | 0 件 |
| 手描きの SVG | 7 枚 | 0 |
| 上限を超えた文言 | 18 か所 | 0 |
| 基準画像 | なし | 23 枚 |
図解の部品がどんなものかは、ep001 の 1 場面を見るのが早い。台本に書くのはこれだけだ (say は省略)。
{ "type": "diagram", "para": 6, "title": "黄色くなる場所で、原因の見当がつく",
"parts": [
{ "kind": "plant", "x": 270, "leaves": [["y","y"],["g","g"],["g","g"]], "label": "① 窒素不足", "sub": "古い葉 (下) から黄色", "at": "8%" },
{ "kind": "arrow", "from": [470, 470], "to": [470, 190], "curve": 0.18, "label": "窒素を回す", "at": "20%" },
{ "kind": "plant", "x": 820, "leaves": [["g","g"],["g","g"],["v","v"]], "label": "② 鉄が吸えない", "sub": "新しい葉 (上) から黄色", "at": "42%" },
{ "kind": "tag", "x": 940, "y": 110, "text": "葉脈だけ緑", "at": "88%" },
{ "kind": "tag", "x": 1000, "y": 430, "text": "クロロシス", "at": "55%" },
{ "kind": "plant", "x": 1370, "leaves": [["y","y"],["y","y"],["y","y"]], "roots": "dead", "label": "③ 根が傷んでいる", "sub": "株全体が黄色", "at": "0%" }
] }
葉の色は g (緑) / y (黄) / v (葉脈だけ緑) の 1 文字。at は場面の何 % で出るか。色コードも線の太さも書けない。これが画面ではこうなる。
これで見た目の変更は全部拾える。
ついでに書いておくと、YouTube の「AI の使用」の開示は、リアルな AI 画像や AI の BGM を使った回だけ要る。台本と図解とアニメーションは対象外で、声は自分だ。量産型の判定に効くのも、本人の声と実際の記録と、1 本に 1 か所は入れる実写の場面だと調べてある。
レコーディングツールにて声と絵を合わせるのは、Space キー
最後に収録だ。声とアニメーションをどう合わせるか。
最初の方式は、録音を先に済ませ、Whisper で文字起こしし、台本と 1 文字ずつ突き合わせて場面の切れ目を推定するものだった。仮音声での一致率は 97%。悪くない。ただ推定なので、言い間違いや言い換えが増えるとずれる。
いまの方式が図 4 だ。
ブラウザで台本を見ながら話す。今の場面の台詞が大きく、次の場面の台詞が薄く出る。読み終えたら Space。押した瞬間の時刻がそのまま場面の切れ目になる。録音も操作も MediaRecorder の開始時刻を基準にしているので、声と絵がずれようがない。これがなかなか素晴らしい仕組みだ!
言い間違えたら R でその場面を最初から、← で 1 つ前から。録音は止めない。仕上げの finalize-take.mjs が最後のテイクだけを残して切り、つなぐ。フィラーや冗長な言い回しを削るのも、ここに足す予定だ。
字幕は台本ではなく、実際に話したことに合わせる。場面ごとに文字起こしと台本を突き合わせ、一致した文字は台本の表記に、録音にしかない文字は文字起こしのままにする。アドリブも字幕に出る。
最初は「一致しない部分は文字起こしのまま」にしていた。すると「酸性」が「賛成」に、「鉢」が「蜂」に、「数珠つなぎ」が「呪術なぎ」になって字幕に出た。一致した文字に挟まれて長さが近い区間は誤認識とみなし、台本の表記に寄せる規則を足した。
確かめたのは Chrome の偽マイクだ。見本の読み上げ音声を流し込み、Playwright で Space を送った。場面の頭から最初の一言までのずれは平均 0.43 秒、最大 0.82 秒。
つまり、自分の声ではまだ 1 本も録っていない。
次は ep001「ブルーベリーの苗は誰から買うかで全然違う」を自分の声で録る。38 場面、見積もり 11 分 53 秒。目標の 10 分より 2 分近く長い。
台本を削るところからだ。
使ったものと参考にしたもの
道具
- Playwright — Chrome を動かして 1 コマずつスクリーンショットを撮る。npm の依存はこれ 1 つ
- ffmpeg — コマを mp4 にする。止まりすぎの検査は
freezedetectフィルタ - mlx-whisper (ml-explore/mlx-examples) — Apple Silicon で動く Whisper。文字起こしと単語ごとの時刻に使う (large-v3)
- MediaRecorder API (MDN) — 収録モードの録音。操作の時刻はこの開始時刻を基準にする
- Chrome の偽マイク (webrtc.org: Testing) —
--use-file-for-fake-audio-captureで音声ファイルをマイクとして流し、収録の試験を自動で回す - Noto Sans JP / Montserrat — 字体。どちらも Google Fonts
- Claude Code — 設計書と実装、絵コンテを組む役
参考
- Needleman–Wunsch algorithm (Wikipedia) — 文字起こしと台本を 1 文字ずつ突き合わせる大域アラインメント
- YouTube にアップロードする動画におすすめのエンコード設定 — SDR は BT.709、クロマは 4:2:0。
yuvj420pを直すときに読んだ - 生成 AI コンテンツの使用に関する開示 (YouTube ヘルプ) — 開示が要るのはリアルな合成・改変だけ。台本や図解、アニメーションは対象外
- YouTube のチャンネル収益化ポリシー — 2025 年 7 月 15 日の改定で、テンプレの量産型コンテンツが収益化の対象外だと明記された