How to take off with WebAssembly for Go in React

WebAssembly is an exciting new technology that enables high-performance applications on the web. It provides a compact, low-level binary instruction format that runs in modern web browsers at near-native speeds. This makes it possible to run computationally intensive tasks, like gaming engines, 3D graphics, computer vision, and more, directly in the browser.

One of the most powerful aspects of WebAssembly is that it can be targeted by a variety of programming languages, not just JavaScript. For example, Go version 1.11 introduced experimental support for compiling to WebAssembly. This allows developers to leverage Go‘s simplicity, safety, and performance in the browser and integrate with existing JavaScript libraries and frameworks like React.

In this post, we‘ll take a deep dive into using WebAssembly with Go in a React application. We‘ll cover the end-to-end process, from writing and compiling Go code to loading the WebAssembly module in React and invoking functions between Go and JavaScript. By the end, you‘ll have a solid foundation for supercharging your React apps with WebAssembly.

What is WebAssembly?

Before we jump into the code, let‘s take a step back and look at what WebAssembly is and why it‘s a game changer for web development.

WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. It is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.

WebAssembly aims to execute at native speed by taking advantage of common hardware capabilities available on a wide range of platforms. This is achieved by having a compact binary format that is fast to transmit, decode, and compile to the actual machine code.

One of the key benefits of WebAssembly is that it can be used as a complement to JavaScript. It doesn‘t aim to replace JavaScript, but rather to work alongside it and provide a way to run performance-critical code at near-native speeds. JavaScript can leverage WebAssembly modules to offload computationally heavy tasks, like number crunching, game physics, image/video processing, and more.

WebAssembly is also designed with security in mind. It executes in a sandboxed environment, just like JavaScript, and enforces the browser‘s same-origin and permissions policies. WebAssembly code has no direct access to the DOM or other external APIs – it can only interact with the host environment through clearly defined JavaScript APIs.

Why Go and WebAssembly?

Go is a statically typed, compiled language that has excellent support for concurrency and a simple, minimalist syntax. It compiles to native machine code and has a very efficient runtime and small memory footprint. This makes it an ideal language for systems programming, cloud services, CLIs, and other domains where simplicity, safety, and performance are critical.

With the release of Go 1.11, the Go compiler gained the ability to compile to WebAssembly. This opened up an exciting new frontier for Go developers – the ability to write high-performance web applications using the language they know and love.

Go‘s support for WebAssembly has a number of advantages:

  • Go is a very simple and easy to learn language compared to languages like C++. This lowers the barrier to entry for traditional systems programmers who want to bring their skills to the web.

  • Go has excellent support for concurrency via goroutines and channels. This maps well to WebAssembly‘s single-threaded execution model and allows leveraging Go‘s concurrency patterns in the browser.

  • Go produces small and efficient WebAssembly binaries that are fast to download and instantiate.

  • Go has a first-class WebAssembly integration via the syscall/js package. This makes it easy to call between Go and JavaScript with minimal overhead.

  • The Go community has created a number of great tools and libraries for working with WebAssembly, like https://github.com/golang/go/wiki/WebAssembly.

Combining the performance and safety of Go with the reach and flexibility of the web platform via WebAssembly is a powerful combination. It enables developing sophisticated, high-performance web applications that were previously only possible with native desktop or mobile apps.

Getting Started with Go WebAssembly in React

Now that we‘ve covered the background, let‘s dive into the code! We‘ll walk through the process of creating a simple React app that uses a Go WebAssembly module to perform some computation.

The full source code for this example is available on Github: https://github.com/your-repo-link

Prerequisites

To follow along with this tutorial, you‘ll need the following:

  • Go 1.11 or later installed on your machine
  • Node.js and npm installed on your machine
  • A basic understanding of React and JavaScript
  • Familiarity with Go syntax and concepts

Creating the React App

First, let‘s create a new React app using create-react-app. Open your terminal and run:

npx create-react-app go-wasm-react-app
cd go-wasm-react-app

This will create a new React project in the go-wasm-react-app directory and switch to it.

Next, we‘ll install some additional dependencies we‘ll need:

npm install --save-dev @babel/cli @babel/core @babel/plugin-transform-react-jsx @babel/preset-env

These Babel packages will allow us to transpile our JavaScript code to a format that‘s compatible with older browsers.

Now, let‘s update our package.json file to add a few new scripts:

"scripts": {
  "build:go": "GOOS=js GOARCH=wasm go build -o ./public/main.wasm ./src/go/main.go",
  "build:js": "babel ./src/js/index.js -o ./public/index.js",
  "build": "npm run build:go && npm run build:js",
  "start": "react-scripts start"
},

These scripts will allow us to:

  • Compile our Go code to a WebAssembly module
  • Transpile our JavaScript code with Babel
  • Build both the Go and JavaScript code with a single command
  • Start the React development server

Writing the Go Code

Next, let‘s write the Go code for our WebAssembly module. Create a new directory src/go and add a main.go file with the following:

package main

import "fmt"

//export add
func add(x, y int) int {
    return x + y
}

func main() {
    fmt.Println("Go WebAssembly Initialized")
}

This code exports a single add function that takes two integers and returns their sum. The main function simply prints a message to the console.

To compile this to WebAssembly, we‘ll use the build:go script we defined above:

npm run build:go

This will compile main.go to a WebAssembly module located at public/main.wasm.

Writing the React Component

Now, let‘s create a React component that will load and use our WebAssembly module. Create a new file src/js/index.js with the following:

import React, { useState } from ‘react‘;

function WasmAdd() {
  const [result, setResult] = useState(null);

  const handleClick = async () => {
    const go = new Go();
    const wasm = await WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject);
    go.run(wasm.instance);

    const add = window.add;
    const sum = add(2, 3);
    setResult(sum);
  }

  return (
    <div>

      <button onClick={handleClick}>Compute 2 + 3</button>
      <p>Result: {result}</p>
    </div>
  );
}

export default WasmAdd;

This component does a few key things:

  1. It creates a new Go instance and loads the main.wasm module using WebAssembly.instantiateStreaming. This returns a Promise that resolves to a WebAssembly.Instance.
  2. It invokes go.run on the instance, which will run the Go main function and initialize the WebAssembly module.
  3. It grabs a reference to the exported add function from the global window object.
  4. When the button is clicked, it invokes add with the arguments 2 and 3, and stores the result in component state.
  5. It renders the result in the component template.

To use this component in our React app, replace the contents of src/App.js with:

import React from ‘react‘;
import WasmAdd from ‘./js/index‘;

function App() {
  return (
    <div className="App">
      <WasmAdd />
    </div>
  );
}

export default App;

Finally, let‘s transpile our JavaScript code using the build:js script:

npm run build:js

Running the App

We‘re now ready to run our app! Start the development server with:

npm start

Open http://localhost:3000 in your browser and you should see the "WebAssembly Addition in React" heading and a button. Click the button and you should see the result "Result: 5" displayed on the page.

Congratulations! You just created a React app that uses a Go WebAssembly module to perform computation. Of course, this is a very simple example, but the same principles apply to more complex applications.

Real-World Use Cases

While our example was trivial, there are many compelling real-world use cases for WebAssembly with Go in React applications. Some examples include:

  • Porting existing Go libraries and applications to the web. Many companies and developers have significant investments in Go code that could be reused on the web via WebAssembly without a complete rewrite.

  • Computationally intensive tasks like image/video editing, computer vision, data compression, etc. The combination of Go‘s performance and WebAssembly‘s near-native execution speeds make it possible to build web apps that rival the performance of native desktop apps for demanding tasks.

  • Game engines and 3D graphics. Go‘s simplicity and support for concurrency make it a great fit for game development. Projects like https://github.com/EngoEngine/engo allow using Go to write game engines that compile to WebAssembly and can be embedded in web pages.

  • Cryptography and security-sensitive applications. Go‘s security guarantees and WebAssembly‘s sandboxed execution environment make it possible to implement secure cryptographic routines and other security-critical tasks in a web app.

Performance Considerations

While WebAssembly enables near-native performance in the browser, there are still some important performance considerations to keep in mind:

  • Startup time. WebAssembly modules can be large and take some time to download and compile. This can impact the startup time of your application. Techniques like code splitting and lazy loading can help mitigate this.

  • Memory usage. WebAssembly modules have their own memory space separate from JavaScript. Passing data between WebAssembly and JavaScript requires serialization and deserialization, which can be costly for large data sets. Minimizing data transfer and using shared memory techniques can help.

  • Garbage collection. The Go runtime includes a garbage collector that runs periodically to free unused memory. This can introduce pause times that impact the responsiveness of your application. Tuning the garbage collector and minimizing allocations can help reduce this impact.

Resources and Next Steps

To learn more about WebAssembly with Go in React, check out these resources:

I hope this post has inspired you to start experimenting with WebAssembly and Go in your React projects. The combination of Go‘s simplicity and performance with React‘s declarative UI model is a powerful one that enables building a new class of high-performance web applications.

While there are still some rough edges and limitations, WebAssembly is a rapidly evolving technology with a bright future. It‘s an exciting time to be a web developer, and I can‘t wait to see what new types of applications become possible as WebAssembly matures.

Similar Posts