illustration-ink-sketch について
デザインやコーディング向けのイラストスキルで、手描きのインク風カートゥーンを作成します。動物や物にコーディングの比喩を込め、揺らぎのある線やラフな色面、大きな目で表現します。
主な機能
- 01手描きブラシの輪郭
- 02ラフなマーカーの色面
- 03大きな目と表情の細部
- 04コーディングを題材にしたキャラクターと小物
- 05再現可能なSVG生成ツールキット
活用シーン
- 01ローディング画面のイラスト制作
- 02エラーや競合の表現
- 03製品の挨拶イラスト制作
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ mengto/ skills --skill illustration-ink-sketchインストール後、Claude Code で/illustration-ink-sketchを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
style.jsonagents/openai.yamldemo/PROMPT.mdexamples/hello-world.svgmerge-conflict.svgslow-build.svgreferences/craft.mdscripts/ink-kit.mjslint.mjsrender.mjs詳細
イラスト:インクスケッチ
ラフな手描きインクのカートゥーンです。大きな動く目をした生き物や物体(カタツムリ、2匹のミミズ、鉢植えのサボテンなど)が、体でコーディングの比喩を表現しています。ゆるく塗ったマーカーの色の上に、揺らぎのあるブラッシュペンで描きます。読み込み、エラー、挨拶、競合など、遊び心のあるプロダクトの場面に適しています。
境界線:人物に均一な約2 pxのインク線とベタ塗りを使う場合は、illustration-outlined-cartoon を使用します。黒いブラッシュマーカーとミント色の2色目で人物を描く場合は、illustration-two-colour-brush を使用します。粒子感のある柔らかな輪郭線なしの動物を描く場合は、illustration-grainy-gouache を使用します。
14種類のスタイルからなる42枚のオリジナルデザイン・コーディングカードのセットから抽出しました。examples/ にある3枚のカードは、このスタイルで公開されているカードです。
ひと言で表すと
すべての輪郭は、約1〜4.4 pxの幅で膨らみ、先端に向かって細くなる、塗りつぶされた黒いブラシのポリゴンです。色の塗りより2〜3.6 pxはみ出すように、数本の独立したストロークで描きます。暗い部分は塗りつぶさず、ハッチングで表現します。
パレット
作例から測定しました。各色系統はベース/ダーク/ライトで構成されます。ダークはまだら模様やマーカーのストロークに、ライトは光を受けた1本のストロークに使います。
| 役割 | Hex | 使う場所 |
|---|---|---|
| カード背景 | #ffffff(描画なし) | 背景の矩形は描画しません。 |
| インク | #1d1a18 | すべての輪郭線、ハッチング線、髪、斑点、瞳、文字に使います。純粋な黒は使いません。 |
| 赤 | #f0563d / #b8311f / #ff8a6a、濃色 #9a2414 | 殻、鉢、1匹のミミズ |
| 黄 | #ffc83d / #ef9a24 / #fff1b0 | カタツムリの体、火花、花の中心。#fff1b0 は吹き出しの影にも使います。 |
| 緑 | #5cb946 / #2f8a38 / #a6dc62 | 草地、サボテン |
| 青 | #66cdea / #2a8fbf、しずく #bfe8f7 | スライム、マグカップ、進捗バーの塗り、汗のしずく |
| ティール | #2fb5a7 / #167a71 / #8fe3d6、濃色 #0f5c55 | 赤いキャラクターの隣に描く2人目のキャラクター |
| 木 | #eaa85c / #b8722f / #f8d6a0、濃色 #8a5320 | 机、テーブル |
| 赤み | #ff7f86(不透明度0.5〜0.75)、線 #d9434f または #c8303f | 3本の線が入った頬の丸い部分、舌 |
| 目の影 | #c9d6ea | 白目の内側の三日月形の部分 |
| 紙 | #fffaf0、折り目 #d9d2c4 | ステッカー、紙片、ラベル |
| 単色 | 口 #3b1f1a、花びら #ff8fa0、土 #5b3b2c、コーヒー #6b4330、柔らかな影 #7f97ab(不透明度0.22) | 個別のディテール |
| 白 | #ffffff | 白目、光沢のストローク、吹き出し、ハイライト |
- 主役には2つの色系統を使い、地面には緑を使います(赤+黄色のカタツムリ、赤とティールのミミズ、緑のサボテン+赤い鉢+木の机)。3つ目の色系統は、小さな小道具にだけ使います。
- グラデーションは使いません。不透明度を使うのは、まだら模様、ストローク、光沢、赤みだけです。瞳より大きな範囲を黒一色で塗りつぶすことはせず、ハッチングで表現します。
線と塗り
すべてのインク線はブラシ(キットの stroke())で描く塗りつぶされたポリゴンであり、SVG のストロークは使いません。サンプルには stroke 属性は一切含まれていません。
| マーク | 幅 480 の場合の数値 |
|---|---|
| 輪郭 | OUT(centre):w 4.6、最大 4.4、最小 1.0、筆圧 0.4(波長 26)、ペン先 0.3、右下の影側を太くします(k 0.3~0.36)。テーパー t0/t1 は 3~30 px。形ごとに 3~6 本の独立したストロークを使い、明るい左上に 1 か所の隙間を設け、2~14 px のオーバーシュートを 1 か所加えます |
| 補助線(縁、吹き出しのしっぽ、口) | w 2.4~3.2、ペン先 0.45、筆圧 0.4、pf 8 |
| ディテール(葉脈、しわ、木目) | w 1.1~1.8、不透明度 0.55~0.85 で描画します |
| ハッチング | w 1.0~1.15、間隔 2~3.2、角度 -48~-58 deg(右側が明るい形では -130 に反転)、端は縁の内側で揺らします |
| 毛 | 輪郭ストロークごとに 2~5 本、w 0.9~1.5、長さ 2.5~7、影側で外向きにします |
| 点描 | カードごとに 9~11 個、サイズ 0.8~1.7、余白部分に配置します |
| 塗り | ベースカラーを使い、輪郭から 2~3.6 px ずらして配置します(fillShape はオフ)。制御点を 1.2~1.6 揺らします |
| 斑点 | すべての塗りの上に、斑点フィルターを通した不透明度 0.36~0.5 の暗色を重ねます |
| マーカーのかすれ | 暗色、w 4~16、不透明度 0.3~0.55、影側に沿って 1~3 本。形の内側でクリップします |
| 光沢 | 白、w 2.4~6、不透明度 0.85~0.95、丸い形の明るい左上に 1~2 本のストロークを入れます |
各カードには 2 つのフィルターを使い、どちらにも接頭辞を付けます。rough(フラクタルノイズ 0.45、変位 0.7)はカード全体に適用し、すべての縁を揺らします。mottle(ノイズ 0.035 x 0.06、3 オクターブ、アルファ 2.6/-1.25)は水彩の斑点をマスクします。svgOut は両方を書き出します。
キャラクター
人物は登場しません。主役は生き物、または生きている物体です(カタツムリ、ミミズ、サボテンのほか、マグカップ、虫、ロボット掃除機など)。その体そのものがメタファーになります。殻はローディングスピナー、ミミズの細いしっぽはコミットの点が付いた Git ブランチ、鉢には </> のステッカーを貼ります。
- 体:
tube()を使い、背骨に沿って幅関数を適用したチューブにするか、12~24 個の制御点で閉じた塊を描きます。チューブの端では頭を膨らませます(胴体 40、頭 49)。モチーフの幅はカード幅の約 60~70% にします。 - 目、特徴的な要素: 大きなギョロ目で、半径は 19~27.5(直径 40~55 px、頭の幅の 1/3~1/2)。通常は大きさを少し変えた一対を、接触または重ねて配置します。白い円盤に、右下へ
#c9d6eaの陰影の三日月を入れます。縁は描き始めを膨らませてオーバーシュートさせます。瞳は半径の 0.5 倍で、見ている方向へ 4~10 px ずらします。大きなハイライトと小さなハイライトを 1 つずつ入れます。体の色(h 0.34~0.38)でまぶたを 17 deg 傾けて描くと、不機嫌な表情になります。 - 顔: 8 x 4.6 の楕円形の頬紅に、短い赤い線を 3 本添えます。口は、両端に小さな線を添えた曲線 1 本の笑顔、ピンクの舌を入れた暗色の塗りつぶしの口(歓喜)、歯の線を 2 本入れた白い食いしばった歯(怒り)、または下唇を添えた下向きの線(ふくれ顔)にします。
- 手(生き物に必要な場合): 手足と同じ色を使い、手のひらと 3 本の指、親指をカプセル形にします。指の関節は線をつなげずに描き、関節のしわと影側の暗い筋を加えます(キットの
hand())。サボテンと同程度の大きさにし、スケール 1.25 で指の長さを 12~14.5 にします。親指はreferences/craft.mdの利き手の表に従って配置します。 - 触角、茎、しっぽ: 両側に輪郭を付けたテーパー状のチューブ、またはブラシストローク 1 本のらせんにします。棒状の脚を使う場合は、幅 2.4~3 のブラシストローク 1 本で描き、端に小さな塗りつぶしの楕円を付けます。
- 短い脚と肉球(小型哺乳類):
shadeTubeとlimbOutline({ under: bodyLoop })で幅 9~14 の短いチューブを描き、花びら色の楕円(5.4 x 3.2)と短いインクの下線で終わらせます。体の奥側の脚を先に描き、手前側の脚を後から描きます。走っているように見えるよう、後ろ脚は腰の後ろへ、前脚は肩の前へ傾けます。 - 力や感情を表すマーク: 汗、弓なりの括弧を 4 つ使った怒りマーク、動きの弧(幅 2.4 と 1.8 の同心ストローク 2 本をテーパーさせたもの)、湯気のカール。
飾りと小道具
- 地面(どちらか1つを選択): x 52 から 428 に広がる、ぼさぼさの草地。上端は y 272 から 280、下端は両端が上向きに反り、ドライブラシの端、草の葉の跳ね、両端に1つか2つの高い草むらを加えます(
grassPatch、tuft)。または、木目の線とハッチングした脚が1本ある、少し上から見た木製の机の天板。 - 接地部分: 地面に接する各パーツのすぐ下にハッチングした楕円(幅 1.0、間隔 3 から 3.2、不透明度 0.85 から 0.9)を描き、胴体の付け根にさらに濃い筋を加えます。小道具は何かの上に置きます。看板は草地に立てた杭に、マグカップは机に置きます。浮かせてよいのは、吹き出し、飛んでいる紙片、マークだけです。
- 小道具、1から3個:
</>のコードステッカー、手描き文字を入れた吹き出し、コードの山かっこを描いた破れた紙片、湯気がくるくる立ちのぼるマグカップ、プログレスバー(白いトラック、ハッチングした青い塗り、手描きの %)、コミットを示す点、きらめく星形、花。 - 文字: ブラシのストロークで手描きします(幅 2.4 から 2.6、字形の高さ 12 から 17 px)。フォントは使いません。
- 禁止事項: ブラシの輪郭線がない整然とした幾何学的なベクター形状、グラデーション、ドロップシャドウ、黒一色の塗り、背景の風景、人。
構図
- 背景の矩形は描きません。すべて x 36 から 444、y 40 から 340 の範囲に収めます。x 36 まで達してよいのは、地面のドライブラシの端だけです。
- 主役は地面の中央下部に配置し、カード幅の約 60 から 70%、高さの約 60%を占めるようにします。目を上半分の焦点にします。空いている上部の隅を、吹き出し、紙片、マークのいずれかで埋めます。丸い、または背の高い主役(車輪、鉢など)は幅を約 50%にとどめ、幅を埋めるために、地面に接する小道具を横に1つ配置します。
- このスキルに記載された数値はすべてカードサイズでの値です。拡大縮小するグループ内では、サイズを scale で割った値で描画します。また、接地部分(縁に乗せた足、机に置いた鉢など)は、まずカード座標で描画し、その後グループ内に変換します。
const G = w => [B0[0] + (w[0] - B0[0]) / S, B0[1] + (w[1] - B0[1]) / S]。 - 主役は、地面との接点を基準に
<g transform="translate(cx gy) scale(1.12) translate(-cx -gy)">内でデザインサイズで描画し、setWScale(1/1.12)を設定して線幅を本来の値に保ちます。その後、setWScale(1)に戻します。 svgOut(file, {shift: [0, -8]})を使うと、余白のバランスを整えるためにカード全体を移動できます。
テクニック
scripts/ink-kit.mjs は、3つの例の生成に使われたシード付きブラシライブラリです。元のライブラリの代わりにこれを使うと、Merge Conflict と Hello World のジェネレーターは、出荷されたカード上のすべてのマークを完全に再現します(違いは、以前のファイル書き込み処理が <defs> に残した余分なテキストノードだけです)。各マークにはそれぞれ独自の乱数ストリームを持つ文字列キーが割り当てられているため、マークを編集または追加しても、ほかのマークの配置は変わりません。キーを変更すると、特定のマークだけを再生成できます。
// card.mjs, next to a copy of ink-kit.mjs
import * as K from './ink-kit.mjs';
const { PAL, OUT } = K;
K.setPrefix('xx-'); // every id: filters and clip paths
K.grassPatch('grass', { x0: 60, x1: 420, top: 276, bot: 320 });
K.hatch('cast', K.arc(240, 286, 70, 6, 0, 360, 10), { ang: -58, sp: 3, w: 1.0, inset: 1.2, over: 0.4, op: 0.9 });
const ctrl = []; // a round body: control points
for (let a = 0; a < 360; a += 30) ctrl.push([240 + 70 * Math.cos(a * K.deg), 220 + 62 * Math.sin(a * K.deg)]);
const loop = K.fillShape('body', ctrl, PAL.red.c, { off: [-3, 2], j: 1.4 }); // slipped fill
K.mottle(loop, PAL.red.d, 0.46);
K.clipOpen(loop); // shading stays inside
K.stroke('streak', K.arc(240, 220, 60, 54, -10, 120, 6), { w: 13, t0: 20, t1: 25, wob: 1.5 }, PAL.red.d, 0.45);
K.stroke('gloss', K.arc(240, 220, 52, 48, 198, 236, 4), { w: 6, t0: 8, t1: 12 }, '#ffffff', 0.95);
K.clipClose();
const dl = K.resample(K.catmull(ctrl, true, 10), 1); // outline in two strokes: gap top-left, overshoot
const o1 = K.stroke('o1', K.loopSlice(dl, 0.62, 1.18), { ...OUT([240, 220]), dense: true, t0: 12, t1: 10 });
K.stroke('o2', K.ext(K.loopSlice(dl, 0.2, 0.58), 3), { ...OUT([240, 220]), dense: true, t0: 8, t1: 6 });
K.hairs('hairs', o1.C, 4, { c: [240, 220] });
K.eye('eyeL', [216, 172], 21, [223, 176], 10.5);
K.eye('eyeR', [262, 168], 23, [269, 173], 11.5);
K.blush('blL', 206, 222); K.blush('blR', 280, 218);
[[96, 120, 1.5], [400, 96, 1.2], [420, 230, 1.1]].forEach((p, i) => K.fleck('fk' + i, ...p));
K.svgOut(new URL('./card.svg', import.meta.url).pathname, { label: 'Title, Ink sketch style' }); // next to card.mjs
キットに含まれるパーツ:stroke(ブラシ)、OUT(c)(輪郭線のプリセット)、hatch、fillShape、mottle、clipOpen/clipClose、hairs、fleck、dry、eye(lid を含む)、tube と across/acrossLine、shadeTube(手足または胴体の塗り、斑点、筋、光沢、リブ)、limbOutline(胴体の外に出ている手足の部分だけにインクを引く)、hand、grassPatch、blade/tuft、blush、sweatDrop、codeSticker、paperScrap(破れたメモ。書き込み位置として T(x, y) を返します)、fillRing(正面から見た車輪またはドーナツの表面。偶奇塗りつぶしと斑点を使用)、arc、loopSlice、ext、runsOutside、setWScale、svgOut。
無限大記号(幅 26 px のレムニスケート)のように、1本のブラシストロークで描く記号:
const inf = []; for (let t = 0; t <= 2 * Math.PI + 0.35; t += 0.07) { const s = Math.sin(t), c = Math.cos(t), d = 1 + s * s; inf.push([x + 13 * c / d, y + 13 * s * c / d]); }
K.stroke('inf', inf, { w: 3, t0: 3, t1: 5, nib: 0.45, press: 0.3, pf: 8 });
node scripts/ink-kit.mjs --demo $W/demo.svg を実行すると、デモカードが書き出され、wrote <path> 203.5KB 316 elements と表示されます。このカードには、草地の上で3本指の手を振る、目が飛び出した赤い blob が描かれ、ハッチング、コードステッカー、汗のしずく、細かな点が加わります。--prefix ikd- を指定すると、リンターを問題なく通過します。
失敗しやすいポイント
- クリップアートのような線。 均一な太さの線や SVG の
stroke属性は、ベクター画像のように見えます。すべての線を、先細りと筆圧のあるブラシ形状にします。 - 閉じた輪郭線。 形を一周する閉じた線は、なぞったように見えます。各輪郭線を 3~6 本に分け、光が当たる側に 1 つの隙間と、1 つのはみ出しを作ります。
- 輪郭線にぴったり収まった塗り。 塗り絵のようなベクター画像に見えます。形ごとに塗りを 2~3.6 px ずらし、ずらす方向を変えます。
- 平坦な色。 斑点や筋、光沢のない塗りは、印刷されたように見えます。すべての塗りに斑点を加え、丸みのある形には暗い筋を 1 本以上、光沢の線を 1 本以上入れます。
- ベタ塗りの黒い影。 ハッチングを入れ、
keepを使って光の方向に向かってハッチングの密度を下げます。 - 生気のない目。 瞳が中央にあったり、ハイライトがなかったりすると、ぼんやりした目に見えます。瞳を動きのある方向へ 4~10 px ずらし、白い点を必ず 2 つ入れます。
- 毛だらけ。 線 1 本につき 5 本を超える毛は、線が毛羽立って見えます。影側に 2~5 本加えます。
- 宙に浮いた被写体。 ハッチングした接地楕円も、暗いベースの筋もないと、動物が草の上に浮いて見えます。
- 拡大縮小されたグループ内で線幅がずれる。
setWScale(1/s)を忘れると、すべての線が 12% 太くなります。 - 編集するたびにすべてが入れ替わる。 キーを再利用したり番号を振り直したりすると、マークが再生成されます。それぞれのマークに、安定した一意のキーを割り当てます。
- 1 ページにある 2 枚のカードで同じフィルターを共有する。 カードごとに一意のプレフィックスを指定して
setPrefixを呼び出し、--prefixで lint を実行します。 - 活字のテキスト。 吹き出し内のフォントは手描きの雰囲気を損ないます。ブラシの線で文字を書き込むか、字形を描くのにジョークより時間がかかる場合は、記号(正の字の線、山形、シンボルなど)で表現します。
- 小道具に顔がくっつきすぎている。 テストでは、ハムスターの鼻、口、前足が車輪の縁と一体化してしまいました。顔の周囲には 25 px の間隔を空け、手足の線を口の位置で終わらせないようにします。
- 紙からはみ出したマーク。 正の字の点が切れ端の破れた縁からはみ出し、インクのしみのように見えてしまいました。マークは紙の縁から 4 px 内側に収めます。
- 宙に浮いた看板。 被写体の横に空中で浮かぶメモは、配置ミスのように見えます。ハッチングした接地部分を作り、杭に立てます。
例
slow-build.svg(ビルドが遅い):進捗バーの 27% の位置で汗をかくカタツムリ。殻はハッチングした彗星の尾が付いた、らせん状のローディングスピナーです。眼柄、コードのステッカー、草むら、手描きの %、柔らかな接地影を表現しています。merge-conflict.svg(マージコンフリクト):ブランチ線が交わる場所で、赤いミミズと青緑色のミミズが頭をぶつけています。節の輪がある筒状の体、不機嫌なまぶた、歯を食いしばった口とへの字口、コミットの点、火花、怒りのマーク、飛ばされた<<<<の紙切れを表現しています。hello-world.svg(ハローワールド):鉢植えのサボテンが机の上で手を振り、コーヒーマグの横で「hello, world」と言っています。親指の位置が正しい 3 本指の手、鉢の縁に乗せた腕、棘の目盛りがある稜、木目、湯気の渦巻き、吹き出し内の手描き文字を表現しています。
ワークフロー
このスキルのフォルダーから実行します。render.mjs を使うには、playwright-core(または playwright)がこのフォルダー、現在のフォルダー、もしくはグローバルにインストールされていることと、Chromium が必要です。
- 概要。 題材とメタファーを決めます。どの生き物を描き、体のどの部分にアイデアを託すのかを決めます。480 px でそれを伝える表現(目、口、模様)を1つ書き留めます。
- 主役とポーズ。 シルエットを紙の上かコメントに描きます。背骨、目の位置、地面の線を示します。手ごとに、左右と手のひら側/手の甲側を
craft.mdに書き留めます。 - セットアップ:
W=/tmp/ink-card; mkdir -p $W; cp scripts/ink-kit.mjs $W/を実行し、Techniques のひな形を使って、一意のプレフィックスを付けた$W/card.mjsを作成します。 - ブロック: 地面、体の塗り、目だけを描きます。レンダリングしてサイズ、配置、1x での見え方を確認します。
- 描画: 影(まだら、筋、ハッチング、光沢)、別々の線で描く輪郭、毛、顔、小道具、文字、点描を加えます。
- レンダリング:
node $W/card.mjs && node scripts/render.mjs $W/card.svg $W/card.pngを実行します(card.mjs は同じフォルダーに card.svg を書き出します)。 - シート:
node scripts/render.mjs --sheet $W/sheet.png examples/*.svg $W/card.svgを実行します。「同じ手で描かれているか、同じセットの一部に見えるか」を確認します。 - 拡大: 目、手、接地部分、文字を 6~8 倍で確認します。
node scripts/render.mjs $W/card.svg $W/eyes.png --zoom x,y,w,h --scale 8 references/craft.mdの5つの基準と Verify のリストに沿って批評し、ジェネレーターを編集して修正します(SVG は決して編集しないでください)。繰り返します。3~4回の反復を見込んでください。- Lint:
node scripts/lint.mjs $W/card.svg --prefix xx- --palette style.jsonを実行します。すべての ERROR を修正します。各 WARN の色は、意図的に一度だけ使われている必要があります。
確認項目
-
stroke=属性がどこにもないこと。すべての線が#1d1a18の塗りつぶされたブラシ形状であること。 - 輪郭が太くなったり細くなったりしていること(細い部分は約1 px、太い部分は約4.4 px)。各形状の輪郭に隙間と行き過ぎがあること。
- すべての塗りが、線から2~3.6 px はみ出していること。
- すべての塗りにまだら模様があること。丸い形状には暗い筋と白い光沢の筆跡があること。
- 影と暗い領域に、-48~-58 度、間隔2~3.2のハッチングがあること。ベタ塗りの黒い領域がないこと。
- 動眼は直径40~55 px で、瞳が動きの方向に寄り、それぞれに反射光が2つあり、内側に陰影の三日月形があること。
- 輪郭の線ごとに毛が2~5本あること。余白に点描が9~11個あること。
- 主役が草地または机の上にあり、接地影にハッチングがあること。意図せず浮いているものがないこと。
- 主役に2つの色系統があり、地面には緑または木の色を使っていること。パレットの16進カラーコードのみを使うこと。
- カード全体がプレフィックス付きの
roughフィルターで囲まれていること。すべての id にプレフィックスが付いていること。 - 文字がフォントではなく、ブラシで描かれていること。
- タイトルがなくても、1x でメタファーが伝わること。