I’ve been using WebAssembly aka WASM for a while in production to do some incredible stuff in the browser, things that would be prohibitive in terms of performance otherwise. Here are some real use-cases I’ve used WASM for:
- Running statistical processing 10 million rows (roughly 50 columns in each row) of CSV data in browser. This feature required us to create a temporary playground of reports generated for our clients where they could run their own analysis without the need of permanent storage or costly servers.
- Parsing DOCX and PPTX files in browser and running DL Models on the data in browser. We saved thousands of $ by eliminating the persistent backend and reduced the time to result from minutes to a few seconds.
- One use-case ended up having tons of complex user input validations, we were building these validators on the server-side anyway, and with WASM we just put the same validator code in the client improving productivity and minimizing chances of mistakes and discrepancies between client and server validators.
- Audio-Video encoding in browser, saving time and thus making content generation more dynamic and open to experimentation.
And a few more …
Since you are here you’ve probably already heard of or read about the success stories of Figma, Adobe has deployed their browser based Acrobat, Lightroom etc. using WASM, Google uses WASM in countless products and plenty more. WASM also finds it’s uses beyond the browser - Shopify Functions, Cloudflare Workers and many more.
In this post, I’m going to focus on WASM in Browser by building a small text-diff application with Rust.
TL; DR
The Final Output

What is WASM?
I think Mozilla provides us with the best introductory definition:
WebAssemblyWebAssembly is a type of code that can be run in modern web browsers — it is a low-level assembly-like language with a compact binary format that runs with near-native performance and provides languages such as C/C++, C# and Rust with a compilation target so that they can run on the web. It is also designed to run alongside JavaScript, allowing both to work together.
-— MDN
And my nutshell version
WASM brings the power of low-level programming languages to the browser environment. That’s it!
When to use WASM?
It’s important to note that WASM is not a replacement for your current web app stack. JavaScript, HTML, CSS are the native standards, and they are your primary building blocks for your web-app. Think of WASM as a tool to augment your browser stack or a tool to eliminate bottlenecks.
Here are my top 3 reasons of using WASM:
- Performance: If your application’s Data Processing far outweighs DOM Manipulation, WASM will help.
- Portability: If you use a bunch of battle-tested libraries but their JS port is not available, not as performant or not feature-complete, WASM might help
- Edge Compute: This has been the most common trigger for my WASM usage. If your application does a bunch of stuff that would require backend-level programs, but the client-server communication latency is a major limiting factor to the experience; or a specific feature is not used frequently enough to justify a backend server deployment - WASM probably would be the right tool for you.
How to use WASM?
Ok, enough with the rationale and explanations let’s dive into building something real. We are going to be creating a simple web-application to display a diff between two text blocks and hopefully in this process see some performance benefits. We are going to write a simple Rust program to do the diffing in WASM while rest of our interface will be in HTML + JS.
Setup
Let’s start by creating a basic Webpack project.
mkdir wasm-diff
cd wasm-diff
npm init -y
npm install --save-dev webpack webpack-cli webpack-dev-server html-webpack-plugin nodemon
npm i -D tailwindcss
npm i -D style-loader css-loader postcss postcss-loader postcss-preset-env
npx tailwindcss init
In the first 3 commands we initialized a basic npm project, the 4th line is webpack related stuff. The 5th, 6th and 7th lines are for tailwindcss library, I just HATE working with vanilla CSS.
Configuring TailwindFollow these instructions for configuringtailwindcssfor our project. This is optional and vanilla CSS will work just fine.
| |
Now, let’s wrap up our working app with a working src/index.js file.
| |
Let us run our app now to see if everything is in order.
npm run dev
Navigate to http://localhost:8080 in your browser and you should see the app running.

That should complete our setup! Let’s move on to getting the diff to work.
Diff with JavaScript
We are going to be using diff-match-patch npm package for our JavaScript diff, let’s install it.
npm i diff-match-patch
The interface is simple, we’ll use the diff_main API of the package to create the diffs of our old and new texts and then use the example diff_prettyHtml API to create the HTML string and display it.
Let’s change our src/index.js to do that.
| |
We initialized the JavaScript DiffMatchPatch class, and in our jsDiff() function which is called by onclick event listener, we computed the diff, cleaned it up a bit and put the results and the stats in their respective DOM containers.

Note the diff time, in my current run it took about 512 ms to generate the diff. Let’s see if diffing with a WASM module adds any performance advantage to it or not!
We are going to compile a rust library to WASM, but if you are comfortable with C++, Go or C#, this will work just as well. Each one would have their own nuances to handle but that’s a story for another day!
Getting ready to WASM
Let’s start by creating a new rust project to house our WASM code, I’m creating an unimaginative wasm directory, it could be anything. We’ll use wasm-pack tool to help us with the build process.
PrerequisitesRust toolchain
Assuming you have rust and cargo toolchain installed, let’s key in the commands.
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
mkdir wasm
cd wasm
cargo init --lib
The first command installs the cargo extension wasm-pack, through command 2 to 4 we are just creating a rust project of type --lib, it’ll not contain a main() function.
To ready our wasm crate let’s go ahead make some changes to wasm/Cargo.toml
| |
The wasm-bindgen crate we added to our [dependencies] section provides us with the batteries required to interface with JS and browser modules. The [lib] crate-type declaration basically tells our compiler to generate dynamic system library. This is used when compiling a dynamic library to be loaded from another language.
We’ll also need to modify our webpack.config.js to enable loading the generated wasm files.
| |
By adding the declaration experiments.asyncWebAssembly: true we are telling webpack to load wasm modules asynchronously.
As a final step of the setup, let’s go ahead and add the build command to our package.json file to compile and generate the wasm modules and put it in our src/pkg directory.
| |
Let’s break down the dev command wasm-pack build wasm --out-dir ../src/pkg --target web && webpack serve --config webpack.config.js
wasm-pack build- thecargo extensionwe installed earlier, we are using it to generatewasmmodules from ourrustlibrarywasm- tellingwasm-packthat ourrustlibrary code resides in<PROJECT_ROOT>/wasmdirectory--out-diris tellingwasm-packto put the generated output files in<PROJECT_ROOT>/src/pkgdirectory--target webis tellingwasm-packto build for the browser. You could write awasmlibrary for sayNodeJSenvironment, this option will change&& webpack ...- rest of the command is basically to run the dev server we have already been running
That concludes our setup, if we run npm run dev now, we should see a bunch of files generated under the src/pkg directory.
But wait, nothing really happens yet! We have not written any code to call or do anything with the WASM module we just generated. Let’s work on that!
Hello World from WASM
We are going to write a function in our rust library return a Hello World string, and then we are going to call it from our JavaScript code.
Let’s modify our wasm/src/lib.rs file to return the Hello World.
| |
The code is simple:
- the
wasm-bindgen::prelude::*imports the batteries required to generate wasm modules and helpers that enable ourrust fnor code to be called from JavaScript #[wasm_bindgen]macro tells the compiler that this function needs to be exposed for an external interfaceconsole_error_panic_hook::set_once();is initializing a globalhookso that if ourwasmcode generated fromrustpanics, aconsole.errorwould be thrown
Rest of it is basic rust code, just returning a Result<String>, Hello from Wasm! in this case.
Now, let’s add a couple of lines to our src/index.js file to call this function.
| |
Basically we are importing the wasm initializer and initializing the wasm module, once the initializer resolves we are simply calling the run() function and printing its output.
Let’s run it .. if everything goes well, we should see Hello from Wasm! in our console!
npm run dev
And there you go, our first real stuff from WASM…

Diff with WASM
Let’s pick up a rust crate to do our Diff, there are a few options available if you search crates.io with the keywords diff match patch but as far as I know none of them support WASM out of the box. For this example, I’m going to use my own UNPUBLISHED crate diff-match-patch for the WASM diffing.
Pro TipsNot allrust crateswould beWASM compatiblefor various reasons. A sure-shot way of figuring this out is to navigate todocs.rspage for thecrateand look through thePlatformstab. Most mature crates would showwasm32-unknown-unknownif they areWASMready. The other way is to clone or use the crate in a toy project and usecargo check --target wasm32-unknown-unknownto look for errors. Some Crates might also enableWASMwithfeatureflags.
Let’s add diff-match-patch-rs to our dependencies in wasm/Cargo.toml.
| |
Now, let’s change our wasm/src/lib.rs to do the actual diffing.
| |
NoteWe don’t REALLY need to declare a separatestruct Differfor our relatively simple use-case here, but often you’ll need to create an instance of a struct and access their methods. This is just to demonstrate the possibilities and introduce a few handy concepts.
Let’s break down what’s happening here.
line 4-8we are declaring astruct Differand annotating it with the#[wasm_bindgen]macro, basically telling the compiler that instance of this struct will be accessed fromforeign code.line 10-11we are annotating theimpl Differblock with#[wasm_bindgen]- in a more complex usecase we could have otherimpl Differblocks which will not be accessed from ourJavaScript, we’ll just skip#[wasm_bindgen]for thoseimpl Differblocks.line 12is interesting, here we are using the#[wasm_bindgen(constructor)]macro to tell the compiler that thepub fn new()should be called as a constructor if we donew Differ()from ourJavaScriptcode.
The rest of it serves the exact same flow to our JavaScript diff function call, it calls the diff_main() method of diff_match_patch_rs::dmp::DiffMatchPatch module instance and then created the html string and returns it.
Let’s modify our src/index.js to make use of this WASM diff code.
| |
Let’s run it now …
npm run dev
And, then, lets browse to localhost:8080 and click the Run JS Diff and Run WASM Diff buttons …
🥁🥁🥁 … drum rolls …

WHOA, the WASM variant is 80% faster than the JavaScript version.
Closing thoughts & Next steps
80% speedup is a LOT of performance gain. In a static setup like our current version, it may not make a difference but imagine you are writing a collaborative editing tool like Google Docs, and you need to diff on every keyup … the performance gain we saw is going to be the difference between an usable and an unusable product.
Here are some ideas you can explore based on what you’ve done today:
WASM modules tend to be larger in size than your standard JavaScript variant. But good news is, there are ways of optimizing it. Read up about those optimizations (and easier ways of setting up WASM) in the
rust wasm book. Here’s the chapter on specific size optimizations.Modify the code and apply the
patchto the source text. Hint: You’ll need to figure out a way of sendingJsValueinstead of plainStringwe’ve been using so far.
A word of cautionIf you are using mydiff-match-patch-rscrate, be careful. It’s still a work in progress and I’ll probably work on optimizations soon.
- Convert this application to a
real-time diffmachine.
Before we close today …
If you have found this tutorial helpful consider spreading the word, dropping a star to the git repo or the crate repo it would act as a strong motivator for me to create more. If you found an issue or hit a snag, reach out to me @beingAnubhab. I’d love to hear about stuff you did with what you learnt today.
Acknowledgements, references and further reading
diff-match-patchis based onMyer's Diffalgorithm, which has been a standard since 1989, how cool is that! This specific version we worked on is based ongoogle/diff-match-patchlibrary; the original source code can be found here.- We used
wasm-bindgenwhich is a rust library and CLI tool that facilitates interactions betweenWASMmodules andJavaScriptcode. Like many things in Rust, it’s well documented and there’s abookfor that. wasm-packmakes our life a lot easy when we work withRust WASMand the book gives a nice and easy to follow walkthrough.- If you are as fascinated by
WebAssemblyas I am, and you are looking for the next cool project idea - browse through made with webassembly.