前回まで、ファミリーベーシックのバイブコーディングを3通りの相手で試してきました。第6回はClaude(claude-cli)で、「スペースインベーダー風のスプライトを使ったキーボードで操作するゲームを作って」の一行から7分でRUNまで到達し、SCORE 60まで動きました。ただし敵は1体だけで実行時エラーも出る、部分的な成功です。
第7回はOpenAI(gpt-5.6-sol)に差し替え、依頼文一言だけではLOCATEとPRINTのテキスト表現止まりでしたが、「スプライトを使って」と明示するとハードウェアスプライトの編隊がエラーなく動きました。応答速度はgpt-5.6-solのほうが明らかに速く(30秒台。claude-cliは約6分)、Claudeと遜色ない結果です。
第8回のローカルLLMは、構造化したベンチマークでは健闘したのですが、自由な依頼でのバイブコーディングまでは届いていません。LoRAで方言を焼く実験も、自動評価の数字は良く見えても実際は学習サンプルの丸暗記で、汎化はしていませんでした。現時点では課金してでもクラウドの大型LLMを使うのが実用的、というのが前回までの結論です。
前回まで3回は、エミュレータ画面の横に専用のチャットパネルを増設し、そこに日本語で書いて、外部LLMの応答を待つという形でした。使い勝手はよいものの、専用の作り込みが要ります。連載の最終回で目指すのはもっと大きな絵です。ファミリーベーシックのコーディングを、普段使いのVS Codeスタイルのコーディング支援にそのまま組み込むこと。
これは特定のツールに限った話ではありません。エミュレータの操作をエディタ側のAIエージェントに道具として渡せれば、Claude Codeに限らず、CursorやAntigravityのような他のAIコーディングエディタでも同じことができるはずです。今回はその実装を一つ、Claude Code + MCP(Model Context Protocol)で示します。
本記事はローカル LLM による自動執筆パイプラインで生成されました。現段階ではクラウド AI(Claude 等)の補助や人間の編集が介在していますが、pareido.jp では最終的に AI が自律的にコンテンツを制作できる仕組みの構築を目指しています。
依頼も、待ち時間も無くす
今回使うのは、nesemu-ai/extras/mcp-vibe-coding に新しく作ったMCPサーバーです。MCP(Model Context Protocol)は、AIエージェントに外部の道具を持たせるための接続規格で、ここではエミュレータの操作一式を道具として差し出しています。受け取るのは、VS Code上で動く開発エージェントのClaude Codeです。
サーバーが内部で呼んでいるのは window.app.autoTyper.typeCode() という関数、つまりChatWndの「Type」ボタンが内部で叩いているのとまったく同じものです。違いは、その手前にあった「日本語で依頼して、外部LLMの応答を待つ」という工程が丸ごと消えることにあります。副産物として、ChatWndのUIもポート3001のバックエンドサーバーも起動不要になりました。
| 項目 | 内容 |
|---|---|
| 実行環境 | macOS / Node.js(--experimental-strip-types でTypeScriptを直接実行) |
| ブラウザ操作 | Playwright + Chromium |
| MCP SDK | @modelcontextprotocol/sdk ^1.30 |
| 対象 | ブラウザ版nesemu(ファミリーベーシック V2.0A) |
| AIバックエンド | 不要(ポート3001は起動しない) |
用意したツールは9つです。引数と戻り値まで並べると、こうなります。
| ツール | 引数 | 戻り値・動作 |
|---|---|---|
vibe_open |
mode(landscape / portrait、既定 landscape)、record("wholepage" を指定すると後続の録画をセッション開始時から予約) |
ブラウザを開き、OK. プロンプトが出るまで待つ。sessionId・ライブビュー用のviewerUrl・現在のscreenTextを返す |
vibe_read |
sessionId |
画面のテキスト(screenText)、OAM=スプライト一覧(64エントリの{i,y,tile,attr,x})、errorMarker(?SN等のエラー種別、無ければnull)を返す |
vibe_type_code |
sessionId、code(BASICソース文字列) |
「Type」ボタンと同じ内部関数でコードを1行ずつ画面へ流し込み、完了後の画面状態(screenText/oam/errorMarker)を返す |
vibe_key |
sessionId、key(DOMキー名)、holdMs(既定120ms) |
キーを1つ、指定時間だけ押す。ファミリーベーシックはフレームの一部でしか入力を拾わないため、既定値より短いと無視されることがある |
vibe_run |
sessionId |
RUNとEnterを打って実行し、結果の画面状態(screenText/oam/errorMarker)を返す |
vibe_screenshot |
sessionId |
現在の画面をPNGで保存し、保存先パスを返す |
vibe_record_start |
sessionId、captureMode(canvas=NES画面のみ/wholepage=ページ全体、既定canvas) |
録画を開始する。wholepageはvibe_open時点で予約が必要 |
vibe_record_stop |
sessionId |
録画を停止し、保存先パスを返す。wholepageの場合はセッションも同時に閉じる |
vibe_close |
sessionId |
セッション(ブラウザとライブビューサーバー)を閉じる |
作業中の画面が見えないと不便なので、ライブビューア用の小さなHTTPサーバーも同梱しました。VS Code標準のSimple Browserは受け身のiframeで、Playwrightが動かしているChromiumには接続できません。別タブでnesemuのURLを開いても、エミュレータの状態はページの中にあるので同期しない。そこで300ミリ秒ごとに実際の操作対象のスクリーンショットを配信するページを立て、そのURLをSimple Browserで開く形にしました。エディタの隣で、ほぼリアルタイムに画面が動きます。
書いて、間違えて、その場で直した
最初に書いたのは、スプライトを横へ動かすだけの短いコードです。
10 CLS
20 SPRITE 0,8,100
30 FOR I=8 TO 240 STEP 8
40 SPRITE 0,I,100
50 NEXT I
60 PRINT "DONE"
70 END
vibe_type_code で流し込み、vibe_run を叩いた結果が ?SN ERROR IN 50 でした。原因はファミリーベーシックの NEXT が変数名を取らないことです。一般的なBASICなら NEXT I と書けますが、この方言では NEXT だけが正しい。第6回で書いた「一般的なBASICの知識だけでは通らない」の、そのままの実例に当たった形です。

NEXT を直したうえで、内容も画面の見栄えがわかるものに変え、すぐ2本目を流しました。
10 CLS
20 FOR I=1 TO 10
30 LOCATE I,I
40 PRINT "*"
50 NEXT
60 LOCATE 1,12
70 PRINT "HELLO FROM CLAUDE CODE"
80 END
今度はエラーなしで通り、斜めに並んだ * が10個と、その下に HELLO FROM CLAUDE CODE が出ました。

書いていて手応えがあったのは、エラーから修正までの間に何も挟まらなかったことです。VS Codeでコードを書き、流し込み、vibe_read で返ってきた画面のエラーを読み、1行直して再実行する。第6回は依頼から7分待って動きました。第7回はスプライトを使わせるのに依頼文を書き直す一往復が要りました。今回は、その待ち時間や往復に当たる区間が最初から存在しません。
2枚のスクリーンショットの右側には、これまでの主役だったChatWndパネルが「Family BASIC AI Chat ready.」の初期メッセージのまま映っています。一度も使っていないからです。ここが今回の方式の見た目上の違いで、パネルは画面にあるだけで、経路からは外れています。
正直に書いておくと、ここまで試したのは簡単なサンプル2本だけです。「速い」と言えるのは、あくまでこの2本を書いて直した範囲の話でした。
インベーダー級で試すと、40分かかった
そこで、第6回のインベーダー級のものをこの方式で組めるかを実際に確認しました。依頼文は前回までと揃えて「スプライトを使って、スペースインベーダー風のゲームを作って」の一行です。今回はリファレンスをWebFetchで参照させたうえで、vibe_open→vibe_type_code→vibe_runのループはClaude Codeに任せ、動いたら人間がSimple Browserを開き、vibe_keyで実際にプレイさせて確認する、という2段階の指示にしました。
結果、敵6体編隊のインベーダー風ゲームがエラーなく完成し、左右移動・発射・命中判定・スコア加算(0→10)まで実際に確認できました。自機のスプライトも、第7回・第8回の経緯で修正したリファレンス(スターシップの「上」ポーズ)どおり、正しく上向きに表示されています。
ただし、事実として書いておかなければならないのは所要時間です。依頼文を渡してから、この動作確認が終わるまでに約40分かかっています。内訳のほとんどは、リファレンスの参照や次の一手を考える「思考」の時間で、単発で100秒を超える思考ブロックも複数回ありました。これは第7回のgpt-5.6-sol(30秒台)はもちろん、第6回のclaude-cli(約7分)と比べても大幅に長い所要時間です。「待ち時間や往復が最初から存在しない」という今回の方式の利点は、Claude Code自身が費やす思考・調査のコストまで消してくれるわけではありません。エージェントとして自律的に動く分、むしろ寄り道や検証に時間を使いやすい面もある、というのが実際にやってみた結果です。
4つの道を並べてみる
第6回からの4本を、経路の形で並べるとこうなります。
| 回 | AI側 | 経由するもの | 応答待ち | 依頼の形 | 結果 |
|---|---|---|---|---|---|
| 第6回 | Claude(claude-cli) |
ChatWnd + バックエンド3001 | 約7分 | 日本語一行 | △ 動いたが完成度は低い |
| 第7回 | OpenAI(gpt-5.6-sol) |
ChatWnd + バックエンド3001 | 30秒〜45秒 | 日本語一行(スプライト明示で変化) | ○ エラーなく、スプライトも動いた |
| 第8回 | ローカルLLM | ChatWnd + バックエンド3001 | あり | 構造化ベンチのみ | △ ベンチでは健闘、自由な依頼は未検証 |
| 第9回 | Claude Code + MCP | なし(JS関数を直接) | 待ち時間はゼロだが思考時間は別途かかる(簡単なサンプル即時、インベーダー級は約40分) | コードを直接書く | ○ サンプル2本、インベーダー級とも動いた |
第6回・第8回は、思ったとおりに動くまでにもう一往復が必要でした。画面のエラーをパネルに見せ、直した版を待ち、また流し込む。第7回はエラーこそ出ませんでしたが、スプライトを使わせるには依頼文を書き直すもう一往復が要りました。今回の方式では、コードを書いた当人が画面を読んでその場で直します。直せる相手が、道具の中に最初から同居している。今回の4本を並べて見えたのは、速度の差というより、この同居のありなしでした。
もっとも、これは「どんなゲームでも作れる」という話ではありません。第6回・第7回で見たとおり、クラウドの大型LLMなら依頼文を工夫することで方言の壁を越えられますが、第8回で見たとおりファミリーベーシックの方言は今もローカルLLMには重く、自由な依頼まではまだ届いていません。今回外せたのは往復の手間であって、モデル側の実力そのものを底上げしたわけではありません。
利点と、薄れるロマン
この方式の利点は、AIエージェントの能力やエディタのハーネスをそのままフル活用できることです。同じワークスペースにある他のプログラムやドキュメントを文脈として渡せますし、必要ならWeb検索と組み合わせて仕様を調べさせることもできます。画面操作もPlaywrightで自由に自動化できるので、今回用意した9つのツール以外にも、必要な道具はいくらでも足していけます。専用パネルを作り込む必要がない分、汎用性は明らかにこちらが上です。
引き換えに失うものもあります。前回までの3回は、エミュレータの画面そのものが主役で、そこに日本語で語りかけ、待ち、動いた瞬間を録画する、というのが記事の芯にありました。今回の方式はAIエージェントとエディタの間で完結してしまうため、わざわざデータレコーダを回す理由がほとんどなくなります。普段使いのコーディング作業とほぼ地続きになった分、これまであった「1984年の機械にAIが話しかける」というロマンの成分は、確実に薄まりました。
この連載は、起動画面のメッセージ枠を借りたAIチャット(第1〜2回)から始まり、素通りしてきた3枚のボード(第3〜5回)を経て、バイブコーディングの4つの経路(第6〜9回)で終わります。1984年の機械に2026年の道具をどう繋ぐか、9回かけて手を動かした記録でした。手元のローカルLLMがこの方言を扱えるようになる日は、まだ来ていません。そのときが来たら、また同じ画面で確かめようと思います。