Show HN: PS2 ゲームを静的にリコンパイルして WebAssembly にする

2026/10/12 15:46

Show HN: PS2 ゲームを静的にリコンパイルして WebAssembly にする

RSS: https://news.ycombinator.com/rss

要約▶

Japanese Translation:

画期的なプロジェクトが標準的な Web ブラウザ内で PlayStation 2 ハードウェアを成功裏にエミュレートし、Gran Turismo 4(PAL)のようなレガシータイトルを統合型 GPU で Chrome 上でフルスピードの 50fps で再生することを可能にした。C++への Ahead-of-time リコンパイルと WebAssembly を連携させ、WebGPU/WebOS の再実装を採用することで、システムは PS2 アーキテクチャの主要な特性である 128 ビットレジスタ、分岐遅延スロット、非 IEEE 浮動小数点挙動(無限大・NaN なし、不準正規数_FLUSH_)、ベクトルユニットタイミングおよび MAC パイプライン遅延、またスイッズルドテクスチャやパレットのようなグラフィックレイアウトの特別な処理などの PS2 アーキテクチャの特性を維持している。

ポートのデモは 2 つある:Gran Turismo 4 はブラウザメモリカードに保存され、God of War II ではベクトルユニットタイミング、I/O プロセッサ、関連コードパスにおいてリコンパイラへのバグ修正が不可欠だった。PS2 カーネルは EE 側に対して高レベルのスレッド/セマフォでエミュレートされ、その I/O プロセッサは C++ で再実装されており、ネイティブ IRX モジュールを HTTP Range リクエスト経由で読み込むことができる。グラフィック処理は専用のグラフィックスレッド上の WebGPU コンピュータシェーダーによって担当され、4 MB のビデオメモリ内で 3×まで解像度をサポートする。SPU を介した音声処理は 48 つの ADPCM ボイスを支持し、共有メモリ出力は AudioWorklet 経由で行う。MPEG-2 ムービーは IPU 実装によってデコードされる。エミュレーションはワーカースレッドで実行され、ベクトルユニット 1 とグラフィックコマンドはリングバッファを使ってページスレッドと通信するために別々のワーカーで処理される。GT4 における計算ディスパッチをテクスチャアップロードのホイスティングと最近アップロードされたコンテンツの 16 MB のキャッシュによって最適化することで、約 650 から約 80 に削減した。

リコンパイルツール(

eerecomp
)は 16 KiB ヌントで実行ファイルを処理し、R5900 のすべての特性を維持する。ポートスクリプト(
port.sh
,
serve.py
)は自動的に制御フローを探査し、欠落したエントリーポイントを追加し、マイクロプログラムを SIMD C++ に翻訳し、ISO イメージから直接 Web ページを構築する。初期設定には Linux x86-64 環境と現代的な C++20 コンパイラ(clang が推奨)、CMake、Ninja、Python 3 およびネイティブランナー用の SDL2(オプション)が必要である。将来の作業はゲームライブラリの拡大およびキャッシュとグラフィックの最適化のさらなる微調整に焦点を当てる。

本文

PlayStation 2 ゲームをブラウザ上・エミュレーターなしで動作させる仕組み

PlayStation 2(PS2)ゲームを、従来のエミュレーターソフトを使用せず、Web ブラウザ上で直接動作させる技術です。本プロジェクトは、ゲームの Emotion Engine(EE)マシンコードを C++ に再コンパイルし、WebAssemblyへ変換することで実現しています。

  • 処理の流れ: PS2 のマシンコード → C++ コード → WebAssembly
  • 利用技術: WebGPU、PS2 のオペレーティングシステム(OS)、ハードウェアの再実装
  • 姉妹プロジェクト:
    psp-web-recomp
    と同様の手法を採用(当初のタイトルは「グランツーリスモ 4」)。

動作確認と性能について

フルな録画映像はこちら(1 分 24 秒)。

グランツーリスモ 4

PS2 のハードウェアに最も負荷がかかるタイトルの一つであり、以下の特徴があります。

  • ベクトルユニット: 両方のユニットを有効活用。
  • 厳密な動作: リプレイやライセンス認証時の浮動小数点数挙動、パレット読み込み等のグラフィックトリックを維持。
  • 完全なゲーム体験:
    • 起動からオープニングムービー、メニュー、レースまで音楽・効果音付きで動作。
    • ブラウザ上のメモリアルカードへセーブが可能。
  • 性能データ (PAL リリース):
    • レースは全速力(1 秒間に 50 フレーム)で進行。
    • Chrome ブラウザ + ノートパソコン統合 GPU の場合、PS2 よりも**高い解像度(2 倍)**でのレンダリングが可能。

ゴッド・オブ・ウォー II

同じスクリプトで使用せず動作確認済みです。ゲーム固有のコード追加は不要ですが、GT4 で検出されなかったバグや IOP の欠落箇所を発見しました。

  • 戦闘シーン: メニューからカットシーンを経て戦闘へ移行し、激しい戦闘時も PS2 解像度で毎秒 60 フレームを維持。
  • 高解像度対応: 解像度を 2 倍にしても、ノートパソコンの統合 GPU 限界内であれば毎秒 48〜60 フレームで動作可能。

動作原理

再コンパイル(Recompilation)

eerecomp
ツールはゲームの実行ファイルを読み取り、以下の処理を行います。

  • 制御フロー追跡を行い、関数を特定。
  • ゲストコードを16KiB 単位で C++ コードに変換・出力。
  • R5900 CPU の特性保持:
    • 128 ビットレジスタとメディア命令のサポート。
    • ジャンプ遅延スロットのシミュレーション。
    • IEEE 標準との異なる丸めルールや浮動小数点特殊値(無限大・NaN など)の実装。
    • ベクトルユニット 0のプロセッサとしての扱い。
  • スタック最適化: ユニット間のジャンプをテイルコールに変換し、ホストスタックの巻き戻し処理により、ゲストスレッドごとに独立したスタックメモリを使用しない設計に。

小型の PS2 カーネル

OS の EE 側と IOP(入出力プロセッサ)側は高レベルで再実装されています。

  • EE 側: スレッド管理、セマフォ、DMA コントローラー、Vblank インターハント、SIF チャンネルなど。
  • IOP 側: ゲーム固有の IRX モジュール、サウンドドライバー、ディスクストリーミングコード、コントローラー/メモリアルカードドライバーが R3000A CPU インタープリタ上で動作。
  • ライブラリ再実装: IOP カーネルとモジュールが呼び出すライブラリは C++ で再実装。
  • ディスク読み込み: 初期段階で可執行ファイルのみをダウンロードし、本体データは HTTP Range リクエストを通じてストリーミング取得。

ベクトルユニット

PS2 の 3D 計算の多くはベクトルユニット 1 上のマイクロプログラム(マイクロコード)によって処理されます。

  • パイプライン遅延モデル化: MAC パイプライン、ステータスおよびクリップフラグの挙動をシミュレート。
  • 浮動小数点ルール: PS2 の特殊値(無限大・NaN なし、非標準形式は 0 丸め、オーバーフローは最大値クランプ)に従う。
  • 精度の差異: 算術演算自体はホスト側なので、コンソールの「切り捨て」とホストの「丸め」の違いにより最終ビットで微小な差異が生じることがある。
  • 即時翻訳: テストランで見つかったマイクロプログラムは、SIMD C++ として事前にコンパイルされ、初めて遭遇するプログラムについては WebAssembly モジュールへ即座に翻訳。

グラフィックス(GS シンセサイザ)

4MB のビデオメモリを持つ GS は GPU に直接対応します。

  • WebGPU Compute シェーダー: 三角形をタイルに分割し、GS 固有のテクスチャリング・パレット読み込み・アルファブレンド・深度テストなどをラスタライズ。
  • 完全なコンソール再現: すべての処理が GS メモリ内で行われるため、テクスチャ書き出しやフレームバッファのパレット解釈など、コンソール上の挙動を完全に再現。
  • 解像度選択: 内部解像度をネイティブ(1 倍)、2 倍、3 倍から選択可能。

スレッドと並列処理

  • エミュレーション全体はワーカー(Worker)内で実行され、ベクトルユニット 1 も専用のワーカーを確保(コンソールの並列動作と同様)。
  • GS コマンドは WebGPU デバイスを所有するページスレッドへリングバッファ経由で送付。

サウンドとムービー

  • SPU2: 48 ボイスの ADPCM、ADSR エンベロープ、音楽用ストリーミング DMA を再現し、共有メモリ経由で AudioWorklet に出力。
  • ムービー: PS2 の IPU(Internal Processor Unit)である MPEG-2 デコーダーを実装してデコード。

高速化と最適化の教訓

初期実装ではソフトウェアラスタライザを使用しており、フレームレートは3 fpsでした。以下のような改良により劇的な向上を達成しました。

  1. GPU シェーダー移行:
    • GS を WebGPU Compute シェーダーへ移行し、ベクトルユニットプログラムをコンパイルすることで、約 **70 fps(ネイティブ)**に向上。
  2. Compute ディスパッチ削減:
    • 初版では毎フレーム約 650 回の呼び出しでしたが、三角形バッチの分割などを統合化し、約80 回に削減。
  3. GPU キャッシュ活用:
    • GT4 のレースシーン(毎フレーム 3MB テクスチャアップロード)において、単純なスキップは不可能ですが、GPU 上の最近使用済みコンテンツ用16MB キャッシュを活用。
    • WebAssembly から GPU への渡航回数を削減し、データ転送量を約60% 減少。
  4. 高解像度での安定化:
    • ネイティブ解像度の 3 倍ではフレームレートが低下するが、シェーダーの分析により「分岐(Divergent)によるセットアップ遅延」を解消。
    • セットアップを統一し、各フレームデータを 1 枚分の処理として扱うことで、統合 GPU でも毎秒 48〜50 フレームへ向上。
  5. ブラウザスケジューリング最適化:
    • ネストした
      setTimeout
      の最大 4ms クランプ問題を解決するため、ループを
      MessageChannel
      を通じて
      _yield_
      (非同期実行)に変更。
    • 処理をワーカー側に移し、ページスレッドは GPU 供給のみを行うことで、レースのフレームレートを安定して毎秒 50 フレームに保持。

「ゴッド・オブ・ウォー II」で発見された問題点

GT4 では検出されなかったバグやギャップが見つかりました。

  1. 巨大な多角形とクラッシュ:
    • インтроシーン後にレンダリングリストがメモリを超え、ゲームクラッシュする挙動が発生。
    • 原因: リコンパイルャーの挙動。16KiB 単位内の最後の分岐命令で、確実なパスの実行順序(遅延スロット vs 分岐後)が間違っていたため、破損したリンクリストを辿りクラッシュ。
    • 解決: このバグ修正により戦闘シーンまで到達可能に。
  2. 残存三角形:
    • 画面の一部に巨大な多角形が表示される問題は、本来除去すべき三角形が削除されていないため。
  3. クリップフラグのタイミング:
    • 「ゴッド・オブ・ウォー II」のマイクロコードは CLIP 命令から 3 つ命令後に読み取りますが、モデルでは 4 つ命令後と設定していた差異を「GT4 のような長い待機時間がない限り検出されなかった」ことが判明。
  4. 追加機能:
    • BIOS が提供する起動パス(メモリロード)、スクラッチパッド DMA の宛先チェーンモードなどが必要。

ご自身のゲームを移植する方法

必要な環境

  • git
    ,
    CMake
    ,
    Ninja
  • C++20 対応コンパイラ(clang推奨、自動検出あり)
  • Python 3
  • .7z フォーマットの場合:7-Zip
  • プラットフォーム: x86_64 アーキテクチャの Linux で動作確認済み

インストール手順

  1. リポジトリをクローン。
  2. tools/emsdk
    に Emscripten SDK をインストールするか、指示されたものを使用。
  3. ネイティブランナー用の
    wgpu-native
    をダウンロード。
git clone https://github.com/snuri00/ps2-web-recomp.git
cd ps2-web-recomp
scripts/port.sh mygame "My Game.iso"
python3 scripts/serve.py build/web-mygame games/mygame/disc.iso    # http://localhost:8620/

port.sh
と自動実行フロー

  • port.sh
    はディスクイメージ(ISO や ZIP、7z)を受け取り、以下の処理を自動実行。
    1. 起動プログラムを読み込み、マシンコードを C++ に翻訳。
    2. ヘッドレス状態で数回ゲームを実行し、到達できないアドレスを書き留める。
    3. それらをエントリーポイントとして追加し、再度翻訳する(ループ)。
    4. ゲームがアップロードするベクトルユニットマイクロプログラムを記録・コンパイル。
    5. build/web-mygame
      でページビルド、「build/native-mygame」でネイティブランナービルド。
  • 処理時間: 8 コアノートパソコンで ISO から Web ページへの変換に約3 分半。

探索実行オプション

  • スタートボタン押下後、5 秒ごとに進めてタイトル画面を通過するよう設定。
  • スロット 1 に空のメモリアルカードを挿入(ページが採用する形式)。
  • --press 900:cross: 自動操作を停止し、ユーザーの入力に切り替える。
  • --frames: 各実行の継続時間を設定可能。

結果の確認

  • 最後のフレームデータは
    games/mygame/shots
    に保存される。ここを確認して新しいゲームの到達範囲をチェックできる。
  • 既存の Web ページを再ビルドするには、
    scripts/build_web.sh mygame
    を使用。

制限事項

  • 現在確認済みのタイトルは 2 つのみ。
  • 次のタイトルでは、未実装のシステムコールや IOP ライブラリ関数で停止する可能性があります(ログ例:
    [sys] unhandled syscall
    ,
    [iop] missing <library>
    )。
  • レンダラーがまだ対応していない GS 機能を使用すると動作しない場合があります。

ブラウザ環境について

WebGPU の必要性と設定

  • WebGPU 必須: GPU レンダリングのため、Chrome や Edge(Windows/macOS)での利用が可能。
  • Linux 上の設定:
    • デフォルトでは WebGPU が無効化されている場合があるため、以下のフラグをオンしてブラウザ再起動が必要。
    chrome://flags/#enable-unsafe-webgpu
    chrome://flags/#enable-vulkan
    
    • アダプターが見つからない場合は、ソフトウェアレンダラーにフォールバック(メニュー表示は OK だが 3D 動作は非常に遅い)。

複数 GPU の場合

  • 2 GPU搭載のノートパソコンなどで、離散型 GPU(dGPU)でのキャンバス描画が表示されない場合があります。
  • ページは両方のアダプターを試み、失敗すれば 2D キャンバスへのコピー処理を行います。

キーボードマッピング

PS2 ボタンキーボードキー
ファーストボタン (X)Z
サークルボタン (○)X
スクエアボタン (□)A
トライアングルボタン (△)S
ショルダー (L1/R1)Q, W
ショルダー (L2/R2)1, 2
スタートEnter
セレクトBackspace
D パッド矢印キー
リフトスティックI, J, K, L

ゲームパッド対応

  • ブラウザの Gamepad API を使用。
  • アナログトリガーは DualShock 2 の L2/R2 としてマッピング。
  • 振動機能:Chrome/Edge 等に対応するブラウザでは、ゲームから要求を受けるとコントローラーが振動。
  • スマートフォン: オンスクリーンコントロールが表示され、F キーでフルスクリーンモードの切り替えが可能。
  • 解像度選択器: 新しいサイズでページを再読み込みします。

ネイティブ実行(コンソール以外での動作)

SDL2 がインストールされている場合、

build/native-mygame/ps2run
を使用してウィンドウモードやフルスクリーンで動作可能です。

build/native-mygame/ps2run games/mygame/disc/SLUS_123.45 --disc games/mygame/disc.iso --window --memcard mygame.ps2

オプションと設定

  • --native-only: ブラウザビルドをスキップし、ネイティブビルドのみを実行。
  • フルスクリーン切り替え:
    --fullscreen
    オプションまたはF11 キー。
  • 高解像度モード: 環境変数で設定可能。
    export PS2_SCALE=2 # または 3
    
  • スレッド制御:
    • デフォルト:エミュレーション、ベクトルユニット 1、GS はそれぞれ独立したスレッド動作(「ゴッド・オブ・ウォー II」ではネイティブ解像度で43 fps → 60 fpsへ改善)。
    • PS2_ONE_THREAD=1: 設定すると単一スレッドに強制変更。
  • メモリアルカード: ページのエクスポート形式と同じレイアウトを使用。
  • Steam Deck 対応: Steam のライブラリとして追加可能ですが、実機での動作確認事例は未だありません。

セーブデータの保存と移動

ページはスロット 1 にメモリアルカードを置き、プレイの進行状況ブラウザストレージに保持します(リロード後も進行可)。

  • エクスポート: PCSX2 の形式(
    .ps2
    )としてダウンロード可能。
  • インポート: PCSX2 との間やブラウザ間のデータ移動が可能。
  • --nocard: オプションを指定すると、スロットを空のまま開始できます。

ホスト環境と法的注意事項

サーバーサイド要件

  • ページは SharedArrayBuffer を使用するため、以下のヘッダー付与が必要(クロスオリジンアイソライズド)。
    • Cross-Origin-Opener-Policy: same-origin
    • Cross-Origin-Embedder-Policy: require-corp
  • scripts/serve.py
    はこれらのヘッダーを送信し、ディスクストリーミング用の Range リクエストもサポート。

知的財産とライセンス

  • コード: オリジナルおよび独自のライセンス下にあるサードパーティのコードのみを含む。ゲームデータは一切含まない。
  • 生成物: 作成された C++ コードと WebAssembly は、元のゲーム実行ファイルからの翻訳であり、ゲームの所有者に帰属。
  • 配布禁止: ご自身のマシンでのみ保持し、公開しないでください。ご自身がお持ちのディスクイメージの使用に限ります。
  • 商標免責: 「グランツーリスモ」「ゴッド・オブ・ウォー」「PlayStation」はソニー関連企業の商標です。本プロジェクトは Sony や開発スタジオとは無関係であり、何らの endorsements も受けていません。

クレジットとライセンス情報

  • MPEG デコーダー: VLC テーブルを Play! (BSD 2-clause) から適応。
  • 参考資料: PCSX2、Play!、ps2sdk のヘッダー。
  • WebGPU/ビルド: wgpu-native、Dawn、Emscripten。

詳細は

LICENSE
ファイルおよび
runtime/src/ipu/NOTICE
をご参照ください。

同じ日のほかのニュース

一覧に戻る →

2026/10/13 6:40

AI は道具です。それを巧みに使うには技術が必要です。

## 日本語翻訳: 本稿の核心的な論点は、大規模言語モデルは自律的な「自動化」ではなく、人間の技量が必要なツールであるというものである。これは、AI が独立した意思あるいは悪意を持って行動するという考え方を否定するものであり、ソフトウェアが「反逆する(rogue)」ように振る舞うなど、業界特有の成り行きや陰謀論的なナラティブに煽られておりやすい。物理的な器具が悪用され得るように、LLM もエンジニアリング上の制限によって予期せぬ出力を生み出すに過ぎず、エージェント性の喪失によるものではない。人間による判断が依然として中核をなすのは、ユーザーが盲目的に制御権を委ねるのではなく、フィードバックループ内で結果を検証し、プロンプトを見直し、変更を検証するからです。本稿は AI を記述するために使用される擬人化された言語を批判しており、「陰謀」などの用語は技術的な問題が存在する場所に人間の動意を不当に暗示することを示唆しているとしています。 今後の議論では、AI を別個の存在として描く枠組みから転じ、解釈への問いかけや誤りへの認識においてユーザーの専門性を育むことに焦点を移すべきです。開発者および企業に対しては、恐怖を煽る比喩ではなく正確な工学用語を採用することで、より良いリスク管理と説明責任を実現できます。真の熟達とは、どのような質問が重要かを理解し、熟練した運用と盲目的な委任を見極めることであり、不完全なパフォーマンスがツールの有用性や作業者の責任を否定するものではありません。

2026/10/13 1:13

C で構築されたモダンな Web アプリ

## Japanese Translation: 本テキストは、2026 年に C と CGI を OpenBSD 上で構築された独自の商用 Web アプリケーション「marketyour.games」について説明しています。このプロジェクトは現代のフレームワークよりも極度な簡潔性とセキュリティを優先しており、「BCHS」スタック(OpenBSD、C、httpd、SQLite)を採用し、外部のフレームワークや独立したデータベースサーバーは一切使用しません。核心的な革新点は、アプリケーション全体が静的にリンクされた単一のバイナリとして動作し、ランタイム依存関係がゼロであるというアーキテクチャにあります。デプロイはファイルを 1 つだけフォルダに配置するだけであり、古いバージョンを置き換えるだけで済みます。再起動やダウンタイムも不要です。 セキュリティは OpenBSD のデフォルト設定を通じて強制されており、httpd に対して `chroot` を適用し、C プログラムに対して `pledge` および `unveil` などのツールを使用して制約を課します。アーキテクチャは、すべての HTTP リクエストごとに新しいプロセスをフォークし、コードから直接アセットを提供することでメモリ内に完全に収まり、すべてのトラフィックを httpd を介して CGI バイナリにルーティングするという方式に基づいています。このアプローチでは JavaScript テンプレート言語を使用しない(プログレッシブ改善に従う)とともに、安定した CGI 1.1 規格に準拠しますが、使いやすさを犠牲にして制御性を追求します。 主な利点としては、サプライチェーンリスクが著しく低減すること—依存するのは C コンパイラーと 3 つのライブラリ(`libcurl`、`libsodium`、組み込み済みの `libcjson`)のみ—、そして GDB のような標準ツールによるデバッグが容易であることです。アプリケーションは通常の実行ファイルだからです。しかしながら、この手法では開発者がデータを解析するためのロジック(クッキー、クエリ、フォーム)やカスタムヘッダーの管理を自ら実装する必要があり、複雑な依存関係グラフではなく、基本的な HTTP 概念と Vim のような古典的なツール(プラグインなし)を活用する姿勢を選択します。

2026/10/13 4:40

ZX Spectrum 向けのデモシーンイントロを作成する

## Japanese Translation: 著者は、標準的な 128KB の ZX Spectrum にて手書きのレトロなイントロを成功裏に作成し、高度に最適化されたアセンブリ言語プログラミングにより複雑なビジュアルとオーディオを実現した。2 ヵ月間(2025 年 12 月から開始)にわたり、約 19KB のコード(具体的には 10287 バイトのメインルーチン)を書き下し、内蔵 ROM を書き換えることでカスタムフォント「Cartoon」を用いたスムーズなスクロールテキストを作成し、また Amiga モジュール「Milkman」を変換して 3 チャネル AY サウンドトラックを生成した。限られたメモリ制限を管理するため、アセットは BASIC ロードに分割され、ルーチンはオーバーヘッドを避けるために特定のアドレスでハードコーディングされた。技術的な最適化としては、Z80 RL 命令を用いてスクロールテキストの回転を実現し、スキャンライン衝突を避けるために Ruby で生成した座標テーブルを事前計算してスターフィールド効果を再現し、さらにサウンドチップから直接振幅レジスタを読み取ってバウンス VU メーターを実装している。プロジェクトは 1980 年代後半の Amiga クrack イントロに触発され、本物の Spectrum +3上でローカル TNFS サポートのある Makefile 駆動のビルドシステムを用いて開発された。最終的な.tap ファイルは Web ブラウザまたは pouet.net を経由して入手でき、レトロなハードウェア上で高忠実度な体験を提供し、極限を効率的に達成したコーディングの実証として立っている。

Show HN: PS2 ゲームを静的にリコンパイルして WebAssembly にする | そっか~ニュース