AI Dev Lab
VibeCoder

VibeCoder ができるまで — AI とペアプロ中の画面を作る

コーディング風の映像ジェネレーターの開発ログ。 4 ペインのエディタ画面に、 自前のトークナイザによる色分け、 アテンションのヒートマップ、 学習曲線を並べて、 配信の背景や動画素材として使える形にするまでを記録しています。

VibeCoder は、「AI とペアプロしている画面」を映像として流すジェネレーターです。4 ペインのエディタに、色分けされたコード、アテンションのヒートマップ、学習曲線が並んで動きます。配信の背景や動画素材として使うためのものです。

#🎯 中身が無い、を正面から作る

このサービスは何も計算しません。AI も動いていないし、コードも実行されません。「そう見える画面」だけを作っています。

一見ふざけた題材ですが、作るものははっきりしていました。

  • 配信の背景に流す
  • 作業用の BGM 的な視界
  • 動画のカット素材

つまり 見ていて飽きない画面が要件のすべてです。実用性ではなく、時間あたりの情報量と間の取り方で評価が決まります。

#📜 全部を「台本」として持つ

最初にランダム生成を試しましたが、すぐ破綻しました。ランダムだと話の流れがなく、コードが増えたり減ったりして、見ていて意味が取れません。

そこでシーンを台本として持つ構造にしました。

ts
{ type: "thought", chunk: { kind: "tool", text: "→ accelerate launch --num_processes 4 train/trainer.py" } },
{ type: "metric", metric: { tokensPerSec: 1280, gpu: 92, vram: 18.4, loss: 2.84 } },
{ type: "thought", chunk: { kind: "ok", text: "  step 100/3000 · loss 2.84 · lr 2.0e-4" } },
{ type: "delay", ms: 500 },
{ type: "metric", metric: { tokensPerSec: 1310, gpu: 94, loss: 2.41 } },

ステップの種類は数種類だけです。

type意味
thought左ペインに 1 行流す
metric指標パネルを更新
delay間を置く
treeファイルツリーに印を付ける
notify通知を出す

すべて純粋なデータで、動きのロジックは再生側にあります。 台本を書くだけで新しいシーンが増える構造にしたので、5 シーンぶん書くのが苦になりませんでした。

#⏱️ 間(ま)が全部だった

一番調整したのが delay です。等間隔で流すと、機械的で見ていられません。

  • コマンドを打ってから結果が出るまでは少し待つ
  • ログが連続で流れるところは速く
  • 学習が進むところは500ms 刻みで淡々と
  • 完了通知の前はやや長く溜める

この緩急が「作業している感じ」を作ります。実装は setTimeout を挟むだけですが、値の置き方で印象がまったく変わりました。

損失が 2.84 → 2.41 → 1.94 → 1.42 → 1.08 と下がっていくのも、下がり幅を後半ほど小さくしています。実際の学習曲線がそうなるからで、等間隔に下げると嘘に見えます

#🎨 コードの色分けを自前で書く

エディタペインには色分けされたコードが流れます。ハイライタは依存を足さず自前で書きました。

ts
export function tokenize(code: string, lang: Lang): Array<[string, TokenKind]>

対応言語は tsx / py / rust / go / sql / yaml の 6 つ。完全なパーサではなく、

  • 空白と改行
  • コメント(#//)
  • 文字列
  • 数値
  • キーワード(言語ごとの一覧)
  • それ以外

に分けるだけの近似です。映像として流れるだけなので、多少の誤判定は問題になりません。 正確さより、依存ゼロと軽さを取りました。

#🔥 アテンションのヒートマップ

8×8 のマス目に色の濃淡を出します。中身は乱数ですが、それらしく見えるための細工が入っています。

ts
export function randomAttention(seed: number): number[][]

一様乱数だと、ただのノイズにしか見えません。対角線付近を強めにして、行ごとに重みの偏りを作ると、アテンション行列らしい構造が出ます。シード付きなので、シーンを再生し直しても同じ模様になります。

#😓 苦労したところ

本物と誤解されないようにする。 実際に AI が動いていると思われると困ります。画面の説明と FAQ で「これは演出であり、モデルは動いていない」と明示しました。見た目を作るサービスほど、何であるかを言葉で書く必要があります。

5 テーマの配色。 配信背景として使うので、上に人物やワイプが乗ることを前提にしました。中央が明るすぎると文字が読めなくなるので、明度の分布を周辺に寄せています。

永久に流し続ける。 シーンが終わったら次のシーンへ、最後まで行ったら最初へ戻ります。ループしていることに気づかれない程度の長さ(1 シーン 10〜18 ステップ × 5 シーン)にしました。

リソースを食わない。 背景として何時間も流されるので、requestAnimationFrame で重い描画を続けるわけにはいきません。更新はステップ単位のイベント駆動にして、何も起きていない間は何も描かない構成にしています。

#🔭 今後の拡張

  • 台本のカスタマイズ(自分のプロジェクト名を入れる)
  • 解像度の指定(配信レイアウトに合わせる)
  • 動画としての書き出し
  • シーンの追加(デプロイ、障害対応、レビュー)

#💡 このサービスから言えること

演出だけのものを作るときも、データと再生ロジックを分けると作りやすくなります。 台本を配列として書けるようにしたから、シーンを増やすのが単なる執筆作業になりました。動きのコードに直接書き込んでいたら、2 シーン目で嫌になっていたはずです。

そして**「間」はコードに書ける**ということ。500ms をどこに置くかで、同じ内容が退屈にも自然にも見えます。数字を並べるだけの調整ですが、ここがこのサービスの本体でした。

[ ./next_action ]

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

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

[ ./related_logs ]

関連する開発ログ

all logs →
ToonCast

ToonCast ができるまで — AnimeGANv2 をブラウザで動かす

AnimeGANv2 の小さな ONNX (約9MB) を onnxruntime-web (単一スレッド WASM=COOP/COEP不要、 color-revive で承認済みライブラリの再利用) で実行。 512x512・[-1,1] 正規化で推論し、 結果を元解像度に戻して表示する設計記録。 写真は端末内処理。

read log →
ColorRevive

ColorRevive ができるまで — onnxruntime-web で白黒写真をカラー化

DeOldify の量子化 ONNX を onnxruntime-web (CDN side-load・単一スレッド WASM=COOP/COEP不要) で実行。 256x256 でモデル推論し、 輝度は元写真・色だけ AI を YCbCr で再合成して輪郭を保つ設計記録。 写真は端末内処理。

read log →
PhotoTwin

PhotoTwin ができるまで — CLIP画像埋め込みで似た写真を見つける

CLIP (Xenova/clip-vit-base-patch32) の image-feature-extraction を transformers.js の CDN ESM で side-load し、 各写真を正規化ベクトル化。 cosine 類似度で重複・似た写真をブラウザ内で検出する設計記録 (新ライブラリ追加なし=what-cam と同じ CLIP の再利用)。

read log →
AkinFind

AkinFind ができるまで — ブラウザ内で完結する embeddings 意味検索

多言語の文章埋め込みモデル (Xenova/multilingual-e5-small) を transformers.js の CDN ESM で side-load し、 各文を正規化ベクトル化。 cosine 類似度で意味検索と似ている文ペア検出を全て端末内で行う設計記録。

read log →
WhatCam

WhatCam ができるまで — CLIP のゼロショット画像分類をブラウザで動かす

CLIP (Xenova/clip-vit-base-patch32) を transformers.js の CDN ESM で side-load し、 写真と候補ラベルの近さをブラウザ内で計算。 日本語ラベルを英語プロンプトに変換し、 図鑑と自由入力の両モードで「これ何?」を判定する設計記録。

read log →
DepthCast

DepthCast ができるまで — 1枚の写真をAIの深度推定で立体にする

Depth Anything (transformers.js) を CDN ESM で side-load し、 1枚の写真から深度マップを推定。 WebGL2 フラグメントシェーダで深度に比例した視差 (iterative backward parallax) を作り、 赤青アナグリフ / WebM 書き出しまで端末内で完結させた設計記録。

read log →