WebAssembly(WASM)は、もともとブラウザでC++を実行するための技術として始まりました。2026年の現在、ブラウザ、サーバー、エッジネットワーク、組み込みデバイスなど、あらゆる環境で動作し、Web上で最も高負荷なアプリケーションを支えています。Figmaのレンダリングエンジン、Web版Adobe Photoshop、Google Meetの映像処理、そしてCloudflareのエッジコンピューティングプラットフォームは、すべてWebAssembly上で動作しています。
Chrome Platform Statusによると、2026年初頭時点でWASMはChromeのページ読み込みの約5.5%を占めており、前年の4.5%から増加しています。WASM 3.0がW3C標準となり、WASIが1.0に向けて成熟する中、エコシステムは転換点を迎えています。
このガイドでは、WebAssemblyで開発を始めるために必要な知識をすべて解説します。
WebAssemblyとは何か?
WebAssemblyは、コンパイルターゲットとして設計されたバイナリ命令フォーマットです。高水準言語(Rust、C、C++、Go、Kotlin)でコードを書き、.wasmにコンパイルし、WASMランタイムを持つ任意の環境 - ブラウザ、Node.js、Cloudflare Workers、Wasmtime、Wasmer - で実行できます。
動作の仕組み
WASMはスタックベースの仮想マシンです。関数はオペランドスタック上で値をプッシュ・ポップします。ホストランタイム(ChromeのV8、FirefoxのSpiderMonkey)がWASMバイトコードをネイティブマシンコードにJITコンパイルするため、ネイティブに近い性能を発揮します。
主な特徴:
- サンドボックス実行: WASMモジュールはホストが明示的に許可したリソースにのみアクセスできます。許可されない限り、ファイルシステム、ネットワーク、OSへのアクセスはありません。これはネイティブコードとは根本的に異なる点です。
- 線形メモリ: WASMとホスト間で共有される単一の連続した
ArrayBufferです。複雑なデータ(文字列、構造体)はメモリに書き込んでポインタを共有することで受け渡します。 - 型の制限: WASMがネイティブにサポートするのは
i32、i64、f32、f64の4つの型のみです。それ以外のもの(文字列、配列、オブジェクト)は線形メモリまたはComponent Modelを通じたエンコーディングが必要です。 - ポータブル: 同じ
.wasmバイナリが、再コンパイルなしにWASMランタイムを持つ任意のプラットフォームで動作します。
WASM vs JavaScript
WASMはJavaScriptを置き換えるものではありません。JavaScriptを補完するものです。
| 項目 | JavaScript | WebAssembly |
|---|---|---|
| パース | 実行時にパース+コンパイル | プリコンパイル済みバイナリ、デコードのみ |
| 実行速度 | JIT最適化、変動あり | ネイティブに近い、安定 |
| 起動 | 小さなスクリプトでは高速 | 高速デコード、予測可能 |
| DOMアクセス | 直接 | 間接(JSグルーコード経由) |
| 最適な用途 | UI、DOM操作、イベント処理 | CPU集約型の計算処理 |
| ガベージコレクション | 組み込み | WasmGC(新)、または手動 |
UIやDOM操作にはJavaScriptを使いましょう。画像処理、動画エンコーディング、物理シミュレーション、暗号処理、データパースなどの重い計算にはWASMを使いましょう。
WASM 3.0: 新機能
WebAssembly 3.0は2025年9月にW3C標準となり、数年にわたって開発されてきた9つの機能を標準化しました。
| 機能 | 実現すること |
|---|---|
| WasmGC | WASMにおけるネイティブガベージコレクション。マネージド言語(Java、Kotlin、Dart)が独自のGCランタイムを同梱せずにWASMにコンパイルできます。Chrome 119以降、Firefox 120以降、Safari 18.2以降でサポート。 |
| Exception Handling | WASMにおけるネイティブtry/catch。以前は、例外処理にJavaScriptへの高コストなラウンドトリップが必要でした。 |
| Tail Calls | スタックオーバーフローなしの効率的な再帰を実現。関数型言語にとって重要です。 |
| Relaxed SIMD | 並列データ処理のための128ビットベクトル命令。ハードウェア固有の最適化を可能にします。 |
| Memory64 | 4GBの線形メモリ制限を突破。大規模データ処理に必要です。 |
| Multi-memory | 1つのモジュール内に複数の独立したメモリ領域。 |
最もインパクトが大きいのはWasmGCです。これ以前は、JavaやKotlinをWASMにコンパイルするとガベージコレクタ全体をバイナリの一部として同梱する必要があり、ファイルサイズが肥大化していました。現在はブラウザ自体のGCがJavaScriptと同様にWASMモジュールのメモリ管理を行います。
WASI: ブラウザを超えたWebAssembly
ブラウザにおけるWASMは強力ですが、WASMをユニバーサルランタイムにしているのは**WASI(WebAssembly System Interface)**です。WASIはシステムリソース - ファイル、ネットワーク、クロック、乱数 - への標準化されたインターフェースを提供し、WASMモジュールをブラウザ外で実行することを可能にします。
WASI Preview 2(現在の安定リリース)は以下のインターフェースを定義しています:
wasi:filesystem- ケイパビリティハンドル(従来のファイルディスクリプタではなく)によるファイル操作wasi:sockets- TCP/UDPネットワーキングwasi:http- HTTPリクエスト/レスポンス処理wasi:clocks- ウォールクロック、モノトニッククロックwasi:random- 暗号学的乱数wasi:cli- コマンドライン引数、環境変数、標準入出力
重要な原則はケイパビリティベースのセキュリティです。WASMモジュールは、ホストが特定のディレクトリへのハンドルを明示的に付与しない限り、ファイルシステムにアクセスできません。これにより、WASIはネイティブ実行ファイルの実行よりも根本的に安全になります。
WASI 1.0への道
WASI 0.3.0(async/並行処理プリミティブの追加)は2026年に予定されており、その後にWASI 1.0が続きます。主な追加機能は、ゼロコピーストリーミングI/Oを備えた言語統合型の非同期処理です。
Component Model
コアWASMモジュールは数値しか交換できません。Component Modelは、WASMの上にリッチな型システムとコンポーザビリティレイヤーを追加することで、この制限を解決します。
WIT (WebAssembly Interface Types)
WITは、コンポーネントがインポートとエクスポートをリッチな型 - 文字列、レコード、リスト、バリアント、列挙型 - で宣言できるインターフェース定義言語です。i32やf64だけではありません。
// calculator.wit package myorg:calculator@1.0.0; interface operations { record calculation { expression: string, result: f64, timestamp: u64, } add: func(a: f64, b: f64) -> f64; multiply: func(a: f64, b: f64) -> f64; history: func() -> list<calculation>; } world calculator { export operations; }
wit-bindgenのようなツールチェーンがWITファイルから言語固有のバインディングを生成します。RustコンポーネントとPythonコンポーネントが、相手の実装言語を知ることなく、WITコントラクトを通じて文字列、レコード、リストを交換できます。
Rustで最初のWASMモジュールを構築する
Rustは最も成熟したWASMツールを持っています。実用的な例を作りましょう。ブラウザで動作する画像処理モジュールです。
セットアップ
# Install the WASM target for Rust rustup target add wasm32-unknown-unknown # Install wasm-pack (builds Rust to WASM + generates JS bindings) cargo install wasm-pack # Create a new library project cargo new --lib image-processor cd image-processor
Cargo.tomlの設定
[package] name = "image-processor" version = "0.1.0" edition = "2021" [lib] crate-type = ["cdylib"] [dependencies] wasm-bindgen = "0.2"
Rustコードの作成
// src/lib.rs use wasm_bindgen::prelude::*; /// Convert an image buffer to grayscale. /// Input: RGBA pixel data as a flat u8 array (4 bytes per pixel). /// Output: Modified in place for zero-copy performance. #[wasm_bindgen] pub fn grayscale(pixels: &mut [u8]) { for chunk in pixels.chunks_exact_mut(4) { let r = chunk[0] as f32; let g = chunk[1] as f32; let b = chunk[2] as f32; // ITU-R BT.709 luminance coefficients let gray = (0.2126 * r + 0.7152 * g + 0.0722 * b) as u8; chunk[0] = gray; chunk[1] = gray; chunk[2] = gray; // chunk[3] is alpha, leave unchanged } } /// Adjust brightness of an image. /// factor > 1.0 brightens, < 1.0 darkens. #[wasm_bindgen] pub fn adjust_brightness(pixels: &mut [u8], factor: f32) { for chunk in pixels.chunks_exact_mut(4) { chunk[0] = ((chunk[0] as f32 * factor).min(255.0)) as u8; chunk[1] = ((chunk[1] as f32 * factor).min(255.0)) as u8; chunk[2] = ((chunk[2] as f32 * factor).min(255.0)) as u8; } } /// Invert all colors in the image. #[wasm_bindgen] pub fn invert(pixels: &mut [u8]) { for chunk in pixels.chunks_exact_mut(4) { chunk[0] = 255 - chunk[0]; chunk[1] = 255 - chunk[1]; chunk[2] = 255 - chunk[2]; } } /// Calculate the average brightness of an image (0-255). #[wasm_bindgen] pub fn average_brightness(pixels: &[u8]) -> f32 { let mut total: f64 = 0.0; let pixel_count = pixels.len() / 4; for chunk in pixels.chunks_exact(4) { let luminance = 0.2126 * chunk[0] as f64 + 0.7152 * chunk[1] as f64 + 0.0722 * chunk[2] as f64; total += luminance; } (total / pixel_count as f64) as f32 }
ビルド
wasm-pack build --target web
これによりpkg/ディレクトリが生成され、以下が含まれます:
image_processor_bg.wasm- コンパイル済みWASMバイナリimage_processor.js- TypeScript型定義付きJavaScriptグルーコードpackage.json- npmに公開可能な状態
JavaScriptでの使用
<!DOCTYPE html> <html> <head><title>WASM Image Processor</title></head> <body> <canvas id="canvas" width="800" height="600"></canvas> <button onclick="applyGrayscale()">Grayscale</button> <button onclick="applyBrightness()">Brighten</button> <button onclick="applyInvert()">Invert</button> <script type="module"> import init, { grayscale, adjust_brightness, invert } from "./pkg/image_processor.js"; let ctx; let imageData; async function setup() { await init(); const canvas = document.getElementById("canvas"); ctx = canvas.getContext("2d"); // Load an image onto the canvas const img = new Image(); img.onload = () => { ctx.drawImage(img, 0, 0); imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); }; img.src = "photo.jpg"; } window.applyGrayscale = () => { grayscale(imageData.data); ctx.putImageData(imageData, 0, 0); }; window.applyBrightness = () => { adjust_brightness(imageData.data, 1.3); ctx.putImageData(imageData, 0, 0); }; window.applyInvert = () => { invert(imageData.data); ctx.putImageData(imageData, 0, 0); }; setup(); </script> </body> </html>
重要なポイント:imageData.dataはArrayBufferに裏付けられたUint8ClampedArrayです。WASMに渡されるとき、同じメモリを共有します - コピーは発生しません。Rust関数がピクセルをインプレースで変更し、JavaScript側は即座にその変更を確認できます。
低レベル: 手動WASMインスタンス化
wasm-bindgenを使いたくない場合は、WASMモジュールを直接インスタンス化できます:
const response = await fetch("calculator.wasm"); const { instance } = await WebAssembly.instantiateStreaming(response, { env: { // Functions the WASM module can call log_result: (value) => console.log("Result:", value), }, }); // Call exported functions const { add, multiply } = instance.exports; console.log(add(5, 3)); // 8 console.log(multiply(4, 7)); // 28
これは最小限のオーバーヘッドで、リッチな型の相互運用が不要な場合に有用です。
パフォーマンス: WASM vs JavaScript
実際のベンチマークでは、CPU集約型タスクで大幅な高速化が示されています:
| タスク | JavaScript | WASM | 高速化 |
|---|---|---|---|
| 4K画像処理 | 180ms | 8ms(SIMD使用) | 22倍 |
| 画像リサイズ(4K) | 250ms | 45ms | 5.5倍 |
| 物理シミュレーション(1万エンティティ) | フレーム落ち | スムーズな60fps | 約10倍 |
| JSONパース(大規模ペイロード) | 12ms | 3ms | 4倍 |
| 暗号学的ハッシュ | 45ms | 6ms | 7.5倍 |
WASMはネイティブコード速度の約95%で動作します。最大のメリットは以下の点から生まれます:
- 予測可能なパフォーマンス(JITウォームアップなし、GCポーズなし)
- 並列データ処理のためのSIMD命令
- ガベージコレクタの干渉なしの直接メモリアクセス
WASMが速くない場面:DOM操作、小さな計算、I/Oバウンドのタスク。JavaScriptはこれらに対してすでに最適化されています。
プロダクション事例
Figma: リアルタイムベクターレンダリング
Figmaのコアレンダリングエンジンは、C++をWASMにコンパイルしたものです。すべてのシェイプ、グラデーション、エフェクトがWASMで計算され、Canvas要素に描画されます。これにより、Figmaはブラウザ上で数千のレイヤーを持つ複雑なデザインを60fpsで処理できます - 純粋なJavaScriptでは不可能なパフォーマンスです。
Web版Adobe Photoshop
AdobeはRustを使ってPhotoshopの主要なフィルターやツールをWASMに移植しました。ベンチマークによると、WASM SIMDによる4K画像処理は22msで、JavaScriptの180msと比較して8倍の改善です - これによりリアルタイムフィルタープレビューが可能になりました。
Cloudflare Workers
Cloudflareは330以上のエッジロケーションにわたるV8アイソレートでWASMモジュールを実行しています。コールドスタートは1-5ms(コンテナベースのサーバーレスの100-500msと比較して)です。2026年2月には、WASMを使用してエッジネットワーク全体にLlama-3-8bの推論をデプロイしました。
Google Meet
Google Meetの背景ぼかしやバーチャル背景は、リアルタイム映像処理にSIMDを使用したWASMを活用しています。WASMモジュールは各ビデオフレームを30fpsのスムーズな映像を維持できる速度で処理します。
ブラウザサポート(2026年)
| 機能 | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
| コアWASM | 完全 | 完全 | 完全 | 完全 |
| Threads | 対応 | 対応 | 対応 | 対応 |
| SIMD(128ビット) | 対応 | 対応 | 対応 | 対応 |
| WasmGC | 119以降 | 120以降 | 18.2以降 | 対応 |
| Exception Handling | 対応 | 対応 | 対応 | 対応 |
| Memory64 | 対応 | 対応 | 部分的 | 対応 |
すべての主要ブラウザがWASMを完全にサポートしています。新しい機能(WasmGC、Exception Handling)は幅広く利用可能になっています。
ツールリファレンス
| ツール | 用途 | インストール |
|---|---|---|
| wasm-pack | RustからWASMへのビルド、npmパッケージ生成 | cargo install wasm-pack |
| wasm-bindgen | Rust/JS相互運用バインディング(wasm-packが使用) | Cargo.tomlの依存関係 |
| wasm-opt | バイナリサイズ最適化(50%以上の削減) | Binaryenの一部: brew install binaryen |
| wit-bindgen | WITファイルからバインディング生成 | cargo install wit-bindgen-cli |
| Wasmtime | サーバーサイドWASMランタイム(WASIリファレンス実装) | brew install wasmtime |
| Wasmer | WASIサポート付きの代替WASMランタイム | curl https://get.wasmer.io -sSfL | sh |
| wasm-feature-detect | ランタイムブラウザ機能検出 | npm install wasm-feature-detect |
バイナリサイズの最適化
WASMバイナリは大きくなることがあります。サイズを縮小する方法は以下の通りです:
# Cargo.toml [profile.release] opt-level = "z" # Optimize for size lto = true # Link-time optimization codegen-units = 1 # Better optimization, slower compile strip = true # Strip debug symbols
# Build in release mode wasm-pack build --release --target web # Further optimize with wasm-opt wasm-opt -Oz pkg/image_processor_bg.wasm -o pkg/image_processor_bg.wasm
一般的なRust WASMモジュールは、これらの最適化により500KBから50KB未満にまで縮小されます。
始めるためのロードマップ
まとめ
WebAssemblyはもはや実験的な技術ではありません。Web上で最も高負荷なアプリケーションに使われているプロダクション技術です。ネイティブに近いパフォーマンス、サンドボックスによるセキュリティ、ユニバーサルなポータビリティ - この3つすべてを提供するコンパイルターゲットは他にありません。
アプリケーション全体をWASMで書き直す必要はありません。まずは1つのCPU集約型関数 - 画像フィルター、データパーサー、物理計算 - をWASMにコンパイルし、JavaScriptから呼び出してください。その違いを測定し、WASMが他のどこで役立つかを判断しましょう。
ツールは成熟し、ブラウザサポートはユニバーサルで、エコシステムは成長を続けています。Rustを書いているなら、ブラウザまでたった1つのコマンドです。
始めるためのチェックリスト:
- Rustとwasm-packのインストール完了
- 最初のWASMモジュールをビルドしてブラウザで実行
- JavaScriptとの相互運用が動作(JSからWASMを呼び出し)
- サイズ最適化を適用したリリースビルド
- 純粋なJavaScript実装との性能ベンチマーク
- サーバーサイド用途のWasmtimeでWASIを検証