blob: ce7ead7c9013cc4bbf05af82e4421e7bac5afad1 [file] [log] [blame]
Justin Cliftc1b68852018-10-26 00:23:01 +11001WebAssembly
2===========
3:toc:
4:toc-title:
5:toclevels: 2
6:icons:
7
8
9# Introduction
10
Justin Cliftff596d52019-04-03 17:41:23 +110011Go 1.11 added an experimental port to WebAssembly. Go 1.12 has
12improved some parts of it, with further improvements expected in Go
131.13.
Justin Cliftc1b68852018-10-26 00:23:01 +110014
15WebAssembly is described on its https://webassembly.org[home page] as:
16
17> WebAssembly (abbreviated _Wasm_) is a binary instruction format for
18> a stack-based virtual machine. Wasm is designed as a portable
19> target for compilation of high-level languages like C/C++/Rust,
20> enabling deployment on the web for client and server applications.
21
22**********************************************************************
Justin Cliftb6a26e42019-05-18 12:32:39 +100023If you're new to WebAssembly read the https://github.com/golang/go/wiki/WebAssembly#getting-started[Getting Started] section, watch some of the https://github.com/golang/go/wiki/WebAssembly#go-webassembly-talks[Go WebAssembly talks],
Justin Clift7c4f0942019-04-27 20:28:45 +100024then take a look at the https://github.com/golang/go/wiki/WebAssembly#further-examples[Further examples] below.
Justin Cliftc1b68852018-10-26 00:23:01 +110025**********************************************************************
26
Justin Cliftbb1ad3d2018-10-26 02:35:20 +110027
Justin Cliftc1b68852018-10-26 00:23:01 +110028# Getting Started
29
30This page assumes a functional Go 1.11 or newer installation. For
31troubleshooting, see the https://github.com/golang/go/wiki/InstallTroubleshooting[Install Troubleshooting]
32page.
33
34To compile a basic Go package for the web:
35
Justin Cliftc1b68852018-10-26 00:23:01 +110036```go
37package main
38
39import "fmt"
40
41func main() {
42 fmt.Println("Hello, WebAssembly!")
43}
44```
45
46Set `GOOS=js` and `GOARCH=wasm` environment variables to compile
47for WebAssembly:
48
49```sh
50$ GOOS=js GOARCH=wasm go build -o main.wasm
51```
52
53That will build the package and produce an executable WebAssembly
54module file named main.wasm. The .wasm file extension will make it
55easier to serve it over HTTP with the correct Content-Type header
56later on.
57
58To execute main.wasm in a browser, we'll also need a JavaScript
59support file, and a HTML page to connect everything together.
60
61Copy the JavaScript support file:
62
63```sh
64$ cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .
65```
66
67Create an `index.html` file:
68
69```HTML
70<html>
71 <head>
72 <meta charset="utf-8">
73 <script src="wasm_exec.js"></script>
74 <script>
75 const go = new Go();
76 WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then((result) => {
77 go.run(result.instance);
78 });
79 </script>
80 </head>
81 <body></body>
82</html>
83```
84
85If your browser doesn't yet support `WebAssembly.instantiateStreaming`,
86you can use a https://github.com/golang/go/blob/b2fcfc1a50fbd46556f7075f7f1fbf600b5c9e5d/misc/wasm/wasm_exec.html#L17-L22[polyfill].
87
88Then serve the three files (`index.html`, `wasm_exec.js`, and
89`main.wasm`) from a web server. For example, with
90https://github.com/shurcooL/goexec#goexec[`goexec`]:
91
92```sh
93$ goexec 'http.ListenAndServe(":8080", http.FileServer(http.Dir(".")))'
94```
95
Christopher65a15692019-06-09 10:40:09 +020096Or use your own https://play.golang.org/p/pZ1f5pICVbV[basic HTTP server command].
97
Christopher6be57f42019-06-10 20:37:57 +020098Note: for the `goexec` command to work on Unix-like systems, you must https://golang.org/doc/install#tarball[add the path environment variable] for Go to your shell's `profile`. Go's getting started guide explains this:
99
Christopher65a15692019-06-09 10:40:09 +0200100> Add /usr/local/go/bin to the PATH environment variable. You can do this by adding this line to your /etc/profile (for a system-wide installation) or $HOME/.profile:
101
102> `export PATH=$PATH:/usr/local/go/bin`
103
104> Note: changes made to a profile file may not apply until the next time you log into your computer
Justin Cliftc1b68852018-10-26 00:23:01 +1100105
106Finally, navigate to http://localhost:8080/index.html, open the
107JavaScript debug console, and you should see the output. You can
108modify the program, rebuild `main.wasm`, and refresh to see new
109output.
110
Justin Cliftc1b68852018-10-26 00:23:01 +1100111# Executing WebAssembly with Node.js
112
113It's possible to execute compiled WebAssembly modules using Node.js
114rather than a browser, which can be useful for testing and automation.
115
116With Node installed and in your `PATH`, set the `-exec` flag to the
117location of `go_js_wasm_exec` when you execute `go run` or `go test`.
118
119By default, `go_js_wasm_exec` is in the `misc/wasm` directory of your
120Go installation.
121
122```
123$ GOOS=js GOARCH=wasm go run -exec="$(go env GOROOT)/misc/wasm/go_js_wasm_exec" .
124Hello, WebAssembly!
125$ GOOS=js GOARCH=wasm go test -exec="$(go env GOROOT)/misc/wasm/go_js_wasm_exec"
126PASS
127ok example.org/my/pkg 0.800s
128```
129
130Adding `go_js_wasm_exec` to your `PATH` will allow `go run` and `go test` to work for `js/wasm` without having to manually provide the `-exec` flag each time:
131
132```
133$ export PATH="$PATH:$(go env GOROOT)/misc/wasm"
134$ GOOS=js GOARCH=wasm go run .
135Hello, WebAssembly!
136$ GOOS=js GOARCH=wasm go test
137PASS
138ok example.org/my/pkg 0.800s
139```
140
Agniva De Sarker2462de62019-06-14 22:40:11 +0530141## Running tests in the browser
142
143You can also use https://github.com/agnivade/wasmbrowsertest[wasmbrowsertest] to run tests inside your browser. It automates the job of spinning up a webserver and uses headless Chrome to run the tests inside it and relays the logs to your console.
144
145Same as before, just `go get github.com/agnivade/wasmbrowsertest` to get a binary. Rename that to `go_js_wasm_exec` and place it to your `PATH`
146
147```
148$ mv $GOPATH/bin/wasmbrowsertest $GOPATH/bin/go_js_wasm_exec
149$ export PATH="$PATH:$GOPATH/bin"
150$ GOOS=js GOARCH=wasm go test
151PASS
152ok example.org/my/pkg 0.800s
153```
Justin Cliftbb1ad3d2018-10-26 02:35:20 +1100154
norunners65950422019-06-16 00:45:20 -0700155Alternatively, use the `exec` test flag.
156```
157$ GOOS=js GOARCH=wasm go test -exec="$GOPATH/bin/wasmbrowsertest"
158```
159
Justin Cliftbb1ad3d2018-10-26 02:35:20 +1100160# Go WebAssembly talks
161
Justin Cliftbda80042018-11-28 15:47:34 +1100162* https://www.youtube.com/watch?v=4kBvvk2Bzis[Building a Calculator with Go and WebAssembly] (https://tutorialedge.net/golang/go-webassembly-tutorial/[Source code])
Justin Cliftbb1ad3d2018-10-26 02:35:20 +1100163* https://www.youtube.com/watch?v=iTrx0BbUXI4[Get Going with WebAssembly]
164
165
Justin Cliftc1b68852018-10-26 00:23:01 +1100166# Interacting with the DOM
167
168See https://godoc.org/syscall/js.
169
Justin Clift8f6cb642019-04-04 01:57:36 +1100170Also:
171
172* An experimental new framework https://github.com/vugu/vugu[Vugu] is
173worth trying out, if you're looking for something like VueJS. :smile:
174
norunners11eda2e2019-06-02 10:11:11 -0700175* https://github.com/norunners/vue[vue - The progressive framework for WebAssembly applications.]
norunnersc2064672019-04-19 08:54:26 -0700176
Justin Clift8f6cb642019-04-04 01:57:36 +1100177* https://github.com/dennwc/dom[A library for streamlining DOM manipulation]
Justin Cliftc1b68852018-10-26 00:23:01 +1100178is in development.
179
Justin Clift8f6cb642019-04-04 01:57:36 +1100180* There is a https://gowebapi.github.io/[binding generator] that can be used.
Justin Cliftbb1ad3d2018-10-26 02:35:20 +1100181
norunners11eda2e2019-06-02 10:11:11 -0700182* https://github.com/norunners/vert[vert - WebAssembly interop between Go and JS values.]
183
Justin Clift1dab4792019-05-06 01:58:18 +1000184## Canvas
185
186* A new https://github.com/markfarnan/go-canvas[canvas drawing library] - seems pretty efficient.
187** https://markfarnan.github.io/go-canvas/[Simple demo]
Justin Cliftff596d52019-04-03 17:41:23 +1100188
Agniva De Sarkerfa413322019-06-03 01:26:17 +0530189# Configuring fetch options while using net/http
190
191You can use the net/http library to make HTTP requests from Go, and they will be converted to https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API[fetch] calls. However, there isn't a direct mapping between the fetch https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters[options] and the http https://golang.org/pkg/net/http/#Client[client] options. To achieve this, we have some special header values that are recognized as fetch options. They are -
192
193- `js.fetch:mode`: An option to the Fetch API mode setting. Valid values are: "cors", "no-cors", "same-origin", navigate". The default is "same-origin".
194
195- `js.fetch:credentials`: An option to the Fetch API credentials setting. Valid values are: "omit", "same-origin", "include". The default is "same-origin".
196
197- `js.fetch:redirect`: An option to the Fetch API redirect setting. Valid values are: "follow", "error", "manual". The default is "follow".
198
199So as an example, if we want to set the mode as "cors" while making a request, it will be something like:
200
201```go
202req, err := http.NewRequest("GET", "http://localhost:8080", nil)
203req.Header.Add("js.fetch:mode", "cors")
204if err != nil {
205 fmt.Println(err)
206 return
207}
208resp, err := http.DefaultClient.Do(req)
209if err != nil {
210 fmt.Println(err)
211 return
212}
213defer resp.Body.Close()
214// handle the response
215```
216
217Please feel free to subscribe to https://github.com/golang/go/issues/26769[#26769] for more context and possibly newer information.
218
Justin Cliftbb1ad3d2018-10-26 02:35:20 +1100219# Editor configuration
220
Justin Cliftfb2e83b2019-01-24 02:27:49 +1100221* https://github.com/golang/go/wiki/Configuring-GoLand-for-WebAssembly[Configuring GoLand and Intellij Ultimate for WebAssembly] - Shows the exact steps needed for getting Wasm working in GoLand and Intellij Ultimate
Justin Cliftbb1ad3d2018-10-26 02:35:20 +1100222
223
224# WebAssembly in Chrome
225
226If you run a newer version of Chrome there is a flag (`chrome://flags/#enable-webassembly-baseline`) to enable Liftoff, their new compiler, which should significantly improve load times. Further info https://chinagdg.org/2018/08/liftoff-a-new-baseline-compiler-for-webassembly-in-v8/[here].
227
228
Justin Cliftc1b68852018-10-26 00:23:01 +1100229# Debugging
230
231WebAssembly doesn't *yet* have any support for debuggers, so you'll
232need to use the good 'ol `println()` approach for now to display
233output on the JavaScript console.
234
235An official https://github.com/WebAssembly/debugging[WebAssembly Debugging Subgroup]
236has been created to address this, with some initial investigation and
237proposals under way:
238
239* https://fitzgen.github.io/wasm-debugging-capabilities/[WebAssembly Debugging Capabilities Living Standard]
240 (https://github.com/fitzgen/wasm-debugging-capabilities[source code for the doc])
241* https://yurydelendik.github.io/webassembly-dwarf/[DWARF for WebAssembly Target]
242 (https://github.com/yurydelendik/webassembly-dwarf/[source code for the doc])
243
244Please get involved and help drive this if you're interested in the Debugger side of things. :smile:
245
Justin Cliftfb688482019-05-31 14:53:28 +1000246## Analysing the structure of a WebAssembly file
247
248https://wasdk.github.io/wasmcodeexplorer/[WebAssembly Code Explorer] is useful for visualising the structure of a WebAssembly file.
249
250* Clicking on a hex value to the left will highlight the section it is part of, and the corresponding text representation on the right
251* Clicking a line on the right will highlight the hex byte representations for it on the left
Justin Cliftc1b68852018-10-26 00:23:01 +1100252
Justin Cliftbb1ad3d2018-10-26 02:35:20 +1100253# Known bug(s)
254
Justin Cliftb910f042018-11-09 11:50:27 +1100255Go releases prior to 1.11.2 https://github.com/golang/go/issues/27961[have a bug] which can generate incorrect wasm code in some (rare) circumstances.
Justin Cliftbb1ad3d2018-10-26 02:35:20 +1100256
257If your Go code compiles to wasm without problem, but produces an error like this when run in the browser:
258
259```
260CompileError: wasm validation error: at offset 1269295: type mismatch: expression has type i64 but expected f64
261```
262
263Then you're probably hitting this error.
264
Justin Cliftb910f042018-11-09 11:50:27 +1100265The solution is to upgrade to Go 1.11.2 or later.
Justin Cliftbb1ad3d2018-10-26 02:35:20 +1100266
Justin Cliftc1b68852018-10-26 00:23:01 +1100267
268# Further examples
269
270## General
271* https://github.com/agnivade/shimmer[Shimmer] - Image transformation in wasm using Go
272
273## Canvas (2D)
274* https://github.com/stdiopt/gowasm-experiments[GoWasm Experiments] - Demonstrates
275 working code for several common call types
276** https://stdiopt.github.io/gowasm-experiments/bouncy[bouncy]
277** https://stdiopt.github.io/gowasm-experiments/rainbow-mouse[rainbow-mouse]
278** https://stdiopt.github.io/gowasm-experiments/repulsion[repulsion]
Justin Clift41abc962018-10-26 00:25:04 +1100279** https://stdiopt.github.io/gowasm-experiments/bumpy[bumpy] - Uses the 2d canvas, and a 2d physics engine. Click around on the screen to create objects then watch as gravity takes hold!
Justin Cliftc1b68852018-10-26 00:23:01 +1100280** https://stdiopt.github.io/gowasm-experiments/arty/client[arty]
Justin Cliftd62b5622019-06-24 03:07:25 +1000281** https://stdiopt.github.io/gowasm-experiments/hexy[hexy] (**new**)
Justin Cliftc1b68852018-10-26 00:23:01 +1100282* https://github.com/djhworld/gomeboycolor-wasm[Gomeboycolor-wasm]
283** WASM port of an experimental Gameboy Color emulator. The https://djhworld.github.io/post/2018/09/21/i-ported-my-gameboy-color-emulator-to-webassembly/[matching blog post]
284 contains some interesting technical insights.
Justin Cliftd62b5622019-06-24 03:07:25 +1000285* https://justinclift.github.io/tinygo_canvas2/[TinyGo canvas]
286** This is compiled with https://tinygo.org[TinyGo] instead of standard go, resulting in a **19.37kB (compressed)** wasm file.
Justin Cliftc1b68852018-10-26 00:23:01 +1100287
288## WebGL canvas (3D)
289* https://bobcob7.github.io/wasm-basic-triangle/[Basic triangle] (https://github.com/bobcob7/wasm-basic-triangle[source code]) - Creates a basic triangle in WebGL
290* https://bobcob7.github.io/wasm-rotating-cube/[Rotating cube] (https://github.com/bobcob7/wasm-rotating-cube[source code]) - Creates a rotating cube in WebGL
291* https://stdiopt.github.io/gowasm-experiments/splashy[Splashy] (https://github.com/stdiopt/gowasm-experiments/tree/master/splashy[source code]) - Click around on the screen to generate paint...
292
Justin Clift1322f1a2018-11-02 14:44:21 +1100293# Reducing the size of Wasm files
294
295At present, Go generates large Wasm files, with the smallest possible size being around ~2MB. If your Go code imports libraries, this file size can increase dramatically. 10MB+ is common.
296
297There are two main ways (for now) to reduce this file size:
298
Johan Brandhorstbee185a2019-04-18 12:18:46 +01002991. Manually compress the .wasm file.
300 a. Using `gz` compression reduces the ~2MB (minimum file size) example WASM file down to around 500kB. It may be better to use https://github.com/google/zopfli[Zopfli] to do the gzip compression, as it gives better results than `gzip --best`, however it does take much longer to run.
301 b. Using https://github.com/google/brotli[Brotli] for compression, the file sizes are markedly better than both Zopfli and `gzip --best`, and compression time is somwhere inbetween the two, too.
Justin Clift1322f1a2018-11-02 14:44:21 +1100302
Justin Clift4f39c8b2019-04-18 20:58:25 +1000303Examples from https://github.com/johanbrandhorst[@johanbrandhorst]
304
305**Example 1**
Johan Brandhorstd2de0d42019-04-18 12:28:24 +0100306[width="25%",cols="^m,e,e",frame="topbot",options="header"]]
Justin Clift4f39c8b2019-04-18 20:58:25 +1000307|=======
Johan Brandhorstd2de0d42019-04-18 12:28:24 +0100308| Size | Command | Compression time
309|16M | (uncompressed size) | N/A
310|2.4M | `brotli -o test.wasm.br test.wasm` | 53.6s
311|3.3M | `go-zopfli test.wasm` | 3m 2.6s
312|3.4M | `gzip --best test.wasm` | 2.5s
313|3.4M | `gzip test.wasm` | 0.8s
Justin Clift4f39c8b2019-04-18 20:58:25 +1000314|=======
315
316**Example 2**
Johan Brandhorstd2de0d42019-04-18 12:28:24 +0100317[width="25%",cols="^m,e,e",frame="topbot",options="header"]]
Justin Clift4f39c8b2019-04-18 20:58:25 +1000318|=======
Johan Brandhorstd2de0d42019-04-18 12:28:24 +0100319| Size | Command | Compression time
320|2.3M | (uncompressed size) | N/A
321|496K | `brotli -o main.wasm.br main.wasm` | 5.7s
322|640K | `go-zopfli main.wasm` | 16.2s
323|660K | `gzip --best main.wasm` | 0.2s
324|668K | `gzip main.wasm` | 0.2s
Justin Clift4f39c8b2019-04-18 20:58:25 +1000325|=======
326
Johan Brandhorstbee185a2019-04-18 12:18:46 +0100327Use something like https://github.com/lpar/gzipped to automatically serve compressed files with correct headers, when available.
328
Justin Cliftb0fc0772019-05-09 08:52:37 +1000329**2.** Use https://github.com/tinygo-org/tinygo[TinyGo] to generate the Wasm file instead.
Justin Clift626e3ea2019-05-02 17:53:30 +1000330
331TinyGo supports a subset of the Go language targeted for embedded devices, and has a WebAssembly output target.
332
Justin Cliftb0fc0772019-05-09 08:52:37 +1000333While it does have limitations (not yet a full Go implementation), it is still fairly capable and the generated Wasm files are... tiny. ~10kB isn't unusual. The "Hello world" example is 575 bytes. If you `gz -6` that, it drops down to 408 bytes. :wink:
Justin Clift626e3ea2019-05-02 17:53:30 +1000334
335This project is also very actively developed, so its capabilities are expanding out quickly. See https://tinygo.org/webassembly/webassembly/ for more information on using WebAssembly with TinyGo.
Justin Cliftc1b68852018-10-26 00:23:01 +1100336
337# Other WebAssembly resources
338
norunnersc2064672019-04-19 08:54:26 -0700339* https://github.com/mbasso/awesome-wasm[Awesome-Wasm] - An extensive list of further Wasm resources. Not Go specific.