スクショを1枚渡すと、AIが要素を検出して、入場アニメとホバー・プレス付きの「動くプロトタイプ」を作ってくれる——そんなツールを個人開発で作りました。エディタ不要、ローカル完結、無料です。
正体は Rive というインタラクティブアニメーション形式向けの MCP サーバー(rive-mcp-server)で、Claude などの AI アシスタントから使います。今回の v0.6 の目玉は 2 つです。
-
UI スクリーンショット 1 枚 → 入場アニメ・hover/press つきの .riv(
riv_ui_detect→riv_ui_prototypeの 2 コール) - SVG(Figma の Copy as SVG)を渡せば推定ゼロ。矩形・塗り・角丸・テキスト・レイヤー名をファイルから読み出し、全要素が編集可能なまま動く
まず一番小さい例から。サインインカードのスクショ 1 枚を渡すと、こうなります。
出てきた .riv はベクターなので、塗りの差し替えも 1 コールです。
ページ全体を渡した場合が冒頭のこれです。元のスクショ(ダッシュボード)はこちら。
これを渡すと、こう動きます。
この記事は機能紹介というより、開発中に踏んだ「計測が嘘をつく」話を中心に書きます。数字はすべて実測です。
スクショからの再構成で何が難しいか
DOM も元データも無い PNG 1 枚から、画面上の要素を「編集可能なベクター矩形」か「元画像の切り抜き」として再構成します。ここで効いてくるのが損失の非対称性です。
- 写真を誤って単色矩形に塗り替えると、見た目そのものが壊れる
- 逆に、パネルを誤って切り抜きに落としても、見た目は保たれて編集性が落ちるだけ
だから迷ったら切り抜き側に倒す。この方針は最初から決めていたのですが、問題は「迷ったかどうか」を判定する分類器のほうでした。充填率や辺の埋まり方といった形状統計をどう組み合わせても、「文字入りのボタン」と「たまたま矩形っぽい写真」は分離できません。実装して計測して捨てた案が 3 つあります(詳細はリポジトリのドキュメントに、数字ごと残してあります)。
転機: 分類をやめて、塗ってみて測る
最終的に採ったのは「これはパネルか写真か」を当てるのをやめる方法です。
- 要素ツリーを組み終えたあと、実際に描画順で合成してみる
- 平坦に塗った各矩形について、「最終合成でその塗りが見えている画素」だけを取り出す
- 元のスクショと比べて、量子化 1 段(RGB 8)を超えて外れた画素の割合を測る
- 2% を超えたら、その塗りは出荷しない
検出の途中経過はこういう見た目です(緑=編集可能なベクター矩形として再構成、赤=元画像の切り抜き。番号は要素 ID で、この番号にロールを割り当てます)。
ボタンのラベルは上にテキスト要素が乗るので誤差に寄与しません。写真は覆われないので大量に外れます。「穴の意味」を推測する必要が消えて、判定がそのまま品質保証になります。
さらに、外れた画素が少数の塊(アクセントバー、破線、小さなアイコン)に集中している場合は、その塊だけを切り抜いてパネルの上に乗せ、パネル自体はベクターのまま残すようにしました。破線が横切る角丸矩形は「破線つきの編集可能な矩形」として出てきます。
計測が嘘をついていた話
この改修の途中で、「良い数字が出ている」という当時の判断そのものが間違いだったと分かりました。
原因はテキスト検出の実装バグです。近くにある文字の断片を「同じ行」としてまとめる処理があり、まとめた行が大きくなるほど、次の断片を拾う許容範囲も広がる作りになっていました。これが正帰還(フィードバックループ)を起こします。
あるボタンの縁のアンチエイリアス——幅1px・高さ31pxのごく細い断片——を、最初に「文字の一部」として拾ってしまった行が1つありました。すると許容範囲が少し広がり、次に近くの何かを拾ってまた少し大きくなり、許容範囲がさらに広がり……という連鎖が止まらず、実画像では最終的に **1440×513px という、画面の半分を覆う「1行のテキスト」**ができあがっていました。
これが数字を狂わせた理由は単純です。この巨大な「テキスト行」は、元のスクショをそのまま切り抜いた画像として最前面に貼られます。切り抜きの中身は元のスクショそのものなので、当然ピクセル単位で一致する。つまり:
- 本来なら露呈するはずのベクター化ミスが、上に貼られた巨大な切り抜きの下に隠れて見えなくなる
- 再構成誤差は p99 でゼロという、実態以上に良い数字が出る
- ボタンのラベルは、本来は独立したテキスト要素になるはずが、この巨大な行に呑み込まれて出てこない
「leak 0・誤差 0」という当時の数字は、検出器が優秀だったからではなく、画面の半分を元画像のコピーで覆い隠すバグの副作用でした。数値上は満点でも、実際にはほとんど何も再構成できていなかったわけです。
バグを直せば、当然この「満点」は崩れます。退行検知の基準値を「良かった頃の数字」から「バグを直した後の、悪化して見える実測」へ書き換えるのは気持ちのいい作業ではありませんが、どの数字が何によって隠されていたかを記録に残した上で更新しました。良く見える嘘の基準と、悪く見える本当の基準なら、後者にしか使い道がありません。
最終的な実測はこうなりました(tuning 9 枚 / holdout 7 枚の実ページ)。
| 指標 | 改修前 | 改修後 |
|---|---|---|
| ラベル入りパネルのベクター化 | 7/20 | 15/20 |
| 実害のある leak(最悪領域) | 0.032 | 0.007 |
| 再構成誤差 p99 | 0.028 | 全ページ 0 |
それでも限界はある。だから SVG を読む
ここまでは実装のバグの話でした。バグを直したあとも、もう一段、原理的な壁が残ります。
写真主体のページ(GitHub のトップページのような)では、単色で塗った合成結果が元のスクショと2%以内に収まる領域が、ほとんどありません。写真は場所ごとに色がバラバラなので、「1色で塗って画素を突き合わせる」という検査に原理的に通らないのです。これはバグではなく、スクリーンショット1枚から情報を復元しようとする方式そのものの限界です——ピクセルの並びから「これは元々何だったか」を当てている以上、当て方をどれだけ改善しても、情報量が足りない入力には勝てません。検査に通らない以上、ここまでの設計判断(迷ったら切り抜き側に倒す)がそのまま働いて、要素の8割が「フェードするだけの切り抜き」になります。見た目は壊れませんが、Rive の .riv を作る意味は半減します。
だったら、当てるのをやめればいい。元データがまだ残っている場面——つまり Figma などのデザインファイルが手元にある場面——では、ピクセルから推定する必要が最初から無いはずです。そこでもう 1 つの柱を用意しました。元データが残っているなら、推定しない。
riv_ui_detect svgPath: design.svg → 要素ツリー(座標・塗り・角丸は属性値そのまま)
riv_ui_prototype svgPath + roles → .riv
Figma で右クリック → Copy as SVG したものを渡すと、
- 矩形・塗り・角丸・親子ツリーはファイルから読むだけ(誤差ゼロ)
- イラストは本物のベジェ頂点のまま編集可能
-
<text>は編集可能な Rive テキストに。フォントはfontsで渡せて、グリフの無い文字(指定フォントに無い漢字など)を含む行は豆腐にせず画像として焼いて、必ず警告 - レイヤー名(Button / Card / Nav …)がロール候補になり、入場アニメ・hover/press が自動で付く
レイヤー名付きの SVG(左)を渡すと、ロール推定込みでこう動きます(右)。
| 元の SVG | 生成された .riv の入場アニメ |
|---|---|
![]() |
![]() |
同じダッシュボードで比べると、スクショ経路の .riv が 3MB(切り抜き画像込み)に対し、SVG 経路は 2.6KB・全要素編集可能でした。
なお Rive のエディタ自体は 2023 年から SVG 貼り付けに対応しています。この 2 コールが足すのは「エディタも人も介在せず、CI やエージェントのループの中で回る」ことと「ロールからモーションが一緒に付いてくる」ことの 2 点です。ネットワークには一切出ません——FIGMA_TOKEN を設定して opt-in したときだけ、figmaUrl で Figma REST API から直接フレームを取れます(変数が無ければ引数ごとエラーになり、リクエストは 1 つも飛びません)。
使い方
npm install -g rive-mcp-server # または npx rive-mcp-server
MCP クライアント(Claude Code 等)に登録して、
この screenshot.png を動くプロトタイプにして
と言うだけです。検出→番号付きオーバーレイ→ロール割り当て→.riv 生成までをモデルが 2 コールで回します。限界の一覧(±2 の画素ノイズで分類が揺れる、グラデーションの制約など)は実測値つきでドキュメントにまとめてあります。「できること」より「できないことがどう計測されているか」を見てもらえると、この機能の性格が伝わると思います。
今の課題、次にやること
正直に書くと、まだ荒いところが残っています。
- 写真主体のページでは、今も「フェードするだけの切り抜き」が多数派のままです。安全側に倒す設計なので見た目は壊れませんが、編集可能な部品は増やしたい。ここをどう崩すかはまだ模索中です
- ±2程度のごく小さな画素ノイズで、パネルとして再構成されるか切り抜きになるかの判定が揺れることがあります。PNG の再圧縮程度でも起こりうるので、もう少し安定させたいところです
- アクセントバーや破線のような細い装飾は、まだ独立した「動く要素」としては拾えていません。今はパネルの上に静止画として乗るだけです。パネルごとベクター化する精度を落とさずにここを動かせるようにするのが次の本命だと思っています
-
FIGMA_TOKENを使った Figma REST API 連携は、実装はしたものの自分の実トークンではまだ試していません(今はテストのみで検証済み)。近いうちに実際に通して確認する予定です
このあたりを次に潰していくつもりです。触ってみて気になった挙動があれば、GitHub で教えてもらえると助かります。進捗はまたこちらで書きます。







