見出し画像

ブラウザ上で動作する、ずんだもんと四国めたんの寸劇エンジンをChatGPT o3-mini-highを使って作成する

前回の続きっぽい記事です

ローカルLLMを使って、ずんだもんと四国めたんのシナリオ寸劇を作成し音声ファイルの生成までできることを確認しました。
今回は完成形として、Youtubeにある「ずんだもん動画」を擬似的にブラウザで再現します。またそのブラウザでの寸劇をPCの録画機能でキャプチャすればそのまま「ずんだもん動画」ができあがります

できあがったサンプル
デモページ(テキストボックスをクリックすると寸劇がすすむ)


用意するもの

VOICEVOXをPCにインストールしましょう。
VOICEVOX(サーバー)と音声生成プログラムが別のPCにある場合や
プログラムがWSL、サーバーはWindowsの場合は下記のコマンドで起動しておく

C:\Users\XX\AppData\Local\Programs\VOICEVOX> .\vv-engine\run.exe --host 0.0.0.0


また開発は今回VSCode上で行い、デバックのためサーバー経由でindex.htmlを開く必要があるためLive Preview拡張をインストールしておきましょう。

設計:シーケンス図

  • ChatGPTかローカルLLMでscenario.js(シナリオファイル)を作成

  • scenario.jsをもとにvoice_generator(golang)でVOICEVOX APIに問い合わせ

  • できあがった音声ファイルと用意したあるキャラクター画像をもとにengine.jsでノベルゲーム風にずんだもん寸劇を再生

シナリオファイルは以下のようなもの

{
    "backgrounds": {
      "1": "bg1.png",
      "2": "bg1.png"
    },
    "defaultLeftCharacter": "四国めたん-ノーマル.png",
    "defaultRightCharacter": "ずんだもん-ノーマル.png",
    "scenes": [
      {
        "setBackground": "1",
        "lines": [
          {
            "character": "ずんだもん",
            "serif": "今日は雨がふりそうなのだ",
            "sound": "1.wav",
            "images": ["ずんだもん-喋り1.png", "ずんだもん-喋り2.png"],
            "color": "#00ff00" 
          },
          {
            "character": "四国めたん",
            "serif": "そうね、曇ってきたわね",
            "sound": "2.wav",
            "images": ["四国めたん-喋り1.png", "四国めたん-喋り2.png"],
            "color": "#ff00ff"
          }
        ]
      },
      {
        "setBackground": "2",
        "lines": [
          {
            "character": "ずんだもん",
            "serif": "本を買いに行こうと思ってたのに最悪なのだ",
            "sound": "3.wav",
            "images": ["ずんだもん-喋り1.png", "ずんだもん-喋り2.png"],
            "color": "#00ff00"
          },
          {
            "character": "四国めたん",
            "serif": "傘を持っていったらいいんじゃないかしら?",
            "sound": "4.wav",
            "images": ["四国めたん-喋り1.png", "四国めたん-喋り2.png"],
            "color": "#ff00ff"
          }
        ]
      }
    ]
  }
  


ChatGPT o3-mini highにエンジンを書いてもらう

次にこのコードをもとに、Youtubeでよく見かけるずんだもんと四国めたんの会話劇をHTML上で再現するための
コードを書きたいと思います
まず設計を理解し、レビューと完全な設計を提示してください


設計としては下記です
・シナリオ出力コード(現在のコードを改良)
・会話劇描写エンジン(シナリオ設定ファイルを読み込み、ブラウザ上で会話劇を再現)

# シナリオ出力コード

与えられたプロンプトにそって、ずんだもんと四国めたんの会話劇テキストをLLMで生成
会話劇テキストにそって音声を生成(セリフごとに音声ファイルを出力)
最後にシナリオ設定ファイルを出力

## シナリオ設定ファイル
会話劇描写エンジンがブラウザ上で会話劇を再現できるように
キャラクター画像、キャラクターのセリフ、キャラクターの音声、背景画像を設定できる
フォーマットはJSONなどでもいいし独自のものでも可、可視性の高いもの

### シナリオ設定ファイルのイメージ

set background 1.png #背景を設定
defoult left character 四国めたん-ノーマル.png
defoult rigiht character ずんだもん-ノーマル.png

charcter ずんだもん 
serifu 今日は雨がふりそうなのだ
sound 1.wav
image [ずんだもん-喋り1.png, ずんだもん-喋り2.png]

charcter 四国めたん
serifu そうね、曇ってきたわね
sound 2.wav
image [四国めたん-喋り1.png, 四国めたん-喋り2.png]

charcter ずんだもん 
serifu 本を買いに行こうとおもってたのに最悪なのだ
sound 3.wav
image [ずんだもん-喋り1.png, ずんだもん-喋り2.png]

set backround 2.png #背景を変更

charcter 四国めたん
serifu 傘もっていったらずんだもん
sound 4.wav
image [四国めたん-喋り1.png, 四国めたん-喋り2.png]

# 会話劇描写エンジン

シナリオ設定ファイルをもとにブラウザ上で、ずんだもんと四国めたんの会話劇を再現するHTML/JSのコード
画像の切り替えや音声の再生を行う。
セリフの進行は、ユーザーがセリフをクリックすることで行う

キャラクターが喋っているときは image [四国めたん-喋り1.png, 四国めたん-喋り2.png] に指定されている
画像を0.1秒単位で切り替えて、喋り終わったら defoult rigiht character もしくは defoult left character
に切り替える。

描写サイズは擬似的にYoutubeを再現するためワイドで横幅1920を基本とする

youtube会話劇のイメージを添付します

プロンプトの最後にyoutubeのずんだもん寸劇動画のキャプチャ画像をいくつか参考に読み込ませます。するとo3-mini highが理解してコードをつらつらと書き始めます

2. 会話劇描写エンジン(HTML/JS)の設計レビュー

2-1. 機能概要シナリオ設定ファイルを読み込む
例:fetch("scenario.json") などで JSON を取得
ブラウザ画面に背景・キャラクター画像を表示
document.createElement("img") や <div> の background-image などで表示
デフォルトの立ち絵は左右に配置し、横幅 1920px 程度を想定したレイアウト
セリフを一つずつ進行
クリックやキー入力で次のセリフへ進む
発話中は音声ファイルを再生(HTMLAudioElement or Web Audio API)
喋っている間はキャラクターの「口パク用画像」リストを 0.1 秒ごとに切り替え
発話終了(音声終了 or 次へボタン押下)でデフォルト画像に戻す
背景変更
setBackground があれば、そのシーンの最初に背景を切り替え
シーンの概念
シーンごとに背景が変わったり、配置が変わる場合は、HTML/JS 側で scenes[i] の背景を適用 → lines のセリフを順番に再生


2-2. HTML/JS 構成例index.html
html
~~略


今回の設計難易度に対してChatGPT o3-mini highはオーバースペックかもしれませんが、プロンプトに対して1発でイメージ通りのコードを出してきたのでかなり優秀なのは間違いないです。o3-migi やGPT4oではやはり一発だと難しい。

口パクの制御なども理解しガンガン意図どおりに書いてくれます

o3-miniにシナリオファイルから音声ファイルを生成するコードをGoで書いてもらう

今後はo3-miniに書いてもらいました

下記のJSONは
ずんだもんと、四国めたんの会話劇のシナリオファイルです
JSONをパースし
serifのテキストを指定されたキャラクターにもとずいた
 speaker 番号(ずんだもん→3, 四国めたん→2)
    を下にVOICEVOXを利用して音声ファイルを
指定されたファイル名で出力するコードをgo言語で作成してください
VOICEVOXのホストは環境変数VOICEVOX_HOSTから取得
ない場合はlocalhost
 


{
    "backgrounds": {
      "1": "bg1.png",
      "2": "bg1.png"
    },
    "defaultLeftCharacter": "四国めたん-ノーマル.png",
    "defaultRightCharacter": "ずんだもん-ノーマル.png",
    "scenes": [
      {
        "setBackground": "1",
        "lines": [
          {
            "character": "ずんだもん",
            "serif": "今日は雨がふりそうなのだ",
            "sound": "1.wav",
            "images": ["ずんだもん-喋り1.png", "ずんだもん-喋り2.png"],
            "color": "#00ff00" 
          },
          {
            "character": "四国めたん",
            "serif": "そうね、曇ってきたわね",
            "sound": "2.wav",
            "images": ["四国めたん-喋り1.png", "四国めたん-喋り2.png"],
            "color": "#ff00ff"
          }
        ]
      },
      {

するとVOICEVOXへのリクエストの投げ方が間違っていたので誘導しつつ直しました。やはりo3-miniだとちょとポンコツなのかもしれません

失敗します
おそらくリクエスト部分がまちがっています下記は成功しているPythonのコードです
参考にして改善してください



def get_voicevox_audio(text, speaker):
    """
    text: 読み上げるテキスト
    speaker: VOICEVOX の speaker 番号(ずんだもん→3, 四国めたん→2)
    """
    audio_query_url = f"http://{VOICEVOX_HOST}:{VOICEVOX_PORT}/audio_query"
    params = {"text": text, "speaker": speaker}
    r = requests.post(audio_query_url, params=params)
    if r.status_code != 200:
        print("audio_query に失敗しました:", r.text)
        return None
    query = r.json()
    
    synthesis_url = f"http://{VOICEVOX_HOST}:{VOICEVOX_PORT}/synthesis"
    headers = {"Content-Type": "application/json"}
    r2 = requests.post(synthesis_url, params={"speaker": speaker}, data=json.dumps(query), headers=headers)
    if r2.status_code != 200:
        print("synthesis に失敗しました:", r2.text)
        return None
    return r2.content  # WAV バイナリデータ
23 秒 の間、推論済み
以下は、Pythonコードの挙動を再現するように修正した Go コード例です。
修正点は、audio_query のリクエストも POST に変更し、URL のクエリパラメータとして text と speaker を渡すようにしている点です。

go
コピーする
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
)

// シナリオ用の構造体(必要なフィールドのみ)
type Scenario struct {
	Backgrounds           map[string]string `json:"backgrounds"`
	DefaultLeftCharacter  string            `json:"defaultLeftCharacter"`
	DefaultRightCharacter string            `json:"defaultRightCharacter"`
	Scenes                []Scene     

Go言語でつくっているのはpythonなど入ってない環境下でも動くバイナリが作成できるためです。
実行するとVOICEVOXに問い合わせて音声ファイルを作成します

pengui:~/code_wsl/zundamon_browser_player$ ./voice_generator scenario2.json 
2025/02/16 01:03:23 saved audio file: 1.wav
2025/02/16 01:03:24 saved audio file: 2.wav
2025/02/16 01:03:24 saved audio file: 3.wav
2025/02/16 01:03:25 saved audio file: 4.wav
2025/02/16 01:03:26 saved audio file: 5.wav
2025/02/16 01:03:26 saved audio file: 6.wav
2025/02/16 01:03:27 saved audio file: 7.wav
2025/02/16 01:03:27 saved audio file: 8.wav
2025/02/16 01:03:28 saved audio file: 9.wav
2025/02/16 01:03:29 saved audio file: 10.wav
2025/02/16 01:03:29 saved audio file: 11.wav
2025/02/16 01:03:30 saved audio file: 12.wav

ずんだもん寸劇の再生

リソースファイルの読み込み等があるためサーバー経由でindex.htmlを開きます。VSCodeがある場合はLive Preview拡張で開けます


テキストボックスをクリックすると寸劇が進む

ずんだもん寸劇っぽい、ノベルゲームっぽい寸劇がブラウザ上で再現できました。

ソースコード



今後の応用

ブラウザ上でずんだもん寸劇が再生できるようになりました。
あとはシナリオJSONをローカルLLMをつかって物語を生成すれば簡単に音声付きの寸劇が作れます。またローカルLLMがなくてもサンプルのscenario.jsonをChatGPTに読み込ませて同じフォーマットでシナリオを書いてもらえれば新しい物語をつくることができます。



下記はブラウザ上で実行する
ずんだもんと四国めたんの会話劇のシナリオファイルです
フォーマットにのっとってシナリオの続きを書いてみてください
流れとしては
傘を買わないで本屋にでかけたずんだもん(傘が高いから買わない)
四国めたんは傘をもってついていく
雨がやむまで本屋に居座ろうとするずんだもんだったが・・


{
    "backgrounds": {
      "1": "bg1.png",
      "2": "bg1.png"
    },
    "defaultLeftCharacter": "四国めたん-ノーマル.png",
    "defaultRightCharacter": "ずんだもん-ノーマル.png",
    "scenes": [
      {
        "setBackground": "1",
        "lines": [
          {
            "character": "ずんだもん",
            "serif": "今日は雨がふりそうなのだ",
            "sound": "1.wav",
            "images": ["ずんだもん-喋り1.png", "ずんだもん-喋り2.png"],
            "color": "#00ff00" 
          },
          {
            "character": "四国めたん",
            "serif": "そうね、曇ってきたわね",
            "sound": "2.wav",
            "images": ["四国めたん-喋り1.png", "四国めたん-喋り2.png"],
            "color": "#ff00ff"
          }
        ]
      },
      {
        "setBackground": "2",
        "lines": [
          {
            "character": "ずんだもん",
            "serif": "本を買いに行こうと思ってたのに最悪なのだ",
            "sound": "3.wav",
            "images": ["ずんだもん-喋り1.png", "ずんだもん-喋り2.png"],
            "color": "#00ff00"
          },
          {
            "character": "四国めたん",
            "serif": "傘を持っていったらいいんじゃないかしら?",
            "sound": "4.wav",
            "images": ["四国めたん-喋り1.png", "四国めたん-喋り2.png"],
            "color": "#ff00ff"
          }
        ]
      }
    ]
  }
  

いいなと思ったら応援しよう!