Today we are going to talk about a story. A story about a small software development company.
One day the manager calls on a meeting where she presents the idea for the new project that needs to be developed.
The idea is to create a mobile app that applies filters to your images. Simple as opening the app, selecting an image from your phone's gallery and applying a filter on it.
The development team decides to create a Web API, that will allow developers to upload images and download the images with applied filters.
After they developed the web service and the mobile apps, they needed a super fast algorithm that applies filters on images. All they had available as a development resource at the time was a C++ ninja that had a strong algorithm development background, so they assigned him to develop that feature.
...Fast forward 3 months, everything was working like a Swiss watch.
...Say no more, the developers integrated a simple website with the Image Conversion API within 24 hours, and everything worked without a shadow of a doubt perfectly.
...Well until the point when customers scaled so much, that the server could not perform enough calculations to accomplish the needs of every client, so temporary the app was scaled horizontally.
In the meantime, the company was seeking a solution to the problem they had.
They decided to move the conversion logic to the frontend. Which means, the Android and IOS apps would convert images within the app, without sending it to the Web API.
Well, let's be honest. At least once in your lifetime, you imagined running some Non-JavaScirpt programming language in a web browser.
Guess what, they were hoping that such technology exists because they had to come out with the new Web release as soon as possible.
So, how it all began...?
It all began on one sunny day, when three friends, called Microsoft, Google, and Mozilla, decided to drink a coffee and discuss on topic "browsers".
Furthermore, they decided that they will create a standard, that will be equal in every browser.
So, what's the new standard about?
That's all ok, but how are we going to load our native code in the browser?
The point is, we won't load our native code in the browser at all, instead, we will load some binary files that hold the ".wasm"(WebAssembly) file extension.
We are going to load binary files instead of text files. Now, you are probably asking your self, about what's the binary content in that file and how are we going to generate it. The answer is simple. We will use magic.
The same magic that we use to convert our source code into binary files called ".dll" and ".exe", and that magic is called Compiler.
If you try to open a generated ".wasm" file, you will see nothing else than a shitload of non-human-readable characters. That's because ".wasm" files are precompiled files, they do not contain any source code.
If you are a developer with dotnet background, you probably know what Intermediate Language(IL) stands for. It allows multiple technologies to run in the same runtime and interact with each other. For instance, there could be a desktop app that has some of its plugins developed in VisualBasic, or C++. First, the compiler turns source code into Intermediate Language, and then the Intermediate language gets executed by the runtime. In other words, it's a compilation target.
Furthermore, WebAssembly is nothing more than a set of instructions packed into a Module. There is also a text representation of the wasm file, the human-readable one, and it's called WebAssembly Text(".wat"). The ".wat" is consisted of AST(Abstract Syntax Tree) and of so-called S-Expressions. You can find more information on AST on this link
One of the best things that WebAssembly as technology provides, is performance. And it's preferred to use WebAssembly when we have heavy bound CPU computations, like games, video editing, image processing etc.
Why? Because they don't need a special runtime. Unlike C/C++, high-level languages like C# and Java, have special requirements, i.e. you will need to ship their entire runtime to be able to run them.
We mentioned some compile-time optimization. There is no normal compiler that would not optimize simple addition operation between 2 or more constants in
var result = 1 + 2;
//will be optimized into
var result = 3;
Later on, when the WebAssembly technology had its blueprints released, Binaryen was created to serve as a compiler that turns "asm.js" into wasm.
All we needed was a way to use our C++ code in the browser. Now with WebAssembly that is more than possible. We can easily use Emscripten and Binaryen to compile our C++ libraries with WebAssembly as a compilation target.
Guess What! That's what exactly happened to a lot of large code-bases like Unity3D Game Engine, Unreal Engine 4 and many more
For now, WebAssembly as technology is mature enough for using it, but what about using some of the browser APIs? Like "console", "window", how are we going to access the "DOM elements"?
For now, there are no concrete implementations on these topics, neither for garbage collection, multithreading(best candidate to come next).
Lacking garbage collection means we are the ones that have to care about it.
In cases when we ship our runtime, e.g. with C# we also ship the mono runtime, and the runtime has already implemented the garbage collection. The case is the same with the multithreading. If the runtime has implemented it, it's available.
Having the browser APIs implemented by the framework is awesome, but how are they really implemented, or what if some of them are missing, how can we add to WebAssembly a functionality that is not available yet?
Well, remember when we mentioned earlier the "glue code"? It's the code that gets our wasm file from the server and loads it into the browser. The "glue code" can also do some extra stuff, like adding some information to the module for method implementations.
It's can be confusing at first, but we'll try to get it through an example.
Let's suppose that we have a button on our website that has a simple hit-counter in it.
The hit-counter will behave as follows:
When we click the button we want to increment a value in our code. Next, a paragraph will be populated with the new value.
The implementation will be simple. Binding an "OnClick" event on the button, so when the button is clicked, a method from the WebAssembly module with name "incrementCounter" will be called. We will store our "counterValue" in a global variable in the C++ code.
The implementation of "incrementCounter" method will increment the global variable "counterValue". Next, we need to update the paragraph with the newly incremented value.
Since we have no garbage collection, we cannot track the references of DOM elements.
We need to make another workaround by calling a method that is marked as "extern", which means that the implementation of that method will be provided externally(this varies in every programming language).
When loading the WebAssembly module using "glue code", we can provide to the module implementations for the missing methods(the ones marked as "extern"). In our case, we can pass to the WebAssembly module, an implementation that updates the value of the paragraph.
The source code for the hit-counter example is provided here
I hope that you enjoyed the article.
If you have any comments, please share them :)