深掘り

WebAssembly 解説: 解決する問題と限界

koboshiCo-founder
·3 分で読めます
WebAssembly 解説: 解決する問題と限界
要約

WebAssembly は JavaScript の代替ではない。C、C++、Rust などからコンパイルしたコードをブラウザでネイティブに近い速度で動かせる、コンパクトなバイナリ命令形式だ。本記事では、従来の Web アプリの限界、WebAssembly が解決すること、実際のトレードオフ、一般的なユースケース、そしてクライアントサイドでの HEIC 変換がなぜこれなしでは不可能かを解説する。

HEIC 写真をブラウザのコンバーターにドロップしてみる。そのファイルは、HEVC でエンコードされた画像データを入れたコンテナーだ。4K 動画と同じ H.265 圧縮方式を使っている。ブラウザには HEVC デコーダーが組み込まれておらず、それを JavaScript から一から書くのは遅く、脆弱で、巨大になる。実際に必要なデコーダーは、strukturag がメンテナンスする C++ ライブラリの libheif だ。WebAssembly は、その C++ デコーダーをブラウザのタブ内で動かすための仕組みだ。

この例が本質を表している。WebAssembly は新しいプログラミング言語ではなく、JavaScript の代替でもない。ポータブルなバイナリ命令形式であり、ブラウザ内で JavaScript と並行して動作するサンドボックス化された実行環境だ。

WebAssembly とは何か

WebAssembly(Wasm)は W3C 標準である。コンパクトなバイナリ命令形式とスタックベースの仮想マシンを定義している。通常、開発者が手書きで Wasm を書くことはなく、C、C++、Rust、Go、Zig、C# など、対応言語からコンパイルする。成果物は .wasm モジュールとなり、ブラウザがそれをダウンロード、検証し、メモリセーフなサンドボックス内で実行する。

主要なブラウザはすべて 2017 年から WebAssembly をサポートしている。モジュール形式は以下を目指して設計されている。

  • コンパクト: バイナリなので、同等の JavaScript ソースより高速にパースできる。
  • 高速: ブラウザが事前(ahead-of-time)にマシンコードへコンパイルする。
  • 安全: 各モジュールは独立した線形メモリを持ち、範囲チェックが行われる。
  • 言語非依存: Wasm 命令セットをターゲットにできる言語なら、Web 上で実行できる。

従来の Web の限界

JavaScript はユーザーインターフェース、ネットワークリクエスト、つなぎの処理に適した言語だ。動的型付け、ガベージコレクション、JIT コンパイルを備えている。これらの特性は柔軟性を生む一方、重い計算処理では予測不可能になる。

ガベージコレクションの停止は、アニメーションの最中に UI スレッドを固まらせることがある。JIT のウォームアップにより、同じコードでも実行タイミングで速度が変わる。数値演算やビット演算、画像デコード、動画エンコード、暗号化、物理シミュレーション、大規模な行列演算などでは、C や Rust からコンパイルした同じロジックに比べて、JavaScript は 1 桁遅いことが多い。

もう一つの限界はエコシステムのロックインだ。数十年にわたる画像、音声、動画、科学計算ライブラリは C や C++ で書かれている。それらを JavaScript に書き直して Web に持ち込むのは現実的ではない。

WebAssembly が存在する理由

Wasm は二つの問題を同時に解決するために作られた。

  1. 既存のネイティブコードをブラウザで動かす: JavaScript に書き直さずに済む。
  2. Web アプリに予測可能な高パフォーマンス実行レイヤーを与える: JavaScript がもともと想定していない処理向けだ。

これは明示的に JavaScript のパートナーであり、後継者ではない。DOM、ネットワークスタック、ほとんどの API は引き続き JavaScript が所有する。Wasm は分離された計算処理を担当し、JavaScript はオーケストレーションを担当する。

WebAssembly が解決すること

  • CPU 密集型のワークロード: 画像デコード、動画トランスコード、物理演算、データ暗号化。
  • 予測可能なパフォーマンス: ガベージコレクターがなく、JIT ウォームアップの勾配もない。
  • コードの再利用: libheif、FFmpeg、SQLite、OpenCV など、実績のあるライブラリをブラウザに持ち込める。
  • プライバシー: 機密データをサーバーにアップロードせずにローカルで処理できる。
  • オフライン実行: .wasm モジュールをダウンロードすれば、追加のネットワーク往復なしで動作する。

メリットとデメリット

観点利点限界
パフォーマンス数値演算やメモリ拘束型タスクでネイティブに近い速度ネイティブを超えることはない。ブラウザとデバイスの制約を受ける
移植性1 つのモジュールがあらゆるブラウザと OS で動作DOM やほとんどのブラウザ API には JavaScript ホストが必要
セキュリティ境界チェック付きのサンドボックス化された線形メモリ他のブラウザコードと同様に Spectre 系のサイドチャネルの影響を受ける
エコシステム既存の C/C++/Rust コードベースを再利用可能JavaScript との相互運用には複雑さとオーバーヘッドが伴う
起動バイナリは高速にパースされる初回ロード時のモジュールダウンロードとインスタンス化が大きくなることがある

率直な結論: Wasm はブラウザに以前できなかった処理をさせるが、あくまでユーザーのデバイス上で動くという制約は残る。

一般的なユースケース

WebAssembly は、Web アプリがローカルで重い処理をする必要がある場合のデフォルトの選択肢になっている。

  • 画像・動画編集: Figma、Photopea などはレンダリングやエフェクトに Wasm を使う。
  • 動画トランスコード: FFmpeg.wasm は FFmpeg をブラウザ内で動かし、形式変換を行う。
  • 光学文字認識: Tesseract.js は Tesseract OCR エンジンを Wasm に移植している。
  • ゲーム: Unity、Godot、Unreal Engine は Wasm へエクスポートしてブラウザゲームを実現できる。
  • データ分析: Pyodide は Python と科学計算パッケージを、sql.js は SQLite をブラウザ内で動かす。
  • 機械学習推論: ONNX Runtime Web は Wasm バックエンドを使って CPU 推論を行う。
  • CAD・3D ビューアー: かつてはデスクトップ専用だった複雑なジオメトリカーネル。
  • 暗号化: ハッシュ、暗号化、ゼロ知識証明をクライアントサイドで実行する。

WebAssembly ベースの代表的ライブラリ

ライブラリ由来役割
heic-tolibheif via EmscriptenHEIC/HEIF 画像をデコードし、ブラウザ内で JPEG、PNG、ビットマップに変換する。
FFmpeg.wasmFFmpeg via Emscriptenクライアントサイドだけで音声・動画のトランスコード、リマックス、フィルタリングを行う。
Tesseract.jsTesseract OCR via Emscriptenブラウザ内で画像からテキストを認識する。
sql.jsSQLite via Emscriptenメモリ内ファイルを使って、ブラウザ内で完全な SQLite データベースエンジンを動かす。
PyodideCPython via EmscriptenNumPy、Pandas、SciPy などのパッケージ付きで Python をバックエンドなしで実行する。
ONNX Runtime WebONNX Runtime via EmscriptenONNX 機械学習モデルを CPU(Wasm)または GPU(WebGL/WebGPU)で実行する。

表の各ライブラリは同じ形だ。成熟したネイティブプロジェクトを Wasm にコンパイルし、JavaScript API でラップする。これが実際の WebAssembly の価値だ。

なぜオンラインの HEIC to JPG/PNG/WebP は WebAssembly を必要とするのか

HEIC はあくまでコンテナーであって、コーデックではない。実際の画像データは HEVC でエンコードされており、計算コストが高く、特許上の制約もある。Web ページが使えるネイティブ HEVC デコーダーを搭載したブラウザは存在しない。サーバーサイド変換は選択肢として自明だが、それにはユーザーの写真をアップロードする必要がある。

代替案は、デスクトップツールと同じ libheif を WebAssembly にコンパイルして配布することだ。当社の HEIC to JPG コンバーター を使うと、デコードはブラウザ内で行われる。

  1. ファイルは ftyp ボックスのシグネチャを読み込むことで検証される。
  2. libheif の Wasm モジュールが HEVC ビットストリームをローカルでデコードする。
  3. デコードされたビットマップが canvas に書き込まれ、JPEG としてエクスポートされる。

同じモジュールは、透過対応のロスレス出力向けに HEIC to PNG と、Web 向けに最適化されたサイズ向けに HEIC to WebP でも動作する。アップロードもサーバー処理もなく、第三者が画像を見ることもない。

このワークフローが成立するのは、WebAssembly が本物の画像デコーダーを動かせるからだ。JavaScript だけでは HEVC を現実的な時間内に解析できず、サーバーへのアップロードはプライバシーの約束を破ることになる。WebAssembly は、その不足を補う存在だ。

WebAssembly の将来

Wasm はブラウザの外へ広がりつつある。WebAssembly System Interface(WASI)は、Wasm モジュールがサーバー、エッジワーカー、IoT デバイス上で動作するためのポータブルなシステムインターフェースを定義している。Wasmtime、Wasmer、WasmEdge などのランタイムは、すでにブラウザ外の本番環境に展開されている。

Component Model 提案により、Wasm モジュールは型付きインターフェースを公開し、ライブラリのように合成できるようになる。Rust モジュールを Python ホストや Go サービスに組み込むことが簡単になる。WebAssembly GC は Java や Kotlin のようなマネージド言語へのサポートを強化する。これらはいずれもブラウザ内の JavaScript を置き換える話ではなく、Wasm を普遍的で軽量なランタイムターゲットにする話だ。

ブラウザ内でも同じ傾向が見られる。より多くのネイティブ機能がクライアントサイドへ移り、プライバシーを守るツールが増え、かつてデスクトップ専用だったライブラリがタブ内で動くようになっている。WebAssembly はユーザーが目立って気づく機能ではないが、複雑な処理を Web 上で実行できる土台として、ますます重要になっている。

その他のおすすめ記事