Communityコーディング&開発github.com

piyush140104/screen-record-skills

Claude Codeスキル:スクリプトベースのブラウザウォークスルー動画を作成。ElevenLabsによるナレーション、BGM、クリック効果音、字幕を合成します。

screen-record-skills とは?

このスキルは、Claude Codeエージェントがユーザーの要望に基づき、ブラウザ操作を自動化した高品質なウォークスルー動画を生成するためのものです。主にLinkedInやTwitter、営業資料、投資家向けアップデート、オンボーディング資料などで利用される、洗練されたデモ動画を作成します。ユーザーが画面の内容、カーソルの動き、ナレーションの概要を指定すると、エージェントがスクリプトを書き、エンジンがカーソルアニメーション、ElevenLabsによるテキスト読み上げ(TTS)、BGM、クリック効果音、字幕の焼き込みを自動処理します。このスキルはClaude Code上で動作し、Playwrightを使用してヘッドレスChromiumを制御、ffmpegで音声・動画を合成します。APIキーはElevenLabsの無料枠(月10,000文字)で十分利用可能であり、出力はLinkedIn対応の1920×1080.mp4と.srt字幕ファイルです。

対応✓Claude Code~Codex CLI✓Cursor
npx skills add piyush140104/screen-record-skills

Installed? Explore more コーディング&開発 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

/screen-record — スクリプトベースのブラウザウォークスルー動画

使用するタイミング

ユーザーがウェブサイトの動作を示す洗練された動画を必要としている場合 — 典型的にはLinkedIn、Twitter、営業資料、投資家向けアップデート、オンボーディング資料など。ユーザーは画面に表示すべき内容、カーソルの動作、ナレーションの大まかな内容を説明します。あなたがスクリプトを作成し、エンジンがカーソルアニメーション、ElevenLabsによるTTS、BGM、クリック効果音、字幕の焼き込みを処理します。

このスキルを使用しない場合:実際の実行中のセッションのスクリーンレコーディング(macOSのスクリーンショットまたはQuickTimeを使用)、GIF(GIFツールを使用)、または実際の人間の声によるナレーションが必要な動画(スクリプトを作成して、ユーザーに再録音してもらうことは可能)。

仕組み

エンジンは engine.mjs にあります。runDemo(config) をエクスポートし、以下の処理を行います:

  1. 各キューに対してElevenLabsで1つのTTSクリップを生成。
  2. ffmpegでソフトなアンビエントBGMとUIクリック効果音を生成。
  3. ヘッドレスChromiumを1920×1080で起動し、動画を録画。
  4. あなたの scenes(helpers) 関数を実行し、ブラウザを操作。
  5. 音声(ナレーション大+BGM小+クリック小)をミックスし、ASSスタイルの字幕を焼き込み。
  6. 最終的な .mp4、.webm、.srt、.ass のパスを返す。

ワークフロー

ステップ1 — ユーザーのリクエストを読む

以下の項目を確定する:記録するURL、フロー(カーソルが何をするか)、トーン(フォーマル/カジュアル)、長さの目標。不足がある場合は、推測せずに1つの焦点を絞った質問をする。

ステップ2 — APIキーを確認する

以下の順序で ELEVENLABS_API_KEY を探す:

  1. $CLAUDE_PLUGIN_ROOT/.env.local(またはスキルフォルダ自身の .env.local)
  2. 現在のプロジェクトの .env.local
  3. process.env

存在しない場合、ユーザーに伝える:

ElevenLabs APIキーが必要です。https://elevenlabs.io/app/settings/api-keys から無料のキーを取得できます(月10,000文字、約15デモ分に十分)。チャットに貼り付けてスキルの .env.local に保存するか、ご自身で追加してください。

キーは存在するがエンジンが ApiKeyDeadError(HTTP 401/402/403)をスローした場合、ユーザーに伝える:

ElevenLabsキーが拒否されました(取り消し、期限切れ、またはクォータ超過)。https://elevenlabs.io/app/settings/api-keys でローテートし、.env.local を更新してください。更新されるまで再試行しません。

ユーザーが明示的に要求しない限り、静かに再試行したり、macOSの say にフォールバックしたりしないでください。

ステップ3 — 音声を選択する

デフォルトは Liam(TX3LPaxmHKxFdv7VOQHJ) — 活発なアメリカ英語の男性、モダン。George(イギリス英語)やSarah(アメリカ英語女性)よりも軽め。他の無料枠の音声は engine.mjs の FREE_TIER_VOICES にあります。ユーザーが「X音声を使って」と言った場合、カタログからその音声を探す。「深い/重い/軽い/暖かい/女性」と言った場合は、それに応じて選択する。

ライブラリ音声(Charlotte、Rachel、Adamなど)は有料のElevenLabsサブスクリプションが必要です。ユーザーが無料枠でこれを要求した場合、HTTP 402が返る — 最も近い無料枠の音声にフォールバックし、その旨をユーザーに伝える。

ステップ4 — 対象ページを調査する(必要な場合のみ)

ページの構造がわからない場合は、Playwrightまたは curl を使用してDOMを調査し、セレクタを見つける。推測しない — セレクタが重要で、あいまいなマッチは録画をタイムアウトさせる。

動的コンテンツを持つSPAの場合は、クラス名よりも aria-label、role、テキストセレクタを優先する。page.locator(s).first() のセマンティクスを使用する(エンジンのヘルパーは既にこれを行っている)。

ステップ5 — シーンファイルを作成する

/tmp/screen-record-<name>.mjs にファイルを作成し(ユーザーのリポジトリを汚染しない)、エンジンをインポートして runDemo を呼び出す。テンプレート:

import { runDemo } from '/Users/<you>/Desktop/Projects/skill/screen-record-skills/engine.mjs';
// または、シンボリックリンクでインストールした場合:
// import { runDemo } from '/Users/<you>/.claude/skills/screen-record/engine.mjs';

await runDemo({
  baseUrl: 'http://localhost:3000',
  outputDir: './output/onboarding-demo',
  outputName: 'onboarding',

  splash: {
    label: 'Visit us at',
    url:   'www.yourapp.com',
    footer: 'YourApp — onboarding flow',
  },

  cues: [
    { id: 'intro',   text: 'Welcome to YourApp. Let me show you around.' },
    { id: 'signup',  text: 'Tap Sign Up to create an account.' },
    { id: 'form',    text: 'Enter your email and a password.' },
    { id: 'done',    text: 'And that is it — you are in.' },
  ],

  // オプション:デモを決定論的にするためのモックバックエンド。
  routes: [
    { match: /\/api\/signup$/, fulfill: { status: 200, body: { ok: true } } },
  ],

  // オプション:認証済みユーザーを事前設定してログインをスキップ。
  // initialAuth: { id: 'u1', email: '[email protected]', name: 'Demo User' },

  scenes: async ({ page, baseUrl, sleep, speak, awaitCue,
                   clickAt, typeInto, moveCursorTo, hideSplash, typeSplashUrl,
                   configSwitch, showSwitch, hideSwitch, setAuth }) => {

    // シーン0: URLスプラッシュ(エンジンは既にオフカメラでbaseUrlに移動済み)。
    speak('intro');
    await typeSplashUrl('www.yourapp.com');
    await awaitCue('intro');
    await hideSplash();
    await sleep(750); // スプラッシュフェードに合わせる

    // シーン1: サインアップをクリック
    speak('signup');
    await clickAt('button:has-text("Sign Up")');
    await awaitCue('signup');

    // シーン2: フォームに入力
    speak('form');
    await typeInto('input[type="email"]', '[email protected]');
    await typeInto('input[type="password"]', 'CorrectHorseBattery');
    await awaitCue('form');

    // シーン3: 送信
    speak('done');
    await clickAt('button[type="submit"]');
    await sleep(1000);
    await awaitCue('done');
  },
});

ステップ6 — 実行する

cd /Users/<you>/Desktop/Projects/skill/screen-record-skills
# 初回のみ:
npm install --silent
# 毎回:
node /tmp/screen-record-<name>.mjs

実行時間は最終的な動画の長さに約10秒のオーバーヘッドを加えた程度です。

ステップ7 — 報告する

ユーザーに以下を伝える:

  • 最終的な .mp4 のパス(LinkedIn対応、1920×1080、H.264 + AAC)
  • .srt のパス(LinkedInがクローズドキャプションとして自動インポート)
  • macOSで afplay <path> で再生する提案

ヘルパーリファレンス(シーンで呼び出せるもの)

ヘルパー説明
page必要に応じて使用する生のPlaywright Page
baseUrl設定されたベースURL
sleep(ms)await sleep(800)
moveCursorTo(x, y, durationMs?)座標へスムーズに移動
moveToSelector(selector, durationMs?, offset?)要素の中央へスムーズに移動
clickAt(selector, durationMs?)移動 + パルス + クリック(クリック時間をログに記録して効果音用)
typeInto(selector, text, perChar?)クリック + クリア + 1文字ずつタイプ
recordClick()クリックタイムスタンプを手動でログに記録(効果音用)
speak(cueId)キューの開始をマーク。終了は start + audioDuration + 0.35s
awaitCue(cueId)キューのナレーションが終了するまでスリープ
typeSplashUrl(text, perChar?)スプラッシュURLバーへのアニメーションタイピング
hideSplash()スプラッシュをフェードアウト(その後は常に約750msスリープ)
configSwitch({ fromInitial, fromLabel, toInitial, toLabel, title, sub })アカウント切り替えカードの内容を設定
showSwitch() / hideSwitch()アカウント切り替えオーバーレイの表示/非表示を切り替え
setAuth(user)localStorage[authStorageKey] を新しいユーザーに設定(アカウント入れ替えデモ用にshowSwitchと組み合わせて使用)

ペーシングの経験則

  • キューごとに1文。約12語以内に抑える。
  • UI操作では、アクションの前に speak(cue) を呼び、カーソルが動いている間にナレーションが再生されるようにする。
  • タイピングが多いシーンでは、タイピングがナレーションより長くなる — awaitCue の代わりに sleep を使用してペーシングを調整する。
  • 動画の合計時間は通常 sum(audioDurations) + sum(sleeps)。LinkedInでは30~75秒を目指す。

壊れやすい一般的なセレクタ

  • 単体の text=Foo は多くの要素にマッチする可能性がある。エンジンが既に行っている page.locator(...).first() を使用するか、button:has-text("Foo") のようにタグに範囲を絞る。
  • Tailwindのクラス名は脆弱。aria-label、role、セマンティックタグ+テキストを優先する。
  • モーダル/ダイアログ:内部をクリックする前に、[role="dialog"] またはモーダルの見出しテキストを待つ。

やってはいけないこと

  • 実際のナレーションテキストをシーンファイルに書かない — ナレーションは cues[].text に保持し、字幕と音声の同期を保つ。
  • カーソルをページCSSに焼き付けない — エンジンが注入する。
  • シーン途中で await page.screenshot() しない — カーソルがおかしな位置でキャプチャされる;必要な場合は後処理で extractFrames を使用する。
  • .env.local をコミットしない(.gitignore に含まれている)。

スキル指示の終わり。

関連スキル