WordPressが採用したWASM画像変換ライブラリでPNG→AVIF変換の速度とファイルサイズを検証する
WordPressがWASMの画像変換 wasm-vips を採用した


2026年にリリースされたWordPress 7.1では@wordpress/vipsパッケージが正式に導入され、ブラウザ上での画像圧縮・リサイズ・フォーマット変換・サムネイル生成を、libvipsをWebAssembly化した wasm-vips が担うようになりました。
- Web Worker上で動作し、メインスレッドをブロックしない設計
- Document-Isolation-Policyに対応
今回はwasm-vipsを使い、実際にPNG→AVIF変換がどれくらいの速度・ファイルサイズになるのかを検証しました。
あわせて、ネイティブ実装のAVIFエンコーダであるcavifとも比較します。
検証環境の実装
Vite + React + TypeScriptで検証用アプリを構築し、wasm-vipsをWeb Worker上で実行する構成にしました。UIをブロックせず、実運用に近い形で計測するためです。
実装にあたって、いくつかハマりどころがありました。
- wasm-vipsは`SharedArrayBuffer`を使うため、開発サーバーに以下のヘッダーが必須
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp- Viteでのバンドルには
vite-plugin-wasmとvite-plugin-top-level-awaitが必要 - AVIF書き出しは
image.avifsaveBuffer()のような専用メソッドは存在せず、汎用のimage.writeToBuffer('.avif', { Q: quality })を使うのが正解(ドキュメント上わかりにくい点)
検証1: Q値ごとの変換時間・ファイルサイズ(ブラウザ内)
800×600pxのテストPNG(約437KB)を、Q値30・50・70・90でAVIFに変換した結果です。
| Q値 | 変換時間 (ms) | 元サイズ (KB) | AVIFサイズ (KB) | 圧縮率 |
|---|---|---|---|---|
| 50 | 911.7 | 436.6 | 5.6 | 98.7% |
| 70 | 963.7 | 436.6 | 7.9 | 98.2% |
| 90 | 1579.6 | 436.6 | 16.5 | 96.2% |
Web Worker上でも、wasm-vipsの初期化自体は150〜350ms程度で完了し、実用上問題ないレベルでした。
検証2: ネイティブcavif(rav1eエンコーダ)との比較
同じPNGサンプル(実写真ベースの3枚、約1000×1280px)を使い、Q値50〜90(10刻み)で、wasm-vipsとネイティブの`cavif`(rav1eエンコーダを使うRust製AVIFツール)を比較しました。
| Q値 | wasm-vips サイズ | cavif サイズ | サイズ比(wv/cavif) | wasm-vips 時間 | cavif 時間 | 速度比 |
|---|---|---|---|---|---|---|
| 50 | 52.6 KB | 37.4 KB | 1.41倍 | 4,146 ms | 279 ms | 約15倍遅い |
| 60 | 83.9 KB | 49.1 KB | 1.71倍 | 4,461 ms | 352 ms | 約13倍遅い |
| 70 | 120.4 KB | 65.3 KB | 1.84倍 | 4,943 ms | 368 ms | 約13倍遅い |
| 80 | 172.2 KB | 84.2 KB | 2.05倍 | 5,636 ms | 644 ms | 約9倍遅い |
| 90 | 282.8 KB | 165.6 KB | 1.71倍 | 8,844 ms | 824 ms | 約11倍遅い |
同じQ値を指定しても、wasm-vipsの方が全帯域でファイルサイズが1.4〜2倍大きく、変換時間も9〜15倍かかるという結果になりました。ここで重要なのは、「Q値」はツールごとの内部スケールであり、両者間に互換性はないという点です。同じ見た目の圧縮率を狙うなら、wasm-vipsはcavifよりだいぶ低いQ値を使う必要があります。
なぜ差が出るのか:libaom vs rav1e
AVIFは「HEIFコンテナ + AV1コーデック」という規格なので、両者とも最終的には同じAV1というコーデックにたどり着きます。しかしAV1のエンコーダ実装が異なります。
| エンコーダ | 実装言語 | |
|---|---|---|
| wasm-vips | libaom (AOM) | C |
| cavif | rav1e | Rust |
wasm-vipsが実際にどのエンコーダを使っているかは、配布されているWASMバイナリを`strings`コマンドで調査することで確認できました。
vips-heif.wasm(AVIF/HEIF変換プラグイン)の中身にはAOMedia AV1 encoderの文字列のみが含まれており、APIの型定義上はrav1eやx265という選択肢も存在するものの、このビルドでは実際に機能するのはAOMだけでした。
libaomは「リファレンス実装」として圧縮率を追求する設計、rav1eは実用速度を重視した設計というアプローチの違いが、今回の速度・サイズ差にそのまま表れた形です。
rav1eをWASM化する道はあるか
「それならrav1eをWASM化すればいいのでは?」という疑問から、追加で調査しました。
cavifおよびrav1eはいずれもオープンソースです。
- kornelski/cavif-rs(MIT/BSD)
- xiph/rav1e(BSD-2-Clause)
Rust製なのでwasm-packでwasm32ターゲットへのビルド自体は技術的に可能です。実際に試みたリポジトリ(saschazar21/wasm-rav1e)も存在します。しかし、作者自身が認める深刻な課題がありました。
- 3072×2048pxの画像1枚のエンコードに約4分(デフォルト設定)
- 4:2:0以外のクロマサブサンプリング使用時に画像が破損するバグ
rav1eのネイティブでの速さは、x86_64向けアセンブリ最適化やSIMD命令に強く依存しています。WASMではこれらの最適化の多くが使えないため、rav1eの最大の強みである「速さ」が失われてしまうと考えられます。
さらにnpm上を調査したところ、rav1eベースの実用的なWASMパッケージは実質的に存在しないことがわかりました。
| パッケージ | 実態 |
|---|---|
| rav1e(npm, v0.3.0) | 中身のwasmファイルがわずか150バイトのスタブ。機能しない |
| wasm-rav1e(GitHub) | npm未公開。ローカルビルド前提 |
| @saschazar/wasm-avif | 説明文にrav1e/dav1dへの言及があるが、実際に配布されているwasmバイナリの中身はlibaom |
| @jsquash/avif(Squoosh由来) | 同様に実体はlibaom |
つまり、既存のWASM AVIFライブラリは軒並みlibaomベースであり、今回のwasm-vipsと同じ土俵にいることが確認できました。
まとめ
- WordPress 7.1が採用したことからもわかる通り、wasm-vips(libaom)はブラウザ内画像変換の現実的な選択肢として十分実用に耐える
- ただし、変換速度・圧縮率を最優先するなら、サーバーサイドでcavifやrav1e相当のネイティブ実装を使う方が合理的
- rav1eをWASM化する試みは技術的には可能だが、現状は実用速度に届いていない。npm上にも実用的な公開パッケージは存在しない
- 「同じQ値」でもエンコーダが異なれば単純比較はできない点に注意が必要