2026年1月に「山手線 3Dジオラマ」を公開してから、気がつけば3ヶ月が経とうとしています。
公開直後の慌ただしい日々、ユーザーからの予想外の反応、途中で心が折れかけた週末など、振り返ると本当に色々ありました。
この記事では、ここまでの運営を正直に振り返り、何が上手くいって何が失敗だったのか、そして今後どうしていきたいのかを書き残しておきたいと思います。
アクセス数と反応の推移
公開初日のユニークユーザーは、正直ほとんどゼロに近い数字でした。SNSで告知した直後は数十人が見てくれましたが、翌日には1桁に戻っていました。「個人開発を公開したからといって、誰も見に来ない」という現実を、公開3日目あたりで痛感しました。
転機が訪れたのは公開から2週間後、ある鉄道系のアカウントが「これはすごい」と引用RTしてくれた日です。その日のPVはそれまでの100倍以上に跳ね上がり、夜中にアクセスログを眺めながら、嬉しさと「サーバ大丈夫か?」という不安が入り混じった奇妙な感覚を味わいました。CloudFrontのおかげでサーバは落ちませんでしたが、AWSの転送量グラフがいきなり垂直に立ち上がったのを見て、冷や汗が止まらなかったことを覚えています。
その後も、鉄道系YouTuberの動画で紹介されたり、技術系のまとめサイトに載ったりしながら、少しずつ安定的なアクセスを積み上げてきました。現在は1日あたり数千PVで推移しており、個人開発サイトとしては十分すぎる結果だと思っています。
ちなみにバズった当日、私はちょうど別件で外出しており、駅のホームでスマホを見ていたら通知が鳴り止まなくなって青ざめました。家に帰ってAWSのダッシュボードを確認するまでの電車の30分は、手汗が止まらない時間だったのを覚えています。結果的にはCloudFrontが全部吸収してくれましたが、もし原点から自前配信していたらと考えるとゾッとします。事前に「トラフィックの波」を織り込んだ設計をしておくことの重要性を、身体で理解した瞬間でした。
想定外だった反応
運営していて最も驚いたのが、海外からのアクセスが思った以上に多かったことです。Google Analyticsで見ると、アメリカ、ドイツ、台湾、香港、オーストラリアからのアクセスが一定数あり、「Yamanote Line」で検索して辿り着いてくれる方も少なくありません。
公開当初は完全に日本語UIだったのですが、海外のTwitterで「english please」というリプを見かけた時に、初めて「あ、英語対応しなきゃ」と意識しました。そこから簡易的な英語UIを追加したところ、海外からの滞在時間がぐっと伸び、「Japan trip memory」「I want to go back to Tokyo」といった温かいコメントをいくつもいただくようになりました。
日本の鉄道ファンだけではなく、かつて東京に旅行した海外の方にとっても懐かしさを感じる体験になっているのだと知り、作って良かったと心から思いました。
もう一つ意外だったのが、お子さん連れの保護者層からの反応です。「5歳の息子が毎日遊んでいます」「電車好きの娘がマスコンに夢中です」というDMをいただくたびに、ターゲット設定もなく作り始めたこのサイトが、想像しなかった層に届いていることを実感しました。
この反応を受けて、私は急いで「うっかり間違って課金ポップアップが出ないか」「刺激的な広告が流れないか」を見直しました。AdSenseの表示カテゴリにフィルタを設定し、アダルトやギャンブル系の広告を明示的にブロックする作業を、公開から1ヶ月ほど経った時点で慌てて実施しました。子どもが触る可能性を織り込んだ設計を最初からしていれば、と反省した出来事です。
技術的に良かった選択
MapLibre GL JSの採用
一番満足している技術選択は、地図エンジンにMapLibre GL JSを選んだことです。最初はGoogle Maps APIやMapbox GL JSも検討しましたが、OSSで自前ホスト可能、スタイルカスタマイズが自由という特徴が個人開発とマッチしました。3Dモデルを配置するための CustomLayerInterface も用意されており、Three.jsとの統合も比較的素直に書けました。
特に良かったのは、ライセンスコストを気にせず使える点です。アクセスが増えてもAPI呼び出し課金が発生しないため、精神的な余裕を持って運営できています。
S3 + CloudFrontの構成
バックエンドを持たない純粋な静的サイト構成も、結果的に正解でした。AWSの請求は月数ドル程度に収まっており、バズって数千PVが来ても全く慌てる必要がありませんでした。
もしEC2+Node.jsのような構成にしていたら、スケーリングやOS更新の対応で開発に使う時間が削られ、ここまで機能追加できていなかったと思います。個人開発では、運用負荷を下げる技術選定が最優先だと強く感じました。
余談ですが、公開してすぐにCloudFrontのキャッシュ料金を気にして「キャッシュTTLを短く」設定してしまい、かえってオリジン(S3)へのリクエストが増えて請求が倍になったという失敗もやりました。キャッシュヒット率のモニタリングを怠った結果で、今は CloudWatch Metrics でヒット率を週次で確認するようにしています。
後悔している選択
PLATEAU LOD2への初期投資
最初の頃、私はPLATEAUのLOD2(屋根形状まで再現したモデル)を全区間で表示するという野望を持っていました。しかし実際にやってみると、モデルが巨大すぎてモバイルで動かず、結局LOD1(立方体)に退化させる羽目になりました。
このLOD2対応に費やした2週間は、今振り返ると完全に無駄でした。もっと早く「モバイル実機で動くか」を基準に決定していれば、最初からLOD1で作って他の機能に時間を使えたはずです。「理想の品質」より「実機で成立する品質」を最初に握るべきだったというのが最大の反省点です。
// 初期のLOD2ビルダー(結局お蔵入り)
class LOD2BuildingLoader {
async load(geojson) {
// 屋根の傾斜まで計算する複雑な処理
const roofs = this.extractRoofShapes(geojson);
// ...2000行ほどのコードを書いたが、動作が重すぎて未使用
}
}
独自フレームワーク化の誘惑
開発中盤、「どうせなら複数路線に流用できるフレームワークにしよう」と思い立ち、独自の抽象化レイヤを作り始めました。LineSimulator、TrainController、StationManager といったクラス群を設計したのですが、単一路線しかないのに過剰な抽象化をした結果、コードが無駄に複雑化してデバッグが苦痛になっていきました。
巻き戻し後のコードは、恥ずかしいくらい素朴になりました。
// 巻き戻し後の「素朴な」走行ロジック
function updateTrain(dt) {
const train = yamanoteTrain; // 1つしかないのでグローバル
train.speed += (train.throttle - train.brake) * dt;
train.dist += train.speed * dt;
const posRot = yamanoteLUT.getPosRot(train.dist);
renderTrain(posRot);
}
結局、この抽象化は一週間ほどで巻き戻し、「山手線専用」の素朴なコードに戻しました。YAGNI(You Aren't Gonna Need It)の原則を、私は身をもって学んだのです。将来必要になるかもしれない柔軟性より、今動くシンプルなコードのほうが常に価値があると痛感しました。
巻き戻しの途中、Gitのブランチを切り間違えてmasterに直接revertコミットを積んでしまい、一時期本番から機能が消えるという二次災害も起こしました。幸い1時間ほどで元に戻しましたが、焦って作業するとまたミスが連鎖することを学び、「落ち着いてからrevertする」「少なくとも一晩置いて判断する」を自分ルールに加えました。
個人開発だからこそ辛かったこと
技術面よりも、むしろモチベーション管理のほうが難しかったと正直に書いておきます。
公開後2週間ほどは興奮でエンジンがかかっていましたが、バグ報告への対応が落ち着くと、急に「で、これから何を作ろう?」という虚無感が襲ってきます。仕事なら上司や同僚がいて締切も設定されますが、個人開発はすべて自分次第。平日の夜や週末に机に向かうかどうかが、完全に自分の気分で決まります。
土日がすべて開発で消えていった3月、ふと「これって何のためにやってるんだっけ」と手が止まった瞬間がありました。その時、Twitterで「毎朝通学前にこのサイトを見て電車気分を味わっています」という小学生(のお母さん)のDMが届き、誰かの日常の一部になっているという感覚に救われました。以後、このメッセージを壁紙にしてPCを開くようにしています。
もう一つ辛かったのは、一人で技術判断を下す重さです。「この設計で本当に良いのか」「この依存ライブラリは3年後も生きているか」を誰にも相談せず決める場面が多く、夜中にググりながら不安になることもしばしばありました。エンジニアコミュニティやDiscordで壁打ちしてくれる友人を見つけてからは、だいぶ精神的に楽になったと思います。
また、個人開発は「完了」の定義が曖昧なのも悩ましいポイントです。会社のプロジェクトなら要件を満たした時点でリリース、という明確な区切りがありますが、自分のプロダクトは作り込もうと思えば永遠に作り込めてしまいます。結果的に、「週末に作業する時間は最大6時間まで」「平日は2時間を超えない」と自分に制約を課すようにしました。制約を設けることで、残りの時間を読書や散歩に使えるようになり、バーンアウトを避けられている実感があります。
今後の展望
向こう半年〜1年で挑戦したいことは以下の通りです。
複数路線化:まずは中央線快速、そして大阪環状線に広げたいと思っています。ただし前述の通り、早すぎる抽象化はしないつもりです。2路線目を実装しながら、共通化できる部分だけ少しずつ抜き出す方針です。
時刻表連携:オープンデータで公開されている時刻表情報を取り込み、「今この時刻に走っている電車を表示」する機能を作りたいと考えています。運休情報の取り込みまでは重すぎるので、まずは静的な平日ダイヤ表示から始める予定です。
UGCの可能性:これは夢物語に近いですが、ユーザーが独自の路線をYAMLなどで投稿できる仕組みがあると面白いと思っています。ただ、モデレーションの負荷を考えると一人では厳しく、「いつか誰か手伝ってくれたらやる」くらいのスタンスで温めています。
同じように個人開発する人へ
最後に、もしこの記事を読んでいるあなたが、何かを作ろうか迷っている個人開発者なら、一言だけ伝えさせてください。
「とにかく公開してしまう」ことが、何より大事です。
私のサイトも、公開前は「バグが多い」「UIが雑」「PLATEAUが重い」と自分で感じて、公開を何度も先延ばしにしました。しかし、実際に公開してみると、想像していなかった角度からのフィードバックに溢れ、それがまた次の改善への燃料になります。
完璧なものを待っていたら、永遠に公開できません。「動く最低ライン」を決め、そこに達したら勇気を出してpushする。それが、個人開発を続ける一番のコツだと、3ヶ月経った今、自信を持って言えます。
まとめ
技術的な反省も、運営の苦労も、色々ありましたが、公開して良かったと心から思える3ヶ月でした。
このブログでは今後も、淡々と開発記録を積み上げていくつもりです。もし同じように個人開発をしている方がいれば、ぜひお互いに励まし合っていきましょう。
これからも「山手線 3Dジオラマ」をよろしくお願いします。