Hello friend! Welcome to part 4 of our in-depth guide on mastering WebAssembly as a web developer.
In this chapter, we‘ll explore the symbiotic relationship between WebAssembly and JavaScript. We‘ll learn how they complement each other to build blazing fast web apps.
By the end, you‘ll understand:
- How to leverage WebAssembly modules in JavaScript code.
- The WebAssembly JavaScript API for loading and running WebAssembly.
- How WebAssembly and JS compare and work together.
- Real-world examples of WebAssembly boosting JS performance.
So buckle up your seatbelts! This is going to be a fun ride.
A Quick WebAssembly Refresher
Before we dive in, let‘s recap what WebAssembly is and why it matters:
- WebAssembly (abbreviated Wasm) is a new binary instruction format that runs in the browser.
- It provides near-native performance for web applications.
- Code is compiled ahead of time from languages like C/C++, Rust, Go, etc to highly optimized .wasm modules.
- These modules can be loaded into JavaScript to boost performance of compute-heavy parts of your app.
- WebAssembly is supported across all major browsers and is a W3C open web standard.
So in a nutshell, WebAssembly introduces native-app levels of performance to the web!
In the first parts of this guide, we learned:
- Part 1: Introduction to WebAssembly goals, design and usage
- Part 2: WebAssembly key concepts, use cases and limitations
- Part 3: How WebAssembly portability and security works
Now we‘re ready to see WebAssembly in action working together with JavaScript!
Using WebAssembly Modules in JavaScript
The most common way to leverage WebAssembly is by loading and running .wasm modules inside JavaScript.
This allows mixing the performance of WebAssembly with the flexibility of JavaScript to build highly responsive web apps.
For example, you could use WebAssembly for number crunching and image processing while JavaScript handles app logic, DOM manipulation, etc.
But how does this work under the hood?
Well, there are 3 main steps to load and run WebAssembly from JavaScript:
-
Load the .wasm binary module into the browser, usually via
fetch(). -
Compile the .wasm bytes into a
WebAssembly.Moduleinstance usingWebAssembly.compile()orinstantiate(). -
Instantiate the module to get an
instancecontaining the .wasm exports.
Once you have a WebAssembly instance, you can call the exported functions directly from JavaScript!
Let‘s see a quick code example:
// 1. Fetch the .wasm bytes
fetch(‘math.wasm‘)
// 2. Compile/instantiate it to a WebAssembly module
.then(response => response.arrayBuffer())
.then(bytes => WebAssembly.instantiate(bytes))
// 3. Call exports on the WebAssembly instance
.then(results => {
const instance = results.instance;
alert(instance.exports.add(1, 2));
});
This allows executing the high-performance WebAssembly add() function directly within our JavaScript app!
Under the hood, WebAssembly provides an efficient binary format and instruction set optimized for execution inside browsers.
But from the developer‘s perspective, it simply provides a way to run near-native C/C++/Rust code on the web!
Next let‘s look at real-world examples of using WebAssembly modules in JavaScript code.
WebAssembly Usage Examples
There are endless possibilities for supercharging your JavaScript apps with WebAssembly!
Some examples:
-
Computer vision – Run OpenCV or other imaging libraries for object detection, filters, etc.
-
3D rendering – Integrate OpenGL, Unity or other 3D engines using WebGL.
-
Math/physics – Accelerate math computations for visualization, simulations, AI, etc. Popular libraries include BLAS and LAPACK.
-
Data compression – Decompress images, audio, video extremely quickly with codecs like Brotli.
-
Gaming – Improve physics, collision detection, pathfinding, AI and more. Unity, Unreal Engine, Cocos2d all compile to WebAssembly.
-
Signal processing – Process audio samples in real-time for visualization or streaming applications.
-
Image processing – Apply filters, transformations and adjustments using low-level image libraries.
-
Data analysis – Link R, Python (NumPy), Julia for statistical analysis and machine learning.
As you can see, WebAssembly opens a world of possibilities for CPU-intensive web apps!
Large companies are taking notice too. For example:
-
Figma uses WebAssembly to power their online collaborative design tool.
-
Adobe redesigned Photoshop‘s filters using WebAssembly, resulting in massive performance gains.
-
Autodesk integrated WebAssembly into their Force.com platform to run demanding 3D CAD models in the browser.
So WebAssembly is moving beyond proofs-of-concept into large production web apps!
Now let‘s look at the WebAssembly JavaScript API that makes all this possible.
The WebAssembly JavaScript API
The WebAssembly JavaScript API provides the core capabilities for loading and interacting with .wasm modules from JavaScript.
Let‘s explore some of the key APIs:
WebAssembly.instantiate()
The WebAssembly.instantiate() function is the easiest way to get up and running with WebAssembly.
It compiles a .wasm binary and generates an instance in a single step:
WebAssembly.instantiate(bufferSource)
.then(results => {
// results.module + results.instance
});
This takes the .wasm bytes in an ArrayBuffer and returns a Promise containing:
module–WebAssembly.Modulerepresenting the compiled code.instance–WebAssembly.Instanceto call exported functions.
For example, we can run our math example like:
fetch(‘math.wasm‘)
.then(response => response.arrayBuffer())
.then(bytes => WebAssembly.instantiate(bytes))
.then(results => {
results.instance.exports.add(1, 2);
});
This provides the simplest way to get up and running with WebAssembly in JavaScript.
WebAssembly.compile()
For more control, you can separate the compile and instantiate steps using WebAssembly.compile():
WebAssembly.compile(bufferSource)
.then(module => {
// Compile to WebAssembly.Module
});
This compiles the .wasm bytes into a WebAssembly.Module. We can then instantiate it:
const importObject = {
imports: {
importedFunc: () => {}
}
}
module.instantiate(importObject)
.then(instance => {
// Instantiated instance
});
Splitting compile and instantiate allows customizing imports and exports.
WebAssembly.instantiateStreaming()
For large .wasm files or streaming use cases, WebAssembly.instantiateStreaming() compiles and instantiates a .wasm module from a response stream:
fetch(‘large.wasm‘)
.then(response =>
WebAssembly.instantiateStreaming(response)
)
.then(results => {
// results.module + results.instance
});
This avoids buffering the entire .wasm module into memory for better performance.
Low-Level APIs
Additionally, the WebAssembly JavaScript API provides lower-level control:
WebAssembly.validate(): Validates .wasm bytes before compiling.WebAssembly.Module: Represents compiled WebAssembly code.WebAssembly.Instance: An instantiated module with applied imports.WebAssembly.Memory: Represents WebAssembly linear memory.WebAssembly.Table: Represents WebAssembly function tables.
Plus APIs for tooling use cases:
WebAssembly.compileStreaming(): Compiles .wasm text format to binary.WebAssembly.serialize() / deserialize(): Serialize and deserialize modules.
So in summary, the WebAssembly JavaScript API provides everything you need to load, compile, instantiate and interact with .wasm modules!
Now that we‘ve seen how to use WebAssembly in JavaScript, let‘s compare the two technologies.
How WebAssembly and JavaScript Compare
WebAssembly and JavaScript have very different approaches and tradeoffs:
| WebAssembly | JavaScript | |
|---|---|---|
| Language type | Low-level, compiled | High-level, interpreted |
| Speed | Near-native performance by compiling to optimized machine code | Slower just-in-time compilation improves over time as code is run |
| Loading | Must be compiled ahead-of-time | Can be delivered as human-readable source |
| Code execution | Non-stop native-speed execution | Pauses for garbage collection, JIT compilation, etc |
| Code size | Very small binary format | Much larger textual format |
| Memory management | Manual | Automatic garbage collection |
| Learning curve | Moderate difficulty due to low-level nature | Easy to pickup since it is high-level and dynamically typed |
| Developer experience | Poor – lack of tools, debugging, IDE integration | Excellent – massive ecosystem of tools, frameworks, tutorials |
| Maturity | Bleeding edge – specifications still evolving | Mature technology with widespread adoption |
| Use cases | Performance-critical computations and tasks | General web development, DOM manipulation, asynchronous programming |
| Accessibility | Lack of tools and standards currently | Mature accessibility capabilities like ARIA roles |
In summary:
-
WebAssembly provides raw performance by compiling to native machine code.
-
JavaScript trades off some speed for a more productive developer experience.
So they complement each other very nicely!
The best practice is to use WebAssembly for the performance-critical parts of your app and JavaScript for everything else.
This gives you the best of both worlds – native speeds where needed while retaining JavaScript‘s flexibility.
Now let‘s look at some real-world examples of how WebAssembly boosts JavaScript apps.
Real-World WebAssembly Use Cases
Let‘s look at some examples of companies using WebAssembly to supercharge their JavaScript apps and frameworks.
Figma
Figma is an online collaborative design tool built with JavaScript, WebAssembly and WebGL.
They use WebAssembly for handling graphics rendering and image processing. Offloading this work resulted in significant speed improvements:
- WebGL rendering sped up by 4x
- Image processing like filters and adjustments became 10-15x faster
This allowed building a highly responsive design tool rivaling desktop apps – a major competitive advantage.
Adobe
After acquiring Figma, Adobe has also invested heavily into WebAssembly.
For example, they rewrote Photoshop‘s filters using WebAssembly and saw massive performance gains:
| Filter | JS time (secs) | WebAssembly time (secs) | Improvement |
|---|---|---|---|
| Smart sharpen | 22 | 0.60 | 37x faster |
| Median | 18 | 0.30 | 60x faster |
| Gaussian blur | 18 | 0.25 | 72x faster |
This allows delivering real Photoshop-caliber image processing as responsive web components!
AutoCAD
The popular CAD (computer-aided design) software AutoCAD by Autodesk has historically required heavy desktop clients.
They are adopting WebAssembly to stream CAD models and enable editing on the web. For example, see their Force.com platform.
This demonstrates how WebAssembly delivers desktop-class applications through the browser!
React and Vue
Popular JavaScript frameworks are also leveraging WebAssembly:
-
The React team released a WebAssembly Packager for high-performance React components.
-
The Vue.js core team is developing vue-web-wasm to natively compile Vue components to WebAssembly.
Frameworks stand to gain a lot from WebAssembly performance while retaining their familiar JS APIs.
Other Notable Uses
Some other interesting WebAssembly use cases include:
-
Cloudflare Workers – They use WebAssembly to run tasks on their edge infrastructure for security, optimization and more.
-
Fastly Compute@Edge – Also uses WebAssembly workers for low-latency edge computing.
-
WASI – Emerging standard to run WebAssembly outside browsers, even on servers!
So WebAssembly usage is expanding beyond just boosting client-side web apps.
As you can see, WebAssembly opens some extremely exciting possibilities for JavaScript developers!
Putting It All Together
We‘ve covered a lot of ground exploring WebAssembly and JavaScript together:
- WebAssembly modules can be loaded into JavaScript to boost performance.
- The WebAssembly JS API provides everything you need get up and running.
- WebAssembly is lower-level and faster, JavaScript is higher-level and more flexible.
- Real-world examples demonstrate WebAssembly benefits for graphics, 3D, imaging, data analysis and more.
Here are my key takeaways from all this:
-
WebAssembly unleashes native cross-platform performance for JavaScript apps without sacrificing portability and security.
-
You can incrementally add WebAssembly to bottlenecks rather than rewriting everything.
-
WebAssembly expands the possibilities for web applications – unlocking desktop and native-quality experiences.
-
As standards and tools mature, WebAssembly adoption will only grow exponentially.
So in summary – WebAssembly supercharges JavaScript with near-native performance while remaining 100% complementary to JS.
This opens a new world of possibilities on the web!
I hope you enjoyed this deep dive into WebAssembly and JavaScript. Let me know if you have any other topics you‘d like to see covered.
Until next time, happy coding!