「JavaScriptでは処理が遅い」「ブラウザ上で高度な計算処理を実行したい」——WebAssembly(Wasm)は、こうした課題を解決する技術です。ブラウザ上でネイティブに近い速度でコードを実行できるため、画像処理、動画編集、3Dレンダリング、暗号化などの重い処理をWeb上で実現できます。
本記事では、WebAssemblyの基本的な仕組みから、RustとGoでのWasm開発手順、JavaScriptとの連携方法、実際のユースケースまでを実践的に解説します。
WebAssemblyとは何か
WebAssembly(略称:Wasm)は、ブラウザ上で実行できるバイナリ形式のプログラミング言語です。2017年にW3C(Web標準化団体)によって標準化され、現在ではChrome、Firefox、Safari、Edgeのすべての主要ブラウザでサポートされています。
Wasmの特徴
高速な実行速度
Wasmはバイナリフォーマットであり、ブラウザのJavaScriptエンジンによるコンパイル・最適化が高速です。JavaScriptと比較して、数値計算やデータ処理では5〜20倍の速度向上が得られる場合があります。
言語非依存
Wasmは特定のプログラミング言語に依存しません。Rust、C/C++、Go、AssemblyScript、Zig、Kotlinなど、多くの言語からWasmバイナリをコンパイルできます。既存の資産をWeb上で再利用することが可能です。
安全な実行環境
Wasmはサンドボックス内で実行され、ブラウザのセキュリティモデルに従います。メモリアクセスは自身のリニアメモリに限定され、OSのリソースへの直接アクセスはできません。
JavaScriptとの相互運用
WasmはJavaScriptを置き換えるものではなく、共存する技術です。JavaScriptからWasmの関数を呼び出したり、Wasmからブラウザ APIを利用したりできます。
Wasmが解決する課題
JavaScriptは柔軟で表現力の高い言語ですが、CPU集約的な処理(大量の数値計算、画像処理、暗号化など)はパフォーマンスのボトルネックになります。Wasmは、こうした重い処理を高速に実行するための選択肢を提供します。
具体的には以下のような場面でWasmが効果を発揮します。
・画像・動画の加工(リサイズ、フィルター、フォーマット変換)
・3Dレンダリング・物理シミュレーション
・暗号化・ハッシュ計算
・PDF生成・テキスト処理
・既存のC/C++/Rustライブラリのブラウザ移植
Wasmの動作の仕組み
WebAssemblyがどのように動作するかを理解しましょう。
コンパイルと実行の流れ
Wasmの実行は以下の流れで進みます。
1. ソースコードの記述:Rust、Go、C++などで処理を実装する
2. Wasmへのコンパイル:各言語のツールチェーンで.wasmバイナリを生成する
3. Wasmの読み込み:JavaScriptからfetch()で.wasmファイルを取得し、WebAssembly.instantiate()でインスタンス化する
4. 関数の実行:JavaScriptからWasmのエクスポートされた関数を呼び出す
リニアメモリ
Wasmは「リニアメモリ」と呼ばれる連続したバイト列をメモリとして使用します。JavaScriptからはArrayBufferとしてアクセスでき、データの受け渡しに利用します。
文字列やオブジェクトなどの複雑なデータ型は、リニアメモリ上のバイト列としてエンコード・デコードする必要があります。この処理を簡略化するために、wasm-bindgen(Rust)やsyscall/js(Go)などのバインディングツールが提供されています。
WASI(WebAssembly System Interface)
WASIは、Wasmをブラウザ外(サーバー、CLI、エッジコンピューティング)で実行するための標準インターフェースです。ファイルシステムへのアクセスやネットワーク通信など、OS機能へのアクセスを安全に提供します。
WASIの登場により、Wasmはブラウザ内の技術にとどまらず、コンテナの代替やプラグインシステム、エッジコンピューティングなど、幅広い用途で活用されるようになっています。
RustでのWebAssembly開発
RustはWebAssembly開発で最も人気のある言語です。ゼロコスト抽象化、メモリ安全性、小さなバイナリサイズなど、Wasm開発に適した特性を持っています。
開発環境のセットアップ
# Rustのインストール(未インストールの場合)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Wasmターゲットの追加
rustup target add wasm32-unknown-unknown
# wasm-packのインストール(Rust→Wasmのビルドツール)
cargo install wasm-pack
プロジェクトの作成
# プロジェクトの作成
cargo new --lib wasm-demo
cd wasm-demo
Cargo.tomlを以下のように設定します。
[package]
name = "wasm-demo"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
wasm-bindgen = "0.2"
[dependencies.web-sys]
version = "0.3"
features = ["console"]
Rustコードの実装
// src/lib.rs
use wasm_bindgen::prelude::*;
// JavaScriptから呼び出し可能な関数
#[wasm_bindgen]
pub fn fibonacci(n: u32) -> u64 {
if n <= 1 {
return n as u64;
}
let mut a: u64 = 0;
let mut b: u64 = 1;
for _ in 2..=n {
let temp = a + b;
a = b;
b = temp;
}
b
}
// 文字列を受け取って返す関数
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
format!("Hello, {}! This is from Rust/Wasm.", name)
}
// 画像のグレースケール変換
#[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;
let gray = (0.299 * r + 0.587 * g + 0.114 * b) as u8;
chunk[0] = gray;
chunk[1] = gray;
chunk[2] = gray;
// chunk[3](アルファ値)はそのまま
}
}
ビルドとJavaScriptからの呼び出し
# Wasmのビルド(npm用パッケージとして出力)
wasm-pack build --target web
// JavaScriptからの呼び出し
import init, { fibonacci, greet, grayscale } from './pkg/wasm_demo.js';
async function main() {
// Wasmモジュールの初期化
await init();
// フィボナッチ数の計算
console.log(`fib(40) = ${fibonacci(40)}`);
// 挨拶
console.log(greet('World'));
// 画像のグレースケール変換
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
grayscale(new Uint8Array(imageData.data.buffer));
ctx.putImageData(imageData, 0, 0);
}
main();
GoでのWebAssembly開発
Goも公式にWebAssemblyへのコンパイルをサポートしています。Goの豊富な標準ライブラリと簡潔な文法をWasm開発に活かせます。
GoでのWasmビルド
# Wasmへのビルド
GOOS=js GOARCH=wasm go build -o main.wasm main.go
# wasm_exec.jsをコピー(GoのWasmランタイム)
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .
Goコードの実装
// main.go
package main
import (
"encoding/json"
"syscall/js"
)
// フィボナッチ数の計算
func fibonacci(this js.Value, args []js.Value) interface{} {
n := args[0].Int()
if n <= 1 {
return n
}
a, b := 0, 1
for i := 2; i <= n; i++ {
a, b = b, a+b
}
return b
}
// JSONのパースと変換
func parseJSON(this js.Value, args []js.Value) interface{} {
input := args[0].String()
var data map[string]interface{}
if err := json.Unmarshal([]byte(input), &data); err != nil {
return js.ValueOf(map[string]interface{}{
"error": err.Error(),
})
}
result, _ := json.Marshal(data)
return js.ValueOf(string(result))
}
func main() {
c := make(chan struct{}, 0)
// JavaScriptのグローバルスコープに関数を登録
js.Global().Set("wasmFibonacci", js.FuncOf(fibonacci))
js.Global().Set("wasmParseJSON", js.FuncOf(parseJSON))
// プログラムを終了させない(Wasmは永続的に動作する必要がある)
<-c
}
HTMLからの呼び出し
<!DOCTYPE html>
<html>
<head>
<script src="wasm_exec.js"></script>
</head>
<body>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(
fetch("main.wasm"),
go.importObject
).then((result) => {
go.run(result.instance);
// Wasmの関数を呼び出し
console.log("fib(40) =", wasmFibonacci(40));
});
</script>
</body>
</html>
TinyGoによる軽量化
標準のGoコンパイラで生成されるWasmバイナリは数MBとサイズが大きくなります。TinyGoを使うと、バイナリサイズを大幅に削減できます。
# TinyGoでのビルド(バイナリサイズが大幅に削減)
tinygo build -o main.wasm -target wasm main.go
TinyGoは、Goの標準ライブラリの一部に非対応ですが、多くのユースケースで十分に動作します。バイナリサイズがKB単位に収まることも多く、パフォーマンスが重要なWeb用途に適しています。
パフォーマンス最適化のポイント
WebAssemblyの性能を最大限に引き出すための最適化テクニックを紹介します。
JavaScriptとWasmの境界コストを最小化する
JavaScript↔Wasm間の関数呼び出しにはオーバーヘッドがあります。頻繁な関数呼び出しを避け、バッチ処理で一度にまとめてデータを渡す設計にしましょう。
// 悪い例:ピクセルを1つずつ渡す
for (let i = 0; i < pixels.length; i++) {
processPixel(pixels[i]); // 毎回Wasm呼び出し
}
// 良い例:配列をまとめて渡す
processAllPixels(pixelArray); // 1回のWasm呼び出しで全処理
メモリ管理の最適化
SharedArrayBufferの活用
JavaScriptとWasm間で共有メモリを使用すると、データのコピーを避けられます。大量のデータ(画像ピクセル、音声バッファなど)を扱う場合に効果的です。
メモリの事前確保
Wasmのリニアメモリは動的に拡張できますが、拡張時にコストが発生します。必要なメモリ量が予測できる場合は、初期サイズを大きめに設定しましょう。
バイナリサイズの削減
Wasmバイナリのサイズは、ダウンロード時間とパース時間に直結します。
・Rust:wasm-opt(Binaryenツール)で最適化、ltoの有効化、不要なパニック処理の除去
・Go:TinyGoの使用、不要なパッケージの除去
・共通:gzip/brotli圧縮での配信、遅延読み込み(必要な時にだけWasmをロード)
# Rust: wasm-optによる最適化
wasm-opt -Os -o optimized.wasm original.wasm
実践的なユースケース
WebAssemblyが実際に活用されている事例を紹介します。
画像処理・動画編集
ブラウザ上での画像リサイズ、フォーマット変換、フィルター適用などにWasmが活用されています。Photon(Rust製の画像処理ライブラリ)やFFmpeg.wasm(動画処理)などのライブラリが利用可能です。
PDF生成・ドキュメント処理
サーバーに依存せず、ブラウザ上でPDFの生成・編集・表示を行うアプリケーションにWasmが使われています。プライバシーに配慮した文書処理(データがサーバーに送信されない)が実現できます。
暗号化・セキュリティ
ブラウザ上での暗号化・復号処理にWasmを使うことで、JavaScriptの暗号ライブラリよりも高速な処理が可能です。パスワードハッシュ(Argon2など)やファイルの暗号化に利用されています。
データベース・ストレージ
SQLite(sql.js)やDuckDB-Wasmなど、データベースエンジンをWasmとしてブラウザ上で実行する事例が増えています。オフライン対応アプリやローカルファーストなアプリケーションの構築に活用されています。
プラグインシステム
Wasmのサンドボックス実行特性を活かし、サードパーティのプラグインを安全に実行するシステムに利用されています。Figma、Shopify、EnvoyなどがプラグインシステムにWasmを採用しています。
まとめ
WebAssemblyは、ブラウザ上で高速な処理を実行するための標準技術であり、Web開発の可能性を大きく広げるものです。本記事のポイントを整理します。
・WebAssemblyはブラウザ上でネイティブに近い速度でコードを実行するバイナリフォーマット
・Rust、Go、C/C++などの多くの言語からWasmバイナリをコンパイルできる
・wasm-bindgen(Rust)やsyscall/js(Go)でJavaScriptとの連携を実装する
・画像処理、暗号化、PDF生成など、CPU集約的な処理にWasmが効果を発揮する
・JavaScript↔Wasm間の呼び出し回数を減らし、バッチ処理でパフォーマンスを最適化する
・WASIにより、ブラウザ外(サーバー、エッジ)でのWasm活用も広がっている
まずはRust + wasm-packの環境を構築し、フィボナッチ計算や画像処理のような小さな例から試してみてください。JavaScriptとの速度差を体感することで、Wasmの活用場面が具体的にイメージできるようになるはずです。
関連記事
AIエージェント開発入門|自律型AIの仕組みと構築方法を解説【2026年版】
AI駆動コーディングワークフロー|Claude Code・Cursor・Copilotの実践的使い分け
プロンプトエンジニアリング上級編|Chain-of-Thought・Few-Shot・ReActの実践
APIレート制限の設計と実装|トークンバケット・スライディングウィンドウ解説
APIバージョニング戦略|URL・ヘッダー・クエリパラメータの使い分け
BIツール入門|Metabase・Redash・Looker Studioでデータ可視化する方法
チャットボット開発入門|LINE Bot・Slack Botの構築方法と活用事例
CI/CDパイプラインの基礎|継続的インテグレーション・デリバリーの全体像