hand-drawn-cartoon について
HyperFramesで手描き風の短いカートゥーン動画を企画・制作するためのスキルです。構成、動き、描画スタイル、レンダリングと確認の手順をまとめています。
主な機能
- 01ブリーフとビートの設計
- 02画面比率・長さ・トーンの設定
- 03SVGとGSAPによる作画ガイド
- 04文字の可読性と動きのタイミング
- 05スナップショット確認とレンダリング手順
活用シーン
- 01一つのジョークやアイデアを短編化
- 02ブランド指定に合わせた配色調整
- 03ビートと画面テキストの計画
- 04ループアニメーションの確認と出力
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ phantomtonyt/ motion-design-skills --skill hand-drawn-cartoonインストール後、Claude Code で/hand-drawn-cartoonを入力して読み込まれたか確認します。
詳細
手描きカートゥーン
小さなキャラクターが、紙の舞台でひとつのアイデアを演じます。線はインクで描かれ、数フレームごとに描き直されているかのように、かすかに動きます。動く絵本のページのように、手作りの温かさとユーモアが感じられる仕上がりにします。
オプション
| オプション | デフォルト | 備考 |
|---|---|---|
| フォーマット | 縦型 1080×1920 | または横型 1920×1080、正方形 1080×1080 |
| 長さ | 15 s | 10〜45 s が最適です |
| トーン | 遊び心のある雰囲気 | calm(長めの間、穏やかなイージング)、playful(デフォルト)、slapstick(大きなスクワッシュ、テンポの速いギャグ、多めのカット) |
| キャラクター | シンプルで丸いキャラクター | またはユーザー独自のキャラクター(シルエットと色を合わせます) |
ユーザーは「縦型、20秒、ドタバタ調」のように、普通の言葉で指定できます。
概要(計画前に回答)
- アイデアやジョークの核は何ですか?一文で答えてください。
- キャラクターは誰で、何を望んでいますか?
- 最初に何がうまくいかず、どんでん返しやオチは何ですか?
- 最初の1秒で画面に何を映しますか?(フック)
- 動画を締めくくるセリフやタイトルは何ですか?
次に、ビートリストを作成します。時間、見えるもの、キャラクターの動き、テキスト、音を書きます。各時間の合計を目標の長さに合わせます。基本の構成は、フック(1.5 s)、挑戦と失敗、アイデア、試み、オチ、エンドカードです。
読み取りシート(ビートリストと一緒に作成)
各ビートについて、視聴者が理解すべきことをひとつ(「ロボットは飛べない」「キャッシュがヒットしなかった」など)書き、それが読み取れる状態で表示される時間も記載します。テキストは一度にひとつずつ表示します。テキスト表示時間の最低基準:ラベルは少なくとも0.8 s、文は完全に画面に表示されてから、単語数あたり約0.3 sです。時間内に収まらない場合は、表示を速めるのではなく、単語を削ります。
動きのルール
- 動きには原因があります。キャラクターは画面上の何かに反応し、すべての動作に原因と結果があります。
- キャラクターを完全に静止させません。待っている間も呼吸し、まばたきし、重心を移動させます。こうした待機中の動きも演技の一部なので、小さく不規則にします。
- 左右対称の動きは避けます。腕を鏡写しのように動かさず、目、触角、体をすべて同じフレームで動かし始めないようにします。各パーツの動きは2〜4フレームずらします。
- オーバーシュートやスクワッシュはキャラクターに使います(着地、飛び出し、驚き)。カメラとテキストは滑らかなイーズアウトで動かします。
- シーンごとに主な移動方向をひとつに絞り、クロスフェードではなく、シーンの切り替わりをまたいでキャラクターや小道具を移動させます。
- オチの直前に0.3〜0.75 sのポーズを入れ、オチを際立たせます。
スタイルのルール(常に適用)
- すべての線は温かみのある濃いインク色(
#2b1d16前後)、1080幅で5〜8 px、角は丸くします。真っ黒は使いません。 - 線には揺らぎを加えます。インクレイヤー全体に、わずかに異なる3種類の変位フィルターのいずれかを適用し、2フレームごとに切り替えます(12 fps)。揺らぎがないと、滑らかなベクター画像に見えてしまいます。
- 紙の質感を常に残します。温かみのあるオフホワイトの背景に、かすかな粒子のオーバーレイと柔らかなビネットを加えます。
- 陰影はクレヨンのハッチングか、単色の濃いトーンで表現します。キャラクターにグラデーションは使いません。
- キャラクターは丸くシンプルにします。大きな頭や体、点または楕円形の目、白いハイライト、小さな口、短い手足を基本とします。全身を使って演技させます。
- すべての動作に、予備動作、動き、接触時のスクワッシュ、落ち着く動きを入れます。
デフォルトと上書き設定
優先順位は、ユーザーのブランドや仕様、明示的なリクエスト、これらのデフォルトの順です。ユーザーがブランドカラーを指定した場合は、キャラクターとアクセントカラーひとつに使います。インク、紙、粒子の質感はそのままにします。
- 紙
#efe6d2、明るい紙#fbf6ea、インク#2b1d16、アクセント#b8402f(タイトル、重要な小道具)、空のウォッシュ#b9d8e8、太陽またはアイデアを表す黄色#f5c842、キャラクターの色#7fb6c9。 - フォント:タイトルにはCaveat Brush、ラベルにはPatrick Hand(Google Fonts)。9:16では、タイトルを80〜110 pxにします。
セットアップ(初回のみ)
node -v && ffmpeg -version
npx hyperframes init my-video --non-interactive --resolution portrait && cd my-video # landscape for 16:9
既存の空のフォルダーで npx hyperframes init . --non-interactive --resolution portrait を実行し、次に index.html を構成に置き換えます。
方針
| 要件 | HyperFrames の SVG + GSAP(デフォルト) | p5.js + p5.brush(キャンバス) |
|---|---|---|
| 揺らぎのあるインク線 | はい(変位フィルター) | はい(シードを再設定したブラシの揺らぎ) |
| 水彩のにじみとドライブラシの質感 | 近似(粒子、ハッチング) | はい |
| GPU なしでの高速レンダリング | はい | 遅め |
| 名前付きの再利用可能なパーツを持つキャラクター | はい | 難しい |
デフォルトでは SVG + GSAP を使います。本物の絵の具のようなにじみが重要な場合に限り、p5.brush を使います。その場合は、時間 t から各フレームを描画します(固定シードなしでランダム性を使わないでください)。
ビジュアル表現
- ステージ: 床の線、屋上、またはテーブルの縁と、小道具を 1 つか 2 つ配置し、空または壁を描きます。形は少し不規則にします(完璧な長方形ではなく、手描きのパスを使います)。
- 奥行き: 遠景のレイヤー(太陽、雲、丘)、中景のレイヤー(ステージ)、近景のレイヤー(小道具やカメラの前を通り過ぎる雲)を用意します。ワールドグループを動かしてカメラを移動させ、遠景のレイヤーは移動量を少なくします。
- キャラクターのパーツ: 体、目、口、左右の腕、アンテナや耳をそれぞれ別のグループにし、個別に動かせるようにします。ラッパーグループ(
<g transform="scale(1.7)">)でキャラクターのサイズを調整し、外側のグループで x、y、回転をアニメーションさせます。SVG の変換はデフォルトで左上を原点とするため、アニメーションさせるグループ自体を拡大縮小すると位置がずれます。 - スクワッシュ&ストレッチ: 足を中心に行い、キャラクター自身の座標で足の位置に
svgOriginを設定します(例:svgOrigin: "0 6")。体積を保ちます。高さを 10% 増やす場合は、幅を約 5% 狭めます。 - エフェクト: ふくらませて消える砂ぼこり(
r属性のアニメーションで円を大きくしてからフェード)、回転する目が回った星、スピード線、きらめき。すべてにインク線を付けます。
| 動き | タイミング |
|---|---|
| 予備動作のスクワッシュ | 0.1–0.12 s |
| 跳び上がり | 0.4–0.5 s、power2.out |
| 頂点でのコミカルな滞空 | 0.3–0.5 s |
| 落下 | 0.25 s、power3.in |
| 着地時のスクワッシュ | 0.08 s。その後、0.5 s かけて弾むように落ち着かせる |
| まばたき | 0.12 s、2–4 s ごとに不規則に |
| 頭を傾ける | 0.3 s、8–12° |
目を引く映像にする
- 最初のフレームを完成した絵にします。ステージ、キャラクター、空、手描きのカードに載せたタイトルを入れます。
- すべての拍で何かを動かします。鳥、ゆっくり回る太陽、流れる雲、アンテナの揺れ、まばたきなどです。
- 大きな見せ場ではカメラを使います。視差のあるレイヤーとともに、キャラクターを上または横に追います。
- キーとなる小道具とタイトルのキーワードには、アクセントカラーを 1 色使います。それ以外は落ち着いた色にします。
- ギャグはシルエットだけで伝わるようにします。失敗は砂ぼこりと星を伴う大きなスクワッシュ、成功はひと目で伝わる大きなポーズにします。
- 最後のフレームは最初のフレームと一致させます(同じ構図、同じポーズ)。そうすれば動画をループできます。
- 静止したどのフレームも、投稿する価値があるものにします。スナップショットが空っぽ、または平坦に見えるなら、まだ完成ではありません。
スターター
<div id="root" data-composition-id="main" data-start="0" data-duration="15" data-width="1080" data-height="1920">
<svg viewBox="0 0 1080 1920" width="1080" height="1920">
<defs>
<filter id="boil0"><feTurbulence type="fractalNoise" baseFrequency="0.03" numOctaves="2" seed="2"/><feDisplacementMap in="SourceGraphic" scale="4"/></filter>
<filter id="boil1"><feTurbulence type="fractalNoise" baseFrequency="0.03" numOctaves="2" seed="9"/><feDisplacementMap in="SourceGraphic" scale="4"/></filter>
<filter id="boil2"><feTurbulence type="fractalNoise" baseFrequency="0.03" numOctaves="2" seed="17"/><feDisplacementMap in="SourceGraphic" scale="4"/></filter>
<filter id="grain"><feTurbulence type="fractalNoise" baseFrequency="0.85" numOctaves="3" seed="5"/>
<feColorMatrix type="matrix" values="0 0 0 0 .3 0 0 0 0 .24 0 0 0 0 .16 0 0 0 .2 0"/></filter>
<pattern id="hatch" width="14" height="14" patternUnits="userSpaceOnUse" patternTransform="rotate(35)"><line x1="0" y1="0" x2="0" y2="14" stroke="#2b1d16" stroke-width="3" stroke-opacity=".28"/></pattern>
</defs>
<rect width="1080" height="1920" fill="#efe6d2"/>
<g id="world"><g id="ink" filter="url(#boil0)" stroke="#2b1d16" stroke-width="7" stroke-linejoin="round" stroke-linecap="round">
<path d="M-60 1312 L770 1304 Q810 1306 812 1330 L800 2200 L-60 2200 Z" fill="#c9b79a"/>
<g id="hero"><g transform="scale(1.7)">
<g id="body"><circle cx="0" cy="-100" r="92" fill="#7fb6c9"/><path d="M-92 -100 a92 92 0 0 0 184 0" fill="url(#hatch)" stroke="none"/>
<rect x="-58" y="-128" width="116" height="62" rx="30" fill="#fbf6ea"/>
<g id="eyes" stroke="none" fill="#2b1d16"><ellipse cx="-24" cy="-98" rx="11" ry="15"/><ellipse cx="24" cy="-98" rx="11" ry="15"/></g>
<g id="armR"><path d="M88 -92 q40 10 52 46" fill="none" stroke-width="12"/></g></g>
</g></g>
</g></g>
<rect width="1080" height="1920" filter="url(#grain)"/>
</svg></div>
<script>
const tl = gsap.timeline({ paused: true });
for (let i = 0; i < 90; i++) tl.set("#ink", { attr: { filter: `url(#boil${i % 3})` } }, i / 6); // boil on twos
gsap.set("#hero", { x: 400, y: 1316 }); // feet on the stage
tl.to("#body", { scaleY: 0.85, scaleX: 1.1, svgOrigin: "0 6", duration: 0.1, yoyo: true, repeat: 1 }, 1.7); // anticipation
tl.to("#hero", { y: 956, duration: 0.45, ease: "power2.out" }, 1.95).to("#hero", { y: 1316, duration: 0.25, ease: "power3.in" }, 2.9);
tl.to("#body", { scaleY: 0.5, scaleX: 1.35, svgOrigin: "0 6", duration: 0.08 }, 3.15)
.to("#body", { scaleY: 1, scaleX: 1, svgOrigin: "0 6", duration: 0.5, ease: "elastic.out(1,0.4)" }, 4.3);
tl.to("#armR", { rotation: -60, svgOrigin: "88 -92", duration: 0.16, yoyo: true, repeat: 5 }, 13.6); // wave
tl.set({}, {}, 15);
window.__timelines["main"] = tl;
</script>
<head> に GSAP スクリプト(https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js)、Google Fonts のリンク、html,body{margin:0;width:1080px;height:1920px;overflow:hidden} を追加します。ビートリストに沿って、同じ tl に動きを作り込んでください。
公開するパラメーター
format · duration · tone · characterColor · accent · paper · title · endLine · beats。スクリプトの先頭で定数として定義してください。
避けること
- 完璧な幾何学図形や、揺らぎのないきれいな線(平面的なベクターに見えます)。
- キャラクターへのグラデーション、グロー、ドロップシャドウ。
- 広い空間の中で小さく見失われるキャラクター。キャラクターの高さはフレームの 25–45% にします。
- キャラクターの顔に重なるテキスト、および 9:16 フレームの下から 670 px 以内、または上から 270 px 以内に配置するテキスト。
Math.random()や時刻に依存するロジック。揺らぎやエフェクトはタイムラインから生成してください。
失敗しやすい点
| 状態 | 原因 | 修正方法 |
|---|---|---|
| きれいなベクターで、手描きに見えない | 揺らぎがなく、形が完璧すぎる | 3つの変位フィルターを2フレームごとに切り替え、手描きのパスを使います |
| 拡大縮小するとキャラクターがずれたり沈んだりする | 動いているグループに拡大縮小のアニメーションを設定している | サイズは内側の<g transform="scale()">に設定し、足元をsvgOriginにして縦につぶします |
| エフェクトが隅から始まる | 表示前にx/yを設定していない | 時刻0で開始位置をgsap.setで設定します |
| 色が濁ってコントラストが低い | 中間色が多すぎる | 明るい紙色、暗いインク色、アクセントカラーを1色にします |
| 演技がぎこちない | 予備動作や収まりのない動きになっている | 前にスクワッシュを加え、後に弾むような収まりを加えます |
| タイトルを重ねた際にテキストの重なり警告が出る | 手書きフォントの行ボックスが接触している | 行間に10~20 pxの間隔を設け、スナップショットで確認します |
検証
npx hyperframes check
npx hyperframes snapshot --at <one time per beat, plus one mid-transition>
npx hyperframes render -o renders/<name>.mp4
すべてのスナップショットとsnapshots/contact-sheet.jpgを確認します。
- 1/6秒離れた2つのスナップショットで線がはっきり異なる(揺らぎが機能している)
- キャラクターがステージ上に立っていて(沈んでおらず)、スマートフォンの画面サイズでも見やすい
- シルエットで各ビートの内容が伝わり、失敗と成功の見せ場が大きく表現されている
- テキストがセーフエリア内にあり、トランジション中に要素がぶつからない
- 最終フレームが最初のフレームと一致する
最終レンダリング前の自己評価
スナップショットシートを、フック(最初の1秒)、スマートフォン幅での見やすさ、動きの質、構図、正確さ、「ミュートテスト」(音声なしで視聴者が一文で要約できるか)の各項目について1~10点で評価します。7点以下の項目だけ修正し、再度スナップショットを作成して、すべて8点以上になるまで繰り返します。可能であれば、自分の作品を評価することにならないよう、別の読み取り専用パスで採点します。
| 避けること | 代わりにすること |
|---|---|
| 要素の多い2つのレイアウトをクロスフェードする | 古い内容を退場させてから新しい内容を登場させるか、1つのオブジェクトを引き継ぎます |
| 複数の要素を同時に動かす | タイミングをずらし、各瞬間の焦点を1つにします |
| 間を浮遊や揺れで埋める | 静止させるか、キャラクターに演技させます |
| 「躍動感」を出すために粒子、虹色のグラデーション、3D反転を加える | リビール、衝撃、カメラ移動など、より強いイベントを加えます |
| 時間に収めるためにテキストを急いで表示する | 言葉を削ります |
納品
- 落ち着いた状態のフレームから最も良いものをポスターに選びます:
ffmpeg -ss <t> -i renders/<name>.mp4 -frames:v 1 poster.jpg。サムネイルに表示されるよう、フレーム0として焼き込みます:ffmpeg -i renders/<name>.mp4 -i poster.jpg -filter_complex "[0:v][1:v]overlay=enable='eq(n,0)'" -c:a copy renders/<name>-final.mp4。 - 手法ではなく、アイデアを説明する1~2文のキャプションを書きます。
- 各ビートで何が起きるか、改訂時にどのパラメーターを変更すればよいかをユーザーに伝えます。
参考資料
- @other__reality、「I'm Upping My P(doom)」ミュージックビデオ。p5.js + p5.brushを使ってコードで制作:https://x.com/other__reality/status/2102514581684052169(公開レシピ:github.com/JohnHeibel/PDoomVideo)
- @devteamdrew、Claudeのすべての努力レベルを絵本のような短い場面で表現:https://x.com/devteamdrew/status/2105353836638744578
- @shfred0、「The Life of Opus」。JavaScriptによる手続き型のブラシストローク:https://x.com/shfred0/status/2102495989194236158
- @akokoi1、すべてコードで制作された水墨画風の絵本:https://x.com/akokoi1/status/2102699703309898026
- HyperFramesのモーションドクトリンと顔出しなしの解説動画向けスキル、JohnHeibel/ClaudeAnimationBaseのアニメーションガイド、howseen-ai/claude-motion-designの批評ループ、latent-spaces/bragからアイデアを参考にしています(コピーはしていません)。