WebAssembly: Cos’è, Come Funziona, Potenzialità e Guida Pratica

  



WebAssembly (abbreviato in Wasm) è un linguaggio binario portabile e performante pensato per eseguire codice vicino alla velocità nativa direttamente nel browser. In questo articolo esploreremo cos'è, come funziona, le sue potenzialità, il supporto nei browser moderni e forniremo anche un esempio pratico end-to-end con Rust e JavaScript.

🔗 Ti piace Techelopment? Dai un'occhiata al sito per tutti i dettagli!

Cos’è WebAssembly?

WebAssembly è uno standard aperto (W3C) pensato per portare sul web codice compilato da linguaggi come Rust, C/C++, Go e molti altri, offrendo prestazioni molto elevate e sicurezza in un ambiente sandbox.

Standardizzazione

Wasm è stato ufficialmente riconosciuto come standard del web dal W3C nel 2019.

Caratteristiche principali:

  • Portabilità: può girare ovunque, anche al di fuori del browser (formato binario portabile e ottimizzato).

  • Prestazioni elevate: velocità paragonabile al codice nativo.

  • Sicurezza: esecuzione in sandbox, senza accesso diretto al sistema.

  • Interoperabilità: può interagire in modo efficiente con JavaScript e le API Web.


Come funziona WebAssembly

Il codice scritto in linguaggi come Rust viene compilato in un modulo .wasm, che può poi essere caricato nel browser tramite JavaScript. Il browser esegue quel modulo in sandbox con prestazioni quasi native.

Pipeline tipica:

  1. Scrivi il codice in un linguaggio compilabile in Wasm (es. Rust).

  2. Compili in un modulo .wasm.

  3. Lo carichi nel browser usando JavaScript.

  4. Esegui e interagisci con i dati tramite chiamate tra Wasm e JS.

Esempio JS per caricare un modulo Wasm:

fetch('modulo.wasm')
  .then(res => res.arrayBuffer())
  .then(bytes => WebAssembly.instantiate(bytes))
  .then(obj => {
    console.log(obj.instance.exports.miaFunzione());
  });

Esempio pratico end-to-end: WebAssembly + JavaScript

Vediamo un esempio completo di come creare un modulo Wasm in Rust, compilarlo, e utilizzarlo in HTML/JavaScript per ottenere e stampare un oggetto elaborato.

In questo esempio, vedremo come:
  1. Installare Rust e creare un progetto Rust

  2. Scrivere una semplice funzione in Rust che elabora un'informazione.

  3. Compilarla in un modulo WebAssembly (.wasm) usando wasm-pack.

  4. Caricare il modulo in una pagina HTML con JavaScript.

  5. Stampare in console.log un oggetto restituito dal modulo Wasm.


1. Installare Rust e wasm-pack (Linux, macOS, Windows)

Linux/macOS

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
cargo install wasm-pack

Windows

  • Vai su https://rustup.rs e scarica l’installer per Windows.

  • Dopo l’installazione, apri PowerShell e installa:

    cargo install wasm-pack
In alternativa se usi nodejs:
npm install -g wasm-pack

2. Creare il progetto Rust

cargo new wasm-demo --lib
cd wasm-demo

Modifica Cargo.toml:

[package]
name = "wasm_demo"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = "0.2"
serde = { version = "1.0", features = ["derive"] }
Modifica src/lib.rs:
use wasm_bindgen::prelude::*;
use serde::{Serialize};

#[derive(Serialize)]
pub struct ResultObject {
    pub processed: String,
    pub length: usize,
}

#[wasm_bindgen]
pub fn process_input(input: &str) -> JsValue {
    let result = ResultObject {
        processed: input.to_uppercase(),
        length: input.len(),
    };
    JsValue::from_serde(&result).unwrap()
}

Cosa fa questo codice:
  1. prende una stringa in input, la trasforma in maiuscolo e restituisce un oggetto JSON con il risultato. 
  2. esporta la funzione process_input per poter essere richiamata da JavaScript.

3. Compila per WebAssembly con wasm-pack

cd wasm_demo
wasm-pack build --target web

4. Caricamento e utilizzo in HTML/JavaScript

Copia il contenuto della cartella pkg/ generata da wasm-pack in una directory accessibile da HTML.

Crea una pagina HTML - es. index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Wasm Demo</title>
  <script type="module">
    import init, { process_input } from './pkg/wasm_demo.js';

    async function run() {
      await init();
      const result = process_input("ciao wasm!");
      console.log("Risultato:", result);
    }

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

Aprendo index.html in un browser compatibile, vedrai in console qualcosa del tipo:

Output atteso in console:

Risultato: { processed: "CIAO WASM!", length: 10 }

Un momento: ma qual è la differenza tra il file modulo.wasm visto all'inizo e il file wasm_demo.js dell'esempio precedente?

La differenza tra modulo.wasm e wasm_demo.js (o qualsiasi file .js generato da wasm-pack) è fondamentale per capire come funziona l’integrazione tra WebAssembly e JavaScript. Descriviamoli in modo chiaro:

1. modulo.wasm – Il modulo binario WebAssembly

Questo è il vero modulo compilato. Contiene il codice macchina portabile generato da Rust (o C/C++) in formato binario .wasm.

  • È quello che il browser esegue in sandbox.

  • Non contiene alcuna logica di caricamento o collegamento con JS.

  • Devi istanziarlo manualmente in JavaScript usando le API WebAssembly.instantiate.

Esempio minimale di caricamento diretto:

const response = await fetch('modulo.wasm');
const bytes = await response.arrayBuffer();
const { instance } = await WebAssembly.instantiate(bytes);
console.log(instance.exports.miaFunzione());

2. wasm_demo.js – Il wrapper JavaScript generato da wasm-pack

Questo file viene generato automaticamente da wasm-pack, uno strumento da riga di comando sviluppato dalla community Rust (sotto il progetto rustwasm) che semplifica il processo di compilazione di progetti Rust in WebAssembly per l’uso nel web, in particolare con JavaScript.

wasm-pack serve da ponte tra WebAssembly e il mondo JavaScript.

Esempio:

import init, { process_input } from './pkg/wasm_demo.js';

await init(); // inizializza il modulo .wasm
const result = process_input("ciao wasm");
console.log(result);

Quando compili con wasm-pack, ottieni due file principali:

  • wasm_demo_bg.wasm: il modulo WebAssembly vero e proprio.
  • wasm_demo.js: un wrapper JavaScript generato da wasm-pack che:
    • Carica il modulo .wasm.
    • Gestisce conversioni tra tipi Rust e JavaScript.
    • Espone le funzioni in modo idiomatico per JS.
Nota: wasm_demo_bg.wasm è il nome vero generato da wasm-pack, mentre modulo.wasm è un esempio generico usato nei tutorial/manuali.

Riassunto della differenza

File Contenuto Ruolo principale
wasm_demo_bg.wasm Codice binario Wasm compilato da Rust Eseguito dal browser (core)
wasm_demo.js Wrapper JavaScript Collegamento JS ↔ Wasm (gestione tipica)


Quando usare uno o l’altro?

  • Solo modulo.wasm → se stai lavorando manualmente con WebAssembly usando solo le API WebAssembly standard in JS e se vuoi il massimo controllo (serve codice manuale e non supporta tipi complessi senza lavoro extra).

  • wasm_demo.js + .wasm → se vuoi semplificarti la vita e usare funzioni con stringhe o JSON. Ad esempio se usi Rust + wasm-pack + wasm-bindgen, che è il metodo moderno e più produttivo.


Caricamento manuale di un modulo .wasm [per curiosi e accademici]

Se arrivato a questo punto sei curioso di sapere come sarebbe caricare manualmente un modulo wasm in JavaScript senza passare da wasm-pack, o semplicemente vuoi approfondire il funzionamento a basso livello, allora questo paragrafo è l'ideale per capire step-by-step come funziona l'integrazione tra Wasm e JS.

1. Modulo Wasm semplificato in Rust

Ai fini di questo tutorial (e per evitare che questo articolo introduttivo su Wasm sia infinito), dovremo evitare funzioni complesse e non usare wasm-bindgen (tutto ciò per rendere il caricamento manuale più semplice da effettuare). Scriveremo una funzione semplice che restituisce un numero.

src/lib.rs (senza wasm-bindgen)

#[no_mangle]
pub extern "C" fn square(x: i32) -> i32 {
    x * x
}

Nota:

  • #[no_mangle] serve per evitare che il compilatore cambi il nome della funzione.

  • extern "C" rende la funzione compatibile con la chiamata da linguaggi esterni (C / JS).

2. Compilazione in .wasm manuale

Compila con cargo e wasm32-unknown-unknown:

rustup target add wasm32-unknown-unknown
cargo build --release --target wasm32-unknown-unknown

Il file .wasm lo trovi qui:

target/wasm32-unknown-unknown/release/<nome_progetto>.wasm

Copialo in una cartella accessibile dal tuo progetto web.


3. HTML + JS senza wrapper

index.html

<html>
<head>
  <meta charset="UTF-8"></meta>
  <title>Manual Wasm</title>
  <script>
    async function loadWasm() {
      const response = await fetch('modulo.wasm');
      const bytes = await response.arrayBuffer();
      const { instance } = await WebAssembly.instantiate(bytes);

      const result = instance.exports.square(7);
      console.log("Il quadrato di 7 è:", result);
    }

    loadWasm();
  </script>
</head>
<body>
  <h1>WebAssembly senza wrapper</h1>
</body>
</html>

Output in console:

Il quadrato di 7 è: 49

Differenze principali rispetto al wrapper wasm-pack

Caricamento manuale Con wasm-pack (wasm_demo.js)
Devi gestire tutto a basso livello Espone funzioni e conversioni comode
Non puoi usare tipi complessi (stringhe, oggetti) senza lavoro extra Supporta stringhe, JSON, oggetti via wasm-bindgen
Richiede #[no_mangle] e extern "C" Usa macro Rust idiomatiche
Più adatto per moduli minimalisti Più adatto per applicazioni moderne

Supporto Browser

WebAssembly è supportato nei principali browser a partire dalle seguenti versioni:

Browser Supporto Wasm da Versione
Google Chrome Marzo 2017 57
Mozilla Firefox Marzo 2017 52
Microsoft Edge Ottobre 2017 16
Apple Safari Settembre 2017 11
Opera Marzo 2017 44

Tutti i browser mobili moderni basati su WebKit, Blink o Gecko (come Chrome per Android e Safari per iOS) supportano Wasm.

Puoi verificarlo anche su Can I Use.


Estensioni e futuro di WebAssembly

WebAssembly continua a evolversi, tra le funzionalità avanzate in fase di standardizzazione:

  • WASI (WebAssembly System Interface): esecuzione server-side.

  • Garbage Collection: per linguaggi con memoria gestita (es. Kotlin, C#).

  • Threading e SIMD: per performance parallele e operazioni vettoriali.

  • Exception Handling: migliore gestione degli errori.


Alcuni casi d’uso reali:

  • Gaming (Unity via WebGL + Wasm)
  • App desktop (es. Figma usa WebAssembly)
  • Criptografia client-side
  • Video/audio processing direttamente in JS

Risorse ufficiali e tutorial

Documentazione:

Tool e ambienti:

Tutorial pratici:


Conclusione

WebAssembly rappresenta una delle evoluzioni più importanti del web moderno. Porta sul browser codice compilato ad alte prestazioni in modo sicuro e versatile. Saperlo usare, anche solo per estendere le capacità di JavaScript, è una skill preziosa per sviluppatori frontend e backend. E grazie a strumenti come wasm-pack, l’ingresso nel mondo Wasm è oggi molto più accessibile.



Follow me #techelopment

Official site: www.techelopment.it
facebook: Techelopment
instagram: @techelopment
X: techelopment
Bluesky: @techelopment
telegram: @techelopment_channel
whatsapp: Techelopment
youtube: @techelopment