WavedashDocs

Ebiten

Ship Ebiten games to the web with Go's built-in js/wasm target.

View example project on GitHub

Ebiten runs on any platform Go runs on, including the browser via GOOS=js GOARCH=wasm. Ebiten handles canvas creation and the game loop, so you just write Update/Draw/Layout and call ebiten.RunGame.

Setup

The Go toolchain includes the js/wasm target out of the box. No extra install.

GOOS=js GOARCH=wasm go build -ldflags="-s -w" -o build/game.wasm .

You also need the wasm_exec.js shim that ships with your Go install:

cp "$(go env GOROOT)/lib/wasm/wasm_exec.js" build/wasm_exec.js
# (older Go versions keep it under misc/wasm/)

SDK integration

Go accesses browser globals through syscall/js. Ebiten's game loop is synchronous from Go's perspective — call the SDK just before RunGame:

Calling Wavedash.init() is required. Your game stays hidden behind the Wavedash loading screen until you do. Call it once your game is ready to play.

package main

import (
    "syscall/js"

    "github.com/hajimehoshi/ebiten/v2"
)

type Game struct{}

func (g *Game) Update() error                            { return nil }
func (g *Game) Draw(screen *ebiten.Image)                {}
func (g *Game) Layout(_, _ int) (int, int)               { return 900, 600 }

func main() {
    sdk := js.Global().Get("Wavedash")
    sdk.Call("updateLoadProgressZeroToOne", 1)
    sdk.Call("init")

    if err := ebiten.RunGame(&Game{}); err != nil {
        panic(err)
    }
}

Call updateLoadProgressZeroToOne(...) with intermediate values during async asset loading (embedded files, HTTP fetches, etc.). init() automatically signals load completion, so call it last.

HTML shell

A minimal shell that loads wasm_exec.js, instantiates the module, and hands off to Ebiten:

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <style>
      html, body { margin: 0; width: 100%; height: 100%; overflow: hidden; background: #000; }
      canvas { display: block; width: 100%; height: 100%; outline: none; }
    </style>
  </head>
  <body>
    <script src="./wasm_exec.js"></script>
    <script type="module">
      const go = new Go();
      const result = await WebAssembly.instantiateStreaming(fetch("./game.wasm"), go.importObject);
      go.run(result.instance);
    </script>
  </body>
</html>

Build script

#!/usr/bin/env sh
set -eu
mkdir -p build

GOOS=js GOARCH=wasm go build -ldflags="-s -w" -o build/game.wasm .

if   [ -f "$(go env GOROOT)/lib/wasm/wasm_exec.js" ];  then cp "$(go env GOROOT)/lib/wasm/wasm_exec.js"  build/wasm_exec.js
elif [ -f "$(go env GOROOT)/misc/wasm/wasm_exec.js" ]; then cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" build/wasm_exec.js
fi

cp web/index.html build/index.html

wavedash.toml

game_id = "YOUR_GAME_ID_HERE"
upload_dir = "./build"

Other SDK features

Once initialized, Wavedash exposes leaderboards, achievements, stats, and user data. Call them the same way:

sdk.Call("setAchievement", "first_win", true)
user := sdk.Call("getUser")

See the SDK reference for the full API.