太陽系アプリ · 開発記録

調査・実装・試行錯誤の記録 · 2026 09 07

実スケールの太陽系を、
HTML 1枚で旅する

天文データの選び方から、地球儀、カメラの移動、視線の反転、光の軌跡、オルゴール三重奏のBGMまで。今回の制作で確かめたことと、別の開発へ持ち帰れる知見をまとめる。

20収録天体
242国・地域の地図要素
2026年内蔵天体暦の範囲
20.28 MB完成アプリ・外部読込不要

対象:solar-voyager.html のBGM追加版。本書は実装記録と保存済みの検証結果を再確認した開発ドキュメントであり、新しい天文学上の研究成果を主張するものではない。

今回の更新:第11節にBGMの作曲・合成・ループ再生を追加。検証、容量、限界、対象ファイルの識別情報もBGM追加版へ更新した。

1. この制作で得た中心的な知見

実際の大きさ・距離・運動を守る部分と、人が観察しやすくする部分を、別々に設計して検証することが有効だった。遠くの惑星を見つけやすくするために惑星自体を拡大するのではなく、目印やカメラの構図を調整する。運動は天体暦に従わせ、移動速度を感じる演出は独立した粒子で補う。この区別が、後の改善をしやすくした。

特に再利用しやすい6点
  1. データには座標の中心・座標系・時刻系・単位をセットで持たせる。正しい数値でも、組み合わせ方を間違えると正しい景色にはならない。
  2. 巨大な世界は、カメラに近い原点へ移して描画する。物理量とGPUへ渡す座標を分ける。
  3. 移動経路、視線、画面の傾きを別々に扱う。位置だけ滑らかでも、視線の分岐で体験は壊れる。
  4. 情報を消すタイミングと、操作を消すタイミングを分ける。鑑賞中も「何を見ているか」は残す。
  5. 作り込む処理と、再生時に必要な処理を分ける。音色と残響を事前生成し、再生中の計算を減らす。代わりに必要になるメモリも記録する。
  6. 数値の一致・画面の美しさ・人の快適さを混同しない。それぞれ異なる確認が必要になる。

以下では、実装数値・状態試験設計上の知見未確認を区別して記録する。利用者から得た「UIが邪魔」「俯瞰が遠すぎる」「移動を自然にしたい」という指摘も、改善の根拠である。

2. 要望が設計を変えた過程

要望・観察実装の変化得られた知見
多くの国を見られる地球儀と、実スケールの太陽系を1ファイルに地球画像・国境・天体データ・描画コードを内蔵。国名検索と近接観察を用意「1ファイル」でも、素材・データ・ロジックの責任を内部で分ける
移動途中で俯瞰し、自動で旅を繰り返したい出発・俯瞰・接近の経路と、20天体を巡るギャラリーを追加自動鑑賞は通常操作とは異なる状態管理を必要とする
UIが天体を隠す。俯瞰が遠すぎるパネルを折りたたみ、移動する2天体だけを収める俯瞰へ変更観察対象に合わせた構図を作る。世界全体の広さを毎回見せる必要はない
視線も自然に、速度感は派手に。ただし見やすく五次補間、視線とロールの分離、方向・速度に連動する光跡、天体保護マスクを追加カメラを揺らさずに速度感を伝える余地がある
宇宙の広がりを感じるオルゴール三重奏。低負荷で音質重視80秒のオリジナル曲を残響込みで事前生成し、単一音源でループ。小さな♪と設定内の音量操作を追加音作りと再生を分け、計算量・配布容量・復号後メモリを別々に扱う

これは要求の変化と実装の対応を整理した表であり、統制された比較実験ではない。各段階の作業時間や主観評価の点数は記録していない。

3. 天体の運動:簡略モデルから天体暦へ

まず調べたもの

初期調査では、半径・周期・軌道要素とケプラー方程式による近似を調べた。こうした近似は軽量だが、与えられた有効期間や誤差の範囲がある。JPLの近似要素表で地球に相当する行が地球・月の重心を表す点にも注意が必要だった。JPL 近似惑星位置

最終版では、位置と速度を事前取得したJPL Horizonsの天体暦を再生する方式を採用した。惑星や衛星の運動をアプリ内で重力積分しているわけではない。相互重力などを含む元の天体暦を、保存した状態から補間している。

取得・組み込み時に固定した条件

項目今回の条件
期間2026 01 01 00:00〜2027 01 01 00:00 TDB、両端を含む
時刻TDB。UTCの日付・時刻として扱わない
位置の種類同時刻の幾何学的位置。VEC_CORR=NONE
座標系J2000黄道座標。REF_SYSTEM=ICRFREF_PLANE=ECLIPTIC
単位位置 km、速度 km/s
中心惑星・準惑星は太陽中心、衛星は親惑星の中心。太陽を表示座標の原点に固定
収納形式状態ベクトルのJSONをgzip+base64で内蔵し、pakoで展開
期間外外挿しない。末尾で運動を止め、ギャラリーは次の出発時に年初へ戻す

取得条件の意味はHorizons API仕様、今回の指定値は保存済み取得記録と最終データによる。「観測者に届く光が示す位置」と「同時刻の幾何学的位置」は区別している。

惑星本体と、惑星系の重心を混ぜない

最終データでは地球中心を 399、木星中心を 599 などとして照会した。衛星の親側も同じ実体の中心に合わせ、同じ時刻のベクトルを加える。

衛星の太陽中心位置(t)
= 親惑星の太陽中心位置(t) + 衛星の親惑星中心位置(t)

ここで親だけ惑星系の重心にすると、衛星との位置関係へ別のずれを持ち込む。名前が似ている対象を接続する前に、物理的に何の中心なのかを確かめることは、他分野のデータ統合にも応用できる。

最終版のサンプリング間隔

対象間隔
水星・木星6時間
それ以外の惑星・準惑星12時間
月・カリスト・タイタン3時間
エウロパ・ガニメデ・トリトン1時間
イオ・エンケラドゥス30分

太陽以外の19天体に合計79,589状態を収録した。全天体へ同じ間隔を使わず、短周期の運動や中間点での補間差を見て間隔を変えている。

補間式と、試した別方式

保存点の位置 p₀,p₁、速度 v₀,v₁、区間長 h 秒、区間内の割合 u を使う3次Hermite補間を採用した。

p(u) = (2u³ − 3u² + 1)p₀
     + (u³ − 2u² + u)h v₀
     + (−2u³ + 3u²)p₁
     + (u³ − u²)h v₁

区間端で位置と速度が一致する。一般には加速度まで連続する方式ではない。hを日で渡し、速度をkm/sのまま使うような単位混同を避ける必要がある。

Chebyshev多項式への区分フィットも試した。水星では、フィットに含めていない730中間点への最大位置差が約0.1766 mだった。一部天体では状態ベクトル方式へ戻す試作構成となった。最終版はChebyshev係数を読み込まず、Hermiteへ統一している。

不採用理由を明示した記録は残っていない。そのため「Chebyshevの精度が悪かったから捨てた」とは書けない。残っている数値は、少なくとも水星では良好な近似が得られたことを示している。

4. 自転・地軸・座標系で気をつけたこと

位置の公転モデルとは別に、IAUモデルの極方向と本初子午線角から姿勢を作った。NAIFの pck00011.tpc を基に、19天体の姿勢、月の13周期項、トリトンなどの極方向の周期変化を扱っている。NAIF pck00011PCK仕様

落とし穴今回の扱い
天文学のZ-upを描画用のY-upへ移す際の鏡映(x,y,z) → (x,z,−y) とする。符号を省略した (x,z,y) は座標の向きを反転させる
日と世紀が混ざる姿勢係数各多項式・周期項で、引数の時間単位を明示する
逆行自転の二重反転符号付きの回転角を使い、軸と回転方向を重ねて反転しない
座標を変換する回転と、物体を回す回転の混同基底ベクトルから姿勢行列を組み、CSPICEの対応行列と比較する
地球を慣性座標でちょうど24時間ごとに回すIAUモデルの恒星時に沿う回転を使う。太陽日と恒星日を区別する
未知の姿勢を、見栄えだけで決めるエリスの自転姿勢は未確定として省略する

姿勢の独立検証では、19天体×4時点の76ケースをCSPICEと比較し、行列要素の最大差は約 3.64×10⁻¹¹ だった。これは同じ姿勢モデルを正しく実装できているかの確認であり、現実の天体の向きがこの桁まで分かるという意味ではない。

ローカル根拠:rotation-guidance.mdrotation-verification.json。最終アプリへの移植後には、別の10フィクスチャでも比較している。

5. 実スケールを守りながら、近くも遠くも描く

カメラを描画原点にする

太陽系の広がりと、地球表面の近接観察では必要な桁が大きく異なる。JavaScript側の世界座標は倍精度で保持し、各フレームでカメラの位置を差し引いた値を描画へ渡した。

描画位置 = 天体の世界位置 − カメラの世界位置

この方法は、巨大な絶対座標の近くで小さな違いを扱う負担を減らす。対数深度バッファと、近くの天体までの距離に応じたnear平面の調整も併用した。座標精度の問題と、前後関係を判定する深度精度の問題は別なので、一方だけで全てを解決したとは考えない。

惑星を拡大せず、目印を別に描く

実比率の全景では、多くの惑星は1画素より小さくなる。天体の球は実際の代表半径のままにし、名前や点を観察用の目印として使った。土星の主な環も、約74,700〜136,800 kmの半径に収めている。

天体の半径は球としての代表値で、扁平率や山の高低までは表していない。物理量の取得元はJPL 惑星物理量衛星物理量。環の画像や補助光は観察表現の層に置く。

単一HTMLの利点と費用

Three.js、pako、天体暦、画像、地図、BGMを内蔵し、初回起動も外部読み込みなしで動かす構成にした。配布と再現は簡単になる一方、画像とデータを展開するメモリや初回処理は必要になる。BGM追加後の20.28 MBというファイルサイズを、そのまま実行時メモリ使用量と見なすことはできない。

6. 地球儀:小国は形だけで探させない

地球画像はNASAのBlue Marble、国境と日本語名はNatural Earthの1:50mデータを使った。これは過去の観測を合成した地図であり、現在の雲や都市の状況を示すライブ画像ではない。NASA Blue MarbleNatural Earth

国境データのポリゴンと穴を保持して座標を小数4桁へ丸め、242の国・地域を扱った。242は主権国家の数ではない。属領・係争地域なども含む地図要素の数である。

小さい国や島は、画面上の形だけでは選びにくい。日本語・英語名の検索、国名の目印、対象へ近づく操作を組み合わせた。元データのラベル点のうち10点は対象の領域外だったため、領域内へ補正し、全242点について穴を除く対象領域内にあることを確認した。

地軸と自転を反映すると、国の緯度経度をそのまま世界座標へ置く方法は使えない。国を選ぶときは地球の姿勢を掛けてカメラの方向へ変換し、クリックで国を調べるときは逆回転で地球のローカル座標へ戻している。

見つけられることと、海岸線が精密であることは異なる。小国へのアクセスを改善しても、縮約された地図の細部が増えるわけではない。

7. 俯瞰を「世界全体」から「今回の移動」へ

最初の経路は、一度太陽系全体を見渡す位置まで離れるものだった。ところが地球から月のような移動では、無関係な外側の天体まで収めるために、必要以上に遠くへ引いてしまう。

変更後は、移動元Aと移動先Bの中点を基準とし、A→Bを画面の横軸にする。2天体が同じ奥行きで左右へ並ぶため、その間隔と大きさから俯瞰距離を決められる。天体は動き続けるので、この枠も毎フレーム更新する。

実装上の工夫
  • 横幅だけでなく、縦の画角と画面の縦横比も使う。
  • 土星を含む経路では、球の半径に代えて環まで含む外接半径を使う。
  • 画面の右端ではラベルを左向きに配置し、長い天体名が切れるのを防ぐ。
  • 衛星を遠距離で隠す処理から、移動中の両端を除外する。
  • 画面回転時は俯瞰だけでなく、観察距離と到着距離も再計算する。
2天体を収める距離の式

2天体の中心間距離を L、大きい方の外接半径を R、縦画角を f、横幅/縦幅を a とする。今回の余白係数は横0.78、縦0.75。

M  = (A + B) / 2
hₓ = tan(f/2) × a × 0.78
hᵧ = tan(f/2) × 0.75
D  = max((L/2 + R√(1+hₓ²)) / hₓ,
         R√(1+hᵧ²) / hᵧ)

カメラは中点Mから、横軸に垂直な方向へ距離Dだけ置く。画面に左右の天体を収める幾何のための式であり、最適な映画的構図を証明する式ではない。

手動で見る「太陽系全景」は残している。全体を把握する操作と、対象間を移動する操作は、別の構図を持てるという整理になった。

8. 滑らかな移動:位置・視線・傾きを分ける

距離を対数で、進行を五次式で補間する

近接距離と惑星間距離を直線的に混ぜると、出発直後に天体が急に小さくなりやすい。そこで半径方向の距離を対数で補間し、進行率には、両端で一階・二階微分が0になる五次の補間関数を用いた。

s(u) = 6u⁵ − 15u⁴ + 10u³
d(u) = exp((1−s(u)) ln d₀ + s(u) ln d₁)
2026-09-06T23:07:14.599276 image/svg+xml Matplotlib v3.10.8, https://matplotlib.org/ 0.0 0.2 0.4 0.6 0.8 1.0 Progress u 0.0 0.2 0.4 0.6 0.8 1.0 Blend s(u) Cubic Quintic 0.0 0.2 0.4 0.6 0.8 1.0 Progress u −6 −4 −2 0 2 4 6 Second derivative
補間関数の形と二階微分。五次式は両端の二階微分も0になる。数式から描いた説明図で、カメラの実測加速度ではない。

最終経路は、進行率0〜0.36が出発、0.36〜0.58が俯瞰、0.58〜1が接近。俯瞰枠の傾きを旅全体で少しずつ変え、区間ごとに別の停止姿勢へ切り替わらないようにした。手動の旅は約18秒、ギャラリーは選んだテンポに従う。

五次式を使っただけで、アプリ全体の加速度が連続だと証明したわけではない。天体暦の補間点、画面のリサイズ、衝突補正、カメラ枠の選択などは別に考える必要がある。今回確認したのは、代表的な接続点の数値的なつながりである。

通り過ぎた中点を見続けない

カメラが目的地へ進むと、中点は後方へ回る。それでも中点そのものを注視点に使うと、視線が短時間に大きく回り得る。最終版は、俯瞰時の方向から、目的地を見る方向へ球面上で補間する。巨大な距離を持つ注視点同士を直接混ぜない。

到着方位は予測した到着時刻の天体姿勢から決め、旅の間は固定する。これにより、地球の自転を追っていたカメラが、到着直後に突然追従をやめる不連続を避けた。ギャラリーの観察中の周回も、出発前と到着直後に速度を緩めている。

見つかった反転:Quaternionでも自動的に滑らかにはならない

滑らかさを改修する途中の試作では、出発時の姿勢と、その時点の俯瞰姿勢をQuaternionの最短経路SLERPで混ぜていた。木星→イオを天体時刻1日/秒で試すと、ある50 msで約116.956°回転した。独立した別時刻の再現では、約178.218°の変化も見つかった。

原因は、動く俯瞰姿勢との内積が0をまたぎ、「どちら回りが最短か」が途中で切り替わったことだった。単に隣り合うQuaternionの符号をそろえるだけでは、固定した出発姿勢との相対回転が180°をまたぐ問題を解消できない。

2026-09-06T23:07:14.691802 image/svg+xml Matplotlib v3.10.8, https://matplotlib.org/ 170.0 172.5 175.0 177.5 180.0 182.5 185.0 187.5 190.0 Target rotation (degrees) −100 −50 0 50 100 Halfway orientation (degrees) Re-select shortest path Keep a continuous angle branch
1軸回転で示した分岐の模式図。目標が180°をまたぐと、その都度最短経路を選び直した「半分の姿勢」は反転する。実測ログのグラフではない。

最終版では、視線を運ぶ基準姿勢と、視線まわりのロール角を分けた。前の基準姿勢を新しい視線へ平行輸送し、目標ロール角は前回値に続くよう角度をアンラップする。表示したロールを基準姿勢へ戻さないことも重要だった。戻すと回転を二重に蓄積してしまう。

実装の考え方を示す擬似コード
base = transport(previousBase, forward)
pairRoll = unwrap(angleAroundForward(base, pairFrame), previousPairRoll)
endRoll  = unwrap(angleAroundForward(base, arrivalFrame), previousEndRoll)

出発: roll = s(u) × pairRoll
俯瞰: roll = pairRoll
接近: roll = (1−s(u)) × pairRoll + s(u) × endRoll

view = rotationAround(forward, roll) × base

到着用ロールは、接近開始時に俯瞰ロールへ最も近い枝を選ぶ。その後は前回の値から連続的に追う。これは今回の経路で分岐を抑える方法であり、あらゆる3D姿勢補間に対してSLERPより優れているという一般論ではない。

9. 光の軌跡:速度感を加えて、天体は守る

光跡は天文シーンとは別のCanvasに描いた。実在する宇宙塵の密度や相対論的な光学現象を再現するものではなく、進行方向と速度を伝える演出である。

設計目的
カメラの移動方向に対して粒子を逆向きへ流す前進の放射状の流れだけでなく、後退・横移動も伝える
速度に応じた強度と流れの速さを、対数で圧縮するkm/sから光速の多数倍までを、極端な明滅なしで表す
約0.11秒の画面上の位置履歴を使い、軌跡長を制限する方向の変化を短い曲線として残し、画面を横断する長い線を避ける
中央と天体周辺を柔らかく消す進行感を周辺視野に任せ、対象の形や模様を保つ
狭い画面は84粒子、広い画面は120粒子。描画倍率は最大1.5描画量を限定する。実機でのFPS保証とは別
停止時にフェード。手動オフと「動きを減らす」設定に対応必要なときに演出を抑えられるようにする

天体時刻と、カメラの移動速度を混ぜない

天体時刻を早送りすると、カメラが親惑星について行くだけでも世界座標は大きく変わる。その差分をそのまま「ワープ速度」とすると、旅行していない移動まで粒子へ混ざる。

そこで、同じ天文時刻に固定した経路上で進行率だけを少し前後へ動かし、カメラの移動速度を差分で求めた。小さな数値表示が定量情報を担い、軌跡は方向と速さの印象を補う。

v ≈ [P(p+h, t) − P(p−h, t)] / (2h × 旅行時間)
pは旅の進行率、tは固定した天文時刻。端点は片側の差分を用いる。

画面端の球は、中心と同じ半径でマスクできない

最初の保護マスクは、カメラからの距離を基に画面上の半径を求めていた。しかし画面端では透視投影の広がりが変わり、余白を少し増やすだけでは輪郭を覆いきれない場合がある。

最終版は、球へ接する線から横・縦の投影範囲を求め、それを囲う保守的な円を作った。土星は環まで含む外接球で保護する。複数の消去領域は destination-out で重ね、重なったところが再び見えるマスクの穴も避けている。

ここでの「守る」は実装上の設計を指す。Canvasの実ピクセルの合成結果や、端末で見たときの演出の強さは、今回のモック試験では評価できていない。

10. UI:情報と操作で、表示時間を変える

通常表示では、目的地・天体名・移動開始の小さなバーを入口とし、詳細・操作・目的地一覧は必要なときだけ開く。複数の大きなパネルが同時に視野を占めないよう、開閉を排他的にした。

ギャラリーでは天体名、距離、旅の段階、周回番号などを画面の端へ残し、操作ボタンは約4秒で隠す。タップは操作の再表示に使い、ギャラリーを止めない。一時停止中とキーボードで操作中は隠さず、終了ボタンとBGMの♪は常にアクセスできるようにした。

全画面への切り替えも試みるが、利用環境により許可されない場合がある。アプリ自身が開始した全画面だけを終了時に戻すよう管理した。画面の向きが変わったときには、UIだけでなく天体の観察距離も調整している。

操作を隠すことと、状況が分からなくなることを分ける。この考え方は、映像鑑賞、顕微鏡・地図ビューア、展示用の自動デモにも転用できる。

11. BGM:音質を作り込み、再生中の処理を減らす

オリジナルのオルゴール三重奏を、残響まで含めた80秒の音源として事前生成した。宇宙の壮大さを感じること、3つの音域が重なること、負荷は低めで音質を重視することが追加要件だった。映画『インターステラー』は雰囲気の参考として挙がったが、映画の音源や旋律を素材として取り込まず、今回のアプリ用に音符から作った。

作曲:3つの役割と、4つのまとまり

曲名は「光の間を渡る / Across the Quiet Light」。6/8拍子、付点四分音符=48、32小節で構成する。八分音符は約0.4167秒、1小節は2.5秒なので、全体は正確に80秒になる。ニ長調とロ短調の響きを中心に、9度などの付加音と休符で広がりを狙った。これは作曲上の意図であり、聴感評価の結果ではない。

パート音の役割譜面の範囲
高音休符を挟み、音域を広げていく旋律107発音、MIDI 76〜93
中音6/8の流れを支えるアルペジオ184発音、MIDI 57〜78
低音和音の土台になる、間隔を空けた鐘61発音、MIDI 45〜62

合計352発音。三重奏とは譜面上の3パートを指し、同時に鳴る音を3音へ制限するものではない。打音の余韻は次の音へ重なる。

小節・時間まとまり作曲上の狙い
1〜8小節 · 0〜20秒窓辺の青い光Dmaj9を入口に、静かなモチーフと拍の流れを示す
9〜16小節 · 20〜40秒遠くの呼び声Bm9から始め、短調の色と旋律の変化を加える
17〜24小節 · 40〜60秒星々の広がり音域と強弱を広げる。19小節目で旋律がA6へ届く
25〜32小節 · 60〜80秒光の向こうへ還る静まりながらAの和音へ進み、最後のC♯を冒頭のDへつなぐ

音作り:金属の立ち上がりと、減衰する空間

実録サンプルではなく、Python・NumPy・SciPyで音を合成した。基音に整数倍付近の成分と非整数倍の成分を重ね、音域ごとに配合・減衰・定位を変えている。高い成分を早く減衰させ、短い打撃音と控えめな木箱の共鳴も加えた。実在するオルゴールの振動を測定して同定したモデルではない。

残響も事前に合成する。左右で異なる初期反射と、帯域ごとに減衰を変えた拡散残響を用意した。残響のインパルス応答は7秒で、設計上の減衰時間は低い帯域で約5.5秒、高い帯域で約2秒。金属音の輪郭を残しながら空間を広げる狙いがある。最終段では低域・高域を整え、微小なノイズを加えるディザ処理を経て16bitへ量子化した。

ループ:最後の音だけでなく、残響も先頭へ戻す

80秒で波形を切ると、その時点で残っている減衰音や残響が失われる。そこで、各発音の80秒を越える部分を先頭へ足し戻し、残響を畳み込んだ後のはみ出しも同様に折り返した。フィルターへは末尾の1秒を先に通し、毎周の先頭で内部状態がゼロから始まる影響も抑えている。

周期内の追加位置 = サンプル位置 mod 3,840,000
48,000フレーム/秒 × 80秒。左右のチャンネルへ同じ周期で折り返す。

毎周フェードアウト・フェードインを掛ける方式は採用していない。曲の和声的な帰着と、波形・残響の接続を別々に用意した。ただし、数値的に接続を確認したことと、人に継ぎ目が聞こえないことは同じではない。

2026-09-07T00:47:07.477844 image/svg+xml Matplotlib v3.10.8, https://matplotlib.org/ −0.2 −0.1 0.0 0.1 Left amplitude Decoded PCM across the 80-second loop Loop boundary −8 −6 −4 −2 0 2 4 6 8 Time relative to loop boundary (ms) −0.05 0.00 0.05 Right amplitude
追加図:完成FLACを復号し、末尾8msと先頭8msを並べた実データ。上下は左右チャンネル、破線は80秒から0秒へ戻る境界。縦軸は正規化されたサンプル振幅で、音圧ではない。この拡大図だけで聴感上の継ぎ目や音質を判定しない。

再生:重い音作りを、3D描画と同時に行わない

音源は48kHz・16bit・ステレオのFLACとしてHTMLへ内蔵した。最初の♪操作で音声処理を作り、デコードを一度行った後は、AudioBufferSourceNode → GainNode → 出力という小さな構成で繰り返す。再生中のJavaScriptによる音符の予約や、音ごとの合成・残響計算は行わない。

調査では、AudioBufferSourceNodeのループ機能と、一つのノードにstart()を繰り返し呼べない点を確認した。本実装はノードを保持し、音声コンテキストの休止・再開で再生位置を保つ。MDN: AudioBufferSourceNode

decodeAudioData()は復号時に音声コンテキストのサンプルレートへ変換するため、ループ終端は固定のサンプル数から計算せず、復号後のbuffer.durationを使った。MDN: decodeAudioData()

容量の種類今回の値読み方
圧縮済み音源4,002,205 bytes · 約4.00 MBFLACのファイルサイズ
HTMLに入る音源文字列5,336,276 bytes · 約5.34 MBbase64化による増加を含む
復号済みの音声サンプル48kHzなら30,720,000 bytes · 約30.72 MB80秒 × 48,000 × 2ch × float32の4bytes。音声バッファだけの計算値
アプリ全体の配布サイズ20,277,710 bytes · 約20.28 MB画像・地図・天体暦・音源・コード等を含む

圧縮ファイルを小さくすることと、再生時のメモリを小さくすることは別である。本方式は再生中の合成処理を省く一方、曲全体をメモリへ持つ。音声バッファの大きさは出力サンプルレートでも変わる。30.72 MBは端末で測ったピーク値ではなく、デコード中の一時領域や3D描画のメモリも含まない。CPU使用率・消費電力の比較測定はしていない。

操作と状態管理:非同期の準備が、消音を取り消さないようにする

場面実装した扱い
起動直後自動再生しない。初期音量は40%、保存済みの音量があれば優先。再生ONは保存しない
最初の♪操作クリックの処理中にresume()を呼び、デコードはその後に待つ。最初の立ち上がりは1.2秒
デコード中に消音・連打共通のデコードPromiseと操作の世代番号を使う。古い準備の完了から勝手に音を出さない
ミュート0.12秒で音量を下げ、160ms後に休止。新しい操作があれば古い休止予約を無効にする
タブ非表示・復帰非表示時は即座に休止。希望がONなら復帰時に再開を試み、拒否された場合は♪による再開を案内
再開・音量変更既存音源の再開は0.18秒、音量変更は0.09秒で追従
ギャラリー♪を右上の常時アクセスできる位置に置く。周遊の一時停止・公転倍率には音楽を連動させない
デコード失敗エラーを表示して再試行できる状態に戻す。成功後は埋め込んだ音源文字列のDOMテキストを空にする
レビューで修正した点:再開フェードの開始値

最初の実装は、休止時の音量を保持したままresume()していた。その後に目標音量へのフェードを予約しても、開始値と終了値が同じなら立ち上がりは緩やかにならない。修正後は、既存音源がありコンテキストが停止している場合、予約を解除してゲインを0にしてから再開し、0.18秒で戻す。再生中の重複イベントでは0へ戻さない。

これはコードレビューで特定し修正した状態管理の問題であり、実際にクリック音を聴いて発見した事例ではない。「フェードを呼んだか」だけでなく、直前に保持されていた音響状態も確認するという知見が得られた。

作曲データは完成HTMLのmusicScore、再生成用のPythonコードはmusicRenderSourceに収録した。後者はブラウザで実行されない保存用テキストであり、Python・NumPy・SciPy・FFmpegを使う別環境向けのコードである。音声の検証範囲は次節にまとめる。

12. 何を、どこまで確認したか

天体暦の補間差

保存点そのものではなく、別に照会した中間時刻のHorizonsと比較した。下の値は、それぞれの標本で得た最大位置差であり、年間全時刻での上界でも、実天体との絶対誤差でもない。

対象・方式標本数最大位置差
水星・試作12時間Hermite7301,253.647 m
水星・採用6時間Hermite2471.001 m
木星・試作12時間Hermite24527.491 m
木星・採用6時間Hermite2434.309 m
地球・採用12時間Hermite245.379 m
土星・採用12時間Hermite2415.159 m
水星・Chebyshev試作7300.1766 m

旧版と新版、方式間で標本数・標本時刻が異なる。この表から厳密な改善倍率や方式の順位を決めない。根拠:planet-validation.jsonplanet-chebyshev-validation.json

衛星最大位置差衛星最大位置差
1.527 mカリスト27.446 m
イオ33.940 mエンケラドゥス52.946 m
エウロパ55.959 mタイタン23.957 m
ガニメデ5.548 mトリトン4.030 m

衛星は各24点、合計192点。これは親惑星に対する相対位置の補間差で、親惑星の補間差を含めた太陽中心位置の保証ではない。根拠:satellites-summary.jsonと各衛星の取得・検証記録。

カメラの反転と、その後の測定

段階・条件記録された角度変化読み方
旧SLERP方式。木星→イオ、天体時刻1日/秒116.956° / 50 ms反転の発見例
別の開始時刻での独立再現、同じ1日/秒178.218° / 50 ms最短経路の分岐を再現
ロール分離後の中間版、1日/秒の試験群最大17.862° / 50 ms高速の天体運動への追従は残る
最終版。天体時刻30分/秒、手動旅とギャラリー「速め」を含む試験群最大4.787° / 50 ms最終コードで得た記録。最悪例はケレス→木星

会話内に残る実行結果と再現報告による。条件とコードが異なるので、これらを一本の改善率や同条件の性能曲線として扱わない。4.787°も人間の快適性を示す閾値ではない。

天体・移動の数値・状態試験

check-solar-v4.cjs は、実際のThree.jsの数学処理を使い、描画器・DOM・Canvasを代用品に置き換えたNode試験である。BGM追加前の光跡版で記録された結果は次のとおり。BGM追加時には天体・移動コードと天文データがその版から変わっていないことを別途照合した。

確認した項目結果・範囲
実装した姿勢行列10フィクスチャで最大要素差 約9.88×10⁻¹²
保存点での状態ベクトルの一致試した保存点の成分差0。中間時刻の精度は別途検証
移動中に動く目的地へ到着6経路の確認。別途、縦画面での衛星ラベルの経路も確認
俯瞰のフレーミング1440×900、393×851、851×393。6組の天体で球・環の外接半径の標本を投影
姿勢のフレーム間変化7,223個の飛行ステップを確認。自動到着時の最大姿勢差 約1.71×10⁻⁶°
経路と衝突補正後の位置の差最大約0.0000395 km。極小の丸め差を含み、物理的な衝突精度を意味しない
ギャラリー一時停止・再開・次へ・終了・年境界・1周終了から2周目への移行を確認
光跡方向の一致、描画・マスク命令、停止時の減衰、表示オフを確認
光跡版の配布HTML外部アセット参照0、IDの重複なし、3つの実行用scriptの構文を確認。BGM追加後の再確認は下表
記録の読み直しで分かったこと

試験結果の completedGalleryLoops: 2 は「2周完走」を意味しなかった。コードは周回番号が1から2へ変わるところまで確認している。正確な記述は「1周終了し、2周目へ移行」である。成功を示すフィールド名も、実際の判定条件と照合する必要がある。

BGMの符号化・ループ・操作状態

以下は保存済みの音源検証結果、コードレビュー、BGM追加時のモック試験・配布ファイル検査による。音楽の好ましさや、端末から出た音の評価とは区別する。

確認した項目結果証拠の範囲
長さ・形式48,000Hz、2ch、3,840,000フレーム、80秒完成FLACをFFmpegで復号したWAVを照合
符号化の保存性FLACの復号PCMが量子化後の元PCMと全サンプル一致16bit化より前の浮動小数点波形との一致ではない
サンプルのピーク約−6.79 dBFS保存波形が飽和していないことを確認。再構成後の真のピークや端末の歪みは未測定
全体のRMS約−21.50 dBFS左右全サンプルから算出。知覚的な音量指標LUFSではない
ループ境界の1サンプル差左 +0.010437、右 −0.000580正規化サンプル値。周辺の差分RMSに対し約1.09倍/0.12倍で、差はゼロではない
音源の状態管理遅延生成、デコード中の中止、連打、単一ソースとデコードの再利用、音量、非表示・復帰、中断・拒否からの再開、失敗後の再試行を確認check-music.cjs。AudioContextとDOMを代用した状態試験
フェードの設定時間定数と、停止中の再開前にゲインを0にする修正を確認ソースコードの確認。モック試験はフェード時間や開始値を直接assertしていない
BGM追加後の配布HTMLID重複なし、外部script参照なし、4つの実行用scriptの構文、内蔵音源・譜面・合成コードの一致を確認check-package.py。天体・移動コード、天文データ、組み込み依存コードも前版と一致

dBFSはデジタル信号の最大値を基準とする表記。表の音量は音源そのものの値であり、アプリの初期音量40%や端末側の音量を適用した出力の測定値ではない。ループ境界の比較は異常に大きな段差を調べる材料であり、不可聴性の証明ではない。

未実施:実ブラウザ/GPUの表示試験、実ピクセルでのマスク評価、Androidでの全画面動作確認、FPSやメモリの測定、長時間連続運転、操作性・酔いやすさの定量評価、BGMの実聴、実ブラウザでの音声出力、端末ごとの復号・再開・継ぎ目、音楽の主観評価。数値試験の成功は、これらの代わりにはならない。

13. 人間とAIの協働として持ち帰れること

今回、人間からの観察は、単なる好みの指定にとどまらなかった。「俯瞰が遠い」という指摘で、カメラが収めるべき対象が太陽系全体から2天体へ変わった。「視線も滑らかに」という指摘で、位置だけを検証していた設計から姿勢の連続性へ検証が広がった。

AI側では、データ取得、数式化、実装、読み取りレビュー、数値試験を分担した。ただし読み取りレビューだけでは、動く目標が180°をまたぐ反転を見つけきれなかった。フレーム間の姿勢差を測り、独立した条件でも再現して、初めて分岐の機構がはっきりした。

次にも使える進め方今回の具体例
音作りと統合を独立して進める譜面・合成音源の制作と、再生UI・状態管理を並行して進め、容量と長さを照合して統合
聴覚の代用品の限界を明示する波形・ピーク・符号化一致は機械的に確かめる。響きの好みと継ぎ目の聞こえ方は実聴に残す
感覚的な指摘を、調べられる量へ変換する「滑らかでない」から、角度変化・接続点の差分・到着時の姿勢差へ
読み取りと実行を、異なる証拠として使う構図やマスク式はレビュー、動的反転は実行で発見
確認結果に条件を添える時刻倍率、旅行時間、画面サイズ、標本数を残す
成果の文章も監査する「周回番号2」と「2周完走」、「補間差」と「実天体への誤差」を区別
次の人間の観察を具体的にする視線の振れ、文字の可読性、光跡の強さ、BGMの響きと継ぎ目、端末の負荷をそれぞれ確かめる

ここから得た実務上の知見は、実装を作ることに加えて、何を確かめればよいかを作り、その確認結果の意味も点検することにある。今回の制作だけから、人間とAIの協働方式一般の優越性を証明したとは言わない。

14. 残っている限界と、次に確かめるなら

モデルの限界

  • 太陽・8惑星・8衛星・3準惑星を収録する。全ての衛星、小惑星、彗星などを網羅していない。
  • 天体暦は2026年の収録範囲に限定。光の到達遅れや相対論的な飛行映像は再現しない。
  • 地球の姿勢はUT1観測・章動・極運動をそろえた精密地球姿勢ではなく、月もDEの高精度な姿勢モデルではない。
  • 太陽や巨大惑星の表面を剛体的に回すため、実際の差動回転や雲の動きを予報しない。
  • 地球以外のテクスチャ経度は厳密に較正していない。未収録の衛星表面は模式色。冥王星画像の黒い領域には観測の欠落がある。
  • 軌道ガイドはその瞬間の位置・速度とGMから作る接触楕円で、将来の正確な軌跡ではない。
  • 小国を検索できても、地図の縮約を超える地形精度は得られない。

BGMで残る確認

金属音の硬さ、残響と旋律の釣り合い、80秒の反復が鑑賞を邪魔しないかは未聴取である。ヘッドホン・スマートフォンのスピーカー・モノラル再生での印象も未評価。ロスレス形式は量子化後の波形を保つことを意味し、合成音の好ましさや実楽器への忠実度を保証しない。

次に優先する確認

  1. 実機での鑑賞:スマートフォンの縦・横、通常・全画面で、天体名と光跡が読みやすいか。
  2. BGMの実聴と復帰操作:音の強さ、つなぎ目、♪の連打、タブ切り替え後の立ち上がりを実端末で確かめる。
  3. 負荷の測定:起動時間、ピークメモリ、長時間のフレーム時間、BGMの有無によるCPU・電力の差。描画回数をFPSと読み替えない。
  4. 同条件の比較:固定した日時・経路・速度・画面比で旧版と新版を比較し、角速度と主観評価を併記する。
  5. 試験範囲の拡張:全ての天体間の組合せや極端な画面比、途中での操作変更を、必要な範囲から追加する。
  6. 天文データの拡充:対象年や天体を増やす際は、ファイル容量・補間間隔・比較標本を一緒に見直す。

これらは本書で整理した候補であり、今回すでに実施した作業には含めない。

15. 対象ファイルと根拠記録

後から別の版と混同しないよう、この記録が対象とする完成HTMLを次の値で識別する。

ファイルsolar-voyager.html
サイズ20,277,710 bytes
SHA-2564985ec682e65fcfe3c419394f436fb1174ecd20fe8aef98730834a5df770c75c
確認時点2026 09 07、光跡・連続した視線移動・オルゴール三重奏BGMを含む版
前版との対応

BGM追加前の光跡版:14,878,118 bytes。

SHA-256: 33d746c858d9dbda43886419869349c42739d6bfc1590dcca99ce44662f67eb2

今回のBGM追加版:20,277,710 bytes。天体・移動コードと天文データは前版と一致し、音源・音声処理・操作UI・説明・再生成用データが増えた。先行する数値・状態試験の条件を、新版で実測し直したものとは扱わない。

主要なローカル根拠の対応表

以下は開発時のファイル名であり、本書だけで元データ一式が再配布されるわけではない。今回の数値は、保存済みJSON、最終実装、会話内の実行出力を突き合わせて整理した。

内容根拠ファイル
最終の実行処理app.jsmusic.js、完成HTML
データ組み込みassemble-physics.pyphysics-data.jsonbuild.py
初期調査・近似軌道astronomy-parameters.jsonapp-body-data.json
惑星の補間検証planet-validation.jsonplanet-states.json
Chebyshev試作fit_planets.pytest_cheb.pyplanet-chebyshev-validation.json
衛星の取得・検証SATELLITES_README.mdsatellites-summary.json、各衛星の *-provenance.json*-validation.json
姿勢rotation-guidance.mdrotation-verification.jsonrotation-fixtures.json
地図・画像ASSET_SOURCES.mdCREDITS.md
経路の変遷flight-gallery.jspair-travel.jscinematic-flight.js
光跡travel-effects.jsapp.js
天体・移動の数値・状態試験check-solar-v4.cjs と会話内の実行結果
作曲score.json。HTML内のmusicScoreにも収録
合成・符号化render-music.pymusic-box-trio.flac。コードはHTML内のmusicRenderSourceにも収録
音源の数値検証render-stats.jsondecoded-check.wav
音声の操作状態・統合確認music.jscheck-music.cjscheck-package.py と実行結果

最終仕様は app.jsmusic.js と完成HTMLを優先する。初期の近似パラメータや説明、試作スニペットが残っていても、それをそのまま最終挙動と解釈しない。

16. 出典・素材

数値や素材の外部出典と、今回の実装で得た測定値を分けた。NASA/JPLの出典を挙げたことは、このアプリの表示や精度をNASA/JPLが保証したことを意味しない。

資料用途
JPL Horizons API状態ベクトル、天体指定、中心・時刻系・補正条件
JPL Approximate Positions初期の近似軌道要素調査
JPL Planetary Physical ParametersSatellite Physical Parameters半径・周期など
NAIF pck00011PCK Required ReadingIAU姿勢モデルと仕様
NAIF gm_de440接触楕円を作る重力定数
NASA Blue Marble地球の2048×1024画像。NASA/GSFC、Reto Stockli、Earth Observatory
Natural Earth Admin 0 Countries利用条件国境・日本語名。Public domain
Solar System Scope TexturesCC BY 4.0太陽・惑星・月・土星環の画像。色調整や補間を含む可視化素材
NASA Pluto Global Color Map冥王星画像。NASA/JHUAPL/SwRI
MDN AudioBufferSourceNodeメモリ上の音源、ループ、startの扱い
MDN decodeAudioData()復号とコンテキストに合わせたサンプルレート変換
「光の間を渡る」本アプリのためのオリジナル譜面・合成音源。既存録音の取り込みはない
Three.js r160、pako描画と圧縮データの展開。各ライセンスはアプリに同梱

本書の補間・角度分岐の図は説明用に作成した。BGMの波形図は完成FLACの復号データを描いたものである。いずれもアプリのスクリーンショットや端末出力の録音ではない。本書自体も画像・スタイルを内蔵しており、出典リンクを開く場合を除き外部通信は不要。