AI支援で百人一首アプリを作った話
TL;DR
- 百人一首100首を「情景を味わいながら暗記する」Webアプリとして実装した
- Claude Codeで仕様化・実装・テスト修正を進め、Gemini imageで100首分の情景画像とOGP画像を生成した
- Gemini QAでモバイル表示やUI崩れをレビューし、指摘を実装に戻す流れを作った
- VOICEVOXで100首 × 2話者 × 上の句/下の句の朗詠音声を生成し、クイズ形式も用意した
- Next.jsの静的書き出しとWeb App Manifestで、インストール可能なWebアプリとして配布できる形にした
概要
この記事は、個人制作として作った百人一首暗記アプリの制作記録です。単に「AIにコードを書かせた」話ではなく、Claude Codeで仕様と実装を進め、Gemini imageで画像アセットを作り、Gemini QAで画面品質を確認し、VOICEVOXで音声を生成し、最後に静的Webアプリとして配布可能にするまでの流れをまとめます。
対象読者は、生成AIを使って小さなプロダクトを作ってみたいエンジニア、AI生成物をそのまま終わらせずWebアプリ品質に乗せたい人、Next.jsの静的サイトに画像・音声・クイズを組み込みたい人です。
目次
- 作ったもの
- 背景: なぜ百人一首だったか
- 全体構成
- Claude Codeに任せたこと
- Gemini imageで画像を量産したこと
- Gemini QAを実装ループに入れたこと
- VOICEVOXで朗詠音声を生成したこと
- クイズ形式を提供したこと
- インストール可能なWebアプリとして配布したこと
- ハマったポイント
- 活用事例
- まとめ
- 参考
作ったもの
作ったのは、百人一首100首を一覧・詳細・音声・クイズで学べるWebアプリです。
公開URLはこちらです。
主な機能は次の通りです。
- 100首一覧
- 各首の詳細ページ
- 上の句・下の句・作者・時代・テーマ・決まり字の表示
- 「いつ・誰が」「情景」「意味」の解説
- 同じ作者、同じテーマ、同じ決まり字グループ、同じ時代による関連句リンク
- 上の句/下の句の朗詠音声
- 10問セッション型のクイズ
- OGP画像、JSON-LD、sitemap、robots
- Web App Manifestによるインストール可能なWebアプリ形式
当初から狙ったのは、単語帳のように反復するだけの暗記アプリではありませんでした。句の背景や情景を読み、関連する句へ回遊しながら覚える体験を作ることを目的にしました。
背景: なぜ百人一首だったか
百人一首は、データ構造として扱いやすい一方で、単なる一覧にするとかなり味気なくなります。
各首には、番号、作者、上の句、下の句、読み、決まり字、時代、テーマ、情景、意味があります。これはWebアプリの題材として見ると、構造化データ、詳細ページ、関連リンク、クイズ、音声、画像生成を全部試せるちょうどよいサイズです。
一方で、100件のデータを手で整えるのは重い。そこで、人間が体験設計と品質基準を決め、Claude Codeに実装と反復修正を任せ、画像と音声は別の生成ツールに分担させる構成にしました。
全体構成
実装はNext.js App Router + TypeScriptです。100首のデータはアプリ内に持ち、generateStaticParams()で100首分の詳細ページを静的生成します。
export async function generateStaticParams() {
return getAllPoems().map((p) => ({ slug: p.slug }));
}
next.config.tsでは静的書き出しを有効にしました。
const nextConfig = {
output: 'export',
images: {
unoptimized: true,
},
trailingSlash: true,
};
Next.js公式ドキュメントでも、静的書き出しは output: 'export' を設定して有効化する形です。
To enable a static export, change the output mode inside next.config.js.
今回はログインや進捗保存を入れていません。GW中に完成させるため、スコープは「読む」「聞く」「解く」「巡る」に絞りました。
Claude Codeに任せたこと
Claude Codeには、主に次の作業を任せました。
- 仕様書の整理
- 実装計画の分解
- Next.jsのページ・コンポーネント実装
- 100首データの型定義と検証
- 関連句抽出ロジック
- クイズ選択肢ロジック
- Vitestの追加
- ビルドエラーや型エラーの修正
重要だったのは、最初に「何を作らないか」も決めたことです。
- ログインしない
- 進捗管理しない
- SRSは入れない
- 競技かるた風のリアルタイム機能は入れない
- 多言語化しない
AIに自由に実装させると、機能が増えがちです。GW制作では、作る範囲を絞ったことが効きました。
Claude Codeは、公式リポジトリで「terminalで動くagentic coding tool」と説明されています。今回も、単発のコード生成ではなく、リポジトリを読みながら実装・修正・検証を回す役割として使いました。
an agentic coding tool that lives in your terminal
Gemini imageで画像を量産したこと
画像はGemini imageを使って生成しました。
生成した情景画像の例です。
やったことはシンプルで、100首それぞれの scene をプロンプトに差し込み、情景画像とOGP画像を生成するスクリプトを用意しました。
const prompt = `百人一首 第${p.id}番『${p.kami} ${p.shimo}』(${p.author})。${p.scene} 和の意匠、墨絵風、淡い和紙の質感、雅な色合い。テキストや文字は一切入れないでください。`;
実装上のポイントは次の3つです。
-
--from/--toで範囲指定できるようにする - 既に生成済みの画像はスキップする
- 失敗時は最大3回リトライする
大量生成では、1回の失敗で全体を止めないことが重要です。100首分の画像を作る場合、通信失敗や一時的な生成失敗は普通に起きます。スキップとリトライを入れておくと、途中再開がしやすくなります。
Gemini APIの画像生成ドキュメントでは、Geminiの画像モデルが画像生成・編集を扱えることが説明されています。今回は文章から各首の情景を画像化する用途に使いました。
Generate and edit highly contextual images natively.
Gemini QAを実装ループに入れたこと
実装が一通り動いたあと、Gemini QAで画面確認をしました。
ここで見たのは、コードの正しさというより、ユーザー体験として破綻していないかです。
- モバイルで縦書き表示が窮屈になっていないか
- タブやカードの余白が過剰/不足していないか
- 句本文、画像、解説、クイズ導線の順番が自然か
- ボタンやリンクが小さすぎないか
- 生成画像が本文の邪魔をしていないか
AIに実装させたUIは、動いていても「画面として読むと違和感がある」ことがあります。そこで、Claude Codeで実装し、Gemini QAで画面レビューし、指摘をClaude Code側に戻す流れにしました。
特に百人一首のような題材では、情報密度だけでなく余白や文字組みも体験に影響します。最終的には、縦書きの句、情景画像、解説、音声、関連句が順に読める構成に落ち着きました。
VOICEVOXで朗詠音声を生成したこと
音声はVOICEVOXで生成しました。
対象は100首 × 2話者 × 上の句/下の句なので、合計400ファイルです。
100首 × 2話者 × 2(上の句/下の句) = 400 mp3
VOICEVOX ENGINE APIでは、まず /audio_query で音声合成用のクエリを作り、そのクエリを /synthesis に渡して音声を生成します。
/audio_queryエンドポイントで、音声合成は/synthesisエンドポイントで行っており
実装では、ひらがなの読みを朗詠用に少し加工しました。
function toRecitationText(kana) {
return kana.replace(/\s+/g, '、') + '。';
}
VOICEVOXからはwavを受け取り、ffmpegでmp3に変換しました。
ffmpeg -i input.wav -ac 1 -b:a 64k -af volume=1.5 output.mp3
Webアプリ側では、話者切替、上の句再生、下の句再生、両方を続けて再生するボタンを用意しました。暗記アプリとしては、文字を見るだけでなく耳で確認できることがかなり効きます。
クイズ形式を提供したこと
クイズは10問セッション形式にしました。
- 上の句から下の句を選ぶ
- 下の句から上の句を選ぶ
- 4択
- 即時フィードバック
- 結果画面
- 解説ページへのリンク
選択肢は完全ランダムではなく、少しだけ学習効果を意識しました。
const themeQuota = Math.round(count * 0.6);
const kimariQuota = Math.max(0, Math.round(count * 0.1));
const randomQuota = count - themeQuota - kimariQuota;
意図は次の通りです。
- 同テーマの句を混ぜると、意味や情景の近い選択肢になりやすい
- 決まり字グループを混ぜると、暗記上の紛らわしさが少し出る
- 完全ランダムも混ぜることで難しすぎない
学習アプリでは、間違えたときにすぐ詳細へ戻れる導線も重要です。クイズの正誤だけで終わらせず、該当句の解説ページへ戻れるようにしました。
インストール可能なWebアプリとして配布したこと
今回はネイティブアプリではなく、Web App Manifestを持つインストール可能なWebアプリ形式にしました。
site.webmanifest は次のような構成です。
{
"name": "百人一首暗記",
"short_name": "百人一首",
"theme_color": "#C7402A",
"background_color": "#FAF6E9",
"display": "standalone"
}
MDNでは、standalone はブラウザUIなしの独立したアプリのように表示されるモードとして説明されています。
The application will look and feel like a standalone application.
Next.jsの静的書き出しにしておくと、配信側はかなり軽くできます。今回のようにログインやサーバー処理が不要な学習アプリでは、静的配信とインストール可能なWebアプリ形式の相性が良いです。
ハマったポイント
外部コマンド呼び出しでは環境変数を明示する
画像生成スクリプトでは、Node.jsから外部コマンドを呼び出しています。このとき、APIキーなどの環境変数が子プロセスへ渡らず失敗することがありました。
最終的には spawn を使い、env: process.env を明示しました。
const child = spawn('image-generation-command', args, {
env: process.env,
stdio: ['ignore', 'pipe', 'pipe'],
});
大量生成の途中で失敗すると原因が追いにくくなるので、stdout/stderrを最後の数百文字だけ残すようにしたのも効きました。
生成物はそのまま配信しない
画像生成後のPNGはサイズが大きくなりがちです。Web配信用にはWebPへ変換し、静的書き出し後には元画像ディレクトリを配信成果物から除外しました。
for (const path of [
'images/poems/source',
'images/og/source',
'images/hero.png',
'images/favicon-source.png',
]) {
rmSync(join(outDir, path), { force: true, recursive: true });
}
AI生成物は作るところで満足しがちですが、Webアプリとしては配信サイズ、OGP、代替テキスト、読み込み順まで見た方がよいです。
データはテストで守る
100首データは手で見ても間違いに気づきにくいので、最低限のテストを入れました。
- 100件あること
- IDが1から100まで揃っていること
- slugが重複していないこと
- 必須テキストが空でないこと
- 決まり字の文字数とグループが一致していること
AIに大量データを整えさせる場合、人間が全件レビューするより、まず機械で検査できる条件をテストに落とす方が現実的です。
活用事例
このアプリは、個人制作ではありますが、次のような用途に展開できます。
- 子どもと一緒に百人一首を読む
- 学校教材のプロトタイプにする
- 生成AIによる教育コンテンツ制作の検証に使う
- 画像生成・音声生成・クイズ生成を組み合わせた小規模教材の雛形にする
- 静的配信できる学習アプリのサンプルにする
特に、画像・音声・クイズを組み合わせると、同じ題材でも「読む」「見る」「聞く」「解く」の複数経路で触れられます。百人一首のような暗記題材では、この複数経路が体験としてかなり大きいです。
まとめ
GW制作としては、かなり良い題材でした。
学びは次の通りです。
- Claude Codeは、仕様化から実装、テスト修正までの反復に向いている
- Gemini imageは、100件規模の情景画像生成と相性が良いが、リトライ・再開設計が必要
- Gemini QAを挟むと、動くだけのUIから読めるUIへ寄せやすい
- VOICEVOXを使うと、学習アプリに音声体験を追加しやすい
- 静的書き出し + Web App Manifestなら、小さな学習アプリを軽く配布できる
- AIに任せるほど、テスト・型・ビルド・配信サイズの確認が重要になる
AIで作ると速い。ただし、速く作ったものを使える形にするには、人間側の設計判断と品質ゲートが必要です。今回の百人一首アプリは、その分担を試す題材としてちょうどよい規模でした。
参考
公式 1 次資料
-
Static Exports — Next.js
Next.js App Routerで静的書き出しを行うための公式ガイド。 -
Gemini API image generation — Google AI for Developers
Gemini APIで画像生成・編集を行うための公式ドキュメント。 -
VOICEVOX ENGINE API Document
VOICEVOX ENGINEが提供するHTTP APIのリファレンス。 -
VOICEVOX/voicevox_engine — GitHub
/audio_queryと/synthesisの基本的な流れを確認できる公式リポジトリ。 -
Web app manifest: display — MDN
Web App Manifestのdisplayメンバーに関するリファレンス。 -
anthropics/claude-code — GitHub
Claude Codeの公式リポジトリ。
補足
- 画像生成モデルや料金、提供モデル名は変化が早いため、公開前に最新の公式ドキュメントで再確認する。
- VOICEVOXの利用条件、各話者の利用規約は公開前に必ず確認する。


