NashTech Blog

Rust + WebAssembly vs JavaScript and .NET

Table of Contents

The problem: heavy work and the round trip

When a web app has to resize a large photo or validate a 100,000-row Excel file, the usual answer is to send the file to a server, process it there and send the result back. That works, but it has costs:

  • Waiting on the network: large files must be uploaded and the result downloaded before the user sees anything.
  • Server cost and load: every user’s CPU-heavy job runs on your servers.
  • Privacy: sensitive files leave the user’s device just to be validated or resized.
  • JavaScript has limits: it is fine for UI logic, but CPU-heavy loops are slower and some formats (such as xlsx) lack good libraries.

The question I wanted to answer: when is it worth moving this work into the browser with Rust and WebAssembly, and when is it not?

What are Rust and WebAssembly?

  • Rust is a fast, memory-safe programming language.
  • WebAssembly (WASM) is a compact binary format that every modern browser runs in a safe sandbox, next to JavaScript.
  • Together: you write the heavy logic in Rust, compile it to a .wasm file, and JavaScript loads it and calls it like a normal function. The work stays on the user’s device.

In this project the Rust code is about 235 lines and compiles to a 539 KB file that the browser loads in roughly 30 ms.

The demo: same work, split screen

I built a split-screen app that runs the same work two ways and measures both. The left side is the traditional approach (JavaScript in the browser, or a C# .NET server with upload and download). The right side is Rust compiled to WASM in the browser, with nothing uploaded.

The workloads were a blur filter on a 12-megapixel image and validation of 100,000-row CSV and XLSX files (ids, emails, amounts and dates). Each side reports upload and download time, compute time, total time, data transferred and memory.

Split-screen demo: image blur on the C# server (left) and Rust/WASM in the browser (right), with upload, compute and total times
Image blur: C# server (left) vs Rust/WASM (right)

Full Demo

 

How I kept the comparison fair

  • All three engines use the same integer algorithms and validation rules, and the app checks that their outputs match.
  • JavaScript and WASM both run in Web Workers, so neither blocks the page.
  • Each side does 1 warm-up run and 3 timed runs; I report the median.
  • The server reports its own timings, so I can separate compute from network time.
  • The WASM timing includes copying data between JavaScript and WASM, which is a real cost.

Results

End-to-end time the user waits (lower is better), measured on one machine with the API on localhost.

Scenario Compared with Traditional Rust → WASM Speed-up
Image blur, 12 MP JavaScript (browser) 7.69 s 6.92 s 1.1×
Image blur, 12 MP C# server 14.90 s 6.75 s 2.2×
CSV validate, 100k rows JavaScript (browser) 147 ms 61 ms 2.4×
CSV validate, 100k rows C# server 424 ms 67 ms 6.3×
XLSX validate, 100k rows C# server 3.98 s 1.12 s 3.6×
Image blur on a 12 MP photo: JavaScript in the browser vs Rust/WASM
Image blur: JavaScript (left) vs Rust/WASM (right)
CSV validation of 100,000 rows: JavaScript vs Rust/WASM
CSV validation, 100k rows: JavaScript (left) vs Rust/WASM (right)
CSV validation of 100,000 rows: C# server vs Rust/WASM
CSV validation, 100k rows: C# server (left) vs Rust/WASM (right)
XLSX validation of 100,000 rows: C# server vs Rust/WASM
XLSX validation, 100k rows: C# server (left) vs Rust/WASM (right)

What stands out:

  • The biggest wins come from removing the network, not only from faster compute. Against the server, WASM won by 2.2× to 6.3×.
  • Against tuned JavaScript on pure compute, the gain was small (1.1× on the image blur). WASM is not magic; the architecture matters as much as the language.
  • Absolute numbers will differ on your hardware. Compare the ratios, and note that real network latency would favour WASM even more than my localhost test.

Advantages

  • Faster end to end in every scenario I tested, with the largest gains versus the server.
  • Private by design: files are processed on the device and never uploaded.
  • Cheaper to run: compute moves to the user’s machine, so server cost and load drop.
  • Safe and reusable: one strongly typed Rust crate, with memory safety and no garbage collector.
  • Works offline once the page and module are loaded, and Web Workers keep the interface responsive.

Disadvantages and limits

  • Steep learning curve: ownership and lifetimes take time; Rust is slower to write than JavaScript or C#.
  • Heavier toolchain: you need Rust and wasm-pack, and CI gets more complex.
  • Boundary cost: data is copied between JavaScript and WASM, so tiny tasks may not benefit.
  • Bundle size: my module is 539 KB (I have not applied wasm-opt yet), an extra download on first load.
  • Harder debugging: browser tooling for WASM is improving but trails JavaScript.
  • Not always a win: tuned JavaScript is competitive on simple work.

Caveats on my own test: one machine, API on localhost, browser image-decode time not counted, and the JavaScript engine has no xlsx parser, so XLSX was compared only with the server.

When to use it, and when not to

Good fit

  • CPU-heavy work in the browser (image, audio, parsing, crypto).
  • Large files where upload time dominates.
  • Privacy-sensitive data that should stay on the device.
  • One core library shared across web, server and command line.

Poor fit

  • Simple DOM or UI logic, where JavaScript is the right tool.
  • Small inputs, where the JavaScript ↔ WASM copy dominates.
  • Teams with no Rust skills and tight deadlines.
  • Work that needs server-side data or central auditing.

What I learned and what is next

Beyond the numbers, this project built practical skills: Rust fundamentals (ownership, error handling, crates), the WebAssembly toolchain (wasm-pack, wasm-bindgen, Web Workers and the JavaScript ↔ WASM boundary), fair benchmarking (warm-ups, medians, output-match checks), and a full-stack comparison with React, Vite and a .NET 10 API.

Next I plan to apply wasm-opt and measure the size and speed gain, try SIMD and multi-threading for the image filters, and pilot WASM on one real internal workload with heavy client-side files.

Code: github.com/haima24/WASM_Demo. Questions or your own WASM results? Leave a comment below.

Picture of Huỳnh Minh Tú

Huỳnh Minh Tú

Senior Software Engineer

Suggested Article

Scroll to Top