JavaScript MediaPipe Canvas 2D

顔画像を送らないWebアバター
— MediaPipeの値だけで表情を動かす

スマホで表情を読み、PCの配信画面でサマターゲを動かす。
ネットワークへ渡す情報を小さくするための構成です。

← Articles 一覧へ戻る

全体構成

「サマターゲ LIVE」は、ブラウザのカメラで表情を読み、Canvasで描いたキャラクターへ反映するWebアバターです。PC単体でも動きますが、スマートフォンをトラッカー、PCをOBS用の表示画面として分けられるようにしました。

スマホのカメラ
  ↓ ブラウザ内でMediaPipe解析
11個の正規化された表情値
  ↓ WebSocket
PCのCanvas描画
  ↓
OBS / 配信画面

スマホからPCへ渡すのは、カメラ画像や顔のランドマーク一式ではありません。顔の向き、視線、左右の瞬き、口、笑顔、眉、発話状態に縮約した値だけです。

現在はMVPで、一般公開用ホスティングはありません。この記事はローカル/同一LANで検証した実装を説明しています。

30fpsを上限に顔を読む

face-tracker.js はMediaPipe Face LandmarkerをGPUモード、検出人数1人で初期化します。ブラウザの描画ループはディスプレイによって60Hz以上になることがありますが、顔推定を毎フレーム実行すると負荷が増えるため、推定は30fpsを上限にしました。

requestAnimationFrame 自体は表示更新に使い、前回推定から十分な時間が経ったときだけMediaPipeを呼びます。カメラを止めたときはストリームのtrackを明示的に停止し、推定ループも終了します。

初回のみ、MediaPipeのWASMとモデルを公式配布元から取得します。カメラ処理はブラウザ内ですが、完全なオフライン配布ではない点はプライバシー説明に明記しています。

顔を11個の値へ縮める

MediaPipeから得られるランドマークやblendshapeを、そのまま描画側へ渡すと、入力形式へ強く依存します。そこで motion.js で次の共通形式へ変換しました。

範囲用途
yaw / pitch / roll-1〜1顔の左右・上下・傾き
gazeX / gazeY-1〜1瞳の移動
blinkLeft / blinkRight0〜1左右別のまばたき
mouthOpen0〜1口の開き
smile0〜1口角と頬の表情
brow-1〜1眉の上げ下げ
speaking0〜1発話アニメーション

値は範囲へ収め、前フレームとの線形補間で細かな揺れを抑えます。入力側がMediaPipeでなくなっても、このmotion形式を作れれば描画側は変更せずに済みます。

Canvasで表情を再構成する

avatar-renderer.js は、サマターゲの顔をCanvas 2Dの図形として毎回描き直します。背景、輪郭、眉、縦長の目、瞳、口を、キャンバスサイズに対する比率で配置します。

顔の回転と目線は分けています。頭が右を向いたときに瞳も同じ量だけ動かすと、視線の表現がなくなるためです。まばたきは左右別の値を使い、口は開きと笑顔を組み合わせて幅・高さ・曲線を変えます。

PNGの各部位を重ねる方式と違い、拡大時も輪郭を描き直せます。一方で、髪や服の細かな陰影までCanvasコードへ移すと保守が難しくなるため、配信画面では「表情が読める顔」に範囲を絞っています。

映像ではなくmotionだけを中継する

スマートフォンとPCが同じLANにある場合、ルームコードで接続し、WebSocketでmotionオブジェクトだけを送ります。中継サーバーが保持するのはメモリ上のルーム参加者集合で、映像、顔画像、音声、履歴を保存する処理は入れていません。

カメラAPIは原則としてHTTPSかlocalhostでしか利用できません。同一LANでスマートフォンを使う場合も、ローカル証明書を作り、端末側で信頼する必要があります。HTTPへ戻して動かす方法は採用しませんでした。

PC側は16:9表示と透過背景を切り替えられるため、OBSのブラウザ表示または画面キャプチャへ組み込めます。

LLM・TTSとはイベント境界でつなぐ

顔追跡とは別に、ローカルHTTP APIから表情、字幕、発話量、表示時間を渡せます。LLMやTTSへCanvasの内部状態を直接触らせず、次のようなイベントだけを受け取る設計です。

{
  "expression": "happy",
  "text": "今日も見に来てくれてありがとう!",
  "speaking": 0.8,
  "durationMs": 5000
}

APIはBearer tokenを要求し、本文サイズ、字幕長、表示時間に上限を設けています。表情トラッカーとAI発話の両方が同じmotionへ書き込むため、どちらを優先するかもイベント処理側で明示します。

現在の限界

  • ルームコードだけでは、公開インターネット向けの強い認証にならない
  • Origin検証、レート制限、相互認証は一般公開前に追加が必要
  • MediaPipeのモデルとWASMを外部CDNから取得する
  • 横顔、暗い場所、顔の一部が隠れた状態では値が不安定になる
  • Canvas版は顔の表情に範囲を絞っており、全身リグではない

「映像を保存しない」ことと「公開運用に十分安全」なことは別です。現段階では同一LANとローカル利用を前提にし、一般公開用URLは発行していません。