Hyper efficient front end development with web assembly using Go and LLVM

I wanted to write today about my experience with a very cool project named tinygo

Let me first off by saying web assembly in Go has a BIG problem. It’s way too reliant on it’s very bespoke API’s for getting things done. syscalls/js in my opinion is the very definition of the wrong way to go for web assembly:

  • Go devs should NOT learn javascript
  • As time goes to infinity, web assembly will get its own API to the DOM probably based on WebIDL (https://github.com/WebAssembly/design/issues/1148)
  • It makes way too many assumptions about the host running the web assembly and what functions are required for import to even get your module running
  • calling external functions as they natively are in the web assembly stack machine (not some abstraction) will always be the fastest method.

I was very happy then when I came across tinygo, compile down a web assembly module and come to found it it makes WAY less assumptions and has a very simple based comment based system for importing functions — something to my knowledge currently impossible in the mainline Go compiler.

tinygo takes advantage of LLVM and is able to reduce web assembly modules to a pretty extreme amount pretty well. What this enables me to do is write very minimal modules like this hello world:

package main
//go:export console_log
func console_log(msg string)
//go:export main
func start(){
console_log("hello world")
}
func main() {}

Compiled down, this module only had 2 import requirements:

  • console_log
  • io_std_out (because currently tinygo assumes some runtime )

Exports:

  • main

Out of all the options I’ve seen thus far for go web assembly tech, this is about as good as I could have hoped for.

I’ve been writing some libraries that expose some very efficient bindings to DOM manipulation APIs using a generation approached based on browser Web IDL ( standardized descriptions of what functionality browser environments have ).

Instead of making syscalls/JS you have a huge amount of imperative functions you can call to get handles to resources in the DOM and manipulate them. The advantage of this approach is that it is incredibly simple and C like, and requires no special code generation on your part and is entirely tech agnostic. Simply import in the functions you need.

Here’s an example of a canvas app:

package main
//go:export global_getWindow
func GetWindow() int32
//go:export Window_get_document
func GetDocument(window int32) int32
//go:export Document_querySelector
func QuerySelector(document int32 ,query string) int32
//go:export HTMLCanvasElement_getContext
func GetContext(element int32,context string) int32
//go:export CanvasRenderingContext2D_fillRect
func FillRect(ctx ,x ,y ,w ,h int32)
//go:export CanvasRenderingContext2D_set_fillStyle
func FillStyle(ctx int32, fillStyle string)
func cstr(s string) string{
return s+"\000"
}
//go:export main
func start(){
win := GetWindow()
doc := GetDocument(win)
canvas := QuerySelector(doc,cstr("#screen"))
ctx := GetContext(canvas,cstr("2d"))
FillRect(ctx,0,0,50,50)
FillStyle(ctx,cstr("red"))
FillRect(ctx,10,10,50,50)
FillStyle(ctx,cstr("grey"))
FillRect(ctx,20,20,50,50)
}
func main() {}

You can see this working live here

Overall i’m very excited to have one more tool in my toolbelt to create low level web assembly. Maybe with a bit of work, tinygocan generate a bit cleaner and be as solid as Rust is in the next tech platform of the web.