はじめに
WebAssembly / Wasm Advent Calendar 2025 の3日目の記事です。
この記事では簡単なアプリケーションを、Wasm を使って複数の環境で動作させるところまでやります。
Wasm に触れてみるいい機会だったので、今更ながらとりあえず動くものを作ってみようというやつです。
前置き
書いている人は Wasm についてざっくり知っている程度。
実際に触ったことはない。
触るにあたって簡単に情報収集をしてみたところ、初学者の自分としては以下のような点に関心を持ちました。
- Wasm バイナリを吐ける言語が複数存在する
- これが可能な言語は限られているとはいえ、選択肢が多いのは嬉しい
- 移植性の高さ
- ブラウザ上でも動作させられる他、環境依存の無いランタイムを挟むことで環境を問わず実行可能できるらしい
- 実行環境を意識しなくても良いので、プラグインとかの実装が捗りそう
Wasm ランタイムを間に挟む都合、似たような構成に見える JVM とどう違うのかというところは気になりましたが、言語の選択肢が複数ある、JVM より軽量なランタイム、サンドボックスモデルを用いた環境でセキュアに実行可能、といった独自の強みがあるようです。
せっかくなのでこのうち、「移植性の高さ」を体感できるようなものを簡単に作ってみようと思います。
とりあえずやってみる
1つの Wasm バイナリを複数環境で動作させることを目標として実装を進める。
1. Go でアプリケーションを実装
Go の環境構築は割愛。
main.go にフィボナッチ数列の計算を実装する。とりあえず動くものがあればいいので、Claude に実装してもらった。
package main
import "fmt"
// fibonacci は再帰的にn番目のフィボナッチ数を計算します。
func fibonacci(n int) int {
if n <= 1 {
return n
}
return fibonacci(n-1) + fibonacci(n-2)
}
// main はプログラムのエントリーポイントです。
// WASI環境(Wasmtimeなど)で実行されます。
func main() {
// フィボナッチ数列の最初の10個を表示
fmt.Println("フィボナッチ数列:")
for i := 0; i < 10; i++ {
fmt.Printf("F(%d) = %d\n", i, fibonacci(i))
}
}
JavaScript 環境で動作するようにビルドを実行。
fibonacci-wasm-go % GOOS=js GOARCH=wasm go build -o fibonacci.wasm main.go
※ GOOS でどのOS向けにビルドするかを指定している。今回は JavaScript 環境(Node.js やブラウザ)で実行したいので js を指定。
※ GOARCH でどのCPUアーキテクチャ向けにビルドするかを指定している。今回は Wasm でビルドするので wasm を指定。
問題なくビルドできたら、fibonacci.wasm が作られる。
fibonacci-wasm-go % ls
fibonacci.wasm main.go
2. wasm_exec.js の準備
JavaScript 環境での実行には Go に同梱されている wasm_exec.js が必要になる。
自分の環境(Go 1.25.4)では GOROOT の lib/ 下にファイルがあるようなので、コピーして利用する。
※バージョンによっては、lib/ 下ではなく misc/ 下にファイルが存在するかも
fibonacci-wasm-go % cp "$(go env GOROOT)/lib/wasm/wasm_exec.js" .
Node.js 用には wasm_exec_node.js もコピーしておく。
fibonacci-wasm-go % cp "$(go env GOROOT)/lib/wasm/wasm_exec_node.js" .
3. Node.js 上で実際に動かしてみる
まずは自分の手元の Node.js 環境で実行してみる。
wasm_exec_node.js を利用する場合、バイナリを引数に渡してそのまま実行できる。
fibonacci-wasm-go % node wasm_exec_node.js fibonacci.wasm
フィボナッチ数列:
F(0) = 0
F(1) = 1
F(2) = 1
F(3) = 2
F(4) = 3
F(5) = 5
F(6) = 8
F(7) = 13
F(8) = 21
F(9) = 34
以下のようなスクリプトでラップしてやれば、wasm_exec.js を使って実行することもできる。
// run.js
const fs = require("fs");
// wasm_exec.jsを読み込み
require("./wasm_exec.js");
const go = new Go();
const wasm = fs.readFileSync("./fibonacci.wasm");
WebAssembly.instantiate(wasm, go.importObject)
.then((result) => go.run(result.instance))
.catch((err) => console.error(err));
4. ブラウザ上で実際に動かしてみる
次に、同じ Wasm バイナリをブラウザ上でも動かしてみる。
ブラウザの開発ツール上から動作を確認できれば良いので、wasm_exec.js を実行して関数を呼び出すだけのシンプルな HTML を実装した。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Fibonacci Wasm</title>
</head>
<body>
<script src="wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(fetch('fibonacci.wasm'), go.importObject)
.then(result => go.run(result.instance));
</script>
</body>
</html>
あとはこのHTMLを何かしらのサーバー上で開いて、開発ツール上から動作を確認する。
Node.js で実行した時と同じように、ブラウザ上でも正しく動作することが確認できた。
最後に
1つの Wasm バイナリを複数環境で動作させるという目標を簡単に達成することができました。
WASI を使い、標準入出力なども絡めてもう少し発展的な実装もできそうではありましたが、それは次の機会にトライしようと思います。
