WebAssembly: A New World of Possibilities for the Web Browser

A Little Bit of History

From the beginning of the times the development of web applications have worked mostly in the server side. The progressive adoption of Java and the J2EE specifications made popular and quite extended the development of sites and applications for the Internet’s World Wide Web based on the server side. Java Server Pages first and Java Server Faces were part of these specifications. Well. JSF still is one of them in the current JEE platform.

No, thanks

Interpreted languages were based on the same principle. Ruby and PHP relied on the Web Server middle-ware to build apps.

The question was always the DOM, the Domain Object Model or the result of the rendering of a lot things in a way that another application could read, translate to be rendered and display in a visual format and how the DOM is written or rendered. Yes, you guessed well. This application is the Web Browser.

So, from the very beginning we have wondered if could move some logic to the Web Browser side or client side. Why? Well, the cost of the connection to the web server, the latency, the number of concurrent users, the cost in resources of user sessions at the server side, etc were excellent reasons. But servers were based always on some in-house infrastructure or external hosting service with none or little concern about the consumption of computing resources.

This was the way things were, until everything changed.

HTML 5th generation

Until then, the most extended language for the web browser, ready to perform some useful thing and inter-act with HTML was an interpreted and polemic language called Javascript. When most of web browsers decided to support Javascript the raise of this language was unstoppable.

Javascript played a mere auxiliary role in web programming for many years, understood as the way to implement behavior to web pages. Not well documented, not brilliant in design, Javascript was always a sort of necessary evil. Not many people were really efficient with it and the first attempts to provide some Java-like structure to its way of being written failed with a big noise. Besides, Javascript lacked frameworks. The Javascript tools available in that era were more libraries and tool sets than real frameworks implementing clear design patterns, structure, layout, testing and building processes to Javascript. All the things that makes possible to develop a professional Software Development Life Cycle.

Then, the appearance of HTML5 MVC Javascript frameworks happened…

They changed everything and they were not alone. The HTML5 specs were released and they changed the web. Or at least, they were supposed to.

I wrote in an article in 2013 about what I understood as which we could call a modern way to develop apps for the World Wide Web, what areas should be considered and involved in any Web App development. Please, have a look. You’ll see this image:

At the top right you can see a yellow circle with the legend Multi-threading.

Multi-threading is not possible using plain Javascript in the Web Browser.

Web Browsers uses a single thread (Main execution) to interpret the DOM with the Javascript functions. Everything is read sequentially and therefore concepts like loading order and position in code have a big importance.

With the general adoption of the Cloud the issue of the consumption of resources (CPU, Memory, connections) became in more and more critical. Server side only web applications are more expensive, use more resources and connections. If they are attacked successfully, a DDOS attack vector can trigger processes allocating big amounts of memory and CPU, blocking entire sites for too long.

But HTML5 specs came to the rescue with Web Workers.

What were HTML5 Web Workers?

They allow to run a script operation in a background thread separate from the main execution thread of a web application. The advantage of this is that laborious processing can be performed in a separate thread, allowing the main (usually the UI) thread to run without being blocked/slowed down.

I wrote this show case some years ago for the AppVerse HTML5 framework. I think it still is a perfect example of how Web workers can be used in the client side to delegate heavy processes. Here is the code if you want to have a look.

Web worker, single instance, using a single core. It takes 5615 ms to process the base image at the left
4 Web Worker instances, using in theory 4 cores. It took 1482 ms which is almost 3.8 times faster! And this logic works in the Web Browser side. So, they are 4 core allocations that you do not have to pay to cloud!

However, Web Workers seemed to have several flaws (I do not think in that way but it’s an extended opinion). They are not intuitive and they result a bit difficult to write. Besides, they cannot communicate directly with the main execution thread. Instead, the data is sent between workers and the main thread via a system of message, both sides send their messages using the postMessage() method, and respond to messages via the onmessage event handler (the message is contained within the Message event’s data property). Besides, the data is copied rather than shared. As I said, I do not see the flaws despite the risk of over-buffering the memory of the browser, but it rarely happens in my experience as you can avoid it keeping your parallel processes as short as possible.

So, Web Workers have not been adopted massively.

Front End developers do not like this specification. But I have to say that in the last 6 years I found just a few developers that tried to understand the HTML5 Specs, including Web Workers. The rest were completely ignorant of these specs that are the foundation of modern web development. The community of Front End developers has a serious problem of intelligence and knowledge right now as basic materials are completely ignored. We are paying the price in the form of generations of low quality and web apps that are not aware of the possibilities the Specs offer.

Anyway, please remember this. The provided way to implement parallel processing in the Web Browser was still based on Javascript and therefore, on an interpreted language. Besides, it is a Web Browsers (supporting HTML5) feature.

What is WebAssembly?

WebAssembly (WASM from now on) is a web standard binary instruction format. It’s developed by the W3C and it grew out of the asm.js and Google’s Native Client projects. It aims to provide portable targets to allow code to be compiled once and run across many platforms.

WASM is supported by several browsers: Chrome, Firefox, Safari, Edge and others since late 2017.

Many of these run-times have appeared fairly recently, meaning that WebAssembly is no longer just a run-time for the browser but can be used for code that can run anywhere, including bare metal. This is important as we’re talking here about a new execution platform.

And of course, it runs on the Web Browser!

Compared to Web Workers, that allows to run background, offline & caching processes they do not have access to DOM and can’t share the data (only through messaging) and are running in a separate context. Even more, we can even run WASM instead of Javascript into a Web Worker. As a conclusion, Web Workers only provide some abstraction layer with special privileges, nobody said that these layers have to execute Javascript.

A Little Bit of Code

We can start with a basic basic example, just to see how it works and how it can be enabled

We are Golang fans because many reasons we have published in this blog. So, we’ll try WASM with Golang.

Using Go with VSCode and the WA plugin is a good recommendation to start

First, write a package with some logic. For demonstration purposes will be enough for now to write something:

package main

func main() {
    println("Executed with WASM and Go")
}

Now, we’ll need a HTML page to display a visual way to run the code of the Go code written before:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>WASM Demp</title>
  </head>

  <body>
    <script src="wasm_exec.js"></script>

    <script>
      if (!WebAssembly.instantiateStreaming) {
        WebAssembly.instantiateStreaming = async (resp, importObject) => {
          const source = await (await resp).arrayBuffer();
          return await WebAssembly.instantiate(source, importObject);
        };
      }

      const go = new Go();

      let mod, inst;

      WebAssembly.instantiateStreaming(fetch("lib.wasm"), go.importObject).then(
        result => {
          mod = result.module;
          inst = result.instance;
          document.getElementById("runButton").disabled = false;
        }
      );

      async function run() {
        await go.run(inst);
        inst = await WebAssembly.instantiate(mod, go.importObject);
      }
    </script>

    <button onClick="run();" id="runButton" disabled>Run</button>
  </body>
</html>

Then, we have to compile the logic we wrote in Go, To do this we have to run this command, GOARCH=wasm and GOOS=js. It is necessary to specify the name of the file (e.g. main.go) using the -o flag. This command will compile the Go code into a file called lib.wasm within the current working directory.

OARCH=wasm GOOS=js go build -o lib.wasm main.go

We’ll also need to copy the provided wasm_exec.js file from misc/wasm.

cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .

We’ll be using the WebAssembly.instantiateStreaming() function to load this into our page within the HTML file (index.html) we’ve created before.

WebAssembly.instantiateStreaming(fetch("lib.wasm"), go.importObject).then(
        result => {
          mod = result.module;
          inst = result.instance;
          document.getElementById("runButton").disabled = false;
        }
      );

Now, we have to write a main function with a web server:

package main

import (
    "flag"
    "log"
    "net/http"
)

var (
    listen = flag.String("listen", ":8080", "listen address")
    dir    = flag.String("dir", ".", "directory to serve")
)

func main() {
    flag.Parse()
    log.Printf("listening HTTP on port: %q...", *listen)
    log.Fatal(http.ListenAndServe(*listen, http.FileServer(http.Dir(*dir))))
}

We run the program:

go run main.go

Open the browser at http://localhost:8080 and once the view is rendered click on the RUN button. You should see the message “Executed with WASM and Go” in the browser’s console.

Besides, you can see the downloaded files and the render result. The file lib.wasm weight is about 1.17 MB. As as example, nothing is challenging or useful, we are not running several tasks in a concurrent way but at least WASM works with Go. The gate are open.

In future posts we’ll include real experiments with Go and WASM to proof our hope about WASM and how it can help us to deliver more capable client apps.

Obvious Applications

Well, the main purpose is, the web applications have now amazing capacities to process information at the fastest speed.

The logic developed in certain languages as C, C++, Rust and Golang can provide to Web Applications running in the Web Browser of new functionalities processing bigger amounts of data, perform data transformations, processing images and executing parallel and concurrent processes in the Web Browser.

Therefore, the capacities of hybrid applications (web applications embedded as native mobile apps) are much bigger and more competitive when compared to native apps.

As far as I understand WASM, it is a natural evolution of Web Programming following the line marked by HTML5. Our future web apps can be faster and more capable of doing things in the browser side, freeing the server side (Cloud) of workload and therefore making things cheaper and easier.

It is intended to integrate a Garbage Collector as well, allowing languages as Java and C# to be used in WASM.

Because it’s compiled code, we are not talking of plain text Javascript which is a concern about security. WASM provides a better level of obfuscation and security in this regard.

Resources

Enjoy Xmas!

Jesus de Diego

Author: Jesus de Diego

Software Architect and Team Lead

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.