Hemath's Blog ☘️

Build your first webassembly project

Hey makkals,

This post is a part of a multi-part series on WebAssembly. Check out other parts of the series here

Have you ever heard about WebAssembly and thought, “That sounds cool, but it’s probably too complex for me”?

Well, today you’re going to build a simple WebAssembly project from scratch. Let me set the right expectations. We are going to build a very simple project which generates random hexadecimal color code.

Think of it as the “Hello, World!” of WebAssembly with a splash of color. By the end, you’ll learn how to write simple WebAssembly code, compile it, and use it with JavaScript to generate random colors right in your browser.

Setup WebAssembly

In this blog post series, we will be using C and C++ for examples. So we will be using a tool called Emscripten to compile our C / C++ code to WASM. However, you can use any supported language with the respective compiling tools.

Setting up Emscripten is straightforward. Just follow this page - https://emscripten.org/docs/getting_started/downloads.html

If you’re using macOS, you can simply use homebrew to install it,

brew install emscripten

If you’re using linux, follow this post - https://marcoselvatici.github.io/WASM_tutorial/#install_emscripten

After setting up Emscripten, you should be able to run the following command in your terminal,

emcc -v

Simple addition

Before implementing a random hex code generator, let’s build something simple first to understand the WASM workflow. Let’s build an application that just adds 2 numbers.

We will first build the application in JavaScript and then it into WASM later. Create a file named index.html and paste the following code,

<body>
  <input type="number" id="num1" placeholder="Enter first number">
  <input type="number" id="num2" placeholder="Enter second number">
  <button onclick="addNumbers()">Add</button>
  <p id="result"></p>

  <script>
    function addNumbers() {
      const num1 = parseInt(document.getElementById('num1').value);
      const num2 = parseInt(document.getElementById('num2').value);
      const result = addTwoNumbers(num1, num2);
      document.getElementById('result').textContent = `Result: ${result}`;
    }

    function addTwoNumbers(num1, num2) {
        const result = num1 + num2;
        return result;
    }
  </script>
</body>

The code is self-explanatory. On clicking the button, we get 2 numbers from the input fields and pass it to addTwoNumbers function. And the function returns a number which we display inside a paragraph.

Now let’s move the addTwoNumbers function to C language. Create a file named add.c and write the following piece,

// add.c
int addTwoNumbers(int num1, int num2) {
    return num1 + num2;
}

Now, let’s compile the C code to WASM,

emcc add.c -o add.js -s EXPORTED_FUNCTIONS="['_addTwoNumbers']" -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'cwrap']"

Let’s breakdown what this command does,

After successful compilation, 2 new files would be generated - add.wasm and add.js. Here,

And change the HTML file as follows,

<body>
    <input type="number" id="num1" placeholder="Enter first number">
    <input type="number" id="num2" placeholder="Enter second number">
    <button onclick="addNumbers()">Add</button>
    <p id="result"></p>
  
    <!-- Including the JavaScript glue-code file generated by Emscripten -->
    <script src="./add.js"></script>

    <script>
      // Wrap the function provided by C
      const addTwoNumbers = Module.cwrap("addTwoNumbers", "number", ["number", "number"]);

      function addNumbers() {
        const num1 = parseInt(document.getElementById('num1').value);
        const num2 = parseInt(document.getElementById('num2').value);

        // Calling the wrapped function
        const result = addTwoNumbers(num1, num2);

        document.getElementById('result').textContent = `Result: ${result}`;
      }
    </script>
  </body>

Here, Module.cwrap is used to wrap the C function, so that we can use it as a normal JavaScript function.

cwrap takes three parameters,

Run the application from a local server. If you don’t have a local server installed in your machine, try any one of the following.

If you have Python 3 installed, try these,

python -m http.server
python3 -m http.server

Or, if you have NodeJS installed, try the following,

npm i -g live-server

and run the following command in the directory which contains the all the files that we have created now,

live-server

If everything went well, you will be able to see 2 input fields. Try entering 2 numbers and click the button to see the result.

Demo of adding 2 numbers using WebAssembly

Here, all the DOM manipulations such as event listeners, getting value from inputs, displaying result are done by JavaScript. Whereas, the addition is done by WASM which was written in C language.

You can find the full code for this project - here

Now let’s start working on our actual project - random hexadecimal color generator

What is hexadecimal?

Before building our project, let’s understand what is hexadecimal and how it can be represented as colors.

Hexadecimal is a number system with base 16. It means, the number system contains 16 symbols to represent values. The possible hexadecimal symbols are 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, followed by A, B, C, D, E, F.

Here, A = 10, B = 11 and so on..

Hexadecimal representation

We can use hex code to represent colors with six digits. For example, #FF5733 or #42A5F5

To understand more about colors and their representation, read - Understanding Hexadecimal Colors.

Random color generator

Let’s write our C code first. Create a file named random_color.c

We need to use two libraries: stdlib.h and time.h,

// random_color.c

#include <stdlib.h>
#include <time.h>

We define the generateRandomHexColor function, which returns a character pointer.

char* generateRandomHexColor() {
    // ...
}

Let’s define necessary variable we need, and initiate random seed

char* generateRandomHexColor() {
    static char color[8]; // Store the color as a string (e.g., "#A3B2C7")

    srand(time(NULL));

    color[0] = '#';
}

We can use the rand function to generate a random number between 0 and 255. Then we need to convert it into a hexadecimal digit (i.e., 0 - 9 and A - F).

Let’s first write an util function that converts an integer into a valid hex value,

char* intToHex(int num) {
    static char hexStr[3]; // Store the hex string (2 characters + null terminator)
    const char hexDigits[] = "0123456789ABCDEF";

    hexStr[0] = hexDigits[(num >> 4) & 0xF]; // High nibble
    hexStr[1] = hexDigits[num & 0xF];         // Low nibble
    hexStr[2] = '\0';                         // Null terminator

    return hexStr;
}

char* generateRandomHexColor() {
    // ...
}

Now we can use this intToHex function to convert our random numbers into hex code. The final code is,

// random_color.c

#include <stdlib.h>
#include <time.h>

char* intToHex(int num) {
    static char hexStr[3]; // Store the hex string (2 characters + null terminator)
    const char hexDigits[] = "0123456789ABCDEF";

    hexStr[0] = hexDigits[(num >> 4) & 0xF]; // High nibble
    hexStr[1] = hexDigits[num & 0xF];         // Low nibble
    hexStr[2] = '\0';                         // Null terminator

    return hexStr;
}

char* generateRandomHexColor() {
    static char color[8]; // Store the color as a string (e.g., "#A3B2C7")

    srand(time(NULL));

    color[0] = '#';

    char* r = intToHex(rand() % 256);
    color[1] = r[0];
    color[2] = r[1];

    char* g = intToHex(rand() % 256);
    color[3] = g[0];
    color[4] = g[1];

    char* b = intToHex(rand() % 256);
    color[5] = b[0];
    color[6] = b[1];

    color[7] = '\0'; // Null terminator to make it a valid string

    return color;
}

Now let’s compile it to WASM,

emcc random_color.c -o random_color.js -s EXPORTED_FUNCTIONS="['_generateRandomHexColor']" -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'cwrap', 'UTF8ToString']"

And let’s prepare a HTML file to use this function,

<body>
  <h1>Random Background Color</h1>
  <button onclick="changeBackgroundColor()">Generate Random Color</button>

  <script src="random_color.js"></script> <!-- Include the generated glue code -->

  <script>
    function changeBackgroundColor() {
      // Wrapping the function written in C
      const generateRandomHexColor = Module.cwrap("generateRandomHexColor", "number", []);

      const colorPtr = generateRandomHexColor();
      const color = Module.UTF8ToString(colorPtr);
      document.body.style.backgroundColor = color;
    }
  </script>
</body>

If everything goes well, you should be able to click a button that changes the background color of the page.

Demo of random hex color generator using WebAssembly

It’s totally okay if don’t understand how we get the color from pointer and how C’s pointers are accessible via JavaScript. We will see more about WebAssembly architecture and memory in the upcoming posts.

You can find the full code for this project - here

Conclusion

In this post, we’ve taken our first steps in WebAssembly by writing simple C code, compiling it into WASM using Emscripten, and integrating it with JavaScript to build a functional random color generator. While this project is just the beginning, it shows the power and flexibility of WebAssembly in bringing high-performance code to the web.

With WebAssembly, you can unlock new possibilities for building faster, more efficient web applications. In future posts, we’ll explore more advanced use cases, like image processing, to further understand how WebAssembly can enhance performance and extend JavaScript’s capabilities.

Stay tuned for more hands-on examples and deeper dives into WebAssembly