Create Dev's offline page with (Tiny)GO and WebAssembly 🦄💡✨
Tags: go, webassembly, showdev
Published: 2019-07-06 00:02:18

Dev's [offline page](https://dev.to/offline) is fun and we did [implement it with Rust and WebAssembly](https://dev.to/sendilkumarn/create-dev-s-offline-page-with-rust-and-webassembly-21gn). 

Go is simple. Can we do it in Go and WebAssembly? And the answer is Yes...
 
![](https://thepracticaldev.s3.amazonaws.com/i/1eaxlpr98hv1bedjgyhg.gif)

I have the sample application [here](https://github.com/sendilkumarn/go-wasm). If you want to write from scratch check [this tutorial](https://dev.to/sendilkumarn/tiny-go-to-webassembly-5168).

If you just want to see the source code, [check out this branch](https://github.com/sendilkumarn/go-wasm/tree/dev-board).

# Code Code Code 

We will first import the "syscall/js" and "fmt" in the `go/main.go`. 

> The "syscall/js" package gives access to the WebAssembly host environment when using the js/WASM architecture. Its API is based on JavaScript semantics. This package is EXPERIMENTAL. :unicorn: :unicorn:

```go
package main 


import( 
    "fmt"
    "syscall/js"
)
```

Add some variables globally available, these are the variables that we might need to use it when the functions are called from the JavaScript land or the browsers.

```go
var (
	isPainting bool
	x          float64
	y          float64
	ctx        js.Value
    color      string
)
```

We will manually add the canvas element in the `out/index.html`. 

```html
<canvas id="canvas"> </canvas>
```

Remove the `Hello World` line inside the `main` function and replace it with the following.

Get the `document` object from the Browser. Using the `document` get the `canvas` element that we added just now.

We can do that by using the `syscall/js` API.

```go
func main() {

    doc := js.Global().Get("document")
    canvasEl := doc.Call("getElementById", "canvas")
}

```

The API is simple. From the JavaScript's Global namespace get the document. 

Inside the document call the canvas element by Id.


We will set some attributes of the `canvasEl` like width, height. We will also get the canvas' `context`.

```go
bodyW := doc.Get("body").Get("clientWidth").Float()
bodyH := doc.Get("body").Get("clientHeight").Float()
canvasEl.Set("width", bodyW)
canvasEl.Set("height", bodyH)
ctx = canvasEl.Call("getContext", "2d")
```

We will then define the three event listeners `mousedown` | `mousemove` | `mouseup` to the `canvasElement`.

### Mouse Down

We will start the painting when we clicked down the mouse. 


```go
startPaint := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
    e := args[0]
    isPainting = true

    x = e.Get("pageX").Float() - canvasEl.Get("offsetLeft").Float()
    y = e.Get("pageY").Float() - canvasEl.Get("offsetTop").Float()
    return nil
})


canvasEl.Call("addEventListener", "mousedown", startPaint)

```

>Please note that return nil is important otherwise the program will not compile.

The entire API is based on [Reflect API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect).

### Mouse Move

We will define the paint function.

```go

paint := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
	if isPainting {
		e := args[0]
		nx := e.Get("pageX").Float() - canvasEl.Get("offsetLeft").Float()
		ny := e.Get("pageY").Float() - canvasEl.Get("offsetTop").Float()

		ctx.Set("strokeStyle", color)
		ctx.Set("lineJoin", "round")
		ctx.Set("lineWidth", 5)

		ctx.Call("beginPath")
		ctx.Call("moveTo", nx, ny)
		ctx.Call("lineTo", x, y)
		ctx.Call("closePath")

		// actually draw the path*
		ctx.Call("stroke")

		// Set x and y to our new coordinates*
		x = nx
		y = ny
	}
	return nil
})
canvasEl.Call("addEventListener", "mousemove", paint)
```

### Mouse Up

Finally, we will define the exit method that is called when the Mouse Up event is triggered.

```go


	exit := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		isPainting = false
		return nil
	})

	canvasEl.Call("addEventListener", "mouseup", exit)
```

At last, we will define the color selector. Add a colors div in the `out/index.html` and the necessary CSS.

```html
<style>
     .color {
            display: inline-block;
            width: 50px;
            height: 50px;
            border-radius: 50%;
            cursor: pointer;
            margin: 10px;
       }
</style>

<div id="colors></div>
```

We will define the colors as an array. Then loop through the colors and create color swatch node. Add an on click event listener to the color swatch. 

```go

divEl := doc.Call("getElementById", "colors")

	colors := [6]string {"#F4908E", "#F2F097", "#88B0DC", "#F7B5D1", "#53C4AF", "#FDE38C"}

	for _, c := range colors {	
		node := doc.Call("createElement", "div")
		node.Call("setAttribute","class", "color")
		node.Call("setAttribute", "id", c)
		node.Call("setAttribute","style", fmt.Sprintf("background-color: %s", c))

		setColor := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
				e := args[0]
				color = e.Get("target").Get("id").String()
				return nil;
		})


		node.Call("addEventListener", "click", setColor)


		divEl.Call("appendChild", node)


	}

```

That is it. 

Now start the web server by running `go run webServer.go`. 

Compile the Go into WebAssembly module using 

```
tinygo build -o ./out/main.wasm -target wasm -no-debug ./go/main.go
```

Visit the [localhost:8080](localhost:8080) to see the awesome canvas board ready for your creativity :unicorn:.


:bulb: The generated `WebAssembly` module is just **52KB**. TinyGo is awesome and creates insanely smaller WebAssembly binaries. How does it create it? Stay tuned for the next post in this series. :bulb: 

> Note: Use Firefox / Firefox Nightly. Chrome sometimes crash.


### More optimization :heart: 

> Remove the `fmt` import and replace `fmt.Sprintf` with a normal String concatenation, this will shave off another **26KB**. Check the commit [here](https://github.com/sendilkumarn/go-wasm/commit/f5837b0cee52f7598cff0da0a4f94770a29cb279).

_Thanks to Justin Clift for sharing this tip :)_


Do you :heart: Rust? check out [here](https://dev.to/sendilkumarn/create-dev-s-offline-page-with-rust-and-webassembly-21gn) on how to use Rust for WebAssembly.


I hope this gives you a motivation to start your awesome WebAssembly journey. If you have any questions/suggestions/feel that I missed something feel free to add a comment.

You can follow me on [Twitter](https://twitter.com/sendilkumarn).

If you like this article, please leave a like or a comment. :heart:

Thanks {% user aspittel %} for the [article](https://dev.to/aspittel/how-to-create-the-drawing-interaction-on-dev-s-offline-page-1mbe).