Building Apps With WebAssembly

— Written by chicoxyzzy

Please update Socket to version 0.5.4 or higher to use all the features described in this article.

TLDR; Socket runtime makes it easy to build and distribute apps with Web Assembly!

WebAssembly is a low-level, assembly-like code that runs with near-native performance in modern browsers. It complements JavaScript by providing a way to run code written in other languages on the web. It is designed to be a portable target for compilation of high-level languages like C/C++/Rust/Zig, enabling deployment on the web for client and server applications. WebAssembly allows you to avoid speculative optimization and to write code that is more predictable and easier to optimize. That means that you can write a heavy-computation code.

Step-by-Step Guide to Using WebAssembly in Socket Runtime

Let's create a simple WebAssembly module and integrate it with JavaScript in a Socket runtime environment.

Step 1: Writing the WebAssembly Module in C

We'll start by writing a basic WebAssembly module using WasmFiddle. Here's an example in C:

extern int myFunc(int);

int identity(int num) {
  return num;
}

int runMyFunc(int num) {
  return myFunc(num);
}

This module exports two functions: identity and runMyFunc. identity is a simple function that returns its argument. runMyFunc calls myFunc and returns its result. myFunc is not defined in this module, so we will need to define it in JavaScript.

Step 2: Binding with JavaScript

Now, we'll provide the JavaScript bindings for our WebAssembly module:

const importObject = {
  env: {
    myFunc: arg => arg * arg
  }
};

var wasmModule = new WebAssembly.Module(wasmCode);
var wasmInstance = new WebAssembly.Instance(wasmModule, importObject);

log(wasmInstance.exports.identity(42)); // WasmFiddle log function
log(wasmInstance.exports.runMyFunc(5)); // WasmFiddle log function

Build and run this in WasmFiddle to see the functions in action.

Step 3: Implementing in Socket Runtime

Let's download the WebAssembly module and use it in Socket runtime app. You can download the module by clicking "Wasm" button in WasmFiddle. It will download a file named program.wasm. Later you will need to put this file in webassembly folder in your Socket runtime app source directory. Alternatively, you can take it from our WebAssembly test suite.

Content-Security-Policy

Socket runtime supports WebAssembly modules. To enable them, you need to allow them in CSP. ssc init command (as well as create-socket-app) will do it for you.

Let's take a look at the CSP that is generated by ssc init:

    <meta
      http-equiv="Content-Security-Policy"
      content="
        connect-src https: file: ipc: socket: ws://localhost:*;
        script-src https: socket: http://localhost:* 'unsafe-eval';
        img-src https: data: file: http://localhost:*;
        child-src 'none';
        object-src 'none';
      "
    >

You can see that script-src allows unsafe-eval. This is enough to run WebAssembly modules! Add this line to your CSP if you are using a different template or hand-made index.html in your project. If necessary, you can replace unsafe-eval with a more specific wasm-unsafe-eval value.

Importing WebAssembly modules

Note: There's a WebAssembly feature called ES Modules Integration that allows you to import WebAssembly modules using JavaScript's import statement. It is not finished yet, but it will be supported by Socket runtime as soon as it is available in browsers.

In browsers people usually use fetch to load WebAssembly modules. Socket runtime supports it as well. You can use fetch to load WebAssembly modules from the network or from the file system. For example, you can load a WebAssembly module from the file system like this:

  const response = fetch('./webassembly/program.wasm')
  const { instance } = await WebAssembly.instantiateStreaming(response, importObject)
  // use WebAssembly module instance
  console.log(instance.exports.runMyFunc(5)) // prints 25
  console.log(instance.exports.identity(42)) // prints 42

It is also possible to load WebAssembly modules from file system using built-in socket:fs, socket:fs/promises and socket:fs/web modules.

  import { createFile } from 'socket:fs/web'

  const file = await createFile('./webassembly/program.wasm') // wrap file in a File object
  const stream = file.stream() // get a ReadableStream
  const response = new Response(stream, { // create a Response object
    headers: {
      'Content-Type': 'application/wasm'
    }
  })
  const { instance } = await WebAssembly.instantiateStreaming(response, importObject)
  // use WebAssembly module instance
  console.log(instance.exports.runMyFunc(5)) // prints 25
  console.log(instance.exports.identity(42)) // prints 42

Using a synchronous API to instantiate WebAssembly modules is not recommended, because it is blocking the main thread. However, it is possible to do it in Socket runtime:

  import fs from 'socket:fs'

  const wasm = fs.readFileSync('./webassembly/program.wasm')
  const module = new WebAssembly.Module(wasm)
  const { exports } = new WebAssembly.Instance(module, importObject)
  // use WebAssembly module instance
  console.log(exports.runMyFunc(5)) // prints 25
  console.log(exports.identity(42)) // prints 42

Congratulations! You have just used WebAssembly in Socket runtime!

What is next for WebAssembly?

I already mentioned ES Modules Integration that will make using WebAssembly modules even easier. There are many other interesting features that are coming to WebAssembly.

Garbage collection support will make it easier to write code in languages with automatic memory management, such as C#, Java, OCaml, Kotlin, Dart and others. It is already available in V8 and is coming to JavaScriptCore soon, so you can expect it to be available in Socket runtime as well.

WASI is a system interface for WebAssembly. It allows WebAssembly modules to interact with the host system. We are planning to support it in Socket runtime.

You can find many more interesting upcoming features in the WebAssembly CG Proposals repository.

To enhance the capabilities of Socket runtime, we're actively focusing on elevating WebAssembly (Wasm) to a first-class status within our ecosystem. Our team is currently developing a range of innovative features that leverage the power and versatility of Wasm. These advancements are designed to not only significantly improve user experience but also to broaden the scope of extensibility. By integrating Wasm more deeply into our framework, we aim to unlock new possibilities and ensure that our platform remains at the forefront of technological innovation. Stay tuned!