まえがき ※【2025/2/28 追記】続きを書きました【Claude 3.7】Go言語でwasmのサンプルプログラムをAIに作らせて動かしてみる 
Go言語でボール落下アニメーションのプログラムを書き、Wasmにコンパイルしてブログ上で動かしてみました。
Start 
tinygoを使用した場合、非圧縮では約200KB、gzipで圧縮すると約80KB程度のファイルサイズになりました。
そもそも単純なプログラムで使用するパッケージは3種類のみのため、ファイルサイズが小さくなるのは当然ですが、
ちなみに有名なWebサービスであるFigmaもwasmを使用しています。
ボール落下アニメーションの作り方 では作り方について解説していきます。wasm-testフォルダ直下に作成してきます。
1
 2
 $ mkdir wasm-test
 $ cd  wasm-test
 
まずは、ボール落下アニメーションのプログラムをGo言語で作成します。
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 // main.go 
package  main 
 import  ( 
	"math/rand/v2" 
 	"syscall/js" 
 	"time" 
 ) 
 type  Ball  struct  { 
	X ,  Y    float64 
 	VY      float64 
 	Radius  float64 
 	Color   string 
 } 
 var  ( 
	canvas   js . Value 
 	ctx      js . Value 
 	balls    [] Ball 
 	gravity  float64  =  0.5  // 重力加速度 
 	ground   float64  =  300  // 地面の位置 
 	width    float64  =  500  // キャンバスの幅 
 	height   float64  =  400  // キャンバスの高さ 
 ) 
 // 物理演算の更新処理 
func  updatePhysics ()  { 
	for  i  :=  range  balls  { 
 		balls [ i ]. VY  +=  gravity  // 重力の影響を適用 
 		balls [ i ]. Y  +=  balls [ i ]. VY 
 
 		// 地面に衝突した場合 
 		if  balls [ i ]. Y  >  ground - balls [ i ]. Radius  { 
 			balls [ i ]. Y  =  ground  -  balls [ i ]. Radius 
 			balls [ i ]. VY  =  - balls [ i ]. VY  *  0.7  // 反発係数 
 		} 
 	} 
 } 
 // 描画処理 
func  draw ()  { 
	// キャンバスをクリア 
 	ctx . Call ( "clearRect" ,  0 ,  0 ,  width ,  height ) 
 
 	// 各ボールを描画 
 	for  _ ,  ball  :=  range  balls  { 
 		ctx . Call ( "beginPath" ) 
 		ctx . Call ( "arc" ,  ball . X ,  ball . Y ,  ball . Radius ,  0 ,  2 * 3.1415926535 ) 
 		ctx . Set ( "fillStyle" ,  ball . Color ) 
 		ctx . Call ( "fill" ) 
 	} 
 } 
 // requestAnimationFrame のコールバック関数 
func  render ( this  js . Value ,  args  [] js . Value )  interface {}  { 
	updatePhysics () 
 	draw () 
 	js . Global (). Call ( "requestAnimationFrame" ,  js . FuncOf ( render )) 
 	return  nil 
 } 
 func  main ()  { 
	r  :=  rand . New ( rand . NewPCG ( uint64 ( time . Now (). UnixNano ()),  3 )) 
 
 	// HTML の canvas 要素を取得 
 	document  :=  js . Global (). Get ( "document" ) 
 	canvas  =  document . Call ( "getElementById" ,  "myCanvas" ) 
 	if  canvas . IsUndefined ()  { 
 		js . Global (). Call ( "alert" ,  "Canvas要素が見つかりません!" ) 
 		return 
 	} 
 	ctx  =  canvas . Call ( "getContext" ,  "2d" ) 
 
 	// ボールを複数追加 
 	for  i  :=  0 ;  i  <  5 ;  i ++  { 
 		xx  :=  r . Float64 () 
 		rr  :=  r . Float64 () 
 		balls  =  append ( balls ,  Ball { 
 			X :       xx * ( width - 40 )  +  20 ,  // ランダムなX座標 
 			Y :       0 ,                   // 最初は上部 
 			VY :      0 ,                   // 初速度ゼロ 
 			Radius :  10  +  rr * 10 ,          // 10~20px のランダムな半径 
 			Color :   randomColor ( r ), 
 		}) 
 	} 
 
 	// アニメーション開始 
 	js . Global (). Call ( "requestAnimationFrame" ,  js . FuncOf ( render )) 
 
 	// 永久ループ 
 	select  {} 
 } 
 // ランダムな色を生成する関数 
func  randomColor ( r  * rand . Rand )  string  { 
	colors  :=  [] string { "red" ,  "blue" ,  "green" ,  "purple" ,  "orange" } 
 	return  colors [ r . IntN ( len ( colors ))] 
 } 
次に、上記Wasmプログラムをフェッチして実行するHTMLを作成します。
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 <!DOCTYPE html> 
< html > 
< head > 
  < meta  charset = "utf-8" > 
   < title > Go WebAssembly 落下アニメーション</ title > 
 </ head > 
< body > 
  < canvas  id = "myCanvas"  width = "500"  height = "400" ></ canvas > 
   < script  src = "wasm_exec.js" ></ script > 
   < script > 
     // Go の WebAssembly ランタイムの初期化
  const  go  =  new  Go (); 
    // main.wasm を非同期で読み込み、実行する
  WebAssembly . instantiateStreaming ( fetch ( "main.wasm" ),  go . importObject ). then (( result )  =>  { 
      go . run ( result . instance ); 
     }); 
   </ script > 
 </ body > 
</ html > 
次に、Wasmをロードして実行するための tinygo 用のJSファイル(wasm_exec.js)をカレントディレクトリにダウンロードします。
1
 $ docker run -v " $( pwd ) " :/go -e "GOPATH=/go"  tinygo/tinygo:0.36.0 /bin/bash -c "cp /usr/local/tinygo/targets/wasm_exec.js /go/" 
 
実行後、wasm_exec.jsがカレントディレクトリに作成されていればOKです。
注意点 このwasm_exec.jsはtinygo 0.36用のものになっています。goの公式の方を使用するとWebAssembly.instantiate(): Import #0 module="wasi_snapshot_preview1" error: module is not an object or functionというエラーになってしまうので、tinygo 0.36専用のwasm_exec.jsを使用しましょう。
tinygoでWasmのバイナリを作る では、tinygoを使ってコンパイルする手順を説明します。
なお、Goのバージョンによってはコンパイルエラーになる場合があるためできれば1.24.0を使用してください。
まず、tinygo 0.36をダウンロードしインストールします。(Ubuntu環境の場合)
1
 2
 wget https://github.com/tinygo-org/tinygo/releases/download/v0.36.0/tinygo_0.36.0_amd64.deb
 sudo dpkg -i tinygo_0.36.0_amd64.deb
 
次に、プロジェクトを初期化します。
1
 $ go mod init wasm-test
 
そして、tinygoでコンパイルします。
1
 tinygo build -o main.wasm -target wasm .
 
これで、main.wasmが生成されます。
1
 2
 3
 4
 5
 6
 7
 8
 9
 $ tree wasm-test
 wasm-test
 ├── go.mod
 ├── index.html
 ├── main.go
 ├── main.wasm
 └── wasm_exec.js
 
 0  directories, 5  files
では、ローカル環境でWebサーバーを立ち上げて動作確認をしてみましょう。
1
 python3 -m http.server 8080 
 
サーバー起動後、http://localhost:8080 
にアクセスします。
あとがき 思ったより簡単に動作したので、かなり作りやすくなっていると感じました。
本記事が何かしらの参考になれば幸いです。もふもふ-Xアカウント