Skip to content
All essays
CraftMarch 1, 202511 min

WebAssembly: High-Performance Web Applications

Master WebAssembly. Build near-native performance applications for the web.

Ü
Ümit Uz
Mobile & Full Stack Developer

What is WebAssembly?

Binary instruction format for a stack-based virtual machine. Wasm enables high-performance applications on web pages.

JavaScript:
- Interpreted or JIT compiled
- Dynamic typing
- Garbage collection

WebAssembly:
- Pre-compiled
- Static typing
- Manual memory management
- Near-native performance

Use Cases

  • Games: High-performance game engines
  • Video/Audio: Encoding/decoding
  • Cryptography: Secure algorithms
  • Scientific: Simulations, calculations
  • Legacy: Port C/C++/Rust applications

Hello World (Rust)

Setup

bash
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Add wasm target
rustup target add wasm32-unknown-unknown

# Install wasm-pack
cargo install wasm-pack

Project

rust
// src/lib.rs
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn greet(name: &str) -> String {
    format!("Hello, {}!", name)
}

#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

#[wasm_bindgen]
pub struct Calculator {
    value: i32,
}

#[wasm_bindgen]
impl Calculator {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Calculator {
        Calculator { value: 0 }
    }

    pub fn add(&mut self, n: i32) {
        self.value += n;
    }

    pub fn get_value(&self) -> i32 {
        self.value
    }
}

Build

bash
# Build wasm package
wasm-pack build --target web

# Output:
# pkg/my_wasm_bg.wasm     # WebAssembly binary
# pkg/my_wasm.js          # JavaScript glue
# pkg/my_wasm.d.ts        # TypeScript types

JavaScript Integration

html
<!DOCTYPE html>
<html>
<head>
    <script type="module">
        import init, { greet, add, Calculator } from './pkg/my_wasm.js';

        async function run() {
            await init();

            // Simple function
            console.log(greet('World'));
            console.log(add(5, 3));

            // Class
            const calc = new Calculator();
            calc.add(10);
            calc.add(5);
            console.log(calc.get_value());  // 15
        }

        run();
    </script>
</head>
<body>
    <h1>WebAssembly Demo</h1>
</body>
</html>

Performance Comparison

javascript
// Fibonacci calculation
function fibonacciJS(n) {
  if (n <= 1) return n;
  return fibonacciJS(n - 1) + fibonacciJS(n - 2);
}

// Rust (WebAssembly)
#[wasm_bindgen]
pub fn fibonacci_wasm(n: i32) -> i32 {
    if n <= 1 { return n; }
    fibonacci_wasm(n - 1) + fibonacci_wasm(n - 2)
}

// Benchmark (fibonacci(40)):
// JavaScript:  ~1500ms
// WebAssembly:  ~50ms  (30x faster)

Memory Management

rust
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub struct Buffer {
    data: Vec<u8>,
}

#[wasm_bindgen]
impl Buffer {
    #[wasm_bindgen(constructor)]
    pub fn new(size: usize) -> Buffer {
        Buffer {
            data: vec
![0; size]
        }
    }

    pub fn write(&mut self, offset: usize, byte: u8) {
        if offset < self.data.len() {
            self.data[offset] = byte;
        }
    }

    pub fn read(&self, offset: usize) -> u8 {
        if offset < self.data.len() {
            self.data[offset]
        } else {
            0
        }
    }
}

React Integration

jsx
import React, { useEffect, useState } from 'react';
import init, { Calculator } from './pkg/my_wasm.js';

function App() {
    const [calculator, setCalculator] = useState(null);
    const [value, setValue] = useState(0);

    useEffect(() => {
        async function loadWasm() {
            await init();
            setCalculator(new Calculator());
        }
        loadWasm();
    }, []);

    const handleAdd = (n) => {
        if (calculator) {
            calculator.add(n);
            setValue(calculator.get_value());
        }
    };

    return (
        <div>
            <p>Value: {value}</p>
            <button onClick={() => handleAdd(10)}>Add 10</button>
            <button onClick={() => handleAdd(5)}>Add 5</button>
        </div>
    );
}

Optimization Tips

  1. 1Minimize JavaScript-Wasm boundary calls

- Batch operations - Work in Wasm, return result

  1. 1Use appropriate types

- i32/f32 for numbers - Avoid string manipulation in Wasm

  1. 1Linear memory

- Direct memory access for large data - SharedArrayBuffer for parallel processing

  1. 1SIMD

- Single Instruction, Multiple Data - Parallel processing

rust
// SIMD example
#[target_feature(enable = "simd128")]
pub unsafe fn add_arrays_simd(a: &[f32], b: &[f32], result: &mut [f32]) {
    let len = a.len();
    let chunks = len / 4;

    for i in 0..chunks {
        let i = i * 4;
        let a_simd = v128_load(a.as_ptr().add(i) as *const v128);
        let b_simd = v128_load(b.as_ptr().add(i) as *const v128);
        let sum = f32x4_add(a_simd, b_simd);
        v128_store(result.as_mut_ptr().add(i) as *mut v128, sum);
    }
}

Tools

  • wasm-pack: Build Rust Wasm packages
  • wasm-bindgen: JavaScript-Wasm interoperability
  • wasm-opt: Binary optimizer
  • Wat: WebAssembly Text format (debugging)

Limitations

  • No direct DOM access
  • No garbage collection (manual memory management)
  • JavaScript interop overhead
  • Limited browser support (but improving)

WebAssembly brings near-native performance to the web!

Related essays

Next essay
Product Analytics: Google Analytics, Mixpanel, Amplitude