WebAssembly
ブラウザーでネイティブに近い速度でコードを実行するバイナリ命令形式
WebAssembly とは
WebAssembly (Wasm) は、ブラウザーでネイティブに近い速度でコードを実行するバイナリ命令形式である。C、C++、Rust などのコードを .wasm にコンパイルし、JavaScript から呼び出せる。2017 年に主要ブラウザーの対応が揃い、中核仕様は 2019 年 12 月 5 日に W3C 勧告 (WebAssembly Core Specification 1.0) となった。後継の 2.0 は SIMD や複数戻り値などの拡張を取り込んだ版だが、2026 年 8 月時点では勧告候補の段階にある。
実行モデルはスタックマシンとして定義されている。各命令はオペランドをスタックに積み、積まれた値を取り出して計算し、結果をスタックへ戻す。これは意味を定めるための定義で、実際のブラウザーは受け取った命令列を検証したうえで、その環境のマシンコードへコンパイルして走らせる。モジュールが使えるメモリは線形メモリと呼ばれる 1 本のバイト列で、JavaScript 側からは伸長可能な ArrayBuffer として見える。ポインターは配列の添字に相当し、モジュールはこの範囲の外へ手を伸ばせない。この閉じ込めが Wasm のサンドボックスの土台になっている。
JavaScript vs WebAssembly
JavaScript と WebAssembly は同じエンジンに載るが、得意な仕事が違う。
| 観点 | JavaScript | WebAssembly |
|---|---|---|
| 実行速度 | JIT で高速化 | ネイティブに近い |
| 起動速度 | パース + JIT | デコード + コンパイル |
| 型 | 動的型付け | 静的型付け |
| メモリ | GC | 既定では手動 (線形メモリ) |
| 用途 | UI、ビジネスロジック | 計算集約的な処理 |
実行速度の欄には条件が付く。ネイティブに近い速度が出るのは計算が Wasm の内側で完結している場合で、後述する境界越えが多い使い方では JavaScript との差はほとんど消える。
メモリの欄は 2023 年以降で事情が変わった。Wasm には長く GC がなく、Rust の所有権や C の malloc と free のようにモジュール側でメモリを管理する必要があった。GC を言語機能として持つ Java や Kotlin を Wasm へ載せるには GC の実装ごと .wasm に同梱するしかなく、モジュールが膨らんだ。この制約を緩めたのが WasmGC で、GC の仕組み自体を Wasm 側の機能として持つ拡張である。Chrome と Edge は 119、Firefox は 120 で既定有効になった。2026 年 8 月時点では、GC を持つ言語を Wasm へ移すとき、対象ブラウザーがこの拡張に対応しているかどうかで選べるツールチェーンが変わる。
Rust → Wasm
Rust は Wasm 向けのツールチェーンが最も整っている言語の 1 つで、wasm-pack が .wasm と JavaScript 側のグルーコードをまとめて生成する。#[wasm_bindgen] を付けた関数が JavaScript から呼べる形で書き出される。言語自体の特徴は Rust を参照。
// lib.rs
#[wasm_bindgen]
pub fn fibonacci(n: u32) -> u32 {
match n {
0 | 1 => n,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}
wasm-pack build --target web
JavaScript からの呼び出し
生成された JavaScript 側の入口は非同期になる。.wasm の取得とコンパイルを待つ必要があるためで、初期化の完了前に関数を呼ぶと失敗する。
import init, { fibonacci } from './pkg/my_wasm.js';
await init(); // Wasm モジュールの初期化
const result = fibonacci(40); // ネイティブに近い速度で実行
境界を越えて直接渡せるのは整数と浮動小数点数だけである。文字列や配列、オブジェクトを渡すときは、値を線形メモリへバイト列として書き込み、その位置と長さを数値として受け渡す。wasm-bindgen が生成するグルーコードはこの詰め替えを自動で行うが、コピーそのものは消えない。1 回あたりの計算量が小さいのに境界を何度も往復する使い方では、詰め替えの費用が計算の節約分を食い潰す。引数と戻り値が数値だけの fibonacci のような関数が例に使われるのは、この費用が最小になる形だからである。
ユースケース
WebAssembly は JavaScript では処理速度が不足する計算集約型の処理に適している。C/C++/Rust で書かれた既存のライブラリをブラウザーで実行できるため、ネイティブアプリの Web 移植にも活用される。
| ユースケース | 例 |
|---|---|
| 画像 / 動画処理 | Photoshop Web, FFmpeg.wasm |
| ゲーム | Unity の WebGL ビルド |
| 暗号化 | ハッシュ計算、暗号化処理 |
| データ処理 | CSV パース、圧縮 / 展開 |
| CAD / 3D | AutoCAD Web |
ゲームエンジンの対応状況はエンジンごとに異なる。Unreal Engine は 4.24 でブラウザー向けの書き出しを本体から外し、コミュニティが保守する拡張へ移した。ブラウザー配信を前提にするなら、エンジン側が現在どこまで面倒を見るかを先に確認する必要がある。
WASI (WebAssembly System Interface)
ブラウザーの中の Wasm はファイルもソケットも触れない。ホストから渡された関数しか呼べないためで、ブラウザーの外で Wasm を動かすには、ファイルやネットワークを扱う標準の入口が別に必要になる。それを定めているのが WASI である。
ブラウザ外での Wasm 実行:
Wasm → WASI → ファイルシステム、ネットワーク
用途:
- サーバーサイド・エッジでの実行
- エッジコンピューティング
- プラグインシステム
WASI には 0.1、0.2、0.3 の区切りがあり (それぞれ Preview 1、2、3 とも呼ばれる)、0.2 以降はコンポーネントモデルという別の仕様の上に組み直されている。0.3 では非同期処理が仕様側に取り込まれた。実装は wasmtime、WasmEdge、wazero、Wasmer など複数あり、どの版まで対応しているかはランタイムごとに異なる (2026 年 8 月時点)。権限の扱いは能力ベースで、モジュールは何の権限も持たない状態から始まり、ホストが明示的に渡したファイルやソケットだけを使える。
Lambda での Wasm
Node.js のランタイムにも WebAssembly があるため、Lambda の中で .wasm を読み込んで呼び出せる。
import { readFileSync } from 'fs';
// 初期化はモジュール読み込み時の 1 回だけ行う
const wasmBuffer = readFileSync('./optimized.wasm');
const { instance } = await WebAssembly.instantiate(wasmBuffer);
const heavyComputation = instance.exports.heavyComputation as (n: number) => number;
export const handler = async (event: { value: number }) => {
return { result: heavyComputation(event.value) };
};
モジュールの読み込みとコンパイルはハンドラーの外で 1 回だけ行い、実行環境が再利用される間は使い回す。ハンドラーの中で毎回 instantiate すると、その時間が呼び出しごとに乗る。速くなるかどうかは処理の中身で決まる。純粋な数値計算で、C や Rust の実装が既にあるなら移す価値がある。一方で JavaScript のままでも JIT コンパイル が十分に効く処理では、境界越えと初期化の分だけ遅くなることもある。
Wasm の制限
Wasm は JavaScript の置き換えではない。次の制限は仕様の側でそうなっているもので、外すには JavaScript 側の助けが要る。
| 制限 | 説明 |
|---|---|
| DOM アクセス | 直接アクセス不可 (JS 経由) |
| GC | 既定ではモジュール側で管理 |
| デバッグ | ソースマップが限定的 |
| バンドルサイズ | .wasm ファイルの追加 |
導入の判断は、処理が計算に偏っているか、境界を越える回数を少なく抑えられるかの 2 点でほぼ決まる。UI の組み立てや DOM 操作が主体の部分は JavaScript のままにし、計算の塊だけを Wasm へ切り出すのが扱いやすい。主スレッドを塞ぐ長さの計算なら、Web Worker へ載せる設計も併せて検討する。
この記事は役に立ちましたか?
関連用語
Rust
メモリ安全性とパフォーマンスを両立する、GC なしのシステムプログラミング言語
JIT コンパイル
プログラムの実行時にコードをネイティブコードにコンパイルし、パフォーマンスを向上させる技術
Web Worker
ブラウザーのメインスレッドとは別のスレッドで JavaScript を実行し、UI のブロッキングを防ぐ仕組み
JavaScript
Web ブラウザーで動作する言語として生まれ、今やサーバーやアプリ開発まで担う汎用言語
バンドラー
複数の JavaScript モジュールを 1 つまたは複数のファイルにまとめるビルドツール
C++
C 言語にオブジェクト指向などを加えた高性能言語。処理速度が求められる領域で使われる
関連する記事
OS / 低レイヤー本ガイド - コンピューターの仕組みを学ぶ技術書の選び方
OS、コンパイラー、ネットワークなど低レイヤーを学べる技術書の 4 ジャンルと、どこから始めるべきかの指針、賞味期限の見極め方を紹介します。
有名プログラマの読書習慣 - 天才たちは何を読んできたのか
リーナス・トーバルズ、まつもとゆきひろ、ビル・ゲイツなど、著名なプログラマたちの読書習慣と愛読書を紹介し、天才たちの読書スタイルから学べることを探ります。
Linux 本ガイド - コマンドライン / しくみ / 性能の 3 層で選ぶ技術書
Linux を学ぶ技術書の選び方を 3 層 (コマンドラインの操作 → カーネルのしくみ → 性能と運用) で整理。新しい Linux の教科書や [試して理解] Linux のしくみなどの定番書の使い分けと、学ぶ順番を解説します。