デモ動画を簡単に作成してGIF・MP4に変換するツールをGoogle Colabで作りました。
更新履歴
| 日付 | 内容 |
|---|---|
| 2026/03/29 | BGM追加機能を実装(MP4出力時のみ)。 ・YouTube URLから音声をダウンロードし、開始位置・速度・音量を調整して合成 ・メタデータ解析による高度なライセンス自動検知を追加 ・スライダー操作がリアルタイムでオーディオプレイヤーに反映 |
| 2026/03/08 | 全セルに # @title / # @markdown フォームモードを適用。ズームUIを刷新: ・「🏁 終了位置を記録」ボタンを追加し終了時刻を直接指定可能に ・キープ時間を自動計算へ変更 ・イベントの折りたたみ表示・選択ハイライト機能を追加 |
| 2026/02/24 | 画面録画のズームアップ機能を刷新。プレビュー再生中に開始時刻を自動反映し、重複ズームを防止。 |
開発したツールやプロダクトをGitHubのREADMEやSNSで紹介するとき、デモ動画やGIFを添付すると伝わり方が全然違いますよね。 でも、いざ動画を変換しようとすると「SaaSはサブスクが必要」「ローカルツールは重い」という壁にぶつかります。
そこで、Google Colabの無料環境でffmpegを動かして、画面録画をそのままGIF・MP4に変換できるツールを作りました。ブラウザだけで完結するので、環境構築もソフトのインストールも不要です。
作ったもの
面倒な環境構築は不要です。以下のリンクからブラウザ上ですぐに実行できます。
⚡️ Google Colab で実行する
Make Demo GIF / MP4(日本語版)
クリックして再生ボタンを上から押すだけで動きます。🐙 GitHub でコードを見る
hiroaki-com/colab-video-converter
ソースコードの確認や、Star / Fork はこちらから。
なぜこれを作ったのか
GitHubのREADMEにGIFを貼ると、プロジェクトの第一印象がぐっと良くなります。動いている様子が一目でわかるので、読んでもらえる確率も上がる気がしています。
ただ、画面録画をGIFに変換する作業が地味に面倒でした。
SaaSツールはブラウザで完結して便利だけど、品質設定を細かく触ろうとすると有料プランが必要だったりする。ローカルのGUIツールはOSのアップデートのたびに動かなくなったりして管理が煩わしい。
エンジニアとしては、動画変換ツールの使い方をキャッチアップすることは学習の本質ではないし、SaaSにお金を払い続けるのも気が引けます。どうせなら自分で作って、好きなように使い回せるものにしようと思いました。
Google Colabなら無料でffmpegが動かせるし、UIをノートブック上に作れば設定もGUIで完結する。そういう発想でこのツールを作りました。
使い方
セルを上から順に実行して、UIを操作するだけです。Pythonのコードを書く必要はありません。各セルには # @title によるフォームモードが適用されており、Colabのフォームビューで実行するとすっきりした画面で操作できます。
1. セットアップ
最初のセルを実行すると、ffmpegとyt-dlpが自動でインストールされます。エラー出力を扱いやすくするための _ffmpeg ヘルパー関数もここで定義されます。最初に一度だけ実行してください。
2. 動画をアップロード
ファイル選択ダイアログが開くので、変換したい動画を選択します。複数ファイルを選択すると結合できます。
対応入力形式: .mov .mp4 .avi .mkv .webm
3. 結合順を指定(複数ファイルの場合)
複数の動画をアップロードした場合、ドロップダウンで結合順を指定します。1本の場合は自動的にスキップされます。
4. 出力形式・品質・速度を設定
ラジオボタンで出力形式と品質を選択します。
出力形式の選び方
| 形式 | 特性 | 向いている用途 | 向かない用途 |
|---|---|---|---|
| GIF | 自動再生・ループ・あらゆるMarkdownで動く。256色・ファイル大 | GitHub README・Zenn / Qiita | ファイルサイズが厳しい場面 |
| MP4 (H.264) | 高品質・小容量・フルカラー。再生にはプレイヤーが必要。 | X / Slack / GitHub README(クリック再生) | 自動再生・ループが必須の場面 |
GIF品質プリセット
| プリセット | 横幅 | FPS | 色数 | 用途 |
|---|---|---|---|---|
| GitHub README用 | 960px | 15fps | 256 | README埋め込み(推奨) |
| SNS・軽量用 | 640px | 10fps | 128 | ファイルサイズ重視 |
| 高品質 | 1280px | 20fps | 256 | 品質重視 |
| カスタム | 任意 | 任意 | 任意 | 細かく調整 |
MP4品質プリセット
| プリセット | CRF | 備考 |
|---|---|---|
| 高品質 | 18 | ファイル大きめ |
| 標準 | 23 | バランス重視(推奨) |
| 軽量 | 28 | ファイル小さめ |
| カスタム | 0–51 | 細かく調整 |
再生速度も調整できます。長い録画を1.5〜2.0倍速にするだけで、ファイルサイズもぐっと小さくなります。
5. プレビュー生成
再生ボタン(▶)を押すとプレビューが生成されます。最終出力と同じ解像度・フレームレートのプレビュー動画が作成されるので、ズーム設定のためのタイムスタンプ確認に使います。設定を変更してプレビューを再生成したい場合は、出力形式・品質の設定を変更してこのセルを再実行するだけです。最初からやり直す必要はありません。
プレビューは高速プリセットを使い、音声なしで生成されます。速度変更(setptsフィルター)もプレビュー段階で反映されます。
6. プレビュー確認
プレビュー動画がノートブック内で再生コントロール付きで表示されます。再生・一時停止を使って、ズーム効果を適用したい正確なタイムスタンプを確認してください。
7. ズーム設定(任意)
デモ動画の特定箇所を強調するズーム効果を追加できます。
使い方:
- 「ズームを追加する」にチェックを入れると、最初のズームイベントが自動で追加されます
- セル内のプレビュー動画を再生し、ズームを開始したいシーンで一時停止
- 「📍 開始位置を記録」ボタンを押すと、現在の再生位置が選択中イベントの開始時刻に自動反映されます
- ズームを終了したいシーンで一時停止し、「🏁 終了位置を記録」ボタンを押すと終了時刻に反映されます。キープ(最大倍率の維持)時間はイン・アウト時間から自動計算されます
- 各イベントのヘッダーはクリックで折りたためます。「対象イベントに指定」ボタンまたはドロップダウンで記録先のイベントを切り替えると、選択中イベントが緑のハイライトで示されます
- 「+ Zoomイベント追加」で複数のズームポイントを追加可能。新しいイベントを追加すると既存イベントは自動で折りたたまれ、次のイベントの開始時刻は直前イベントの終了時刻から自動算出されます
- 各イベントで以下を設定:
- ズームエリア: 3×3グリッドから選択(左上、中央、右下など)
- 最大倍率: ズームのピーク倍率(1.1倍〜5.0倍)
- 開始(秒): ズームを開始する時刻(📍ボタンで反映可)
- イン(秒): 等倍から最大倍率まで拡大する時間
- アウト(秒): 最大倍率から等倍まで縮小する時間
- 終了(秒): ズームエフェクトが完全に終わる時刻(🏁ボタンで反映可)。キープ時間は
終了 − 開始 − イン − アウトで自動計算されます
タイムラインの例: 開始=5.0、イン=0.3、アウト=0.3、終了=6.6と設定すると:
- 5.0秒で開始
- 0.3秒かけて等倍から最大倍率へズームイン(5.0 → 5.3秒)
- 1.0秒間最大倍率をキープ(5.3 → 6.3秒)← 自動計算
- 0.3秒かけて最大倍率から等倍へズームアウト(6.3 → 6.6秒)
- 6.6秒以降は通常再生に戻る
ヒント:
- ズームイベントが時間的に重複している場合、最終出力時にエラーで通知されます
- 3×3グリッドで画面上の特定のUI要素に簡単にズームできます
- 「ズームを追加する」のチェックを外すとすべてのズーム効果が無効になります
活用例:
- UIウォークスルーでボタンクリックに注目を集める
- エディタでのコード変更を強調表示
- ダッシュボードデモで特定データを拡大
- フォーム入力のデモで入力フィールドにズーム
8. BGM設定(任意・MP4出力時のみ)
MP4出力時、YouTubeの動画・音楽をBGMとして追加できます。
使い方:
- 「BGMを追加する」にチェックを入れる(GIF出力時は無効)
- YouTube URLを入力し「ダウンロード」をクリック。メタデータを取得後、音声ファイルをダウンロードします
- 取得した音声のプレビューを再生しながら、以下の項目を調整:
- 開始位置(秒): 音声のどの地点から流し始めるか。再生しながら「📍 開始位置を記録」ボタンで反映可能
- ⚡ 速度: BGM自体の再生速度(0.5x〜2.0x)
- 🔊 音量: 0.0〜1.0で調整(スライダー操作がリアルタイムでプレイヤーに反映されます)
- ⑨ で最終出力を生成
自動ループ / カット: 動画の長さに対して、指定した開始位置以降の音声(速度反映後)が短い場合は自動でループし、長い場合は動画終了時点でカットされます。
ライセンス検知: ダウンロード時にメタデータを解析し、以下のバッジで状態を表示します:
| バッジ | 判定条件 |
|---|---|
| 🚫 商用楽曲検知 | artist / track フィールドが存在する場合 |
| 🚫 公式ライセンス表記あり | 概要欄に "Licensed to YouTube by" が含まれる場合 |
| ✅ フリー素材の可能性が高い | タイトル・投稿者名に NCS / No Copyright 等のキーワードが含まれる場合 |
| ✅ Creative Commons | ライセンスフィールドに CC 表記がある場合 |
| ⚠️ ライセンス不明 | 上記のいずれにも該当しない場合 |
いずれのケースも最終的な権利確認はご自身でお願いします。
9. 最終出力
再生ボタン(▶)を押すと、すべての設定(ズーム効果・BGMを含む)を適用した最終出力が生成されます。ズーム設定を調整して再出力したい場合は、ズーム設定を変更してこのセルを再実行します。
プレビュー生成後に解像度設定を変更した場合、最終出力時に不整合を検出して再実行を促すエラーが出ます。これにより、プレビューと最終出力の設定ズレを防いでいます。
最終出力では:
- GIF: 選択したディザリング方式による完全なパレット生成
- MP4(BGMなし): 最大圧縮効率を実現するスロープリセット(音声なし)
- MP4(BGMあり): 速度・音量を適用した音声を合成。動画の長さに応じて自動ループまたはカット
10. 保存先を選択
ラジオボタンで保存先を選択します。
| 保存先 | 説明 |
|---|---|
| ローカルにダウンロード | ブラウザのダウンロードダイアログで保存 |
| Google Drive に保存 | 指定パスに自動コピー(マウント処理含む) |
| 両方 | 両方の処理を同時実行 |
11. 保存実行
保存先の選択後にこのセルを実行すると、ファイルの書き出し処理が走ります。保存先選択と保存実行をステップ分けすることで、設定の見直しをしやすくしています。
主な機能と技術的なポイント
いくつか工夫した点を紹介します。
-
高品質なGIF変換
palettegen / paletteuse はFFmpegに搭載されたGIF専用の2パスフィルターです。
palettegenが映像全体から最適な256色パレットを生成し、paletteuseがそのパレットを使って各フレームを描画します。1パス変換に比べて色再現性が大幅に向上します。GIFの変換品質はffmpegのフィルター設定に大きく依存します。このツールでは
palettegen+paletteuseの2パスアプローチを採用しています。[0:v] fps=15,split [a][b];
[a] palettegen=max_colors=256:stats_mode=full [p];
[b][p] paletteuse=dither=floyd_steinberg:diff_mode=rectanglelanczosフィルターでリサイズ品質を上げ、floyd_steinbergディザリングで色のグラデーションをなめらかに表現します。カスタム設定ではbayerディザリングも選べるので、ファイルサイズと品質のトレードオフを調整できます。 -
ファイルサイズの自動警告
os.path.getsize() はPython標準ライブラリ
osが提供する関数で、指定したファイルのサイズをバイト単位で返します。外部ライブラリ不要で使えるため、変換直後のサイズチェックに適しています。GIFが15MBを超えた場合、ノートブックが自動で警告を表示して軽量化の手順を案内します。GitHubのGIFサイズ上限は10MBなので、READMEに貼る前に気づけるようにしています。
-
再生速度変更
setpts(Set Presentation Timestamps)はFFmpegの映像フィルターで、各フレームのタイムスタンプを書き換えることで再生速度を変更します。
PTS/1.5のように除算すると1.5倍速になります。音声を含む場合は別途atempoフィルターで音声側も変速する必要があります。setptsフィルターで速度を変えています。長めの操作録画を1.5〜2.0倍速にするだけで、ファイルサイズもぐっと小さくなります。速度変更はプレビュー生成段階で適用済みのため、最終出力ではプレビューの映像をそのまま使い回せます。speed_filter = f'setpts=PTS/{speed}' # 1.5倍速なら setpts=PTS/1.5 -
複数動画の結合
FFmpeg concat demuxer は複数の動画ファイルを順番につなぐための仕組みです。
-c copyオプションを組み合わせると映像・音声を再エンコードせずにそのままコピーするため、画質劣化なしに高速な結合が可能です。ffmpegのconcatを使い、
-c copyでコーデックを再エンコードせずにコピーするので結合は高速です。ドロップダウンUIで結合順を自由に指定できます。ファイル名に特殊文字が含まれる場合のエスケープ処理も入れています。 -
プレビューと最終出力の分離
FFmpeg
-presetオプション はエンコード速度と圧縮効率のトレードオフを制御します。fastは処理が速い代わりにファイルがやや大きくなり、slowは時間をかけて最高の圧縮率を実現します。用途に応じて使い分けることで、繰り返し確認は高速に・最終出力は高品質にできます。ワークフローをプレビュー生成と最終出力に分離しました。これにより、ズーム効果を設定する前に高品質プレビューで正確なタイムスタンプを確認でき、ベース動画を再生成せずにズーム設定だけを繰り返し調整できます。最終出力ではプレビューとの解像度整合性チェックも行われるため、設定変更の見落としも防げます。
-
FFmpeg zoompanフィルターによるズーム機能
zoompan はFFmpegの映像フィルターで、フレームごとにズーム倍率(z)・表示起点のx/y座標を数式で指定することで、動的なパン&ズーム効果を実現します。数式内でFFmpegの組み込み変数(
on=フレーム番号、iw/ih=入力幅/高さ)を使えるため、タイムライン連動の複雑なズームも記述できます。ズーム機能はFFmpegの
zoompanフィルターを使い、なめらかで動的なズーム効果を実現しています。各ズームイベントは、フレームごとにズームレベル、x座標、y座標を制御する数式に変換されます。x/y座標の式内でzoompanの
z変数を参照できないという制約に対応し、z_exprをそのままインライン展開することで、ズーム中心がずれる問題を修正しています。# 例: 中央(エリア5)に最大2.0倍でズーム
# タイムライン: 開始=5.0、イン=0.3、終了=6.6(キープ=1.0自動計算、アウト=0.3)
zoompan=z='if(gte(on,150),if(lt(on,159),(1+(2.0-1)*(on-150)/9),\
if(lt(on,189),2.0,\
if(lt(on,198),(2.0-(2.0-1)*(on-189)/9),1))),1)':
x='if(between(on,150,198),floor(max(0,min(iw-iw/(z_expr),480-iw/(2*(z_expr))))),0)':
y='if(between(on,150,198),floor(max(0,min(ih-ih/(z_expr),270-ih/(2*(z_expr))))),0)':
d=1:s=960x540:fps=15ツールは自動的に:
- タイムスタンプを動画のFPSに基づいてフレーム番号に変換
- 3×3グリッドの位置からズーム中心座標を計算
- ズームイン・アウトの遷移用になめらかな補間カーブを生成
- 複数のズームイベントを開始時刻でソートし、重複があれば実行前にエラーで通知
-
開始・終了位置の記録ボタン
google.colab.kernel.invokeFunction はGoogle Colab固有のAPIで、セル出力内のJavaScriptからPython側の関数を呼び出せます。
colab_output.register_callback('名前', fn)で関数を登録しておき、HTML内のonclickからgoogle.colab.kernel.invokeFunction('名前', [引数], {})で呼び出す、という構成になっています。ズーム設定のセル内にプレビュー動画を埋め込み、任意の位置で止めてボタンを押すだけで現在位置をイベントの時刻に反映できます。
google.colab.kernel.invokeFunctionを使ってJavaScriptからPythonのコールバックを呼び出しています。def _record_position(time):
if _zoom_start_widgets and record_target_sel.value is not None:
_zoom_start_widgets[record_target_sel.value].value = round(float(time), 2)
def _record_end_position(time):
if _zoom_end_widgets and record_target_sel.value is not None:
_zoom_end_widgets[record_target_sel.value].value = round(float(time), 2)
colab_output.register_callback('record_position', _record_position)
colab_output.register_callback('record_end_position', _record_end_position) -
折りたたみ式イベントUIとハイライト
ipywidgets はJupyterノートブック(およびGoogle Colab)上でインタラクティブなUIウィジェットを表示するためのライブラリです。
VBox/HBoxでウィジェットをレイアウトし、LayoutオブジェクトでCSS相当のスタイルを設定できます。ウィジェットのプロパティを動的に書き換えるだけでUIが即座に更新される点が特徴です。複数のズームイベントを管理しやすくするため、各イベントを折りたたみ式のコンテナに収めました。新しいイベントを追加すると既存イベントは自動で折りたたまれ、現在の対象イベントは緑のボーダーでハイライトされます。「対象イベントに指定」ボタンで任意のイベントを記録対象に切り替えられます。
-
BGM合成:
atempoフィルターとストリームループatempo はFFmpegの音声フィルターで、ピッチを変えずに再生速度だけを変更します(タイムストレッチ)。ただし一度に適用できる範囲は0.5〜2.0倍に限定されており、それ以上の変速が必要な場合は
atempo=2.0,atempo=2.0のように連結します。-stream_loop -1は入力ストリームを無限に繰り返すオプションで、-tによる時間指定と組み合わせることで「短ければループ・長ければカット」を条件分岐なく1コマンドで実現できます。BGM機能の核心は2段階の処理です。まずダウンロードした音声に速度変更と音量調整を適用して中間ファイルを生成し、それを映像に合成します。
# 速度・音量を適用した中間ファイルを生成
_ffmpeg(
'-ss', start_pos,
'-i', bgm_raw_path,
'-vn', '-af', f'atempo={speed},volume={vol}',
'-c:a', 'aac', '-b:a', '128k', '-ac', '2',
'bgm_segment.aac'
)
# -stream_loop -1 + -t {dur} で短ければループ・長ければカットを1コマンドで解決
_ffmpeg(
'-i', 'preview.mp4',
'-stream_loop', '-1', '-i', 'bgm_segment.aac',
'-t', vid_dur,
*VIDEO_ARGS, *AUDIO_ARGS,
OUTPUT_NAME
)速度変更にはffmpegの
atempoフィルターを使っています。atempoは0.5〜2.0の範囲しか受け付けないため、UIのスライダー範囲もそれに合わせています。ループとカットは-stream_loop -1(無限ループ)と-t {動画尺}(時間でカット)の組み合わせで、条件分岐なく1コマンドで処理できます。 -
スライダーのリアルタイムプレビュー
Widget.observe() はipywidgetsが提供するイベントリスナーで、ウィジェットのプロパティ変化を監視してコールバックを呼び出します。
names='value'を指定すると値の変更時のみ発火します。コールバック内でdisplay(Javascript(...))を実行するとColabのセル出力にJavaScriptが注入され、DOMを直接操作できます。速度・音量スライダーを動かすと、ノートブック内のオーディオプレイヤーにリアルタイムで反映されます。
ipywidgetsのobserveからJavaScriptをdisplay(Javascript(...))で発火させています。プレイヤーが存在する場合のみ実行するガードを入れることで、セル出力の不要な蓄積を防いでいます。def _on_bgm_speed_change(change):
_update_duration_hint()
if bgm_audio_ready:
display(Javascript(
f"var a=document.getElementById('bgm_audio_player');"
f"if(a) a.playbackRate={change['new']};"
))
bgm_speed_slider.observe(_on_bgm_speed_change, names='value')またオーディオプレイヤーの初期化には
oncanplayイベントを使い、ロード完了を待ってからスライダーの現在値を適用しています。_initフラグを立てることで、同じ初期化が重複実行されるのを防いでいます。<audio id="bgm_audio_player" controls
oncanplay="if(!this._init){
this.volume={vol};
this.playbackRate={speed};
this._init=true;
}"> -
メタデータによるライセンス自動検知
yt-dlp
--dump-jsonはURLで指定した動画のメタデータをJSON形式で標準出力に書き出すオプションです。実際のダウンロードは行わないため高速で、title・uploader・artist・track・license・descriptionなど豊富なフィールドを取得できます。yt-dlpの
--dump-jsonで取得したメタデータを解析し、著作権リスクを段階的に判定します。artist/trackフィールドの有無(音楽配信されている商用楽曲に付与される)、概要欄の "Licensed to YouTube by" 文字列、投稿者名・タイトルへのフリー素材キーワードマッチング(NCS、nocopyright等)、という順で評価します。メタデータだけでは確実な判断はできないので、あくまで参考情報として表示する設計にしています。 -
効率的なプレビューワークフロー
FFmpeg
-presetオプション(libx264) はultrafast〜veryslowの段階でエンコード速度と圧縮効率を切り替えます。速いプリセットほどCPU負荷が低くファイルサイズがやや大きくなり、遅いプリセットほど時間をかけて高い圧縮率を実現します。同じCRF値でもslowとfastではファイルサイズに顕著な差が出ます。プレビュー生成は
fastプリセットで実行されるため、繰り返しの調整が高速です。最終出力ではslowプリセットを使用して最大の圧縮効率を実現します。ズーム効果が設定されていない場合、最終出力ではプレビューの映像ストリームをそのまま再エンコードするだけで済むため、短時間で完了します。
サービス別のサイズ制約
貼り先によって上限が異なるので参考までに。Zennの3MB制限はかなり厳しく、SNS・軽量プリセット(640px / 10fps / 128色)+1.5倍速の組み合わせが現実的なラインです。
| プラットフォーム | 対応形式 | サイズ上限 |
|---|---|---|
| GitHub README | GIF / MP4 / MOV | 100 MB(動画)/ 10 MB(GIF) |
| Zenn | GIF | 3 MB |
| Qiita | GIF / MP4 | 100 MB |
| X(標準) | MP4 / MOV | 512 MB |
| Slack | GIF / MP4 / MOV 他 | 1 GB |
まとめ
「変換ツールのサブスクを払いたくない」「ローカルに余計なソフトを入れたくない」、その二つが揃ったので自作しました。
Google Colabのノートブックという形にしたことで、ブラウザさえあればどこでも使えて、設定もGUIで完結します。今回のアップデートではBGM追加機能を実装しました。YouTubeのURLを貼るだけで音声をダウンロードでき、開始位置・速度・音量をスライダーで調整しながら最終的なMP4に合成できます。スライダーの値がリアルタイムでオーディオプレイヤーに反映されるので、仕上がりのイメージを確認しながら設定できるのが地味に便利です。
同じように「デモ動画の変換、毎回面倒だな」「画面録画の特定箇所を強調したい」「BGMを添えてもっと印象的なデモにしたい」と感じている方の参考になれば幸いです。