screenshot to code(abi/screenshot-to-code)は、スクリーンショットを投げると画面を再現するコードを返す OSS です。star は 74,934、fork 9,166、MIT。名前のとおりの機能ですが、実際に動かすと README には書かれていない仕様がいくつも出てきます。本記事ではバックエンドをローカルに立て、日本語UIのスクリーンショットを実際に投入して、再現度・所要時間・4バリアント並列・モデルが選べない仕様までを実測しました。
30秒でわかる screenshot to code
・スクショ→コード生成の OSS。★74,934 / MIT / FastAPI+React。ホスト版もある
・実測:1枚から16,616文字のHTMLが出るまで359秒(約6分)
・NUM_VARIANTS = 4。1回の生成で4バリアントが並列実行されるので、コストは4回分で見積もる
・モデルは選べない。APIキーの組み合わせで決まる。存在しないモデル名を渡してもエラーにならなかった
・Anthropicキーだけでも動くが、img タグは0個。画像はCSSと絵文字による代替表現になる
・再現度は高い。日本語テキストはほぼ正確。ただし右カラムの目次は丸ごと落ちた
AIにコードを書かせる流れ全体の整理はVibe Codingとは?AIコーディングの始め方・ツール比較・実践ワークフロー2026にまとめてあります。本記事はその中でも「画面から入る」タイプのツールの実測です。
screenshot to codeとは — 画面を投げるとコードが返る
abi/screenshot-to-code は2023年11月公開のプロジェクトで、README の説明は「Drop in a screenshot and convert it to clean code (HTML/Tailwind/React/Vue)」です。実測時点の基本情報を整理します。
| 項目 | 実測値(2026-08-27) |
|---|---|
| star / fork | 74,934 / 9,166 |
| ライセンス | MIT |
| 主要言語 | Python(バックエンド)/React・Vite(フロントエンド) |
| 公開開始 | 2023-11-14 |
| 最終push | 2026-08-14 |
| オープンIssue | 130件 |
| ホスト版 | screenshottocode.com(公式) |
構成は素直で、FastAPI のバックエンドと React/Vite のフロントエンドに分かれています。フロントは WebSocket でバックエンドの /generate-code に接続し、画像と設定を投げて生成結果を受け取ります。本記事では UI を介さず、この WebSocket を直接叩いて計測しました。
生成の流れを1本にすると、どこでコストが4倍になるかが見えます。
画像+設定" B->>K: "どのキーがあるか" K-->>B: "ANTHROPIC のみ → (Opus 4.8, Sonnet 4.6)" B->>B: "NUM_VARIANTS=4 まで循環して割当" par 4本を並列実行 B->>M: "variant 1: Opus 4.8" B->>M: "variant 2: Sonnet 4.6" B->>M: "variant 3: Opus 4.8" B->>M: "variant 4: Sonnet 4.6" end M-->>B: "各バリアントのコード" B-->>U: "setCode / status" Note over B,M: "リクエストの codeGenerationModel は
この判定を上書きしない"
セットアップ — Poetry が無くても動かせる
README が案内するのは Poetry ですが、手元に無かったので uv で仮想環境を作って動かしました。
git clone --depth 1 https://github.com/abi/screenshot-to-code.git
cd screenshot-to-code/backend
uv venv --python 3.12
uv pip install --python .venv/bin/python \
fastapi uvicorn websockets "openai==2.16.0" python-dotenv beautifulsoup4 \
httpx anthropic pillow aiohttp pydantic google-genai \
playwright langfuse pillow-heif moviepy
ここで1回つまずきました。 pyproject.toml の依存を上から順に入れて起動したところ、ModuleNotFoundError: No module named 'playwright' で落ちます。原因は単純で、playwright / langfuse / pillow-heif が依存リストの後半にあり、最初の抜粋に入っていなかっただけでした。playwright はプレビュー用スクリーンショット機能(preview_screenshot)が起動時に import するため、使う予定が無くても入っていないと起動自体ができません。
キーは backend/.env に置きます。
echo "ANTHROPIC_API_KEY=sk-ant-..." > backend/.env
.venv/bin/python -m uvicorn main:app --port 7001
起動後、GET /api/capabilities で機能の可否を確認できます(実測では {"screenshot_preview":true} が返りました)。/capabilities ではなく /api/capabilities です——前者は 404 になります。
【実測】1枚のスクショから約6分・16,616文字
入力には日本語のブログ記事ページのスクリーンショット(1,006×1,100px・262KB)を使いました。日本語テキストが多く、カード・ピル・アイコン列など要素の種類も多い、そこそこ難しい題材です。
WebSocket に投げたパラメータの要点は次のとおりです。
{
"generatedCodeConfig": "html_tailwind",
"inputMode": "image",
"image": "data:image/png;base64,...",
"isImageGenerationEnabled": false,
"isAssetExtractionEnabled": false,
"generationType": "create"
}
結果です。
| 指標 | 実測値 |
|---|---|
| 所要時間 | 359.3秒(約6分) |
| 生成されたコード | 16,616文字(HTMLファイルで17,746バイト) |
| HTMLタグ総数 | 151 |
div / a / button / svg |
38 / 23 / 8 / 4 |
img |
0 |
| 日本語文字数 | 510 |
img が0個なのは、画像生成とアセット抽出を無効にしたからです(後述のとおり、そもそも該当のキーを持っていません)。画像が入るべき場所は CSS のグラデーションや絵文字で代替されました。
生成されたHTMLの冒頭はこうなっています。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>uv pythonの使い方2026|install・pin・upgradeと0.12の落とし穴を実測</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:..." rel="stylesheet">
lang="ja" を自分で付け、Noto Sans JP を読み込み、タイトルをスクリーンショットから正確に書き起こしています。 日本語UIを投げても崩れませんでした。
生成中、バックエンドは status メッセージ(Generating code...)と thinking メッセージを流し続けます。chunk によるストリーミングは今回0件で、最終的なコードは setCode で一括して届きました。UI 上でコードが少しずつ書かれていくように見えるかどうかは、モデルとバリアントの実装次第です。
再現できたもの、落ちたもの
生成HTMLを headless Chrome で同じ解像度にレンダリングし、入力と並べて比較しました(冒頭の図)。
再現できたもの
・ヘッダーのロゴ・ナビ5項目・RSSボタン
・パンくず
・タイトルとサブタイトル(英文)を文字どおり
・タグピル6個をラベルごと
・日付・読了時間・更新日のメタ行、著者行
・カバー画像の「作り直し」——グラデーション背景・mac風の3つのドット・カテゴリチップ・kicker・タイトル・リポ名・★数・ライセンスまでCSSで再構成
・引用ボックス、関連記事カード(ラベル付きリンク3本)、シェアボタン列、右下の丸いボタン
落ちたもの
・右カラムの目次が丸ごと消え、1カラムレイアウトになった(最大の差分)
・本文がシェアバー以降で途切れた。画面下部に続くはずの記事本文は生成されていない
・ヘッダーの配色が元の白基調に対して黒基調になった
・シェアアイコンが元のモノクロに対して色付きの塗りボックスになった
傾向としては、「主役の列にある要素とテキストは高精度に拾うが、副次的な列と長い本文は落ちる」でした。ワイヤーフレームや1画面完結のLPには向き、情報密度の高い記事ページを丸ごと再現する用途には追加の指示が要ります。
なぜ目次が落ちたのか(推測)
これは実装を追って確かめたわけではないので推測ですが、傾向としては説明がつきます。今回の入力は縦1,100pxに対して情報量が多く、右カラムの目次は淡いグレーの細い文字で描かれています。生成側は限られた出力トークンの中で「画面の主役」を優先するため、コントラストが低く従属的な列は捨てられやすい。実際、落ちたのは目次と本文後半というどちらも視覚的な優先度が低い領域でした。
対策としては、①スクリーンショットを分割して投げる ②追加のプロンプトで「右カラムの目次も再現して」と明示する ③そもそも1画面完結の題材に使う、のいずれかになります。このツールは「1枚を丸ごと」より「1ブロックずつ」のほうが精度が出るという見立てです(この対策の効果自体は未検証)。
【実測】モデルは自分で選べない — キーの組み合わせで決まる
ここが README を読むだけでは分からない、最も実務的な仕様です。
WebSocket のパラメータには codeGenerationModel があるので、モデルを指定できるように見えます。試しに、キーを持っていない gpt-5.5 を指定してみました。
エラーになりません。 そのまま生成が進み、完了しました。バックエンドのログを見ると、実際に走っていたのは claude-opus-4-8 と claude-sonnet-4-6 でした。
実装を読むと理由がはっきりします。backend/routes/model_choice_sets.py にキーの組み合わせごとのモデル集合が定義されていて、generate_code.py がそれを選びます。
ANTHROPIC_ONLY_MODELS = (
Llm.CLAUDE_OPUS_4_8_MEDIUM,
Llm.CLAUDE_SONNET_4_6,
)
ALL_KEYS_MODELS_DEFAULT = (
Llm.CLAUDE_OPUS_5_MEDIUM,
Llm.GEMINI_3_FLASH_PREVIEW_HIGH,
Llm.GEMINI_3_1_PRO_PREVIEW_HIGH,
Llm.GPT_5_6_SOL_MAX,
)
分岐は「Gemini+Anthropic+OpenAI の3つ揃い」「Gemini+Anthropic」「Gemini+OpenAI」「OpenAI+Anthropic」「Gemini のみ」「Anthropic のみ」「OpenAI のみ」の順に評価され、どれにも当たらなければ例外です。
つまり「どのモデルで生成するか」はユーザーの選択ではなく、キー構成の関数です。 3キー揃えると3社4モデル(Claude Opus 5・Gemini 3 Flash・Gemini 3.1 Pro・GPT-5.6)で比較でき、1キーだとその provider のモデルだけになります。README が「キーを増やすほど強いモデルの組み合わせが自動で選ばれる」と書いているのは、この実装のことです。
【実測】1回の生成=4バリアント並列
もうひとつコストに直結する仕様です。backend/config.py にこうあります。
NUM_VARIANTS = 4
NUM_VARIANTS_VIDEO = 2
1回の生成につき4つのバリアントが並列で走ります(更新生成のときだけ2)。WebSocket の最初のメッセージでも variantCount: 4 が返ってきました。
モデル集合が4つに満たない場合は循環して埋められます。
# Cycle through models: [A, B] with num=5 becomes [A, B, A, B, A]
for i in range(num_variants):
selected_models.append(models[i % len(models)])
Anthropic キーのみの実測ケースでは (Opus 4.8, Sonnet 4.6) が循環して Opus 4.8 ×2 + Sonnet 4.6 ×2 の4本になります。バックエンドのログに両モデル名が繰り返し現れたのはこのためです。
含意はコストです。 「画面1枚を変換した」ときのAPI課金は1回分ではなく4回分になります。しかも Opus 系が半分を占める構成なので、単価も低くありません。ざっと試すだけのつもりで大きな画面を何枚も投げると、想定より早く費用が積み上がります。コストを抑えたいなら NUM_VARIANTS を落とすのが最初の手です。
コストを下げる具体的な手
NUM_VARIANTS は backend/config.py の定数なので、セルフホストなら直接下げられます。
# backend/config.py
NUM_VARIANTS = 1 # 既定は 4
比較検討が目的でなく「1枚を1回変換できればいい」用途なら、ここを 1 にするだけでAPIコストは単純に1/4になります。逆に、複数モデルの出力を見比べたいという用途こそがこのツールの持ち味でもあるので、目的次第で決めてください。更新生成(generationType: "update")は既定でも2本なので、初回生成だけが4本です。
もうひとつ、入力画像の解像度も効きます。今回は元の 2,560×2,800px を 1,006×1,100px に縮小して投げました。マルチモーダル入力は画像サイズがそのままトークン数に効くため、読み取れる範囲で小さくするのが素直な節約です。縮小しても日本語テキストの読み取りは崩れませんでした。
失敗しない条件・静かに劣化する条件
実測でいくつか異常系を投げました。共通しているのは「エラーで止まらない」ことです。
| 投げたもの | 期待 | 実際 |
|---|---|---|
Gemini キー無しで isAssetExtractionEnabled: true |
エラーか警告 | エラーにならず生成が進む(アセット抽出が黙って効かない) |
保有していない provider のモデル名(gpt-5.5) |
エラー | エラーにならず、キー構成どおりのモデルで生成 |
| 空の画像 | エラー | variantCount は返るが以降の応答が確認できず(未検証) |
「指定が無視されても気づけない」のがこのツールの扱いにくい点です。 特にアセット抽出は、README が「Gemini を強く推奨」と書いている中核機能なのに、キーが無くても設定を有効にできてしまいます。生成物に img が1つも無いときは、モデルの能力ではなくキー構成を先に疑うのが正しい切り分けです。
なお README は Ollama によるローカルモデル実行にも触れていますが、「品質が低いため推奨しない」と明記しており、GitHub Issue のコメントへ誘導する形になっています。本記事では Ollama 構成は未検証です。
どう使うのが妥当か
向いている使い方
・ワイヤーフレームや1画面のLPを、たたき台のHTMLに変える。骨格とテキストの再現度は高い
・既存画面のTailwind化の下書き。CDN込みの動くHTMLが出るので、そこから削っていける
・3キー揃えてモデルを比較する。同じ画面に対する Claude / Gemini / GPT の出力差を並べて見られるのは、このツール固有の価値
同じ「AIにコードを書かせる」でも、ターミナル常駐型のエージェントとは入口が違います。Qwen Codeとは|Gemini CLI由来のマルチプロトコルAIコーディングCLIを実測のようなCLIは既存コードベースを読んで直すのが得意で、screenshot-to-code はまだコードが存在しない画面を起点にするのが得意です。段階が違うので競合しません。
向いていない使い方
・情報密度の高い既存ページの丸ごと再現。実測でも目次と本文後半が落ちた
・画像を含む忠実な再現を1キーでやること。Gemini(アセット抽出)と Replicate(画像生成・編集)が無いと、画像は代替表現になる
・コストを気にしながらの大量処理。既定で4バリアント並列
ホスト版という選択肢
ローカル実行の手間を避けたいなら、公式のホスト版(screenshottocode.com)があります。本記事ではホスト版は未検証ですが、セットアップで踏んだ playwright の件やキー管理を考えると、「まず使えるか試したい」段階ではホスト版のほうが早いのは確かです。一方で、NUM_VARIANTS を下げる、プロンプトを差し替える、自前のモデルを足すといった調整はセルフホストでないとできません。試すのはホスト版、運用に組み込むならセルフホストという順序が現実的です。
画像1枚からコードを起こす発想そのものは他にもあり、Three.js に特化した実装はimg2threejsとは|画像1枚からThree.jsコードを生成するAIスキルの仕組み・使い方・品質ゲートで扱っています。汎用のUIコード化なら screenshot-to-code、3Dシーン生成なら img2threejs、という住み分けです。自前のプラットフォームとして組み込みたい場合はVibeSDKとは:自前のvibe codingプラットフォームをCloudflareで構築するOSSも選択肢になります。
まとめ
・abi/screenshot-to-code は ★74,934・MIT の画面→コード生成OSS。FastAPI+React 構成
・実測:日本語UIのスクショ1枚 → 16,616文字のHTMLまで359秒(約6分)
・再現度は高い。日本語のタイトル・タグ・メタ情報・カード・シェア列まで再現。カバー画像もCSSで作り直された
・落ちたのは右カラムの目次と本文後半。「主役の列は拾い、副次的な列は落ちる」傾向
・モデルは選べない。キー構成の関数(Anthropicのみ=Opus 4.8+Sonnet 4.6/3キー=3社4モデル)
・NUM_VARIANTS = 4。コストは1枚あたり4回分で見積もる
・異常系はエラーにならず静かに劣化する。img が0個ならキー構成を疑う
・セットアップの落とし穴:playwright を入れないと起動しない//api/capabilities(/capabilities は404)
「スクショを投げれば終わり」ではなく、キー構成とバリアント数を決めてから使うツールです。そこさえ押さえれば、たたき台の生成器としては十分に実用的でした。
最後に需要について正直に書いておきます。screenshot to code の日本語での月間検索ボリュームは 140 で、決して大きくありません(スクリーンショット コード 変換・デザイン コード 変換 などの言い換えはいずれも計測下限でした)。★7.4万というリポジトリの規模に対して日本語の検索需要は小さく、知名度と検索されやすさが一致していない典型です。ただし SERP を実査すると上位10件のうち GitHub・note・Zenn の3件がUGCで、ベンダーの公式ドキュメントが壁になっている構図ではありません。「実際に動かして測った記事」であれば入り込む余地はあるという判断で、本記事は再現度・所要時間・バリアント数・キー構成という測れるところに寄せました。
参照ソース
- abi/screenshot-to-code — 公式リポジトリ。star・fork・ライセンス・README のキー要件と推奨(2026-08-27 実測)
- Screenshot to Code(公式ホスト版) — ローカル実行以外の選択肢(2026-08-27 参照)
backend/routes/model_choice_sets.py/backend/config.py/backend/routes/generate_code.py(commitd026163) — モデル選択の分岐、NUM_VARIANTS = 4、バリアントの循環割当(2026-08-27 実読)- ローカル実行による計測(macOS・Python 3.12・
ANTHROPIC_API_KEYのみ・2026-08-27 実測)