WebAssembly en 2024 : Révolution ou effet de mode ?
Analyse approfondie de WebAssembly : état actuel, cas d'usage concrets, performance réelle et perspectives d'avenir. Est-ce le futur du développement web ou juste un hype ?
WebAssembly : qu'est-ce que c'est vraiment ?
WebAssembly (WASM) est un format d'instructions binaires portable qui permet d'exécuter du code compilé à des performances quasi-natives dans les navigateurs web. Né en 2017 d'une collaboration entre Mozilla, Google, Microsoft et Apple, WASM promet de révolutionner le développement web en permettant aux développeurs d'utiliser des langages comme C++, Rust, Go ou AssemblyScript pour créer des applications web performantes.
🔧 Concepts clés de WebAssembly
- • Bytecode portable : Format binaire compact et optimisé
- • Sécurité sandbox : Exécution isolée dans l'environnement browser
- • Performance native : 10-20% plus lent que natif, mais 10x plus rapide que JS
- • Multi-langage : Compilation depuis C/C++, Rust, Go, C#, etc.
- • Interopérabilité JS : Appels bidirectionnels JavaScript ↔ WASM
État actuel : adoption et maturité
Support navigateurs et performances
En 2024, WebAssembly jouit d'un excellent support navigateur avec 97%+ de compatibilité. Les performances se sont considérablement améliorées avec l'introduction de nouvelles fonctionnalités.
| Navigateur | Support WASM | SIMD | Threads | GC |
|---|---|---|---|---|
| Chrome 120+ | ✅ Complet | ✅ Stable | ✅ Stable | 🚧 Beta |
| Firefox 121+ | ✅ Complet | ✅ Stable | ✅ Stable | 🚧 Beta |
| Safari 17+ | ✅ Complet | ✅ Stable | 🚧 Partiel | ❌ Non |
| Edge 120+ | ✅ Complet | ✅ Stable | ✅ Stable | 🚧 Beta |
Écosystème et tooling 2024
// Écosystème WASM mature en 2024
const wasmEcosystem = {
// Langages avec excellent support WASM
languages: {
rust: {
maturity: 'Excellent',
toolchain: 'wasm-pack, cargo, wasm-bindgen',
performance: '95% natif',
ecosystem: 'Très riche (crates.io)',
learningCurve: 'Élevée'
},
cpp: {
maturity: 'Excellent',
toolchain: 'Emscripten, clang',
performance: '90% natif',
ecosystem: 'Gigantesque (legacy)',
learningCurve: 'Élevée'
},
assemblyscript: {
maturity: 'Bonne',
toolchain: 'asc compiler',
performance: '80% natif',
ecosystem: 'En croissance',
learningCurve: 'Faible (TypeScript-like)'
},
go: {
maturity: 'Bonne',
toolchain: 'GOOS=js GOARCH=wasm',
performance: '70% natif',
ecosystem: 'Standard library complète',
learningCurve: 'Moyenne'
}
},
// Frameworks et runtimes
frameworks: {
'Yew (Rust)': 'Framework web complet comme React',
'Leptos (Rust)': 'Framework full-stack avec SSR',
'Blazor (C#)': 'Framework Microsoft avec .NET',
'WAGI': 'WebAssembly Gateway Interface',
'Wasmtime': 'Runtime standalone performant',
'Wasmer': 'Runtime universel multi-plateforme'
},
// Outils de développement
devTools: {
'wasm-pack': 'Build tool Rust→WASM',
'wabt': 'WebAssembly Binary Toolkit',
'Binaryen': 'Optimiseur et toolchain',
'WABT': 'Assembleur/désassembleur',
'Chrome DevTools': 'Debug WASM intégré'
}
};Cas d'usage concrets et mesures de performance
1. Calculs intensifs et algorithmes
WebAssembly excelle dans les domaines nécessitant des calculs intensifs où JavaScript montre ses limites. Voici des benchmarks réels sur des cas d'usage typiques.
// Benchmark : Calcul de nombres premiers
// Performance comparée JS vs WASM (Rust)
// Version JavaScript
function isPrimeJS(n) {
if (n <= 1) return false;
if (n <= 3) return true;
if (n % 2 === 0 || n % 3 === 0) return false;
for (let i = 5; i * i <= n; i += 6) {
if (n % i === 0 || n % (i + 2) === 0) return false;
}
return true;
}
function findPrimesJS(limit) {
const primes = [];
for (let i = 2; i < limit; i++) {
if (isPrimeJS(i)) primes.push(i);
}
return primes;
}
// Version WebAssembly (Rust compilé)
import init, { find_primes } from './pkg/primes.js';
async function benchmarkPrimes() {
await init();
const limit = 100000;
// Test JavaScript
console.time('JavaScript');
const jsResult = findPrimesJS(limit);
console.timeEnd('JavaScript');
// Test WebAssembly
console.time('WebAssembly');
const wasmResult = find_primes(limit);
console.timeEnd('WebAssembly');
console.log(`JS: ${jsResult.length} primes`);
console.log(`WASM: ${wasmResult.length} primes`);
}
// Résultats typiques :
// JavaScript: ~890ms
// WebAssembly: ~95ms
// Gain de performance: 9.4x plus rapide
// Code Rust correspondant (src/lib.rs)
/*
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn find_primes(limit: u32) -> Vec<u32> {
let mut primes = Vec::new();
for n in 2..limit {
if is_prime(n) {
primes.push(n);
}
}
primes
}
fn is_prime(n: u32) -> bool {
if n <= 1 { return false; }
if n <= 3 { return true; }
if n % 2 == 0 || n % 3 == 0 { return false; }
let mut i = 5;
while i * i <= n {
if n % i == 0 || n % (i + 2) == 0 {
return false;
}
i += 6;
}
true
}
*/2. Traitement d'images et multimédia
Le traitement d'images en temps réel est un domaine où WASM brille particulièrement, permettant des applications web sophistiquées sans plugins.
// Exemple : Filtres d'images en temps réel avec WASM
class ImageProcessor {
constructor() {
this.wasmModule = null;
this.canvas = document.getElementById('canvas');
this.ctx = this.canvas.getContext('2d');
}
async init() {
// Chargement du module WASM
const wasmModule = await import('./pkg/image_filters.js');
await wasmModule.default();
this.wasmModule = wasmModule;
}
// Appliquer un filtre flou gaussien
async applyGaussianBlur(imageData, radius) {
const width = imageData.width;
const height = imageData.height;
const data = new Uint8Array(imageData.data);
console.time('Gaussian Blur WASM');
// Appel de la fonction WASM compilée depuis Rust
const filtered = this.wasmModule.gaussian_blur(
data, width, height, radius
);
console.timeEnd('Gaussian Blur WASM');
// Création du nouveau ImageData
const newImageData = new ImageData(
new Uint8ClampedArray(filtered), width, height
);
return newImageData;
}
// Comparaison avec version JavaScript pure
applyGaussianBlurJS(imageData, radius) {
console.time('Gaussian Blur JS');
const width = imageData.width;
const height = imageData.height;
const data = imageData.data;
const output = new Uint8ClampedArray(data.length);
// Implémentation simplifiée du flou gaussien
const sigma = radius / 3;
const kernel = this.generateGaussianKernel(radius, sigma);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
for (let c = 0; c < 3; c++) { // RGB (pas alpha)
let sum = 0;
let weightSum = 0;
for (let ky = -radius; ky <= radius; ky++) {
for (let kx = -radius; kx <= radius; kx++) {
const px = x + kx;
const py = y + ky;
if (px >= 0 && px < width && py >= 0 && py < height) {
const weight = kernel[ky + radius][kx + radius];
const idx = (py * width + px) * 4 + c;
sum += data[idx] * weight;
weightSum += weight;
}
}
}
output[(y * width + x) * 4 + c] = sum / weightSum;
}
output[(y * width + x) * 4 + 3] = data[(y * width + x) * 4 + 3]; // Alpha
}
}
console.timeEnd('Gaussian Blur JS');
return new ImageData(output, width, height);
}
// Benchmark des deux approches
async benchmarkFilters() {
const testImage = await this.loadTestImage();
const radius = 5;
// Test WASM
const wasmResult = await this.applyGaussianBlur(testImage, radius);
// Test JavaScript
const jsResult = this.applyGaussianBlurJS(testImage, radius);
// Résultats typiques pour image 1920x1080:
// JavaScript: ~2800ms
// WebAssembly: ~320ms
// Gain: 8.7x plus rapide
return { wasmResult, jsResult };
}
}
// Code Rust correspondant pour les filtres (src/filters.rs)
/*
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn gaussian_blur(
data: &[u8],
width: u32,
height: u32,
radius: u32
) -> Vec<u8> {
let mut output = vec![0u8; data.len()];
let sigma = radius as f32 / 3.0;
// Génération du kernel gaussien
let kernel_size = (radius * 2 + 1) as usize;
let mut kernel = vec![vec![0f32; kernel_size]; kernel_size];
let mut kernel_sum = 0f32;
for y in 0..kernel_size {
for x in 0..kernel_size {
let dx = (x as i32 - radius as i32) as f32;
let dy = (y as i32 - radius as i32) as f32;
let value = (-((dx*dx + dy*dy) / (2.0 * sigma * sigma))).exp();
kernel[y][x] = value;
kernel_sum += value;
}
}
// Normalisation du kernel
for y in 0..kernel_size {
for x in 0..kernel_size {
kernel[y][x] /= kernel_sum;
}
}
// Application du filtre
for y in 0..height {
for x in 0..width {
for c in 0..3 { // RGB
let mut sum = 0f32;
for ky in 0..kernel_size {
for kx in 0..kernel_size {
let px = x as i32 + kx as i32 - radius as i32;
let py = y as i32 + ky as i32 - radius as i32;
if px >= 0 && px < width as i32 && py >= 0 && py < height as i32 {
let idx = ((py as u32 * width + px as u32) * 4 + c) as usize;
sum += data[idx] as f32 * kernel[ky][kx];
}
}
}
let idx = ((y * width + x) * 4 + c) as usize;
output[idx] = sum.round().max(0.0).min(255.0) as u8;
}
// Copy alpha channel
let idx = ((y * width + x) * 4 + 3) as usize;
output[idx] = data[idx];
}
}
output
}
*/3. Jeux et simulations en temps réel
Les jeux web représentent l'un des cas d'usage les plus impressionnants de WebAssembly, permettant de porter des moteurs de jeu complexes vers le web.
🎮 Exemples de jeux WASM réussis
Portages réussis
- • DOOM 3 : Port complet avec Emscripten
- • Unity WebGL : Utilise WASM depuis 2018
- • Unreal Engine : Support WASM expérimental
- • Photoshop Web : Adobe avec C++ → WASM
Frameworks natifs WASM
- • Bevy : Moteur de jeu Rust → WASM
- • Amethyst : Game engine data-driven
- • Macroquad : Simple 2D game library
- • WGPU : Graphics API moderne
4. Cryptographie et sécurité
// Exemple : Hachage cryptographique haute performance
class CryptoWASM {
constructor() {
this.wasmModule = null;
}
async init() {
const wasmModule = await import('./pkg/crypto_wasm.js');
await wasmModule.default();
this.wasmModule = wasmModule;
}
// Benchmark SHA-256 : WASM vs JavaScript vs Web Crypto API
async benchmarkSHA256() {
const data = new Uint8Array(1024 * 1024); // 1MB de données
crypto.getRandomValues(data);
const iterations = 100;
// Test Web Crypto API (natif)
console.time('Web Crypto API');
for (let i = 0; i < iterations; i++) {
await crypto.subtle.digest('SHA-256', data);
}
console.timeEnd('Web Crypto API');
// Test WASM (Rust/RustCrypto)
console.time('WASM SHA-256');
for (let i = 0; i < iterations; i++) {
this.wasmModule.sha256_hash(data);
}
console.timeEnd('WASM SHA-256');
// Test JavaScript pur (crypto-js)
const CryptoJS = await import('crypto-js');
const wordArray = CryptoJS.lib.WordArray.create(data);
console.time('JavaScript SHA-256');
for (let i = 0; i < iterations; i++) {
CryptoJS.SHA256(wordArray);
}
console.timeEnd('JavaScript SHA-256');
// Résultats typiques (1MB x 100):
// Web Crypto API: ~45ms (natif, plus rapide)
// WASM: ~180ms (bon compromis)
// JavaScript: ~2400ms (très lent)
}
// Cas d'usage : Password hashing avec Argon2
async hashPassword(password, salt) {
// WASM permet d'utiliser des implémentations
// cryptographiques performantes et auditées
return this.wasmModule.argon2_hash(
password,
salt,
{
memory: 65536, // 64 MB
iterations: 3,
parallelism: 4,
hashLength: 32
}
);
}
// Chiffrement AES avec mode CTR
encryptAES(data, key, iv) {
return this.wasmModule.aes_ctr_encrypt(data, key, iv);
}
}
// Code Rust pour crypto (src/crypto.rs)
/*
use wasm_bindgen::prelude::*;
use sha2::{Sha256, Digest};
use argon2::{Argon2, PasswordHasher};
use aes::Aes256;
use ctr::Ctr64BE;
use cipher::{KeyIvInit, StreamCipher};
#[wasm_bindgen]
pub fn sha256_hash(data: &[u8]) -> Vec<u8> {
let mut hasher = Sha256::new();
hasher.update(data);
hasher.finalize().to_vec()
}
#[wasm_bindgen]
pub fn argon2_hash(
password: &[u8],
salt: &[u8],
memory: u32,
iterations: u32,
parallelism: u32
) -> Vec<u8> {
let argon2 = Argon2::new(
argon2::Algorithm::Argon2id,
argon2::Version::V0x13,
argon2::Params::new(
memory,
iterations,
parallelism,
Some(32)
).unwrap()
);
argon2.hash_password(password, salt)
.unwrap()
.hash
.unwrap()
.as_bytes()
.to_vec()
}
*/Limitations et défis actuels
1. Taille des bundles et temps de chargement
L'un des principaux défis de WebAssembly reste la taille des fichiers générés et l'impact sur le temps de chargement initial.
| Type d'application | Taille JS équivalent | Taille WASM | Temps de compilation | Recommandation |
|---|---|---|---|---|
| Hello World | 1 KB | 50-100 KB | ~10ms | ❌ Overkill |
| Calculs mathématiques | 10-50 KB | 100-200 KB | ~50ms | ⚠️ À évaluer |
| Traitement d'image | 200+ KB | 300-500 KB | ~200ms | ✅ Recommandé |
| Jeu complet | 2+ MB | 5-10 MB | ~1-2s | ✅ Excellent |
2. Debugging et développement
// Défis de debugging WASM
const debuggingChallenges = {
// 1. Stack traces limités
stackTraces: {
javascript: "Noms de fonctions, numéros de ligne, source maps",
webassembly: "Adresses mémoire, symboles limités",
solution: "DWARF debugging info + Chrome DevTools 2024+"
},
// 2. Inspection des variables
variableInspection: {
javascript: "Inspection complète runtime",
webassembly: "Mémoire brute uniquement",
solution: "wasm-bindgen debugging features"
},
// 3. Hot reload et développement rapide
hotReload: {
javascript: "Modification instantanée",
webassembly: "Recompilation nécessaire",
solution: "wasm-pack build --dev + file watching"
},
// 4. Profiling et optimisation
profiling: {
tools: [
"Chrome DevTools Performance tab",
"wasm-profiler (Rust specific)",
"Emscripten profiling tools",
"Custom timing avec Performance API"
],
limitations: "Granularité limitée par rapport au natif"
}
};
// Exemple de profiling WASM manuel
class WASMProfiler {
constructor() {
this.timings = new Map();
}
startTiming(functionName) {
this.timings.set(functionName, performance.now());
}
endTiming(functionName) {
const start = this.timings.get(functionName);
if (start) {
const duration = performance.now() - start;
console.log(`${functionName}: ${duration.toFixed(2)}ms`);
return duration;
}
return 0;
}
// Wrapper pour fonctions WASM avec timing
wrapWASMFunction(wasmModule, functionName) {
const originalFunction = wasmModule[functionName];
return (...args) => {
this.startTiming(functionName);
const result = originalFunction.apply(wasmModule, args);
this.endTiming(functionName);
return result;
};
}
}3. Intégration avec l'écosystème JavaScript
L'interopérabilité entre JavaScript et WebAssembly, bien qu'améliorée, présente encore des défis notamment pour le passage de données complexes.
// Défis d'interopérabilité JS ↔ WASM
class InteropChallenges {
// 1. Coût de sérialisation des données
demonstrateSerializationCost() {
// Transfert d'un gros objet JavaScript vers WASM
const largeObject = {
matrix: Array(1000).fill().map(() => Array(1000).fill(Math.random())),
metadata: { size: 1000000, type: 'float64' }
};
console.time('JS Object to WASM');
// Option 1: Sérialisation manuelle (lente)
const serialized = JSON.stringify(largeObject);
const bytes = new TextEncoder().encode(serialized);
console.timeEnd('JS Object to WASM');
console.time('WASM Processing');
// Le module WASM doit désérialiser...
const result = this.wasmModule.process_json_data(bytes);
console.timeEnd('WASM Processing');
// Problème : double coût de sérialisation + parsing JSON en WASM
return result;
}
// 2. Gestion mémoire partagée
demonstrateMemorySharing() {
// Approche efficace : mémoire partagée
const wasmMemory = this.wasmModule.memory.buffer;
const matrixPtr = this.wasmModule.allocate_matrix(1000, 1000);
// Accès direct à la mémoire WASM depuis JS
const matrixView = new Float32Array(
wasmMemory,
matrixPtr,
1000 * 1000
);
// Remplissage direct sans sérialisation
console.time('Direct Memory Fill');
for (let i = 0; i < matrixView.length; i++) {
matrixView[i] = Math.random();
}
console.timeEnd('Direct Memory Fill');
// Traitement WASM direct sur la mémoire
console.time('WASM Direct Processing');
this.wasmModule.process_matrix_in_place(matrixPtr, 1000, 1000);
console.timeEnd('WASM Direct Processing');
// Nettoyage mémoire nécessaire
this.wasmModule.deallocate_matrix(matrixPtr);
// Bien plus efficace !
}
// 3. Gestion des callbacks JavaScript depuis WASM
setupWASMCallbacks() {
// WASM peut appeler des fonctions JS, mais avec overhead
const jsCallback = (value) => {
console.log(`Callback from WASM: ${value}`);
return Math.sin(value); // Calcul en JS
};
// Export de la fonction JS vers WASM
this.wasmModule.set_js_callback(jsCallback);
// Problème : chaque appel callback a un coût de marshalling
// Solution : minimiser les callbacks, préférer batch processing
}
// 4. Gestion d'erreurs cross-boundary
handleWASMErrors() {
try {
// WASM peut panic ou retourner des error codes
const result = this.wasmModule.risky_operation(invalidInput);
if (result.is_error()) {
throw new Error(`WASM Error: ${result.get_error_message()}`);
}
return result.get_value();
} catch (error) {
// Les panics WASM deviennent des RuntimeError JS
if (error instanceof WebAssembly.RuntimeError) {
console.error('WASM Runtime Error:', error.message);
// Recovery strategy...
}
throw error;
}
}
}WebAssembly en 2024 : verdict et recommandations
Quand utiliser WebAssembly ?
✅ Cas d'usage recommandés
- Calculs intensifs : Crypto, mathématiques, physique
- Traitement temps réel : Audio, vidéo, images
- Port de code existant : Bibliothèques C/C++/Rust
- Applications performantes : Jeux, simulations
- Sécurité critique : Code cryptographique audité
- Algorithmes propriétaires : Protection IP
❌ Cas d'usage déconseillés
- Manipulation DOM : JavaScript reste plus simple
- APIs Web natives : Fetch, localStorage, etc.
- Applications CRUD simples : Overhead inutile
- Prototypes/MVPs rapides : Complexité ajoutée
- Équipes junior : Courbe d'apprentissage élevée
- Budget/timeline serrés : Développement plus long
Roadmap et tendances futures
🚀 Évolutions attendues 2024-2025
WASI (WebAssembly System Interface)
Standardisation des interfaces système permettant à WASM de s'exécuter hors navigateur avec accès aux fichiers, réseau, etc. Concurrence directe à Docker.
Garbage Collection intégré
Support natif du GC permettant des langages comme Java, C#, Kotlin de compiler efficacement vers WASM sans runtime lourd.
Component Model
Système de modules composable permettant de mélanger du code de différents langages dans une même application WASM.
WebAssembly sur Edge/CDN
Déploiement de fonctions WASM sur les CDNs (Cloudflare Workers, AWS Lambda@Edge) pour du compute distribué ultra-performant.
Guide de migration JavaScript → WebAssembly
// Stratégie de migration progressive JS → WASM
class MigrationStrategy {
// Phase 1: Identifier les bottlenecks
identifyBottlenecks() {
// Utiliser le profiler pour identifier les fonctions coûteuses
const bottlenecks = [
{
function: 'complexCalculation',
cpuTime: '45%',
callFrequency: 'High',
migrationPriority: 'High'
},
{
function: 'imageProcessing',
cpuTime: '30%',
callFrequency: 'Medium',
migrationPriority: 'High'
},
{
function: 'dataValidation',
cpuTime: '15%',
callFrequency: 'Very High',
migrationPriority: 'Low' // Trop d'interop JS ↔ WASM
}
];
return bottlenecks.filter(b => b.migrationPriority === 'High');
}
// Phase 2: Migration modulaire
async migrateModule(moduleName) {
const phases = {
// 2.1: Isoler la logique métier
isolate: () => {
// Extraire la fonction pure sans dépendances DOM/JS
const pureLogic = this.extractPureLogic(moduleName);
this.createRustInterface(pureLogic);
},
// 2.2: Implémentation Rust parallèle
implement: async () => {
await this.buildWASMModule(moduleName);
const wasmModule = await this.loadWASMModule(moduleName);
return wasmModule;
},
// 2.3: A/B test performance
abTest: async (wasmModule) => {
const jsTime = await this.benchmarkJS(moduleName);
const wasmTime = await this.benchmarkWASM(wasmModule);
return {
jsTime,
wasmTime,
improvement: jsTime / wasmTime,
recommendation: wasmTime < jsTime * 0.8 ? 'migrate' : 'keep_js'
};
},
// 2.4: Migration progressive
progressiveMigration: (wasmModule) => {
// Feature flag pour basculer progressivement
const useWASM = this.shouldUseWASM(moduleName);
return useWASM ?
wasmModule[moduleName] :
this.jsImplementations[moduleName];
}
};
return phases;
}
// Phase 3: Monitoring production
monitorProduction() {
const metrics = {
performance: this.trackPerformanceMetrics(),
errors: this.trackWASMErrors(),
bundleSize: this.trackBundleSize(),
userExperience: this.trackUserMetrics()
};
// Alertes si performance dégradée
if (metrics.performance.p95 > this.thresholds.performance) {
this.rollbackToJS();
}
return metrics;
}
}
// Exemple concret : migration d'un algorithme de tri
const migrationExample = {
// Avant : JavaScript
jsSort: (array) => {
console.time('JS Sort');
const result = array.sort((a, b) => a - b);
console.timeEnd('JS Sort');
return result;
},
// Après : WebAssembly (implémenté en Rust)
wasmSort: async (array) => {
const wasmModule = await import('./pkg/sort_wasm.js');
await wasmModule.default();
console.time('WASM Sort');
const result = wasmModule.quicksort(new Uint32Array(array));
console.timeEnd('WASM Sort');
return Array.from(result);
},
// Wrapper intelligent
smartSort: async (array) => {
// Utiliser WASM seulement pour les gros tableaux
if (array.length > 10000) {
return await migrationExample.wasmSort(array);
} else {
return migrationExample.jsSort(array);
}
}
};Conclusion : WebAssembly, révolution ou hype ?
Après analyse des performances, de l'écosystème et des cas d'usage réels, le verdict est nuancé :WebAssembly n'est ni une révolution universelle ni un simple effet de mode. C'est un outil puissant qui excelle dans des domaines spécifiques.
📊 Bilan 2024
Points forts confirmés
- • Performance exceptionnelle pour calculs intensifs
- • Écosystème mature avec Rust/C++
- • Support navigateur universel
- • Sécurité par design (sandbox)
- • Portage de code existant facilité
Limites persistantes
- • Overhead pour petites tâches
- • Debugging plus complexe
- • Courbe d'apprentissage élevée
- • Bundle size impact
- • Interop JS coûteuse
Recommandation finale : Adoptez WebAssembly pour des cas d'usage spécifiques où la performance est critique et où JavaScript montre ses limites. Ne l'utilisez pas par défaut, mais gardez-le dans votre boîte à outils pour les situations appropriées. L'avenir de WASM semble prometteur, particulièrement avec WASI qui ouvrira de nouveaux horizons au-delà du navigateur.