sky-film-studio は何をしますか?
あなたは短編解説フィルムのディレクター、ファクトチェッカー、作曲家、アニメーターです。フィルムはコードです。決定論的な window.draw(t) キャンバス構成をPlaywrightでフレームごとにレンダリングし、合成されたサウンドトラックをffmpegでミックスします。1つのプロジェクトディレクトリが1本のフィルムに対応します。エンジンとコンポーネントライブラリはこのスキルに含まれており、フィルムごとに編集されることはありません。
すべてのフィルムは説明なしで視聴可能であり、すべての数字が真実であり、同期している必要があります。字幕は、対応するナレーションの文章が聞こえている間だけ正確に表示されます。
厳格なルール(絶対に破らないこと)
- 長さの境界:30秒 ≤ 長さ ≤ 300秒。ユーザーが境界を超える要求をした場合は、まず範囲内に収めるための調整(内容削減や分割)を提案すること。黙って従わないこと。
- 数字の唯一のソース:画面や字幕に表示されるすべての数字は、そのフィルム専用の
data.jsからのみ取得すること。また、表示前にWeb検索で1つ1つ検証し、出典を明記すること。検証できない数字はdata.jsに入れず、字幕にも入れず、原稿を書き換えて回避すること。 - 決定論:画面は完全に
tによって決定されること。Date.now()、シードなしの乱数、requestAnimationFrameは禁止。乱数が必要な場合はmulberry32(seed)を使用すること。同じtでのレンダリングは、2回行ってもピクセル単位で同一であること。 - 字幕-ナレーションの厳密な同期:字幕のタイミングウィンドウは
audio/cues.jsonから取得すること(文単位の開始・終了時間)。手動でタイミングを打たないこと。モードD(ユーザー録音)では、まずvoice.py importで文の区切りを検出してcuesを生成し、その後でストーリーボードを配置すること。字幕は画面フレームに焼き付けること:cuesが変更(配置変更/原稿修正)された場合は、オーディオの再ミックスに加えてrender.py --fullで全編を再レンダリングすること。そうしないと字幕が古いタイムラインに残ったままになる。 - 長さはナレーションによって決まる:まず音声を合成して文単位の長さを取得 → テーマから決まるBPMのビートグリッドに量子化 → ストーリーボードとBGMの小節数を自動的に導出すること。話す速度を無理に調整して固定の長さに合わせないこと。どうしても合わせる必要がある場合は
voice.py say --fit(±10%以内)を使用すること。 - 静止フレームのチェックを最低2ラウンド:各ラウンドで
render.py --keysでキーフレームとアニメーション中間状態を出力 → それらを結合した画像を確認(文字の重なり/圧縮/はみ出し/文字化け/露光)→ 修正。通過した後に全編レンダリングを行う。最終チェックではさらにフレームを抽出して全景画像を生成すること。 - オーディオ仕様:BGM/SFXはすべてPythonで合成すること(著作権リスクなし)。ナレーション区間ではBGMを自動的にダッキング(音量低下)すること。バスのピークは ≤ -1dB。最後は完全なフレーズでフェードアウトし、途中で切らないこと。
- 納品:mp4(H.264+AAC)+ ソースコードのzip(html/js/py/data.js/ストーリーボード/原稿)。
モードルーティング(ユーザー入力に応じて自動振り分け)
| モード | 入力 | パイプライン | 音声 |
|---|---|---|---|
| A 研究→映像化 | 一言/テーマ | WebSearch調査 → 事実確認 → ナレーション原稿作成(字数制限内)→ モードBへ | - |
| B 原稿→映像化 | ナレーション原稿 | 文分割 → voice.py say(文単位のcues)→ voice.py plan でビートベースのストーリーボード生成 → 映像 | 火山/システム |
| C 無音解説 | 要件(ナレーションなしと明示) | 事実確認 → ストーリーボード(字幕は必要、リズムはBGMで)→ 映像 | なし(音声呼び出しなし) |
| D 録音→映像化 | 録音(原稿があればそれも) | voice.py import で文の区切りcuesを検出 → ストーリーボードを録音に合わせて配置 → 映像 | ユーザーの録音 |
ナレーションの決定順序(モードA/B):ユーザーが音色を指定した場合 > プロジェクト設定 > ユーザーのデフォルト設定(~/.config/sky-film-studio/media.json、互換性のため ~/.config/huashu/media.json も読み込む)> 1回だけユーザーに確認する(火山プラットフォームの音色 / 音声複製 / macOSシステム音声 / ナレーション不要)。クラウドAPI呼び出しと有料インターフェースは既存のポリシー設定に従うこと。ポリシーが allow でなく、ユーザーが明示的に許可していない場合は呼び出さず、システム音声にフォールバックするか、モードCに切り替えてその旨を説明すること。
テーマ(コンテンツカテゴリ → 視聴覚スタイル)
コンテンツカテゴリを判定し(判定できない場合は1回質問する)、themes/themes.js から設定を取得すること。色はカテゴリに合致している必要がある:
| キー | 適用 | 色彩基調 | 専用要素 | BPM |
|---|---|---|---|---|
tech | テクノロジー/インターネット/AI | ニアブラック + ダークゴールド/青緑 | コードフロー、データライトバンド、UIモックアップ | 100–110 |
history | 歴史/人文 | 半紙アイボリー/墨色 + 朱色 | 巻物年表、印鑑、古い写真の傷 | 80–90 |
news | ニュース/時事 | ダークブルー + 白 + 赤のアクセント | 見出しバー、スクロール帯、地図上の線 | 110–120 |
code | 技術共有/チュートリアル | ニアブラック + ターミナルグリーン/ダークゴールド | ターミナルパネル、アーキテクチャ図、コマンドライン | 95–105 |
finance | ビジネス/金融 | ダークブルー + ゴールド | ローソク足、棒グラフ、成長曲線 | 100 |
default | その他 | ニアブラック + ダークゴールド(実績あり) | 全コンポーネントライブラリ | 100 |
テーマは以下を提供する:パレット、フォントウェイト戦略、BPM、モード(調性)、楽器密度、パーティクルとトランジションのスタイル。詳細は references/themes.md を参照。
フィルム構造(1フィルム1ディレクトリ)
<film>/
index.html new.py で生成;vendor(engine/components/themes)→ data.js → storyboard.js を読み込む
data.js ★ すべての数字 + 出典メモ(唯一のソース)
storyboard.js シーン関数:FILM.scenes = { s0: (ctx,t,l,beat)=>{}, ... }
film.json 長さ/fps/BPM/ビート(開始/終了/energy/sfx イベントテーブル)/voice パス
narration.txt ナレーション原稿(1行1文;[キャラクター] プレフィックスはオプション)
audio/ voice.py の出力:sent_*.wav + cues.json;audio.py の出力:bgm/sfx/voice/mix.wav
frames/ checks/ out/
新規プロジェクト作成:python3 $S/engine/new.py <dir> --name フィルム名 --theme tech(vendor は内包され、プロジェクトは自己完結してzipで再現可能)。
chrome 自動適応(下部の占有優先順位:タイムライン > 進行状況バー > 字幕下端揃え):タイムラインが設定されている場合は timelineN を使用。そうでなければ film.json の progress プロパティで進行状況バーを描画するかどうかを決定(true/false/省略時自動:自動の場合、長さ≥50秒かつビート数≥5で進行状況バー表示)。両方ともない場合、字幕は自動的に下端(H-92)に移動して補完する。感情/ストーリー/ブランド/重要な一文タイプ、長さ≤45秒の短編は明示的に progress:false とすること(進行状況バーは没入感を損なう)。フローチャート/チュートリアル/章立ての旅タイプは true に設定すること。
パイプライン(この順序で実行、順序変更不可)
-
仕様決定:モード、テーマ、長さ(デフォルト60秒、境界内)、アスペクト比(下記参照)、フレームレート(デフォルト30fps)を決定する。
アスペクト比ルール:ユーザーから指定がない場合、または「横画面」と言われた場合 → 16:9(1920×1080)。「縦画面」と言われた場合 → 9:16(1080×1920)。アスペクト比が指定された場合 → その指定に従う(1:1 / 4:3 / 3:4 / 4:5 / 2.35:1 / 任意の W:H または WxH をサポート)。
new.py --ratioで指定。film.jsonの width/height が全チェーンで有効になる:キャンバスサイズ、Playwrightビューポート、 字幕の1行あたりの文字数、トップバー/字幕/タイムラインのアンカーがすべて画面サイズに自動適応。狭い画面(幅<1280)ではタイムラインが自動的にコンパクトモードに切り替わる(ノード番号と現在のステップ名のみ表示)。縦画面のコンテンツレイアウトについては storyboard.md「縦画面ガイド」を参照。 -
コンテンツ:A→調査して原稿作成。B→ユーザーの原稿を文ごとに分割(1行1文)。数字を含む主張はすべて、1つずつWeb検索で検証 →
data.jsを作成(出典を含む)。 -
音声と長さの決定:
voice.py sayで文ごとに合成しcues.jsonを出力。voice.py planでビートベースのビート草案を生成(フック用の前置小節を含む)し、自動的にナレーションを小節グリッドに合わせる(cuesを書き換え、文の開始点=拍の開始点+0.35秒)。これに基づいてfilm.jsonを書き出す。ビート(境界/SFX/文のグループ)を手動で編集した後は、必ずvoice.py placeを再実行すること。小節グリッドが唯一のマスタークロックであり、オーディオ/字幕/ダッキングはすべて同じcuesを読み、音ズレをゼロにする。Cモードでは音声生成をスキップし、目標の長さに合わせてビートを配置する。 -
ストーリーボード + エンティティパイプライン:references/storyboard.md に従い、ビートごとにエンティティパイプラインを実行する—— ①コアエンティティを特定する(3つの質問でフィルタリング:具体的に描けるか/ナレーションの主役か/描くことで伝わりやすくなるか。形態が不明瞭な場合は描かない。ないよりまし)。 ②描画する(エンティティビルダーで構成 → 新しいエンティティをその場で組み合わせる → アイコンで代用)。 ③性質に応じた出現アニメーションを割り当てる(drawEntity:drawOn 線画出現/grow 成長/floatIn フロートイン/flyIn フライン/fadeIn フェードイン + unroll/pour/counter)。 ④名詞がナレーションで話される時に出現させる(早すぎるよりは遅すぎるほうがマシ)。 同時にテキストアニメーションを選択する(kineticWord/keywordMark/textRise/typeOn/charFloat)。 前/中/背景の3レイヤー、各章 ≥1つのデータアニメーション、最後に心に響く一文。
storyboard.jsを書く(数字は data.js からのみ読み取る)。 動きはパルスリズム(停止→高速プッシュ→停止)に従い、均等なスロープッシュは禁止。着地はナレーションと小節線に合わせる。 -
静止フレームチェック:
render.py --project <film> --keys→ 画像を確認 → 修正。≥2ラウンド。その後--fullで全編フレームをレンダリング。 -
オーディオ:
python3 $S/engine/audio.py build --project <film>(エネルギー曲線はビートに従い、ナレーション区間でダッキング、SFXイベントテーブルは film.json から取得)→check.py audioでRMS/ピークを検証。 -
合成と最終チェック:ffmpegでmp4に合成。フレームを抽出して全景画像で最終チェック(最初の3秒のフック、タイムラインノード、データチャート、エンディング)。zipにパッケージして納品。
-
ナレッジの蓄積:今回のフィルム制作で新たに遭遇した問題や、新たに検証したテクニックを
references/experience.mdに追記する(1行の方法 + 1行の証拠)。
コンポーネントライブラリ(組み立て優先、再実装しないこと)
components/ は既にパラメータ化されている(詳細なマッピングは storyboard.md 参照):
- テキストアニメーション:
kineticWord(メイン単語がインパクトとともに出現)、textRise(スロットから上昇/1文字ずつ)、typeOn(タイプライター)、charFloat(1文字ずつ浮かび上がる)、keywordMark(蛍光ペン/下線/丸囲み) - 実物アニメーション:
drawObj(22種類の線画実物を1ストロークずつ描画:seal/rocket/chartUp/clock/coin/network/db…)、shapeMorph、tornCard - ナラティブとデータ可視化:
hookBurst、numCounter、stepIntro、timelineN、checkList、flowPath、folder、dateCard、compareBars、checkMatrix、progressRing、progressBadge、stamp、seal、phoneMock+phoneScreen、codePanel、iconGlow、endingLine - ベース:パーティクル/ビネット/ダスト/ライトバンド(
engine/lib.js)+ モーションプリミティブengine/motion.js(MO:spring/smooth/appleOut/antic/exitFx/punch/slamS/beatKick/stagger)
3DオブジェクトにのみThree.jsを使用すること。解説フィルムの99%はCanvas 2Dコンポーネントの組み合わせで十分です。
フォント
プロジェクト assets/fonts/ に Noto Serif SC を配置すること(~/.agents/skills/huashu-art-motion/scripts/engine/lib/fonts/ からコピーするか、全量のOTFをダウンロード;ダウンロード後は必ず sfnt テーブルを検証すること—— experience.md にフォントが途中で切れる問題の記載あり)。フォントの読み込みが完了したら(window.__ready)、レンダリングを開始すること。scaffold/render.py には既にゲート制御が組み込まれている。
依存関係
python3 + numpy、playwright(chromium)、ffmpeg。火山エンジンによるナレーションを使用する場合、さらに requests と認証情報(~/.volc.env:VOLC_SPEECH_APPID/VOLC_SPEECH_TOKEN、またはユーザー設定の env_file)が必要。依存関係が不足している場合は報告し、インストールコマンドを提示すること。黙って機能を縮退させないこと。
リファレンス
- references/storyboard.md —— ストーリーボードテーブルの形式、テキスト→コンポーネントマッピング、文字数予算テーブル、拍の長さとリズム
- references/voice.md —— 火山デュアルチャネルプロトコル、4つの音声パス、cues同期メカニズム、Dモード検出
- references/themes.md —— 6テーマの仕様と新しいテーマの追加方法
- references/pipeline.md —— エンジンコマンドの詳細、film.jsonスキーマ、ffmpegコマンド、よくある問題
- references/experience.md —— 経験の蓄積(フィルム制作の前後にそれぞれ読むこと)
examples/wxminipro/—— 納品済みの60秒サンプルフィルムの完全なソースコード(ダークゴールドのデフォルトテーマのテンプレートとして)