イラストに、まばたきと表情を

Mesh Avatar
Studio

1枚のイラストから、
動くアバターを作れます。

Studio で作成・調整し、mesh-avatar で自分のアプリに組み込めます。

エディタはローカルで動きます。

動作プレビュー Miko

プレビューを試すには JavaScript を有効にしてください。

アバターの操作

ポインターを動かすと、その方向へ顔を向けます。

口の動きだけを試せます。音声は流れません。

このプレビューも mesh-avatar で動いています。

できること

まばたきも、口の動きも。

Studio で作った動きや表情を、ランタイムで自分のアプリでも使えます。

まばたきと呼吸

操作していない間も、まばたきや呼吸、待機中の動作が続きます。

声に合わせた口パク

声の大きさに合わせて口を動かせます。エディタでは母音や、かなの文章で口の形を確認できます。

顔の向きと傾き

顔や視線をパラメーターで操作できます。エディタのプレビューで動きを確認しながら調整できます。

髪と飾りの揺れ

物理演算で髪が少し遅れて動き、タッセルなどの飾りが振り子のように揺れます。

表情と描き分け

笑顔や驚き、ウインクに切り替えられます。目や口の描き分け画像を用意して、まばたきや口パクに使えます。

ライティング

光源の位置や陰影を調整できます。設定を保存し、配信の操作画面で切り替えられます。

Studio で作る

イラストをアバターに。

コーディングエージェントが動きの設定とレイヤー画像を用意します。仕上がりをローカルのエディタで確認し、パーツの位置を調整します。

  1. エージェントにイラストを渡す

    リポジトリで Claude Code や Codex を開き、手順書(英語)に沿って作るよう頼みます。正面向きで、背景が透明な PNG が適しています。

    Miko のイラスト、編集用の点、動作プレビューを表示した日本語のエディタ画面。
    エージェントが用意したプロジェクトを開きます。
  2. 動きを確認する

    Studio でプロジェクトを開きます。「ポーズ確認」で顔の向きや目の開閉を、「口の動き」で母音ごとの口の形を確かめます。

    母音のボタンが並ぶ「口の動き」の欄と、口を開けたアバターのプレビュー。
    書き出す前に、ポーズと口の形を確認します。
  3. 点をドラッグして調整する

    パーツを選び、画像上の点を動かすとプレビューに反映されます。切り抜く輪郭を変更したときは、「保存してレイヤーを作り直す」を押します。

    目の輪郭を拡大し、位置を調整する点を選んだ編集画面。
    イラストに合わせて、点の位置を直せます。
  4. 描き分け画像を作成して表情豊かに

    「描き分け画像」から目や口の画像を依頼すると、まばたきや口パク、笑顔の表情が豊かになります。仕上がったら「.mavatar を書き出す」で、設定、レイヤー、描き分け画像を1つのファイルにまとめます。

    エージェントに渡す、口の描き分け画像の依頼文を表示した画面。
    目や口の描き分けを加えてから書き出せます。

npm install、npm run dev でローカル起動できます。セットアップ手順を見る

配信に使う

自分の顔や声で動かして、OBS へ。

Studio の操作画面から、OBS 用の画面へアバターの動きを送れます。

顔の動きや声に合わせる

カメラを開始して正面の姿勢を登録し、左右反転や感度、動きのなめらかさを調整します。カメラを使わず、待機動作にマイクの口パクを合わせることもできます。

Miko と、顔の追跡、マイク、表情、背景、ライティングの設定が並ぶ配信の操作画面。
カメラの映像とマイクの音声は、端末内で処理します。

OBS のブラウザソースに追加する

背景を選んで OBS 用の URL をコピーし、「ブラウザ」ソースに貼り付けます。OBS では透明背景で重ねられます。他の取り込みソフトでは、緑の背景をクロマキーで抜く方法も使えます。

操作ボタンを表示せず、クロマキー用の緑の背景に Miko だけを映した画面。
取り込み用の画面には、アバターだけを表示します。

どちらの画面もローカルの開発サーバーで動きます。配信中は、操作画面を専用のウィンドウで開いたままにしてください。配信の詳しい使い方(英語)

mesh-avatar で組み込む

自分のアプリで動かす。

Studio で書き出したファイルを読み込むと、動き始めます。アプリからポーズや表情、声の大きさを操作できます。

  • WebGL
  • ES modules
  • TypeScript の型定義
  • React への依存なし

ランタイムをインストール

npm install mesh-avatar

.mavatar ファイルをアプリの公開アセットに置きます。HTML に canvas を追加し、アプリのモジュールから JavaScript を実行します。

待機動作とまばたきは自動で始まります。話すときは、アプリ側で音声を再生し、その大きさをランタイムに渡します。

ランタイムの API と使用例(英語)

canvas を追加 HTML

<canvas id="avatar" width="720" height="960"></canvas>

読み込んで動かす JavaScript

import { loadMeshAvatar } from 'mesh-avatar';

const canvas = document.querySelector('#avatar');
const avatar = await loadMeshAvatar(
  canvas, '/avatars/character.mavatar'
);

// 表情やポーズを指定します。
avatar.setExpression('smile');
avatar.setParameters({ angleX: 10, gazeX: 0.25 });

// アプリで音声を再生し、その大きさ(0〜1)を渡します。
avatar.setSpeaking(true);
avatar.setVoiceLevel(0.6);

// 話し終わったら、待機状態に戻します。
avatar.setVoiceLevel(0);
avatar.setSpeaking(false);
avatar.setExpression('neutral');
avatar.setParameters({});

// canvas を取り除くときは avatar.destroy() を呼びます。

このプレビューのしくみ JavaScript

import { loadMeshAvatar } from 'mesh-avatar';

const canvas = document.querySelector('#avatar');
const avatar = await loadMeshAvatar(canvas, 'miko.mavatar', { padTop: -0.04 });

// 表情ボタンで顔を切り替えます。
for (const button of document.querySelectorAll('[data-expression]')) {
  button.addEventListener('click', () => {
    avatar.setExpression(button.dataset.expression);
  });
}

// 口パクの切り替え。このデモは音声の代わりに、無音の音量カーブを送ります。
let speaking = false;
document.querySelector('#talk').addEventListener('click', () => {
  speaking = !speaking;
  avatar.setSpeaking(speaking);
  if (!speaking) avatar.setVoiceLevel(0);
});

// ポインターの方へ顔と視線を向けます(x と y は -1〜1)。
const hero = document.querySelector('.hero');
hero.addEventListener('pointermove', (event) => {
  const rect = hero.getBoundingClientRect();
  const x = (event.clientX - rect.left) / rect.width * 2 - 1;
  const y = 1 - (event.clientY - rect.top) / rect.height * 2;
  avatar.setParameters({ angleX: x * 12, angleY: y * 8, gazeX: x * 0.7, gazeY: y * 0.55 });
});
hero.addEventListener('pointerleave', () => avatar.setParameters({}));

// 話している間は、毎フレーム音量(0〜1)を送ります。
function frame(time) {
  if (speaking) avatar.setVoiceLevel(Math.abs(Math.sin(time / 90)));
  requestAnimationFrame(frame);
}
requestAnimationFrame(frame);

このページのスクリプトを簡略化したものです。全文を GitHub で見る