mcpField
H
hero-demo

hero-demo

github.com/editframeクリエイティブ・メディア1974 回インストール2 週間前に更新
リポジトリ

hero-demo について

Editframe のランディングページ向け Hero Demo は、React のタイムラインコンポーネントで構成された約 51 秒のアニメーションです。8 つのシーン、連続したナレーション、単語単位で同期する字幕を備え、構成や音声生成、字幕調整の手順を開発者向けに記録しています。

主な機能

  1. 018 シーンのタイムライン構成
  2. 02シーンの重なりとクロスフェード
  3. 03連続ナレーションと単語単位の字幕
  4. 04音声のローカル生成と分割
  5. 05字幕タイミングとシーン尺の調整

活用シーン

  1. 01ランディングページの Hero Demo の編集
  2. 02音声の再生成と字幕同期
  3. 03変更履歴内の動画コンポーネントの保守

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/editframe/skills --skill hero-demo

インストール後、Claude Code で/hero-demoを入力して読み込まれたか確認します。

詳細

Hero Demo Composition

Editframe の React/タイムラインコンポーネントで構成された約 51 秒のアニメーション作品で、ランディングページで再生されます。sequence Timegroup 内に 8 つのシーンを配置し、クロスフェードで重ねています。音声ナレーションは途切れずに再生され、キャプションは単語単位で同期します。

File Map

FilePurpose
telecine/services/web/app/components/landing-v5/HeroDemo.tsxコンポジション:DUR 定数、CAPTIONS 配列、8 つのシーン関数、HeroDemoContent レイアウト
telecine/services/web/app/styles/landing.cssすべての hero-* キーフレームアニメーション
telecine/services/web/scripts/generate-hero-voiceover-local.pyTTS 生成と whisper 分割スクリプト
telecine/services/web/public/audio/hero/生成された WAV/MP3 ファイル、timing.json

Architecture

FitScale
  Timegroup mode="contain" (960x540, relative)
    Audio src={VOICEOVER_SRC}           ← single continuous track
    Timegroup mode="sequence" overlapMs=495
      SceneTitle      (3333ms)
      SceneAuthor     (6333ms)
      SceneLayers     (7067ms)
      SceneTimeline   (9767ms)
      SceneEditor     (8200ms)
      SceneTemplate   (5500ms)
      SceneStream     (5700ms)
      SceneRender     (8833ms)
    SceneCaptions groups={CAPTIONS}     ← absolute overlay, global timestamps
  • 重なりは 495 ms(30 fps で 15 フレーム)です。各シーンはこの時間の間にフェードイン/フェードアウトします。
  • 重なりを含む合計時間は約 51.3 秒です。
  • DUR の値は、音声の長さにバッファーを加えたものです。バッファーによって、ナレーションが終わった後もシーンの映像に余裕を持たせます。
  • すべてのアニメーションは CSS のみで実行し、各シーンのローカルタイムラインを基準にした animationDelay で制御します。必要に応じて、ef-text 以外の要素にフィルモードを設定してください(css-animations スキルを参照)。ef-text は自動的に処理します。

Voiceover Script

#SceneText
1タイトルVideo is a web page that moves.
2制作者It starts with HTML and CSS. When you need more, it's just React.
3レイヤーStack layers the way you stack divs. Video, text, shapes, 3D, mix everything.
4タイムラインNeed an editor? Snap together GUI primitives. Timeline, waveforms, captions, into any editing experience you want.
5エディターA full NLE. A simple trim tool in a form. It's your UI. These are just the building blocks.
6テンプレートFeed in data, and one template becomes ten thousand unique videos.
7ストリームPreview is instant. Change the code, see the frame.
8レンダリングWhen it's ready, render to the cloud, the browser, or the command line. Same composition, every target.

メッセージ:Editframe は宣言的なレイヤーです。HTML/CSS が基盤で、その上に React と JS という強力なツールがあります。「React だけ」でも「HTML だけ」でもありません。

テキストのルール:エムダッシュは使わないでください(TTS が間を空けてしまいます)。短く、歯切れのよい文にしてください。

Voiceover Regeneration Workflow

スクリプトのテキストが変わった場合は、以下の手順を正確に実行してください。

Step 1: Update the generation script

telecine/services/web/scripts/generate-hero-voiceover-local.py の SEGMENTS を編集します。各セグメントには次の項目があります。

  • key:ファイル名のプレフィックス(例:01-title)
  • text:ナレーションの文
  • last_word:whisper による分割で使う最後の単語の語幹
  • discard:プリアンブルのセグメントのみ true

最初のセグメントは必ずプリアンブル("discard": True)にし、「Here is the introduction.」のような本編に使わないテキストを設定してください。これは TTS モデルのウォームアップ時に発生するノイズを吸収します。削除しないでください。

セグメントは " ... " のポーズ区切り文字でつなぎ、1 回の TTS 処理で生成します。

Step 2: Generate audio locally
cd telecine/services/web
python scripts/generate-hero-voiceover-local.py

以下が必要です。

  • VoiceDesign モデル(Qwen/Qwen3-TTS-12Hz-1.7B-VoiceDesign)を含む qwen_tts パッケージ
  • whisper、soundfile、numpy、torch

CustomVoice モデル(generate_custom_voice)は使用しないでください。 出力が文字化けします。language="English" を指定した generate_voice_design のみ使用してください。

出力先は telecine/services/web/public/audio/hero/ です。

  • voiceover.wav/.mp3:プリアンブルを含む音声全体
  • 01-title.wav から 08-render.wav/.mp3:シーンごとの分割ファイル(参照用のみ)
  • timing.json:シーンごとの長さ(秒)
Step 3: Trim the preamble

プリアンブルは削除する必要があります。スクリプトのコンソール出力に表示されるプリアンブルの終了時刻を確認し、その位置でトリミングしてから、次を実行します。

ffmpeg -y -i public/audio/hero/voiceover.wav -ss <PREAMBLE_END_SEC> public/audio/hero/voiceover-trimmed.wav
ffmpeg -y -i public/audio/hero/voiceover-trimmed.wav -codec:a libmp3lame -b:a 128k public/audio/hero/voiceover-trimmed.mp3
Step 4: Get global word timestamps

トリミングしたファイルに対して whisper を実行し、グローバルタイムスタンプを取得します(すべてトリミング後の音声の t=0 を基準とします)。

import whisper, json
model = whisper.load_model("small")
result = model.transcribe("public/audio/hero/voiceover-trimmed.wav", language="en", word_timestamps=True)
words = []
for seg in result["segments"]:
    for w in seg.get("words", []):
        words.append({"word": w["word"].strip(), "start": round(w["start"] * 1000), "end": round(w["end"] * 1000)})
with open("/tmp/hero-whisper/global.json", "w") as f:
    json.dump(words, f, indent=2)
ステップ 5:GCS にアップロードする

ファイルのアップロード先は gs://editframe-assets-7ac794b/hero/ です(editframe-assets ではありません)。CDN では https://assets.editframe.com/hero/ から配信されます。

ファイル名にはコンテンツハッシュを含めてください。同じ URL を上書きできないよう、CDN キャッシュが設定されています。

HASH=$(md5 -q public/audio/hero/voiceover-trimmed.mp3 | head -c 8)
gsutil -h "Content-Type:audio/mpeg" -h "Cache-Control:public, max-age=31536000" \
  cp public/audio/hero/voiceover-trimmed.mp3 "gs://editframe-assets-7ac794b/hero/voiceover-${HASH}.mp3"
ステップ 6:HeroDemo.tsx を更新する

更新する項目は 3 つです。

  1. VOICEOVER_SRC — コンテンツハッシュを含む新しい CDN URL を設定します。

  2. CAPTIONS 配列 — グローバルな whisper タイムスタンプから再構築します。自然な文やフレーズの区切りに合わせて、単語を字幕グループにまとめます。各グループには次の値が必要です。

    • showMs:グループ内の最初の単語のタイムスタンプ(または少し前の時刻)
    • hideMs:最後の単語の終了時刻 + 約 400ms の余裕
    • words:whisper の出力から得た { w, s, e } の配列

字幕グループは文の区切りで分け、対応するシーンに合わせてください。1 つのシーンに複数のグループを割り当てるのが一般的です。タイムスタンプは単一のナレーショントラックから取得したグローバルな値であり、シーンごとの相対値ではありません。

  1. DUR 定数 — 音声の長さが大きく変わった場合は、シーンの長さを調整します。計算式は DUR.scene = voiceDurationMs + buffer です。余裕時間は通常 800〜1200ms です。30fps に合うよう、33.33ms の倍数に丸めてください。シーンごとの音声の長さは timing.json で確認できます。
ステップ 7:確認する

ブラウザーでコンポジションを再生し、次の点を確認します。

  • 字幕内の単語が音声と同期してハイライトされる
  • シーンの切り替えでナレーションが文の途中で途切れない
  • シーン間に無音の隙間がない(オーバーラップが自然に感じられる)

シーンの編集

各シーンは、明示的な duration={DUR.xxx}ms を持つ Timegroup mode="fixed" を返す関数(SceneTitle、SceneAuthor など)です。

よく使うパターン:

  • sceneStyle(d) は CSS アニメーションでクロスフェードの開始と終了を適用します
  • ビジュアルアニメーションでは、ずらしや順序付けに animationDelay を使用します。ef-text はデフォルトで fill-mode を自動設定しますが、通常の要素には明示的な fill-mode が必要です
  • CompositionCanvas は R3F Three.js のコンテンツ(SceneStream のパーティクルなど)をラップします
  • <Text split="char"> は文字単位のずらしアニメーションに使用します

アニメーションを追加・編集するときは、landing.css に hero- プレフィックスを付けた新しいキーフレームを定義してください。

Changelog MDX の作成

変更履歴のエントリーは telecine/services/web/app/content/changelogs/{version}.mdx にあります。各エントリーでは、HeroDemo と同じコンポーネントセットを MDX の props 経由で使用します。

コンポーネント API

ChangelogIntroCard — 必須の props:version、codename、title。TypeScript は MDX の作成時に検証しませんが、title prop は必須です。title がないと、実行時に title.trim() がエラーをスローし、ハイドレーションが失敗してページに空のプレースホルダーが表示されます。illustration prop は、MDX との互換性のために残されている何もしないスタブです。

<ChangelogIntroCard
  version="0.47.0"
  codename="Mocha Relay"
  title="Short title for the card display"
  durationMs={4000}
/>

ReleaseVideo — 子要素を独自の <Timegroup mode="contain"> でラップします。シーン要素は、内側の <Timegroup mode="sequence"> の直接の子として渡してください。

<ReleaseVideo aspect="16/9">
  <Timegroup mode="sequence" overlapMs={600} style={{ width: 1920, height: 1080, position: "relative" }}>
    <ChangelogIntroCard ... />
    <TextMoment ... />
    <ChangelogOutroCard ... />
  </Timegroup>
</ReleaseVideo>

ChangelogOutroCard — version と tagline は必須です。durationMs にはデフォルト値があります。

TextMoment — headline と body は必須です。motif、accentColor、durationMs は省略可能です。

注意点:ページ読み込み時に空のプレースホルダーが表示される

ReleaseVideo が空の灰色のボックスとして表示され、プレーヤーにハイドレーションされない場合、ほとんどの場合、原因はシーンコンポーネントの実行時エラーです。ReleaseVideo は isClient によるゲーティングを使用するため、SSR では常にプレースホルダーが表示されます。シーンコンポーネントのエラーはクライアントのハイドレーションを黙って失敗させます。ブラウザーのコンソールで、エラーをスローしているコンポーネントの TypeError を確認してください。

注意点

  • TTS CustomVoice モデルは動作しません:VoiceDesign(generate_voice_design)のみを使用してください。CustomVoice では意味不明な音声が生成されます。
  • プリアンブルが必要です:TTS 生成の最初の 1〜2 秒はノイズが入ることがあります。このノイズを使い捨てのプリアンブルで吸収します。アップロードする前に必ず切り取ってください。
  • GCS のファイル名の再利用:CDN は積極的にキャッシュします。新しい音声をアップロードする際は、必ずコンテンツハッシュを含むファイル名を使用してください。
  • キャプションのタイムスタンプは全体基準です:シーンごとのタイムスタンプではなく、1 本にまとめたトリミング済みのナレーションを whisper にかけた結果です。シーンごとの相対時間にしないでください。
  • CompositionCanvas のレンダリング:同期エクスポートパイプラインで動作するよう、flushSync と useLayoutEffect を使用します。手動で gl.render()/gl.finish() を呼び出さないでください。GPU の処理が二重になります。
  • ナレーションテキスト内のエムダッシュ:TTS 出力で不要な間が生じます。代わりにカンマまたはピリオドを使用してください。

コメント