# Komadori

**1080p で 10 秒、100 万ボイドのクリップが、11-14 秒でできます。** ターミナルからは canvas が動くところを見られず、あなたのエージェントも同じです。Komadori はページを 1 フレームずつ動かし、描かれたものを返します。クリップ、コンタクトシート、レポートです。

spryte が描いた 100 万ボイドの群れを、Komadori でクリップにしたものです。

*spryte のデモの 100 万ボイドを、Komadori のクリップにしたものです。1080p の 10 秒を 11-14 秒で作れました。このファイルは 720p です。*

![その実行のコンタクトシート。サンプルした 20 フレームを 1 枚にまとめた画像](/komadori/contact-sheet.jpg)

*同じ実行のコンタクトシートです。サンプルした 20 フレームを 1 枚にまとめ、レポートと並べて書き出します。*

![失敗したゴールデンフレーム。保存したフレーム、星の色を変えた新しいフレーム、その差分](/komadori/golden-diff.jpg)

*失敗したゴールデンフレームです。保存したフレーム、星の色を変えた新しいフレーム、その差分を並べています。ピクセルの 16% が変わりました。*

コマ撮り（Komadori）は stop-motion のことです。Komadori はあなたのページを Chrome で動かし、1 フレームずつ進めて、コンタクトシート（サンプルしたフレームを 1 枚にまとめた画像）とレポートを書き出します。`--mp4` を付けると、クリップも書き出します。[spryte](/ja/spryte/) のシーンのために作りましたが、canvas に描くページならどれでも使えます。

```sh
npx komadori dist --frames 600 --size 1920x1080 --out render --mp4 render/clip.mp4
```

`dist` はビルド済みのフォルダか URL です。Komadori には Node 20、Chrome または Chromium が必要で、`--mp4` を使うなら ffmpeg も必要です。

## ページにフレーム関数を用意する {#give-the-page-a-frame-function}

Komadori は `window.__frame()` という 1 つの関数でページを進めます。この関数は、シーンをちょうど 1 フレーム進めて描画します。通常のループは人間用に残し、ステップ実行のモードをクエリ文字列の後ろに足します。

```ts
if (new URLSearchParams(location.search).has("capture")) {
  window.__frame = () => {
    step(1 / 60);
    render();
  };
}
```

```sh
npx komadori dist --query "capture=1" --frames 600
```

ステップは 1/60 秒に固定してください。そうすると、マシンの速さに関係なく、クリップは意図した速さで再生されます。乱数もシードを固定すると、2 回の実行で同じフレームになります。[spryte の100 万ボイドのデモ](https://github.com/debuffjp/debuff/tree/main/packages/spryte/examples/million)が実際に動く例です。

## 何を検査するか {#what-it-checks}

次のとき、実行は終了コード 1 で失敗します。

- ページがエラーを投げた、またはリクエストが失敗した
- シェーダーがコンパイルできない
- サンプルしたフレームがすべて単色、またはフレームがほとんど変わらない

シェーダーのエラーには、シェーダーのソースの行番号、コンパイラーのメッセージ、その行の内容が付きます。

内容がフレームの端に触れていて、切れているかもしれないときは警告します。終了コード 2 は、レンダリング自体ができなかったことを表します。たとえば、ページが `window.__frame()` を定義していない場合です。

縞模様、読みにくい文字、好みでない色は検出できません。`contact-sheet.png` を開いて見てください。`--keep-frames dir` を付けると、サンプルしたフレームを実寸で保存します。

## クリップ {#clips}

`--mp4` は、フレームごとにページ全体のスクリーンショットを撮ります。`--capture canvas` は、代わりにページの中で canvas をエンコードするので、約 3 倍（実行によって 2.6-3.6 倍）速くなります。spryte の100 万ボイドのデモの 10 秒、1080p のクリップは、Chrome（Windows）で Radeon 780M でも RTX 5060 のノート PC でも 11-14 秒でした。

```sh
npx komadori dist --query "capture=1" --frames 600 --size 1920x1080 --mp4 clip.mp4 --capture canvas
```

クリップに入るのは canvas だけなので、上に HTML で描いたタイトルやロゴは写りません。指定したサイズで Chrome が H.264 をエンコードできないときは、Komadori は止まってその旨を伝えます。

## ゴールデンフレーム {#golden-frames}

ゴールデンフレームは、シーンがこう見えるべきという姿を保存した画像です。以降の実行では、フレームが一致しなくなると失敗します。

```sh
npx komadori approve dist --frames 120 --samples 5 --size 1280x720 --golden golden
npx komadori dist --frames 120 --samples 5 --size 1280x720 --golden golden
```

`approve` はシーンを 2 回レンダリングし、2 回が一致したときだけフレームを保存します。失敗したフレームは、番号、変化したピクセルの割合、領域とともに報告されます。Komadori は、ゴールデンフレームと新しいフレームと差分を並べた画像も書き出します。`--yes` を付けない限り、`approve` は既存のゴールデンフレームを置き換えません。

同じレンダラーでは、フレームの 0.01% のピクセルまでの違いは許容されます。別のレンダラーで作ったゴールデンフレームは 0.25% の上限で比較され、警告が出ます。この上限は、Radeon 780M、RTX 5060 のノート PC、Vulkan SwiftShader での測定から決めました。測定値は[ゴールデンフレームのメモ](https://github.com/debuffjp/komadori/blob/master/docs/golden-frames.md)にあります。spryte の CI は、ショーケースのシーンにこれを使っています。

## エージェント向け：MCP サーバー {#for-agents-an-mcp-server}

`komadori mcp` は、Komadori を 3 つのツールを持つ MCP サーバーとして動かします。`render` は検査結果とコンタクトシートを返し、`look` は 1 フレームを実寸で、またはズームして返します。`diff` は、1 つ前のレンダリングの同じフレームと比べます。

```json
{ "mcpServers": { "komadori": { "command": "npx", "args": ["-y", "komadori", "mcp"] } } }
```

## できないこと {#what-it-doesn-t-do}

- Chrome で動きます。Windows でテストしていて、ゴールデンフレームの検査は Linux のランナー（SwiftShader）でも動いています。
- エージェントがこれを使うと良いシーンを書けるかどうかは、確かめられていません。4 つの小さなタスクでは、Sonnet はレンダリングなしでも同じ結果でした。実行と費用は[評価のページ](https://github.com/debuffjp/komadori/blob/master/bench/eval/README.md)にあります。
- 検査するのは何が描かれたかであって、見た目が良いかどうかではありません。

[ソースと変更履歴は GitHub](https://github.com/debuffjp/komadori)に、パッケージは [npm](https://www.npmjs.com/package/komadori) にあります。