更新日: 2026-01-15
更新情報:
- LipsyncEngineのリファクタリング - DOM分離・Audio分離により他アプリへの組み込みが容易に
- AudioCaptureクラス追加 - マイク入力処理を独立したクラスに分離
- HTTPパスでのアセット読み込み対応 - サーバーからアセットを直接読み込み可能
- 外部音声入力対応 - TTS等マイク以外の音声ソースにも対応
MotionPNGTuber のブラウザ版再生専用パッケージ
ブラウザで動作する軽量なPNGTuberリップシンクプレイヤーです。 Python環境不要で、マイク入力に応じてキャラクターの口がリアルタイムで動きます。
- 本体: 動画解析・キャリブレーション・口消し動画生成などのアセット作成
- このパッケージ: 作成済みアセットを使ったブラウザ再生のみ
アセットの作成方法は 本体リポジトリ を参照してください。
- ブラウザのみで動作 - Python環境不要
- リアルタイムリップシンク - マイク音声に合わせて口が動く
- 5種類の口パターン - closed / half / open / e / u
- 母音認識 - 高低周波数比率から「え」「う」を自動判定
- OBS対応 - ウィンドウキャプチャで配信に使用可能
cd MotionPNGTuber_Player
python -m http.server 8000
8000
div>
http://localhost:8000
- データフォルダを選択 - キャラクターのアセットフォルダを選ぶ
- サンプル:
assets/assets14またはassets/assets23
- サンプル:
- マイクを開始 - マイクへのアクセスを許可
- 感度を調整 - スライダーで反応感度を調整
- 開始 - リップシンク再生がスタート
キャラクターフォルダは以下の構成が必要です(本体リポジトリのGUIで一括生成可能):
character_folder/
├── *_mouthless_h264.mp4 # 口なし動画(必須)
├── mouth_track.json # トラッキングデータ(必須)
└── mouth/
├── closed.png # 口を閉じた状態(必須)
├── open.png # 口を開けた状態(必須)
├── half.png # 半開き(任意)
├── e.png # 「え」の口(任意)
└── u.png # 「う」の口(任意)
| 項目 | 要件 |
|---|---|
| コーデック | H.264(必須) |
| フレームレート | CFR(固定フレームレート)必須 |
| ファイル名 | *_mouthless_h264.mp4 を含む |
重要: VFR(可変フレームレート)だとトラッキングと同期がズレます
- 形式: PNG(透過推奨)
- サイズ: トラッキングデータの
refSpriteSizeに合わせる(例: 128x85px) - 内容: 口部分のみを透過背景で作成
マイク入力
↓
AudioWorklet で音声解析
↓
┌─────────────────────────────────────┐
│ RMS(音量)と高低周波数比を計算 │
│ ・音量小 → closed │
│ ・音量中 → half │
│ ・音量大 + 高周波多め → e │
│ ・音量大 + 低周波多め → u │
│ ・音量大 + それ以外 → open │
└─────────────────────────────────────┘
↓
対応する口スプライトを四角形に変形描画
- 通常のブラウザ(Chrome/Edge)でプレイヤーを開く
- OBSで「ウィンドウキャプチャ」を追加
- ブラウザウィンドウを選択
- 必要に応じてクロマキーやフィルタを設定
file://ではなくhttp://localhostでアクセスしていますか?- ブラウザのマイク許可を確認してください
- 動画がCFR(固定フレームレート)か確認
mouth_track.jsonのfpsと動画のfpsが一致しているか確認calibrationパラメータで微調整
- H.264コーデックでエンコードされているか確認
- ファイル名に
mouthlessとh264が含まれているか確認
- 感度スライダーで調整(0〜100)
- 値を上げると少ない音量でも反応
- ブラウザ: Google Chrome / Microsoft Edge(最新版)
- OS: Windows 10/11, macOS, Linux
- 必要機能: AudioWorklet対応ブラウザ
| コンポーネント | 技術 |
|---|---|
| 音声処理 | Web Audio API + AudioWorklet |
| 周波数分離 | 1次ローパスフィルタ(700Hz) |
| フレーム同期 | requestVideoFrameCallback / requestAnimationFrame |
| 画像変形 | Canvas 2D アフィン変換(三角形ワーピング) |
| UI | HTML5 + CSS3 + Vanilla JavaScript |
MotionPNGTuber_Player/
├── index.html # メインHTML
├── lipsync.js # リップシンクエンジン(LipsyncEngineクラス)
├── audio-capture.js # マイク入力処理(AudioCaptureクラス)
├── audio-worklet.js # 音声解析ワークレット
├── style.css # スタイルシート
├── README.md # このファイル
└── assets/ # サンプルアセット
LipsyncEngineは他のWebアプリケーションに組み込んで使用できます。
// DOM要素を準備
const video = document.getElementById('base-video');
const mouthCanvas = document.getElementById('mouth-canvas');
const stage = document.getElementById('stage');
// LipsyncEngine初期化
const engine = new LipsyncEngine({
elements: { video, mouthCanvas, stage },
callbacks: {
onLog: (msg) => console.log(msg),
onFileStatus: (status, message) => { /* 'success' or 'error' */ },
onVolumeChange: (volume) => { /* 0-1 */ },
onPlayStateChange: (isPlaying) => { /* true/false */ },
onSectionsVisibility: (visible) => { /* true/false */ },
onError: (message) => alert(message)
}
});
// ファイル読み込み(フォルダ選択から)
engine.loadFiles(Array.from(inputElement.files));
// AudioCapture初期化(マイク入力)
const audioCapture = new AudioCapture({
onVolumeData: (data) => engine.processAudioData(data),
onStateChange: (isRunning) => { /* true/false */ },
onDevicesLoaded: (devices) => { /* デバイス一覧 */ },
onError: (message) => alert(message)
});
audioCapture.loadDevices();
audioCapture.start(deviceId); // マイク開始
engine.start(); // 再生開始const engine = new LipsyncEngine({
elements: { video, mouthCanvas, stage },
assets: {
video: './assets/character/mouthless_h264.mp4',
track: './assets/character/mouth_track.json',
mouth_closed: './assets/character/mouth/closed.png',
mouth_open: './assets/character/mouth/open.png',
mouth_half: './assets/character/mouth/half.png', // 任意
mouth_e: './assets/character/mouth/e.png', // 任意
mouth_u: './assets/character/mouth/u.png' // 任意
},
options: {
debug: true, // デバッグログ出力
hqAudioEnabled: true, // 高品質音声モード
sensitivity: 50 // 感度(0-100)
}
});// マイクを使わず、外部から音声データを入力
engine.processAudioData({
rms: 0.15, // 音量(RMS値)
high: 0.08, // 高周波成分
low: 0.12 // 低周波成分
});| メソッド | 説明 |
|---|---|
loadFiles(files) |
File配列からアセットを読み込み |
start() |
再生開始 |
stop() |
再生停止 |
processAudioData(data) |
外部から音声データを入力 |
setSensitivity(value) |
感度設定(0-100) |
setHQAudioEnabled(enabled) |
HQ Audioモード切替 |
resetAudioStats() |
音声統計値をリセット |
cleanup() |
リソース解放 |
| メソッド | 説明 |
|---|---|
loadDevices() |
マイクデバイス一覧を取得 |
start(deviceId) |
マイク入力開始 |
stop() |
マイク入力停止 |
setHQAudioEnabled(enabled) |
HQ Audioモード切替 |
isRunning() |
動作中かどうか |
Copyright (c) 2026 rotejin