ai-lab.org
DoodleDrop

DoodleDrop ができるまで — シミュレーター 2本目を お絵描き物理 で開く

計算ツール 4本目 (side-tax) に続く ラボ 28本目で シミュレーター thesis 2本目 (pile-park に続く)。 matter.js + Canvas + 凸包 (Andrew's monotone chain) でストロークを polygon body 化、 黒板 motif の visual を組んだ設計記録。

·decision改善·stage公開中

DoodleDrop ができるまで — シミュレーター 2本目を お絵描き物理で開く

DoodleDrop は、 matter.js + Canvas で動く お絵描き物理サンドボックス。 黒板にチョークで自由曲線を描くと、 ストロークが凸包 polygon の剛体になって即座に重力で落ち、 既存ストロークと衝突する。 ラボ 28本目、 シミュレーター thesis 2本目 (PilePark = 物理サンドボックス に続く)。

なぜこの形にしたか

直近 5本の SHIP は (side-tax: 計算 / atlas-quest: 学習 / hue-deck: ジェネレーター / astro-cast: データ可視化 / arcana-flip: 占い)。 直近 5本にシミュレーターゼロ → §3.1 OK。

候補比較:

  • お絵描き物理 (採用)
  • 面接シミュ オフライン (server-side AI なしだと弱い)
  • 飲み会幹事シミュ (ルールベース、 弱)
  • 物理パズル (matter.js 拡張、 ステージ作成コスト大)

選んだ理由:

  • SNS シェア性が極めて高い (TikTok / Reels / Shorts ネタ: 描いたものが動く)
  • 既存大手は重い (Procreate / 3D) or 簡素すぎ (シンプル落書き) で間がない
  • matter.js を pile-park から再利用 = 新規 lib コストゼロ
  • 純 JS で軽量、 モバイルでも快適

visual direction — §6.1 Visual Audit 13本目の適用

直近 5本の visual を書き出す:

side-tax     — ledger green + cream + stamp red (帳簿 / 確定申告)
atlas-quest  — dark navy + amber departure (空港 board)
hue-deck     — warm gray + ink + lipstick (Pantone)
astro-cast   — midnight space + 惑星レインボー (天文台)
arcana-flip  — midnight black + plum + antique gold (中世大聖堂)

題材 「お絵描き / 黒板 / 落書き」 → motif:

  • 学校の黒板 (chalkboard) ← 採用
  • 方眼ノート (既存 hue-deck や pile-park 系と近い)
  • ホワイトボード (light theme で被るリスク)
  • 砂浜 (niche)

採用 3 要素:

  • palette: blackboard green #1a3c2e + worn black + chalk pastels 7 色 (白 #fff5e6 / 桃 #f48aaa / 黄 #f5d35e / 青 #6dafe0 / 緑 #7ed09e / 橙 #ff9b6b / 紫 #b78ad8) + worn wood frame #5a3f23 + chalk dust
    • side-tax の ledger green と同じ緑系だが、 motif で完全別 (帳簿事務所 vs 学校黒板)
    • palette 内の主役は chalk pastels、 side-tax は ink graphite + stamp red が主役
  • motif: 学校の黒板 (12px wood frame で canvas を囲む + 木目 4 隅 dot + 横の薄い罫線 23px loop + chalk dust 散布 + dashed border 多用 = チョーク感)
  • typography: Space Grotesk 800 (clear classroom) + JetBrains Mono labels + 漢字を含む見出しを 「黒板のチョーク」 風に rotate(-1.5deg / 1deg)

技術スタック

凸包 (Andrew's monotone chain) でストロークを polygon body 化

function convexHull(points) {
  if (points.length < 3) return [...points]
  const pts = [...points].sort((a, b) => a.x - b.x || a.y - b.y)
  const cross = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x)
  // lower hull
  const lower = []
  for (const p of pts) {
    while (lower.length >= 2 && cross(lower[L-2], lower[L-1], p) <= 0) lower.pop()
    lower.push(p)
  }
  // upper hull
  ...
  return lower.slice(0, -1).concat(upper.slice(0, -1))
}

O(N log N) で凸包を求める標準アルゴリズム。 描いたストロークが凹みのある形状 (☆ / C / 螺旋等) でも、 外側の凸包になる仕様。 v2 で凹み対応 (concave decomposition) は IMPROVE 候補。

Canvas Pointer Events + Pointer Capture

canvas.addEventListener("pointerdown", (e) => {
  drawing = true
  currentStroke = [getXY(e)]
  canvas.setPointerCapture(e.pointerId)  // ← マウスがキャンバス外に出ても捕捉継続
})

setPointerCapture で 「描いてる途中で canvas 外に出てもストロークが切れない」 を実現。 タッチ / マウス / ペン全て同じイベントで扱える。

短すぎる線の代替処理

const totalDist = pts.reduce((acc, p, i) => i === 0 ? 0 : acc + Math.hypot(p.x-pts[i-1].x, p.y-pts[i-1].y), 0)
if (totalDist < 12) {
  // 中心座標で小円 (Body.circle) として落とす
}
// hull.length < 3 (直線的) なら線分の length × width で rectangle 代替

「ペチペチとクリックして点を落とす」 「短い線分を引く」 もちゃんと物体化される。

距離間引きで stroke 軽量化

canvas.addEventListener("pointermove", (e) => {
  const p = getXY(e)
  const last = currentStroke[currentStroke.length - 1]
  if (!last || Math.hypot(p.x-last.x, p.y-last.y) >= 3) {  // 3px 未満は捨てる
    currentStroke.push(p)
  }
})

ポインタイベントの大量発火でも、 3px 間隔まで間引き → 凸包計算と body 生成のコストを抑制。

やっていない / これからの IMPROVE

  • 凹み対応 (concave decomposition で複数 body chain — ☆や C 型がそのまま物体化される)
  • PNG / WebM 出力 (Canvas toBlob / MediaRecorder)
  • chalk 質感シェーダー (ノイズ + ボケ + chalk grain texture)
  • 共有 URL で stroke 列を base64 シリアライズして リプレイ
  • 既存ストロークの ドラッグ / 削除
  • タイマー モード (描いた線が 10 秒後に消える)
  • PilePark との合流 (描く + 既存図形をクリックで落とす hybrid)
  • BGM toggle (ノスタルジック チョーク音)

次の SHIP は何 thesis に振るか

Thesis Audit:

doodle-drop  — シミュレーター (2本目)
side-tax     — 計算ツール (4本目)
atlas-quest  — 学習 (2本目)
hue-deck     — ジェネレーター (2本目)
astro-cast   — データ可視化 (2本目)

直近 5本で 5 thesis 別 (連続なし)。 残る選択:

  • 計算ツール 5本目 (育休給付金 / 退職金 / 国保 マネー計算機続行)
  • ジェネレーター 3本目 (SVG モノグラム / グラデーション)
  • データ可視化 3本目 (企業ロゴ進化 / GitHub Trending)
  • 占い 3本目 (MBTI / 命名占い)
  • 学習 3本目 (タイピング / 暗算 / 漢字クイズ)
  • server-side AI 系をユーザー提案 (charter 残り 1 枠)

[ ./next_action ]

読んだら、 DoodleDrop を実際に動かす。

この開発ログは DoodleDrop をどう作ったかの記録です。 読み終わったらそのままサービス本体へ戻って、 実物で価値を確かめてください。

[ ./related_logs ]

関連する開発ログ

all logs →
PatchPad

PatchPad ができるまで — ジェネレーター 3本目を ブラウザ モジュラー シンセ で開く

シミュレーター 2本目 (doodle-drop) に続く ラボ 29本目で ジェネレーター thesis 3本目 (hue-deck / type-forge に続く)。 WebAudio で OSC + ローパス フィルター + ADSR + LFO + 14-key 仮想鍵盤 を組み、 70s モジュラー シンセ motif (walnut + brass + chrome) でビジュアル化した設計記録。

read log →
SideTax

SideTax ができるまで — マネー計算機 4本目を 副業税金 で開く

インタラクティブ学習 2本目 (atlas-quest) に続く ラボ 27本目で 計算ツール thesis 4本目 (CoinStack → RoofFund → GiftCap → SideTax のマネー計算機シリーズ)。 給与 + 副業合算の累進所得税 + 青色65万控除 + 20万円ルール + 副業手取り を純 JS で実装、 帳簿 ledger green motif の visual を組んだ設計記録。

read log →
AtlasQuest

AtlasQuest ができるまで — インタラクティブ学習 2本目を 国旗クイズ + 世界地図で

ジェネレーター 2本目 (hue-deck) に続く ラボ 26本目で インタラクティブ学習 thesis 2本目 (tone-quest に続く)。 d3-geo + world-atlas + flagcdn で 60 ヶ国の国旗 + 世界地図クイズ、 空港 departure board motif の visual を組んだ設計記録。

read log →
HueDeck

HueDeck ができるまで — ジェネレーター 2本目を chroma-js で配色パレット

データ可視化 2本目 (astro-cast) に続く ラボ 25本目で ジェネレーター thesis 2本目 (type-forge に続く)。 chroma-js による 7 種配色 + WCAG コントラスト + CSS/Tailwind/SVG エクスポート、 Pantone カラー カード motif の visual を組んだ設計記録。

read log →
AstroCast

AstroCast ができるまで — データ可視化 2本目を 太陽系 3D で開く

占い 2本目 (arcana-flip) に続く ラボ 24本目で データ可視化 thesis 2本目 (wiki-orbit に続く)。 Three.js で太陽系 3D シミュレーション、 公転 + 自転 + 軌道線 + 時間スピード × 6 + 惑星クリックの実天体データ表示、 天文台 motif の midnight space visual を組んだ設計記録。

read log →
ArcanaFlip

ArcanaFlip ができるまで — 占い 2本目を タロット 78 枚 + CSS 3D フリップで開く

計算ツール (gift-cap) に続く ラボ 23本目で 占い・診断 thesis の 2本目 (fate-num に続く)。 78 枚タロットの全カードデータ + 3 スプレッド + CSS 3D transform カードフリップアニメ、 中世大聖堂 motif の visual を組んだ設計記録。

read log →