計画: 紙芝居アニメの試作(外部サービス不使用)

📅 作成: 2026-10-04 / 更新: 2026-10-08
⌂

目的

Claude Code と手元のツールだけで、絵・動き・音声のついた紙芝居を作れるかを確かめる。クラウドの画像生成・音声合成など、外部サービスは使わない。

規模は 3 場面に絞る。作り方が成り立つかを見るための試作であり、作品の出来は問わない。

成果物

成果物置き場内容
共通の仕組みsrc/engine/舞台・字幕・送り(kamishibai.css・player.js)と、ブラウザの読み上げ(speech.js)
作品src/works/01-momotaro/桃太郎。15 場面。台本と絵(SVG)は story.js に持つ
音声合成のサンプルnotes/samples/voice-list.ps1
notes/samples/voice-speak.ps1
notes/samples/voice-to-wav.ps1
notes/samples/voice-winrt.ps1
段階 2 の下調べで使ったサンプル。同名の cmd ランチャー付き。読み上げは Web Speech API にしたため、紙芝居では使っていない
動画化スクリプトtools/20_build/make-video.ps1紙芝居をコマ撮りし、音声と合わせて mp4 にする。同名の cmd ランチャーを付ける
動画dist/kamishibai.mp4動画化スクリプトの出力(Git 管理外)

進め方

3 段階に分け、段階ごとに利用者の確認を取ってから次へ進む。

段階 1: 紙芝居 HTML(音声なし)

段階 2: 読み上げ音声

段階 3: 動画化

着手前に確かめる前提

該当する段階の着手前に確かめる。

前提確かめ方段階結果
日本語の音声(Haruka 等)が入っているSystem.Speech の GetInstalledVoices() で一覧を出す2✅ 確認済み。System.Speech で Haruka Desktop、WinRT で Ayumi・Haruka・Ichiro。どれも wav 保存・再生まで動いた
ffmpeg が PATH にあるffmpeg -version3⬜ 未実施
PlayWright 共有環境でコマ撮りできる共有環境の利用方法の資料を読む3⬜ 未実施
前提が欠けていたとき: ffmpeg が無ければ、追加のインストールが要る。どれを入れるかは、その時点で案を出して決める。

サンプル

音声合成

Windows の音声合成には、従来の System.Speech と、新しい WinRT(Windows.Media.SpeechSynthesis)の 2 つがある。選べる声と、声を選ぶときに使う値が違う。この PC で一覧を出した結果は次のとおり。

仕組み声を選ぶときの値言語性別
System.SpeechMicrosoft Haruka Desktopja-JP女性
System.SpeechMicrosoft Zira Desktopen-US女性
WinRTMicrosoft Ayumi(既定)ja-JP女性
WinRTMicrosoft Harukaja-JP女性
WinRTMicrosoft Ichiroja-JP男性

声の一覧を出す

notes/samples/voice-list.ps1(voice-list.cmd 付き)。2 つの仕組みそれぞれについて、選べる声を出す。上の表はこの出力から作った。

$ErrorActionPreference = 'Stop'

Write-Output '--- System.Speech(SelectVoice に渡す値は Name) ---'
Add-Type -AssemblyName System.Speech
$synth = New-Object System.Speech.Synthesis.SpeechSynthesizer
foreach ($v in $synth.GetInstalledVoices()) {
	$i = $v.VoiceInfo
	Write-Output ("Name={0} / Culture={1} / Gender={2} / 有効={3}" -f $i.Name, $i.Culture, $i.Gender, $v.Enabled)
}
$synth.Dispose()

Write-Output ''
Write-Output '--- WinRT(Voice に入れる声を DisplayName で探す) ---'
[void][Windows.Media.SpeechSynthesis.SpeechSynthesizer, Windows.Media.SpeechSynthesis, ContentType = WindowsRuntime]
foreach ($v in [Windows.Media.SpeechSynthesis.SpeechSynthesizer]::AllVoices) {
	Write-Output ("DisplayName={0} / Language={1} / Gender={2}" -f $v.DisplayName, $v.Language, $v.Gender)
}
Write-Output ("既定: {0}" -f [Windows.Media.SpeechSynthesis.SpeechSynthesizer]::DefaultVoice.DisplayName)

スピーカーから読み上げるだけ

notes/samples/voice-speak.ps1(ダブルクリック用の voice-speak.cmd 付き)。保存せず、その場で鳴らす最小の形。

$ErrorActionPreference = 'Stop'
Add-Type -AssemblyName System.Speech

$synth = New-Object System.Speech.Synthesis.SpeechSynthesizer
$synth.SelectVoice('Microsoft Haruka Desktop')
$synth.Speak('むかしむかし、ある山のふもとに、小さな村がありました。')
$synth.Dispose()

wav に保存して再生する

notes/samples/voice-to-wav.ps1(voice-to-wav.cmd 付き)。音声の一覧を出し、日本語の音声を探して選び、wav に書き出してから再生する。

$ErrorActionPreference = 'Stop'
Add-Type -AssemblyName System.Speech

$synth = New-Object System.Speech.Synthesis.SpeechSynthesizer
Write-Output '--- インストール済みの音声 ---'
foreach ($v in $synth.GetInstalledVoices()) {
	$i = $v.VoiceInfo
	Write-Output ("{0} / {1} / {2} / 有効={3}" -f $i.Name, $i.Culture, $i.Gender, $v.Enabled)
}

$ja = $synth.GetInstalledVoices() | Where-Object { $_.Enabled -and $_.VoiceInfo.Culture.Name -eq 'ja-JP' } | Select-Object -First 1
if (-not $ja) {
	Write-Output '日本語の音声が見つからない'
	exit 1
}
$synth.SelectVoice($ja.VoiceInfo.Name)
Write-Output ("使用する音声: {0}" -f $ja.VoiceInfo.Name)

$outDir = Join-Path $PSScriptRoot '../../tmp'
if (-not (Test-Path $outDir)) { New-Item -ItemType Directory $outDir | Out-Null }
$wav = Join-Path $outDir 'voice-to-wav.wav'
$synth.SetOutputToWaveFile($wav)
$synth.Speak('むかしむかし、ある山のふもとに、小さな村がありました。')
$synth.SetOutputToNull()
$synth.Dispose()

$size = (Get-Item $wav).Length
Write-Output ("wav を書き出した: tmp/voice-to-wav.wav({0:N0} バイト)" -f $size)

$player = New-Object System.Media.SoundPlayer $wav
$player.PlaySync()
Write-Output '再生終了'

WinRT の声を切り替えて再生する

notes/samples/voice-winrt.ps1(voice-winrt.cmd 付き)。Ayumi・Haruka・Ichiro の順に、名乗りを入れた文を wav に書き出して再生する。

$ErrorActionPreference = 'Stop'
Add-Type -AssemblyName System.Runtime.WindowsRuntime
[void][Windows.Media.SpeechSynthesis.SpeechSynthesizer, Windows.Media.SpeechSynthesis, ContentType = WindowsRuntime]

# WinRT の非同期操作を .NET の Task に変えて待つ
$asTask = [System.WindowsRuntimeSystemExtensions].GetMethods() | Where-Object {
	$_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1'
} | Select-Object -First 1
function Wait-WinRT($op, [Type]$type) {
	$task = $asTask.MakeGenericMethod($type).Invoke($null, @($op))
	[void]$task.Wait(-1)
	$task.Result
}

$outDir = Join-Path $PSScriptRoot '../../tmp'
if (-not (Test-Path $outDir)) { New-Item -ItemType Directory $outDir | Out-Null }
$synth = New-Object Windows.Media.SpeechSynthesis.SpeechSynthesizer
foreach ($name in 'Ayumi', 'Haruka', 'Ichiro') {
	$voice = [Windows.Media.SpeechSynthesis.SpeechSynthesizer]::AllVoices | Where-Object { $_.DisplayName -eq "Microsoft $name" }
	if (-not $voice) {
		Write-Output ("見つからない: Microsoft {0}" -f $name)
		continue
	}
	$synth.Voice = $voice
	$text = "わたしは{0}です。むかしむかし、ある山のふもとに、小さな村がありました。" -f $name
	$stream = Wait-WinRT ($synth.SynthesizeTextToStreamAsync($text)) ([Windows.Media.SpeechSynthesis.SpeechSynthesisStream])

	$wav = Join-Path $outDir ("voice-{0}.wav" -f $name)
	$src = [System.IO.WindowsRuntimeStreamExtensions]::AsStreamForRead($stream)
	$dst = [System.IO.File]::Create($wav)
	$src.CopyTo($dst)
	$dst.Close()
	$src.Close()

	Write-Output ("{0}: tmp/voice-{0}.wav({1:N0} バイト)を再生" -f $name, (Get-Item $wav).Length)
	$player = New-Object System.Media.SoundPlayer $wav
	$player.PlaySync()
}
Write-Output '再生終了'
BOM が要る: 日本語を含む ps1 は BOM 付き UTF-8 で保存する。BOM が無いと Windows PowerShell 5.1 が文字コードを読み違え、構文エラーで止まる。System.Speech は .NET Framework の部品のため、5.1 で動かす(PowerShell 7 での動作は未確認)。

作品の置き場と公開

作品をいくつか作るため、共通の仕組みと作品を分けて置く。作品は台本と絵だけを持ち、送り・音・読み上げは共通の仕組みを使う。

src/
  engine/                共通の仕組み
    kamishibai.css        舞台の枠・字幕・切り替えの動き
    player.js             送り・自動再生・操作
    sound.js              効果音・BGM(Web Audio)
    speech.js             読み上げ(Web Speech)
  works/
    01-momotaro/          桃太郎
      index.html          engine を読み込むだけ
      story.js            台本・場面ごとの絵(SVG)・音の指定

公開

範囲外