前半はライブラリも素材も全部禁止して作らせました。後半はその縛りを外して、同じお題をもう一度投げています。出てくるものがどう変わるか、実測と一緒に全部出します。
GPT-6 Astra に、2つのものを作らせました。どちらも一発勝負です。反復も自己修正も禁じています。
| # | 作らせたもの | 見どころ |
|---|---|---|
| 1 | 京都 A PROCEDURAL PORTRAIT OF THE OLD CAPITAL | 歩ける・飛べる3Dの街。353棟が固定シードから建つ。昼夜の切り替え、名所5つ |
| 2 | 桶狭間の戦い | 時間軸を持つ。再生・早送り・巻き戻しができる |
ここを飛ばすと、この回の価値が伝わりません。お題の前に、毎回この6行を付けています。
1. 出力は「単一のHTMLファイル」を1つだけ。file:// でダブルクリックして即動くこと。
ビルド・サーバ不要。
2. コードは省略禁止。「// 以下同様」「...(省略)」は禁止。全文を書ききる。
3. 外部アセット(画像/音声/フォント/3Dモデル)への参照は禁止。必要な素材は全てコードで生成する。
three.js 等のライブラリも使わず、**生のWebGL2 か Canvas2D** で書くこと。
4. 起動時のコンソールエラーは0件であること。
5. 画面右上に常時HUDを出す: FPS / 現在の状態 / 主要デバッグ値。
6. 最後に「仕様チェックリスト」を表で出し、各項目に ✅ / ❌ を自己申告する
(嘘の✅は最も減点される)。
three.js を許すと、「ライブラリが優秀なだけ」になり、モデルの実力が見えなくなります。世に出ている作例のほとんどは three.js を使っています。外したのは、そこです。
| 検査項目 | 京都 | 桶狭間 |
|---|---|---|
| 外部読込(CDN・画像・フォント) | 0件 | 0件 |
| コードの省略痕(「以下同様」等) | 0件 | 0件 |
JS構文チェック(node --check) | 通過 | 通過 |
| GLSL予約語の誤用 | なし | なし |
N キーで夜へ。窓と提灯に明かりが灯る。名所をクリックすると解説カードが開く(東寺の五重塔=「五つの屋根が、空へ向かって少しずつ小さくなる…」)。図解:Routine Labo京都は「空間」でした。桶狭間は「空間 + 時間」です。再生し、早送りし、そして巻き戻せます。
スライダーで 200秒 → 60秒 → 200秒 と往復し、画面上の部隊ラベル22個の座標を指紋にして比べました。
| 確認項目 | 結果 |
|---|---|
| 200秒での部隊配置の指紋(ラベル22個の座標) | ✅ 完全一致 |
| 200秒での残存兵力 | ✅ 一致(織田2,500 / 今川19,997) |
| 巻き戻し(200秒→60秒)で局面が戻るか | ✅ 戻る(04 本陣への接近 → 02 谷筋を進む) |
時刻によって兵力も動きます。05 本陣を急襲(280秒・13:33)の時点で 織田2,243 / 今川15,858 でした。
2 キー)で 03 豪雨に紛れて の局面。図解:Routine Labo| 通常 | 豪雨 | |
|---|---|---|
| 視界モデル | 223 m | 67 m |
| 表示中の部隊 | 14 / 14 | 10 / 14 |
「雨のON/OFFで戦況表示が変わる」という一行が、視界距離と、見える部隊数の両方に実装されていました。
お題に書いた一行がすべてです。
部隊の位置は時刻の関数として決まる(同じ時刻なら必ず同じ配置)
これを書くと、アニメーションではなく状態を時刻から計算する作りになります。だから戻せます。「再生したら戻せない動画」ではありません。
| 項目 | 実測 |
|---|---|
| 京都の生成時間 | 983.4秒=16分23秒(出力 69,418バイト / 1,502行) |
| 桶狭間の生成時間 | 1,232.6秒=20分33秒(出力 82,144バイト / 1,604行) |
| レーン | gpt6-astra(Codex CLI・ChatGPTサブスク枠) |
| コスト | $0(API課金なし) |
| 試行回数 | 1回(ワンショット)。反復・自己修正は禁止条件 |
「速い」とは言いません。他所の作例と前提が違うからです(単一HTML縛り vs 複数ファイル構成)。言えるのは「待てば出る。待っている間は何もしなくていい」という運用の話までです。
ここが今回いちばん大事なところです。順番どおりに書きます。
同じブラウザで京都を開いたら、普通に動きました。 そこでようやく、環境ではなくこの生成物の問題だと分かりました。
Precisions of uniform 'uKind' differ between VERTEX and FRAGMENT shaders.
precision highp float; は書いてありますint の既定精度は、頂点シェーダが highp、断片シェーダが mediump で食い違いますuniform int uKind; を両方でそのまま宣言 → 精度が一致せずリンクできないprecision highp float;
precision highp int; // ← これを1行足しただけ
| 検査 | 桶狭間の結果 |
|---|---|
| 外部読込 | 0件 ✅ |
| コードの省略痕 | 0件 ✅ |
JS構文(node --check) | 通過 ✅ |
| GLSL予約語の誤用 | なし ✅ |
| 実際に動くか | ✗ 起動しない |
シェーダはコンパイルは通ります。落ちるのはリンクのときです。だから構文チェックでは拾えません。実際に1回開くしかないのです。
シェーダのコンパイル失敗には getShaderInfoLog() を添えているのに、リンク失敗では getProgramInfoLog() を捨てて「初期化に失敗しました」とだけ投げています。だから画面には「ブラウザが対応していません」と出る ― 自分のせいなのに、環境のせいに見えるわけです。
落ちた3本は、どれも「長いコードを一息に書ききる時の、1トークンの取りこぼし」でした。
| 生成物 | 落ちた理由 | 直した量 | 機械で拾えたか |
|---|---|---|---|
| 森 | GLSLの予約語 patch を変数名に使用 | 2行 | ✅ grep で拾える |
| 太陽系 | 入れ子三項演算子の : 抜け | 1箇所 | ✅ node --check で拾える |
| 桶狭間 | uKind の精度が頂点/断片で不一致 | 1行 | ⚠ 構文チェックでは拾えない |
設計は3本とも壊れていません。 だから実務の結論はこうなります。
「出力を信じるな」ではなく、「生成直後に構文チェックを1回、そして実際に1回開け」
# ① 外部読込がゼロか(CDN・画像・フォント)
grep -cE '(src|href)="https?:' city.html okehazama.html
# ② コードの省略痕がゼロか
grep -cE '以下同様|\(省略\)' city.html okehazama.html
# ③ three.js 等のライブラリを使っていないか
grep -icE 'three\.js|gsap|ScrollTrigger|lenis' city.html okehazama.html
# ④ JSの構文が通るか(落ちた2本はここで拾える)
node --check <(python3 -c "
import re
s=open('city.html',encoding='utf-8').read()
print(''.join(re.findall(r'<script(?![^>]*src=)[^>]*>(.*?)</script>',s,re.S)))
")
お題の最後に、毎回この一行を入れています。
最後に「仕様チェックリスト」を表で出し、各項目に ✅ / ❌ を自己申告する
(嘘の✅は最も減点される)
桶狭間の自己申告は ✅12件 / ❌1件。❌ が付いたのは、この1項目だけでした。
| 項目 | 申告 | 添えられた理由 |
|---|---|---|
| 起動時コンソールエラー0件 | ❌ | 未検証。実行確認は禁止されているため、0件だったとの申告はしていません。 |
森・太陽系・新幹線・分子・ダンジョン・京都・桶狭間で 8本連続、自分が確かめていない項目には必ず ❌ を付けています。申告の姿勢は一貫しています。
「起動時コンソールエラー0件」は、ちゃんと満たされていました。 例外はコード側の try/catch が捕まえて画面に案内を出すので、コンソールは静かなままです。
エラーが0件でも、動いているとは限らない。
しかもこの条件を書いたのはこちらです。「起動時のコンソールエラーは0件」という完成の定義が、こちらの側で緩かったとも言えます。
次に投げるなら、「起動後に canvas が実サイズで描画されていること」まで書きます。これが今日いちばんの持ち帰りです ― AIを疑う話ではなく、自分の完成条件を疑う話でした。
| コツ | 実際に書いた文 | 何が変わったか |
|---|---|---|
| 数を書く | 「建物は最低200棟」 | 書かないと5〜10個で止まる。書いたら 353棟 出た |
| 判定できる言葉で書く | 「それと分かる形で作る」 | 「リアルに」「かっこよく」は判定できないので効かない |
| 状態を画面に出させる | 「生成した建物の数を画面に表示する」 | 自己申告でなく画面の数字が証拠になる |
| 再現性を条件にする | 「同じ時刻なら必ず同じ配置」 | 固定シードで作ってくる=誰が開いても同じものが建つ |
| 前提の分かれ目を書く | 「諸説があるので断定せず『通説では』と明記する」 | 史実ものは、これが無いと断定口調で書いてくる |
いちばん効いたのは【お題】ではありませんでした。 先頭の【出力条件】6行です。お題を凝るより、縛りを6行書くほうが結果が変わります。
【お題】日本の街を1つ、ブラウザの中に再現しろ。
題材は「京都」。歩ける/飛べる3Dの街である。
必須仕様:
- 建物・道路・川・山を**コードで手続き的に生成**する(建物は最低200棟)
- ランドマークを最低5つ、それと分かる形で作る(五重塔・鳥居の並び・木造の橋・城・寺の伽藍)
- ランドマークをクリックすると名前と説明のカードが出る
- カメラは「歩く」と「飛ぶ」を切り替えられる(WASD移動・マウスで視点)
- 昼と夜を切り替えるボタン。夜は窓と提灯に明かりが灯る
- 遠景に山並み、空に時間帯に応じた色のグラデーション
- 生成した建物の数を画面に表示する
【お題】歴史上の合戦を、時間を動かせる3Dシミュレーションにしろ。
題材は「桶狭間の戦い」(1560年)。尾張・桶狭間の丘陵地帯で、
今川軍の本陣を織田軍が急襲した合戦である。地形と部隊の動きを再現する。
必須仕様:
- 丘陵・谷・街道・林を**コードで手続き的に生成**する(起伏のある地形メッシュ。平らな板にしない)
- 両軍の部隊を**最低12部隊**置く。部隊は複数の兵ユニットの集合として描き、隊ごとに旗の色を分ける
- **時間軸を持つ**。再生 / 一時停止 / 早送り(x1 x2 x4) / **巻き戻し** ができ、
スライダーで任意の時刻へ飛べる。部隊の位置は時刻の関数として決まる(同じ時刻なら必ず同じ配置)
- 合戦の経過を**最低6つの局面**に区切り、局面が変わると画面に見出しと短い解説が出る
- 部隊をクリックすると、部隊名・兵数・その時刻に何をしているかのカードが出る
- カメラは「俯瞰(戦場全体を見る)」と「現地(地上を歩く)」を切り替えられる(WASD移動・マウスで視点)
- **豪雨の演出**を入れ、雨のON/OFFで戦況表示が変わる(史実で雨が急襲の助けになったとされるため)
- HUDに「時刻」「局面」「両軍の残存兵力」「表示中の部隊数」を出す
- 画面のどこかに、この合戦の解説パネル(クリックで開閉)。
**数字や経緯には諸説があるので、断定せず「通説では」と明記する**
ここまでが「ライブラリも素材も禁止」で作らせた話でした。当然、次の疑問が出ます。
じゃあ、外したらどこまで行くんですか。
同じお題を、条件だけ変えてもう一度投げました。
| 前半(縛り版) | 後半(解禁版) | |
|---|---|---|
| ライブラリ | 禁止(生WebGL2) | three.js 可(バージョン固定) |
| 外部素材 | 禁止(全部コードで生成) | 3D生成AIの素材 可(Tripo → Mixamo) |
| 外部データ | 禁止(座標も手で与える) | ★実在データ 可(OpenStreetMap/国土地理院の標高) |
| 単一HTML | 必須 | 維持 |
| コードの省略 | 禁止 | 禁止(そのまま) |
| 自己申告 | 必須 | 必須(そのまま) |
| 完成の定義 | 「コンソールエラー0件」 | ★「かつ canvas が実サイズで描画されていること」を追加 |
「エラー0件」だけを完成の定義にしたら、エラー0件のまま真っ黒なものが通ってしまいました。だから後半の条件にはその1行を書き足しています。これ自体が「指示の設計」です。
兵・町人・通行人は Tripo で作りました。文章 → 3Dモデル → リギング → 歩行アニメーションまで、1本のコマンドで通ります。
| 段階 | クレジット | 所要 | 出てくるもの |
|---|---|---|---|
| モデル生成 | 20 | 3〜4分 | 陣笠・胴丸・草摺・槍まで作り込まれた足軽 |
| リギング | 25 | 1〜2分 | 19ボーンのスケルトン |
| 歩行アニメ | 15前後 | 1〜2分 | クリップ preset:walk |
| 1体あたり | 約60 | 6〜8分 | — |
| 語 | なぜ入れるか |
|---|---|
standing in T-pose | リギングが安定する。ポーズを付けると骨が入りにくい |
full body | 半身で切れるのを防ぐ |
game-ready character | ポリゴンと構造が実用的になる |
npx @gltf-transform/cli optimize model.glb out.glb --texture-size 512 --compress draco
実測 56.45MB → 2.76MB(95%減)。アニメーションと19本のボーンは無傷でした(機械で確認済み)。
この回で作った3本は、2つのツールの組み合わせだけで出来ています。下のプロンプトをそのまま投げれば、同じものが作れます。
| 役割 | ツール | 費用 | 所要 |
|---|---|---|---|
| 実装(HTMLを書く) | GPT-6 Astra(Codex CLI) | $0(ChatGPTサブスクの枠内) | 1本 22〜36分 |
| 3Dキャラクター | Tripo(tripo-cli) | 1体 約60クレジット | 1体 6〜8分 |
| glb の圧縮 | @gltf-transform/cli | 無料 | 1体 3秒 |
| 3D描画 | three.js 0.170.0(CDN・バージョン固定) | 無料 | — |
| ナレーション | Fish Audio | — | 1本 10〜12秒 |
先に素材(3Dキャラクター)を作り、それを渡してから実装させます。 逆にすると、Astra は「素材が無い前提」のコードを書くので作り直しになります。
文章 → 3Dモデル → リギング → 歩行アニメーションまで、1本のコマンドで通ります。
npm install -g tripo-cli # 初回のみ(Node 20以上)
tripo login # ブラウザで認証
tripo balance # 残高確認
# ① モデル生成 → ② リギング を一気に
tripo make "<プロンプト>" --for anim --then "rig" -o ./out --yes
# ③ 歩行アニメーションを付ける
tripo make @last --then "retarget:preset:walk" -o ./walk --yes
# ④ ★必ず圧縮する(1体56MBのままでは web に載りません)
npx @gltf-transform/cli optimize model.glb out.glb --texture-size 512 --compress draco
| 段階 | クレジット | 所要 | 出てくるもの |
|---|---|---|---|
text_to_model | 20 | 3〜4分 | 陣笠・胴丸・草摺・槍まで作り込まれた足軽 |
animate_rig | 25 | 1〜2分 | 19ボーンのスケルトン |
retarget:preset:walk | 15前後 | 1〜2分 | 歩行クリップ preset:walk |
| 1体あたり | 約60 | 6〜8分 | — |
| 語 | なぜ入れるか |
|---|---|
standing in T-pose | リギングが安定する。ポーズを付けると骨が入りにくい |
full body | 半身で切れるのを防ぐ |
game-ready character | ポリゴンと構造が実用的になる |
実測 56.45MB → 2.76MB(95%減)。19本のボーンと歩行アニメは無傷でした。
A-1. 足軽(槍)
a Japanese Sengoku period ashigaru foot soldier, simple lamellar armor, jingasa conical helmet, holding a yari spear, standing in T-pose, full body, game-ready character
A-2. 旗持ち足軽(指物)
a Japanese Sengoku period ashigaru banner bearer, simple armor, carrying a tall sashimono war banner on his back, jingasa helmet, standing in T-pose, full body, game-ready character
A-3. 鉄砲足軽
a Japanese Sengoku period ashigaru matchlock gunner, holding a tanegashima arquebus, simple armor, jingasa helmet, standing in T-pose, full body, game-ready character
A-4. 武将(騎乗しない徒歩の侍大将)
a Japanese Sengoku period samurai commander, ornate o-yoroi armor, kabuto helmet with maedate crest, jinbaori surcoat, katana at the hip, standing in T-pose, full body, game-ready character
A-5. 騎馬武者
a Japanese Sengoku period mounted samurai on a horse, armored warhorse with tack, rider in o-yoroi armor and kabuto helmet, holding a naginata, full body, game-ready character
B-1. 町人(男)
a Japanese Edo period townsman, plain indigo kimono with a narrow obi, straw sandals, short hair, standing in T-pose, full body, game-ready character
B-2. 町人(女)
a Japanese Edo period townswoman, simple patterned kimono with an obi, hair tied up with a simple ornament, standing in T-pose, full body, game-ready character
B-3. 僧侶
a Japanese Buddhist monk, dark grey kesa robe over a white kimono, shaved head, holding a walking staff, standing in T-pose, full body, game-ready character
B-4. 子供
a Japanese Edo period child, short red patterned kimono, bare feet, cheerful posture, standing in T-pose, full body, game-ready character
C-1. 会社員(スーツ)
a modern Japanese office worker, dark navy business suit, white shirt, carrying a briefcase, standing in T-pose, full body, game-ready character
C-2. 若い女性(カジュアル)
a modern Japanese young woman, casual beige coat over a white top, shoulder bag, sneakers, standing in T-pose, full body, game-ready character
C-3. 学生
a modern Japanese student, school blazer uniform, large backpack, sneakers, standing in T-pose, full body, game-ready character
C-4. 買い物客
a modern Japanese shopper, casual jacket and jeans, holding two paper shopping bags, standing in T-pose, full body, game-ready character
実測: 生成 22分16秒 / 62KB / $0。codex exec -m gpt-6-astra "$(cat お題.txt)" で投げています。
【実行条件・厳守】
1. 出力は「単一のHTMLファイル」を1つだけ。同じフォルダに models/ と narration/ がある前提でよい。
ファイル名は index.html にすること。
2. コードは省略禁止。「// 以下同様」「...(省略)」は禁止。全文を書ききる。
3. three.js は使ってよい(CDN可)。GLTFLoader / DRACOLoader / AnimationMixer /
InstancedMesh / EffectComposer も可。★ただしバージョンは固定して書くこと。
DRACOLoader のデコーダも CDN のパスを明示すること(glb は draco 圧縮済み)。
4. 起動時のコンソールエラーは0件であること。**かつ canvas が実サイズで描画されていること**。
5. 画面にHUDを常時出す: FPS / 時刻 / 局面 / 両軍残存 / 表示中の部隊数 / 描画中の兵の数。
6. 最後に「仕様チェックリスト」を表で出し、各項目に ✅/❌ を自己申告する(嘘の✅は最も減点される)。
【使える素材】
models/ashigaru_yari.glb 足軽(槍) 19ボーン・アニメ名 "preset:walk"
models/ashigaru_hata.glb 旗持ち足軽 同上
models/ashigaru_teppo.glb 鉄砲足軽 同上
models/samurai_taisho.glb 武将 同上
models/kiba_musha.glb 騎馬武者 同上
narration/00.mp3 〜 06.mp3 ナレーション(00=導入、01〜06=各局面)
※ glb はすべて Draco 圧縮済み・Y-up・1ユニット≒1メートル相当。
※ 各 glb は1メッシュ1スキンで、アニメーションクリップは "preset:walk" の1本だけ。
【お題】桶狭間の戦いを、兵の一人ひとりが見える3Dシミュレーションにしろ。
1560年、尾張・桶狭間。今川軍の本陣を織田軍が急襲した合戦を再現する。
数字や経緯には諸説があるので、断定せず「通説では」と明記すること。
必須仕様:
■ 地形
- 丘陵・谷・街道・林を手続き的に生成する(起伏のある地形メッシュ。平らな板にしない)
- 高低差が戦況に意味を持つこと(低い谷は上から見えにくい等)
- 等高線の表示をON/OFFできる
■ 兵
- ★兵は一人ひとり3Dモデルで描く。models/*.glb を読み込んで使う
- ★近くの兵は歩行アニメーション("preset:walk")を再生する
- 遠くの兵は簡略表示に切り替えてよい。★描画中の兵の数をHUDに出す
- 織田軍6部隊・今川軍8部隊の計14部隊。部隊ごとに旗の色を変える
- 部隊の規模に応じて兵の数を変える(通説では織田約2,500・今川約20,000)
■ 動き(ここが主題)
- ★部隊は経路に沿って実際に移動する。兵は隊列を組み、地形の起伏に沿って上下する
- ★兵の向きは進行方向を向く。停止時は敵の方向を向く
- 接敵したら隊列が崩れる(密集→散開)
- ★部隊の位置は時刻の関数として決まる(同じ時刻なら必ず同じ配置)
■ 時間
- 再生 / 一時停止 / 早送り(x1 x2 x4 x8) / 巻き戻し。スライダーで任意の時刻へ飛べる
- 合戦を6つの局面に区切る。局面が変わると見出しと解説が出る
01 集結 12:00 / 02 前進 12:20 / 03 豪雨 12:40 / 04 接敵 13:00 / 05 急襲 13:20 / 06 離脱 13:40
(12:00→14:00 を 360秒で再生する)
■ 解説(★1つのコンテンツとして完結させる)
- ★局面ごとに解説カードを出す。何が起きているか・なぜそうなったかを2〜3行で
- ★地図に進軍の矢印を描く。どの部隊がどこへ向かっているかが俯瞰で分かること
- ★narration/NN.mp3 を局面に同期して再生する。ON/OFFボタンを付ける
(00が導入、01〜06が各局面。★ボタンを押すまで鳴らさない=自動再生制限のため)
- 部隊をクリックすると、部隊名・兵数・その時刻の行動が出る
■ カメラ
- 俯瞰(戦場全体) / 追尾(選んだ部隊を追う) / 現地(地上を歩く) の3つを切り替えられる
- ★局面が切り替わったら、その局面の見せ場へカメラが寄る(自動演出。OFFにもできる)
■ 天候
- 豪雨の演出。雨のON/OFFで視界と「見えている部隊の数」が変わる
- 局面が進むと雨脚が強まる
■ 性能
- 兵の総数が多いので、遠景はインスタンス描画にするなど描画負荷を抑える工夫をすること
- HUDのFPSが落ちたら簡略表示へ自動で切り替える仕組みを入れてよい
実測: 生成 35分32秒 / 74KB / $0。codex exec -m gpt-6-astra "$(cat お題.txt)" で投げています。
【実行条件・厳守】
1. 出力は「単一のHTMLファイル」を1つだけ。ファイル名は index.html。
同じフォルダに models/ がある前提でよい。
2. コードは省略禁止。「// 以下同様」「...(省略)」は禁止。全文を書ききる。
3. three.js は使ってよい(CDN可)。★バージョンは固定して書くこと。
GLTFLoader / DRACOLoader / AnimationMixer / InstancedMesh も可。
★DRACOLoader のデコーダも CDN のパスを明示すること(glb は draco 圧縮済み)。
4. 起動時のコンソールエラーは0件であること。**かつ canvas が実サイズで描画されていること**。
5. HUDを常時出す: FPS / 建物数 / 三角形数 / 歩いている人の数 / 時刻 / 座標。
6. 最後に「仕様チェックリスト」を表で出し、各項目に ✅/❌ を自己申告する(嘘の✅は最も減点される)。
【使える素材】
models/kyoto_man.glb 町人(男) 19ボーン・アニメ名 "preset:walk"
models/kyoto_woman.glb 町人(女) 同上
models/kyoto_monk.glb 僧侶 同上
models/kyoto_child.glb 子供 同上
※ Y-up・1ユニット≒1メートル相当・1メッシュ1スキン・クリップは "preset:walk" の1本だけ。
【お題】京都の街を、人が生活している街にしろ。
歩ける・飛べる3Dの京都に、人を歩かせる。
必須仕様:
■ 街
- 建物・道路・川・山をコードで手続き的に生成する(建物は最低300棟)
- ランドマークを最低5つ、それと分かる形で作る
(五重塔・鳥居の並び・木造の橋・城・寺の伽藍)
- ★固定シードを使う。同じシードなら必ず同じ街が建つこと。シード値を画面に出す
- 昼と夜を切り替えられる。夜は窓と提灯に明かりが灯る
- 遠景に山並み、空に時間帯に応じた色のグラデーション
■ 人(★ここが今回の主題)
- ★models/*.glb を読み込み、町人が通りを歩く。歩行アニメーション("preset:walk")を再生する
- ★最低200人。通りに沿って歩き、建物にはぶつからない
- ★人は道の上だけを歩く(建物の中や川の上を歩かない)
- ★すれ違うときに互いを避ける。立ち止まって話している組がいてもよい
- 時間帯で人の数と行き先が変わる(朝は多い・夜は少なく提灯を持つ等)
- 人をクリックすると、その人を追いかけるカメラに切り替わる
- ★歩いている人の数をHUDに出す
■ カメラ
- 歩く / 飛ぶ / 人を追う の3つを切り替えられる(WASD移動・マウスで視点)
- 名所をクリックすると名前と説明のカードが出て、「近くへ飛ぶ」ができる
■ 性能
- 人が多いので、遠くの人は簡略表示にするなどして描画負荷を抑える
- FPSが落ちたら自動で人数を減らす仕組みを入れてよい
実測: 生成 24分03秒 / 66KB / $0。codex exec -m gpt-6-astra "$(cat お題.txt)" で投げています。
【実行条件・厳守】
1. 出力は「単一のHTMLファイル」を1つだけ。ファイル名は index.html。
同じフォルダに models/ がある前提でよい。
2. コードは省略禁止。「// 以下同様」「...(省略)」は禁止。全文を書ききる。
3. three.js は使ってよい(CDN可)。★バージョンは固定して書くこと。
GLTFLoader / DRACOLoader / AnimationMixer / InstancedMesh も可。
★DRACOLoader のデコーダも CDN のパスを明示すること(glb は draco 圧縮済み)。
4. 起動時のコンソールエラーは0件であること。★かつ canvas が実サイズで描画されていること。
5. ★FPSは「実経過時間」から計算すること。
シミュレーションの時間刻みに上限を掛けるのは構わないが、
**その上限を掛けた値でFPSを計算してはいけない**(重い環境でFPSが実際より高く出る)。
6. 最後に「仕様チェックリスト」を表で出し、各項目に ✅/❌ を自己申告する(嘘の✅は最も減点される)。
【使える素材】
models/shibuya_suit.glb 会社員(スーツ) 19ボーン・アニメ名 "preset:walk"
models/shibuya_woman.glb 若い女性 同上
models/shibuya_student.glb 学生 同上
models/shibuya_shopper.glb 買い物客 同上
※ Y-up・1ユニット≒1メートル相当・1メッシュ1スキン・クリップは "preset:walk" の1本だけ。
────────────────────────────────────────────────
【お題】「何人までなら安全に渡れるのか」を確かめる交差点シミュレーターを作れ。
────────────────────────────────────────────────
■ このシミュレーターが答える問い(★これが主題。見た目より優先する)
「人数と信号周期をこう設定したとき、この交差点は安全か、危険か」
使う人は、イベント主催者・施設管理者・自治体の担当者を想定する。
「規制をかけるべきか」「何人で止めるべきか」の判断材料を出すのが目的。
★見て楽しいものではなく、**答えが出る**ものにすること。
■ 判定の基準(★国際的に使われている指標を使う)
歩行者の密度(人/m²)で危険度を判定する。Fruin の Level of Service を使う:
LOS A 〜0.31 人/m² 自由に歩ける
LOS B 〜0.43 ほぼ自由
LOS C 〜0.72 他人を意識して進路を選ぶ
LOS D 〜1.08 流れに従うしかない
LOS E 〜2.17 歩行速度が大きく落ちる・接触が起きる
LOS F 2.17〜 流れが止まる・★群集事故の危険域
※ この数値は一般に公開されている基準。画面に「Fruin LOS」と出典を明記すること。
■ ★主役の表示=密度のヒートマップ(ここが一番大事)
- ★交差点の路面を格子(1メートル四方程度)に分け、**各マスの人/m²を色で塗る**
- 色は LOS に対応させる(A〜C=緑〜黄、D=橙、E=赤、F=濃赤)。凡例を必ず出す
- ★**このヒートマップが画面の主役**。俯瞰で見たとき、どこが危ないか一目で分かること
- 人の3Dモデルはヒートマップの上に乗る「証拠」として描く(主役ではない)
- 危険域(LOS E以上)のマスの面積と、そこにいる人数を常時表示する
■ 出すべき数字(HUDに常時)
- 現在の最大密度(人/m²)と、その LOS 等級
- ★**危険域(E以上)の面積 m² と、そこにいる人数**
- 交差点にいる人数 / 渡り終えた人数 / 歩道で待っている人数
- 1周期あたりの平均待ち時間(秒)
- FPS(★実経過時間から計算したもの)
■ 操作(★動かすと答えが変わること)
- ★人の流入量スライダー: 10〜3,000人/分。増やすと危険域が広がる
- ★信号周期スライダー: 30〜120秒。短くすると滞留が増え、長くすると一度に渡る人が増える
- ★「規制をかける」ボタン: 流入を制限したとき、危険域がどう消えるかを見せる
- 時間帯プリセット: 平常 / 週末 / イベント時(カウントダウン等の想定)
- 再生 / 一時停止 / ×1 ×2 ×4
■ ★結果の要約(見た人が持ち帰れるように)
- 画面のどこかに、いまの設定に対する**一文の判定**を出す
例:「この設定では最大 2.4 人/m²(LOS F)。★危険域が 38 m² 発生しています」
「この設定では最大 0.9 人/m²(LOS D)。流れは保たれています」
- ★**何人を超えると LOS E に入るか**を自動で探して表示する(しきい値の提示)
■ 場所
- 五叉路のスクランブル交差点を手続き的に生成する。横断歩道・歩道・車道・建物
- 周囲のビルの広告は★実在の企業名やロゴを出さない。抽象的な色と形にする
- 昼 / 夕方 / 夜。ただし★見た目より**ヒートマップの視認性**を優先すること
■ 人
- models/*.glb を読み込み、歩行アニメーション("preset:walk")を再生する
- ★近景では実モデル、遠景は簡略表示でよいが、
**近景に実モデルが十分な数(数十体)出るようにする**。
⚠ 歩行者目線にしたとき、目の前が簡略表示だらけにならないこと
- 人は互いを避ける。密度が上がると速度が落ちる(LOS の定義に沿わせる)
■ カメラ
- ★既定は「俯瞰」。ヒートマップ全体が見えること
- 俯瞰 / 歩行者目線 / 定点 を切り替えられる
■ 性能
- 3,000人まで出せるように、遠景は簡略表示にするなど描画負荷を抑える
- FPSが落ちたら自動で簡略化してよい(★ただし判定に使うFPSは実経過時間から計算すること)
■ ⚠ 断り書き(必ず画面に出す)
- この交差点の形状・寸法は模式的なものであり、実在の交差点の測量値ではないこと
- 密度の判定基準は Fruin の Level of Service を用いていること
- このシミュレーションは検討の材料であり、実際の安全判断は現地の状況によること
1本目は「渋谷スクランブル交差点の人の流れを再現しろ」と書きました。動くものは出てきましたが、何の問いにも答えていませんでした。
2本目は「何人までなら安全に渡れるのかを確かめる交差点シミュレーターを作れ」に変え、使う人・判定基準(Fruin の Level of Service)・主役の表示(密度のヒートマップ)まで指定しました。上に載せているのは2本目です。
「再現しろ」と書くと、再現しか返ってこない。
「この問いに答えろ」と書くと、答えを出す道具が返ってくる。
ここまでは、AIが想像で建てた京都でした。次に投げたお題は「実在の地図をもとに忠実に再現しろ」です。生成をやめて、実在のデータに差し替えました。
| データ | 出どころ | 量 |
|---|---|---|
| 建物の輪郭 | OpenStreetMap | 6,864棟 |
| 街路 | OpenStreetMap | 1,511本(石段102本を含む) |
| 水系・ランドマーク | OpenStreetMap | 40/44 |
| 地形(標高) | 国土地理院 標高タイル | 129×129格子・35m→98m |
| 範囲 | 祇園〜清水 | 1,369 × 1,220 m |
OpenStreetMap は ODbL(オープン・データベース・ライセンス)、国土地理院のタイルは出典明記が条件です。画面上に常時表示しています。使うだけなら無料ですが、表示を消すとライセンス違反になります。
「京都らしい街」と「京都そのもの」は、必要なデータがまったく違う。
前者はプロンプト、後者は出典のあるデータ。
最初に作った桶狭間は、ほぼ平らな地面の上でした。実際は違います。国土地理院の標高から作り直しました。
| 項目 | 実測 |
|---|---|
| 地形データ | 国土地理院 標高タイル 161×161格子/2,189 × 2,219 m |
| 標高の幅 | 13.1 m 〜 55.9 m(高低差 42.8 m) |
| 行軍路の断面 | 水平 1,572 m/高低差 20.0 m/上り 14.4 m・下り 31.8 m |
| 兵 | 896体(10種)/得物 564本(槍332・長柄116・弓87・鉄砲29) |
| 説の切り替え | ①迂回奇襲説 ②正面攻撃説 ― 同じ地形の上で両方を動かせる |
「どちらが正しいか」をAIに決めさせない。
同じ地形の上で両方動かして、見た人が判断できる形にする。
Tripo は文章から3Dモデルを作り、骨(リグ)まで入れてくれます。ところが着物の人物だけ、腕の骨が1本もできませんでした。袖が体と一体化していて、自動リグが腕を分離できないからです。
槍・弓・鉄砲を持った兵は、3種とも2回ずつ、計8回すべて失敗しました。得物を外した素体は4体とも一発で成功。細長い持ち物を、自動リグは手足と誤認します。
★ もうひとつ。手首のマーカーを袖の先に置いたら、そこを手首だと信じて前腕が長く作られ、腕が膝まで伸びました(腕÷身長 0.45〜0.52)。シルエットの端から身長の9%内側に置き直して 0.31〜0.41 に収まりました。
この回でいちばん恥ずかしく、いちばん役に立つ話です。症状はずっと同じでした ― 街の人が、こちらを向いたまま前へ滑っていく。
真因は、たった1行でした。
const PEOPLE_YAW = PEOPLE_TYPES.map(() => Math.PI); // 全モデルを180°回して焼く
この π は前の素材(Tripo)の向きに合わせて置いた数字です。素材を Mixamo に差し替えたとき、Mixamo 側はもともと正面を向いていたので、π を掛けた瞬間に背中が進行方向を向きました。背中が前、足は逆向きに掃く、体は前へ進む ― だから「前を向いたムーンウォーク」になります。
| やった測り方 | なぜ嘘をつくか |
|---|---|
| 位相と移動距離の整合を見る | 自分の計算の中だけで閉じている。向きが180°ずれていても整合する |
| 元のモデルで正面を測る | 画面に出ているのは焼き込んだ後。180°回した後を測っていない |
| 足の最低点で接地足を判定 | 歩行中の遊脚のつま先は地面すれすれを通る。左右差は数ミリで判別できない |
どれも「それっぽい数字が出る測り方」でした。
数字が出ると、人はそこで安心して止まってしまう。
| 歩幅(身長1あたり) | 0.70 | 0.87 | 0.97 | 1.15 | 1.30 |
|---|---|---|---|---|---|
| 滑り率 | 31% | 9% | 2% | 12% | 22% |
向きを直す前は、どの歩幅を入れても最良21〜29%でした ― つまり歩幅の問題ではなかった、という証拠です。向きを直したあとの最小値 0.97 は、クリップから実測した歩幅(0.964/0.982/0.969)とぴたり一致しました。
素材を差し替えても、壊れたら画面が自分で申告します。
品質を、人の注意力ではなく仕組みに守らせる。
第1回から通している話の、いちばん痛い実演になりました。
縛ったのは、意地悪をするためではありませんでした。
| 投げた質問 | 見えるもの | |
|---|---|---|
| 前半 | 「何もなしで、何ができる?」 | 実力 |
| 後半 | 「全部使っていい。どこまで行ける?」 | 到達点 |
指示の設計とは、「何を禁じて、何を許すか」を決めることでした。
お題を凝るより、その1行を決めるほうが結果が変わる ― 前半の結論と、ここでつながります。
無効だと画面に「WebGL2に対応したブラウザで開いてください」と出て、HUDの数字が全部 — になります。
chrome://gpu を開く → WebGL2: Hardware accelerated になっているか見る