1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

3Dの聴衆が「声・姿勢・表情」に反応する発表練習Webアプリを、サーバー代0円で作った【研究ベースのコツ/MediaPipe/Web Audio】

1
Posted at

はじめに

近日、100人規模の聴衆の前で発表する機会があるのですが、自分自身、発表はまだ得意とは言えず、練習不足も感じていました。

そこで、これまでAIを活用してアプリ開発を行ってきた経験を活かし、「楽しく発表練習ができるアプリ」を試しに作ってみました。

もちろん、世の中には優れた競合サービスが数多くあり、完成度もプロの仕事には到底及びません。

それでも、「現在のAI(Claude Sonnet 5.5、Opus 5.5)を活用すると、個人でもここまで形にできる」という一例として、成果物であるアプリを共有します。

製作期間は3日です。

ぜひ遊び感覚で触ってみてください。

以下、AIで生成した記事...

概要

発表練習をひとりでやると、どうしても「壁に向かって話すだけ」になりがちです。
そこで、3Dの聴衆があなたの声・姿勢・表情にリアルタイムで反応する発表練習アプリ「ひとまえ」 を作りました。

👉 https://hitomae.vercel.app (PC・タブレットのブラウザで動きます。インストール不要・無料)

start.gif

このアプリの本体は、本番に向けてまじめに練習するための「練習モード」 です。
「発表のコツ」の研究や大学の公式ガイドを調べ、根拠のあるものを測定と助言に落とし込みました。
(ありえない聴衆で遊ぶゲームモードもありますが、あくまでおまけです。記事の最後で少しだけ紹介します)

特徴は次の3つです。

  • 研究・大学ガイドにもとづく助言。根拠の強さを「研究/経験則/注意」の3段階で区別しています
  • マイクとカメラで7項目を推定し、聴衆の関心に反映します
  • 全部ブラウザの中で完結します。音声・映像・ファイルは端末の外へ一切送りません

1. まず「発表のコツ」をリサーチした

アプリに組み込む前に、「よく言われるコツ」が本当に根拠のあるものかを調べました。
根拠の強さがバラバラなまま混ざっている、というのが一番の発見でした。

区分 内容 根拠
🟢 研究 話す速さは**会話のペース(英語で140〜170語/分)**を基準にする 査読論文(Adv Physiol Educ, 2022)
🟢 研究 「えー」「あのー」などのフィラーは沈黙に置き換える。目安は1分に1〜2回以内 同論文・大学の発表ガイド
🟢 研究 アイコンタクトはフィラーの減少と関連する 同論文
🟢 研究 歩き回る・体を揺らす・手を動かしすぎるのは気を散らす。動きは意味のあるものだけ Trent大学 Academic Skills
🟢 研究 録音・録画して見直す。音だけ→映像の順で見ると整理しやすい MIT CommLab ほか
🟡 経験則 重要な点の前後に2〜3秒の間 講師やブログで広く言われる。秒数の根拠は弱い
🟡 経験則 最初の20秒は会場を見て、タイトルを読み上げない 同上
🟡 経験則 日本語は「1分300字前後」が目安 広く使われるが、出典は確認できず
🔴 注意 「メラビアンの法則 7-38-55」は一般の発表には当てはまらない 言葉と感情が矛盾する場面だけの実験。「見た目が9割」は誤用

アプリの助言には、この区分のタグを付けています。「研究」と「経験則」を混ぜずに見せることで、数値を鵜呑みにしすぎないようにしました。
同じ内容を読み物ページ(発表のコツ)にもまとめています。


2. お手本デモ:悪い例 → 良い例

最初に「お手本デモ」を見ると、早口・小声・間なしの悪い例と、間・抑揚・目線を意識した良い例とで、聴衆の反応がどう変わるかがわかります。
台本には ⏸ 間 の位置と、提案される ⏸ 2秒 の位置が表示され、今読んでいる文がハイライトされます。

demo.gif

デモ原稿は「3分で伝わる自己紹介」「研究発表(寝る前のスマホ)」「社内提案(会議を15分短く)」と、小学生向けが2本です。
自分の .pptx .docx .txt .md も読み込めます(後述)。


3. マイクで何をどう測っているか

マイクの音は、Web Audio API の AnalyserNode で毎フレーム解析しています。

声量(dB)

  • 音のRMSからdBを求め、声が出ている区間だけを平均します
  • 最初の約1.6秒で周囲の騒音レベル(上位80パーセンタイル)を測り、その上 +8〜11dB を「声」と判定する閾値にしています
  • getUserMedia の noiseSuppression に加え、110Hzのハイパス×2段+5.2kHzのローパスで声の帯域だけを通します。PCのファンや空調の低い唸りはこれでかなり消えます

抑揚(声の高さの変化)

  • 自己相関で基本周波数 f0(75〜420Hz)を推定します
  • 倍音を拾ってオクターブ飛びしやすいので、直近の中央値から±6半音を超えたら12半音ずらして補正します
  • 9秒間の f0(半音単位)の上下10%を除いた標準偏差を「抑揚」とします。±1.5半音未満は「棒読み」寄りです
// 自己相関で f0 を推定(正規化した相関が最大値の88%を超える最初の山)
for(let l=minL;l<=maxL;l++){ let s=0; for(let i=0;i<m;i++) s+=b[i]*b[i+l]; nac[l]=s/r0; }
for(let l=minL+1;l<maxL;l++){ if(nac[l]>=mx*.88 && nac[l]>=nac[l-1] && nac[l]>=nac[l+1]) return sr/l; }

ペース

  • 文字起こしはしていません(無料・端末内で完結させるため)
  • 代わりに、音量の山(音節っぽいピーク)の数 ÷ 発声時間で「音/秒」を出します。日本語のモーラのリズムとおおむね対応します
  • 8.6音/秒を超えると「早口」として助言します

間

  • 0.6〜3.5秒の無音を「良い間」として数えます
  • 25秒以上ノンストップで話し続けると「間が足りない」、6秒以上黙ると「沈黙が長い」と判定します

4. カメラで何をどう測っているか

カメラ映像は MediaPipe Tasks Vision(Pose / Hand / Face Landmarker)で、すべて端末内で推論します。映像はどこにも送りません。

camera.gif

右下のカメラ枠に映っているのは、作者の顔を出さないために描いた架空の発表者アバターです。検出器(MediaPipe)の出力だけをアバターの関節座標に差し替え、その後の採点・手サインの分類・投票・観衆の反応はアプリ本体のコードがそのまま動いています。実際のカメラでも同じ流れで動きます。もちろん、作者本人での動作プレイ済みで、後ろに人がいるともしかしたら誤作動が起きるかもです。

姿勢

開始直後の姿勢を「基準」として記録し、そこからのズレを見ます。

  • 肩の傾き(左右の肩を結ぶ線の角度)
  • 頭の落ち込み(肩に対する鼻の位置の、基準からの差)
  • 体の揺れ(肩の中心の位置の、直近数秒の揺れ幅)

この3つの重み付き和を「姿勢」のスコアにしています。猫背、片肩下がり、ゆらゆらが減点されます。

視線

顔の向き(ヨー角)と頭の下がり具合から、「会場を見ているか」を推定します。
スライドや手元の原稿ばかり見ていると下がります。厳密な視線推定ではなく、顔の向きによる近似です。

身振り

手首の移動量から「身振りの量」を出します。

  • 少なすぎる(手が止まっている)→ 低評価
  • 適度 → 高評価
  • 多すぎる(落ち着きがない)→ 低評価

「多ければ良い」ではなく山なりの評価にしているのがポイントです。研究で「動きすぎは気を散らす」とされているためです。

ハンドサインと表情

Hand Landmarker で各指が伸びているかを判定し、ピース・サムズアップ・指さし・パー(静粛に)・中指などを認識します。
Face Landmarker の blendshape(tongueOut、cheekPuff、eyeBlink など)から、変顔・ほっぺ膨らまし・ウインクなども認識します。
誤検出を避けるため、数フレーム続けて同じ判定が出たら確定する多数決にしています。

// 指の伸び(idx, mid, rng, pnk, thumb)からハンドサインを判定
if(idx&&mid&&rng&&pnk&&thumb) k='open';        // ✋ 静粛に
else if(!idx&&mid&&!rng&&!pnk) k='middle';      // 🖕 会場が凍りつく(実際はNG)
else if(idx&&mid&&!rng&&!pnk) k='peace';        // ✌️

認識したリアクションは「図鑑」に登録されていきます。中指を立てると会場が凍りつき、厳しい上司が一気に不機嫌になります。実際の発表では絶対にやめましょう。

reaction.gif


5. 聴衆はどう動いているか

聴衆は「全員が同じスコアで笑う」わけではありません。一人ひとり、何を重視するかが違います。

性格 初期の関心 重視する項目
熱心な人 高い 視線・身振り・間
厳しい上司・教授 普通 声量・姿勢・ペース・間
眠そうな人 低め 抑揚(棒読みだとすぐ寝る)
スマホを見がちな人 低め 抑揚・身振り・視線
メモをとる人 やや高い ペース・間(早口だと書けない)
隣と話しがちな人 普通 声量・抑揚・視線

各人の関心は、次のように動きます。

  1. 7項目を、その人の重みで足し合わせた「目標値」に向かって、数秒〜十数秒かけてゆっくり近づきます。急には変わらないので、一瞬だけ良くしても効きません
  2. 隣の人の関心に引っ張られます(同じ列の近い人の平均へ12%寄る)。だれかが寝始めると、周りもダレていきます
  3. 難易度「きびしい」では、目標値の振れ幅が大きくなります

関心の値に応じて、実際にいそうな態度をとります。

  • 高い:うなずく、メモをとる、前のめりになる、挙手する
  • 中くらい:腕を組む、よそ見をする
  • 低い:スマホを見る、こっそり落書き、隣と話す、居眠り、あくび

会場の音も関心に連動します。関心が下がるとざわざわ声が増え、咳払いや椅子のきしむ音が客席の位置から聞こえてきます(HRTFによる立体音響)。
右上には「うなずき3・スマホ1・居眠り0」のように、今の会場の様子が数で出ます。

問いかけ・指名もできる

  • 🙋「問いかけ」ボタンで挙手を求めると、関心の高い人だけが手を挙げます。挙手の数で会場の温度がわかります
  • 聴衆をクリックすると名前を呼んだ扱いになり、その人と周りの関心が少し上がります。ただし同じ人をしつこく呼ぶと怒られます

6. 自分の発表資料で練習する

pptx をそのまま使える

.pptx を読み込むと、スライドを自前で再描画し、ノートを台本にします。クリックで出てくるアニメーションも、p:timing から順番を取り出して再生します。
pptx の中身は zip なので、DecompressionStream('deflate-raw') を使えば、ブラウザ標準の機能だけで展開できます。

台本に「間」を書ける

台本に 【間】 と書くと、その位置に ⏸ 間 のマークが出ます。
間が書かれていない場合も、「つまり」「大事」「数字」などの前に ⏸ 2秒 を提案します。

ペースメーカーと時間配分

  • 「🏃 ペース」をONにすると、目安の速さで台本のハイライトが進みます
  • スライドごとに「目安 0.5分 / 経過 0:42 / +12秒」が出て、終了後にスライド別の時間配分表が見られます

練習の形式

  • 20秒オート:スライドが20秒ごとに自動で進みます(短く話す練習)
  • お題ガチャ:ランダムなお題で1分即興します。結論→理由→例→結論(PREP法)の型が表示されます

7. 録画して振り返る(採点は辛め)

スライドとカメラ映像を合成して録画し、終了後にタイムライン付きで振り返れます。
「この時スマホを見られた」「ここで助言が出た」などの場面をクリックすると、その時点の動画に飛びます。

result.gif

総合点は、7項目の平均(0〜1)を辛めに換算しています。

// 0.4以下は0点、0.95でようやく満点
const harsh=v=>Math.round(100*Math.pow(clamp((v-.4)/.55),1.25));
// ★5は90点以上、★4は75点以上

「まあまあ話せた」程度では★3前後になります。甘い点数で満足しないことを優先しました。
良い点は2つまで、直す点は3つまでに絞って表示します。「直すのは3つだけ」は、練習の定番の考え方です。


8. 技術構成とランニングコスト

  • 単一の index.html(+英語対応の i18n.js)。ビルドツールなし
  • 3D:three.js(MeshToonMaterial + 裏面法線押し出しのアウトラインでトゥーン調)
  • 骨格・表情・手:MediaPipe Tasks Vision(端末内推論)
  • 音声解析・効果音・立体音響:Web Audio API
  • 日本語の改行:Intl.Segmenter で文節に近い単位に分け、<wbr> + word-break: keep-all
  • ホスティング:Vercel の静的配信のみ

サーバー処理がないので、ランニングコストは0円です。

権利まわりで気をつけたこと

  • 3Dモデル・スライドの絵・効果音・ざわざわ声は、すべてコードで生成しています
    • ざわざわ声は、のこぎり波を母音のフォルマントのバンドパスに通して合成しています
  • お手本の声はOS標準の音声合成です
    • オープンソースの音声合成モデルも検討しましたが、学習データのライセンスが不明確なものがあったため、公開版では見送りました
  • ライブラリは three.js(MIT)と MediaPipe(Apache-2.0)です

英語対応

ブラウザの言語設定に従って、日本語と英語を切り替えます。英語用の原稿も用意しています。
DOM は MutationObserver、Canvas は fillText のラップで、表示の瞬間に辞書で翻訳しています。
(<script> の中身まで翻訳してアプリが起動しなくなる、という罠を踏みました)

english.gif


おまけ:ゲームモード

練習に疲れたとき用の、副次的な機能です。ありえない聴衆を相手に「何が起きても話し続ける」練習になります。
ミッションに成功しても失敗しても、発表は最後のスライドまで続きます。

  • 💣 炎上スピーチ:地雷入りのふざけた原稿を、ひるまず言い切る
  • 🛸 ドッキリ珍事件:ハウリングや救急車から、UFOや恐竜まで
  • 🎰 スライドカラオケ:初見のヘンなスライドで20秒アドリブ
  • 🎧 音の異変さがし:立体音響の異変の方向を当てる
  • 🍅 回避ステージ、ヤジ道場、練習前の部活ミーティング、ほか
ゲームモードのGIF(クリックで開く)

karaoke.gif
chaos.gif
dodge.gif
enjou.gif


Claude Code で作りました

このアプリは、ほぼすべて Claude Code との対話で作りました。
リサーチ(論文・大学ガイドの確認、ライセンス調査)から、実装、ブラウザでの動作確認、Vercelへのデプロイまで一緒に進めています。

おわりに

測定はあくまで目安で、医療や採点のためのものではありません。
それでも、発表の前日に壁に向かって練習するより、少しだけ本番に近い練習ができたらうれしいです。
感想や「こんな聴衆がほしい」などがあれば、コメントで教えてください。

👉 https://hitomae.vercel.app

参考

気に入っていただけたら、☕ コーヒーをおごってもらえると開発の励みになります。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?