WebAssembly (WASM) comenzo como una forma de ejecutar C++ en el navegador. En 2026, se ejecuta en todas partes - navegadores, servidores, redes edge, dispositivos embebidos - y alimenta algunas de las aplicaciones mas exigentes de la web. El motor de renderizado de Figma, Adobe Photoshop en la web, el procesamiento de video de Google Meet y la plataforma de computacion edge de Cloudflare funcionan todos sobre WebAssembly.
Chrome Platform Status situa WASM en aproximadamente el 5,5% de todas las cargas de pagina en Chrome a principios de 2026, frente al 4,5% del ano anterior. Con WASM 3.0 convertido en estandar del W3C y WASI madurando hacia la version 1.0, el ecosistema ha alcanzado un punto de inflexion.
Esta guia cubre todo lo que necesitas saber para empezar a desarrollar con WebAssembly.
Que es WebAssembly?
WebAssembly es un formato de instrucciones binarias disenado como objetivo de compilacion. Escribes codigo en un lenguaje de alto nivel (Rust, C, C++, Go, Kotlin), lo compilas a .wasm y lo ejecutas en cualquier entorno que tenga un runtime WASM - navegadores, Node.js, Cloudflare Workers, Wasmtime o Wasmer.
Como funciona
WASM es una maquina virtual basada en pila. Las funciones apilan y desapilan valores en una pila de operandos. El runtime host (V8 en Chrome, SpiderMonkey en Firefox) compila JIT el bytecode WASM a codigo maquina nativo, por lo que el rendimiento es casi nativo.
Caracteristicas clave:
- Ejecucion en sandbox: los modulos WASM solo pueden acceder a los recursos que el host concede explicitamente. Sin acceso al sistema de archivos, a la red ni al sistema operativo a menos que se permita. Esto es fundamentalmente diferente del codigo nativo.
- Memoria lineal: un unico
ArrayBuffercontiguo compartido entre WASM y el host. Los datos complejos (cadenas, structs) se pasan escribiendo en memoria y compartiendo un puntero. - Tipos limitados: WASM soporta nativamente solo cuatro tipos:
i32,i64,f32,f64. Todo lo demas (cadenas, arrays, objetos) requiere codificacion a traves de la memoria lineal o el Component Model. - Portable: el mismo binario
.wasmse ejecuta en cualquier plataforma con un runtime WASM, sin recompilacion.
WASM vs JavaScript
WASM no reemplaza a JavaScript. Lo complementa.
| Aspecto | JavaScript | WebAssembly |
|---|---|---|
| Parsing | Parsing + compilacion en tiempo de ejecucion | Binario pre-compilado, solo decodificacion |
| Velocidad de ejecucion | Optimizado JIT, variable | Casi nativo, constante |
| Arranque | Rapido para scripts pequenos | Decodificacion rapida, predecible |
| Acceso al DOM | Directo | Indirecto (a traves de glue JS) |
| Ideal para | UI, manipulacion del DOM, gestion de eventos | Computacion intensiva en CPU |
| Garbage collection | Integrado | WasmGC (nuevo), o manual |
Usa JavaScript para la interfaz de usuario y el trabajo con el DOM. Usa WASM para el calculo pesado: procesamiento de imagenes, codificacion de video, simulaciones fisicas, criptografia, parsing de datos.
WASM 3.0: las novedades
WebAssembly 3.0 se convirtio en estandar del W3C en septiembre de 2025, estandarizando nueve funcionalidades que llevaban anos en desarrollo.
| Funcionalidad | Que permite |
|---|---|
| WasmGC | Garbage collection nativa en WASM. Los lenguajes gestionados (Java, Kotlin, Dart) pueden compilar a WASM sin incluir su propio runtime GC. Soportado en Chrome 119+, Firefox 120+, Safari 18.2+. |
| Exception Handling | try/catch nativos en WASM. Antes, las excepciones requerian costosos viajes de ida y vuelta a JavaScript. |
| Tail Calls | Permite recursion eficiente sin stack overflow. Fundamental para los lenguajes funcionales. |
| Relaxed SIMD | Instrucciones vectoriales de 128 bits para el procesamiento paralelo de datos. Permite optimizaciones especificas del hardware. |
| Memory64 | Supera el limite de 4GB de memoria lineal. Necesario para el procesamiento de datos a gran escala. |
| Multi-memory | Multiples regiones de memoria independientes en un solo modulo. |
La mas impactante es WasmGC. Antes de ella, compilar Java o Kotlin a WASM significaba incluir un garbage collector completo como parte del binario, inflando el tamano de los archivos. Ahora el GC del navegador gestiona la memoria para los modulos WASM, igual que lo hace para JavaScript.
WASI: WebAssembly mas alla del navegador
WASM en el navegador es potente, pero WASI (WebAssembly System Interface) es lo que convierte a WASM en un runtime universal. WASI proporciona interfaces estandarizadas para recursos del sistema - archivos, red, relojes, numeros aleatorios - permitiendo que los modulos WASM funcionen fuera del navegador.
WASI Preview 2 (la version estable actual) define estas interfaces:
wasi:filesystem- operaciones con archivos mediante handles de capacidad (no los descriptores de archivo tradicionales)wasi:sockets- red TCP/UDPwasi:http- gestion de peticiones/respuestas HTTPwasi:clocks- reloj de sistema, reloj monotonicowasi:random- aleatoriedad criptograficawasi:cli- argumentos de linea de comandos, variables de entorno, stdio
El principio clave es la seguridad basada en capacidades: un modulo WASM no puede acceder al sistema de archivos a menos que el host conceda explicitamente un handle a un directorio especifico. Esto hace que WASI sea fundamentalmente mas seguro que ejecutar ejecutables nativos.
El camino hacia WASI 1.0
WASI 0.3.0 (que anade primitivas async/concurrencia) se espera en 2026, con WASI 1.0 a continuacion. La adicion principal es el async integrado en el lenguaje con I/O streaming de copia cero.
El Component Model
Los modulos WASM basicos solo pueden intercambiar numeros. El Component Model resuelve esta limitacion anadiendo un sistema de tipos rico y una capa de composabilidad sobre WASM.
WIT (WebAssembly Interface Types)
WIT es un lenguaje de definicion de interfaces que permite a los componentes declarar sus importaciones y exportaciones con tipos ricos - cadenas, registros, listas, variantes, enumeraciones - no solo i32 y f64.
// calculator.wit package myorg:calculator@1.0.0; interface operations { record calculation { expression: string, result: f64, timestamp: u64, } add: func(a: f64, b: f64) -> f64; multiply: func(a: f64, b: f64) -> f64; history: func() -> list<calculation>; } world calculator { export operations; }
Las toolchains como wit-bindgen generan bindings especificos del lenguaje a partir de archivos WIT. Un componente Rust y un componente Python pueden intercambiar cadenas, registros y listas a traves de contratos WIT sin que ninguna de las partes conozca el lenguaje de implementacion de la otra.
Construir tu primer modulo WASM con Rust
Rust tiene las herramientas WASM mas maduras. Construyamos un ejemplo practico: un modulo de procesamiento de imagenes que se ejecuta en el navegador.
Configuracion
# Install the WASM target for Rust rustup target add wasm32-unknown-unknown # Install wasm-pack (builds Rust to WASM + generates JS bindings) cargo install wasm-pack # Create a new library project cargo new --lib image-processor cd image-processor
Configurar Cargo.toml
[package] name = "image-processor" version = "0.1.0" edition = "2021" [lib] crate-type = ["cdylib"] [dependencies] wasm-bindgen = "0.2"
Escribir el codigo Rust
// src/lib.rs use wasm_bindgen::prelude::*; /// Convert an image buffer to grayscale. /// Input: RGBA pixel data as a flat u8 array (4 bytes per pixel). /// Output: Modified in place for zero-copy performance. #[wasm_bindgen] pub fn grayscale(pixels: &mut [u8]) { for chunk in pixels.chunks_exact_mut(4) { let r = chunk[0] as f32; let g = chunk[1] as f32; let b = chunk[2] as f32; // ITU-R BT.709 luminance coefficients let gray = (0.2126 * r + 0.7152 * g + 0.0722 * b) as u8; chunk[0] = gray; chunk[1] = gray; chunk[2] = gray; // chunk[3] is alpha, leave unchanged } } /// Adjust brightness of an image. /// factor > 1.0 brightens, < 1.0 darkens. #[wasm_bindgen] pub fn adjust_brightness(pixels: &mut [u8], factor: f32) { for chunk in pixels.chunks_exact_mut(4) { chunk[0] = ((chunk[0] as f32 * factor).min(255.0)) as u8; chunk[1] = ((chunk[1] as f32 * factor).min(255.0)) as u8; chunk[2] = ((chunk[2] as f32 * factor).min(255.0)) as u8; } } /// Invert all colors in the image. #[wasm_bindgen] pub fn invert(pixels: &mut [u8]) { for chunk in pixels.chunks_exact_mut(4) { chunk[0] = 255 - chunk[0]; chunk[1] = 255 - chunk[1]; chunk[2] = 255 - chunk[2]; } } /// Calculate the average brightness of an image (0-255). #[wasm_bindgen] pub fn average_brightness(pixels: &[u8]) -> f32 { let mut total: f64 = 0.0; let pixel_count = pixels.len() / 4; for chunk in pixels.chunks_exact(4) { let luminance = 0.2126 * chunk[0] as f64 + 0.7152 * chunk[1] as f64 + 0.0722 * chunk[2] as f64; total += luminance; } (total / pixel_count as f64) as f32 }
Build
wasm-pack build --target web
Esto produce un directorio pkg/ con:
image_processor_bg.wasm- el binario WASM compiladoimage_processor.js- codigo glue JavaScript con definiciones TypeScriptpackage.json- listo para publicar en npm
Uso en JavaScript
<!DOCTYPE html> <html> <head><title>WASM Image Processor</title></head> <body> <canvas id="canvas" width="800" height="600"></canvas> <button onclick="applyGrayscale()">Grayscale</button> <button onclick="applyBrightness()">Brighten</button> <button onclick="applyInvert()">Invert</button> <script type="module"> import init, { grayscale, adjust_brightness, invert } from "./pkg/image_processor.js"; let ctx; let imageData; async function setup() { await init(); const canvas = document.getElementById("canvas"); ctx = canvas.getContext("2d"); // Load an image onto the canvas const img = new Image(); img.onload = () => { ctx.drawImage(img, 0, 0); imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); }; img.src = "photo.jpg"; } window.applyGrayscale = () => { grayscale(imageData.data); ctx.putImageData(imageData, 0, 0); }; window.applyBrightness = () => { adjust_brightness(imageData.data, 1.3); ctx.putImageData(imageData, 0, 0); }; window.applyInvert = () => { invert(imageData.data); ctx.putImageData(imageData, 0, 0); }; setup(); </script> </body> </html>
El punto clave: imageData.data es un Uint8ClampedArray respaldado por un ArrayBuffer. Cuando se pasa a WASM, comparte la misma memoria - sin copias. La funcion Rust modifica los pixeles directamente, y el lado JavaScript ve los cambios inmediatamente.
Nivel inferior: instanciacion manual de WASM
Si no quieres usar wasm-bindgen, puedes instanciar los modulos WASM directamente:
const response = await fetch("calculator.wasm"); const { instance } = await WebAssembly.instantiateStreaming(response, { env: { // Functions the WASM module can call log_result: (value) => console.log("Result:", value), }, }); // Call exported functions const { add, multiply } = instance.exports; console.log(add(5, 3)); // 8 console.log(multiply(4, 7)); // 28
Esto es util cuando quieres un overhead minimo y no necesitas interoperabilidad avanzada entre tipos.
Rendimiento: WASM vs JavaScript
Los benchmarks reales muestran aceleraciones significativas para tareas intensivas en CPU:
| Tarea | JavaScript | WASM | Aceleracion |
|---|---|---|---|
| Procesamiento de imagen 4K | 180ms | 8ms (con SIMD) | 22x |
| Redimensionamiento de imagen (4K) | 250ms | 45ms | 5,5x |
| Simulacion fisica (10K entidades) | Frames perdidos | 60fps fluidos | ~10x |
| Parsing JSON (carga grande) | 12ms | 3ms | 4x |
| Hashing criptografico | 45ms | 6ms | 7,5x |
WASM funciona a aproximadamente el 95% de la velocidad del codigo nativo. Las mayores ganancias provienen de:
- Rendimiento predecible (sin warmup JIT, sin pausas del GC)
- Instrucciones SIMD para el procesamiento paralelo de datos
- Acceso directo a memoria sin interferencia del garbage collector
Donde WASM NO es mas rapido: manipulacion del DOM, calculos pequenos, tareas limitadas por I/O. JavaScript ya esta optimizado para estos casos.
Casos de uso en produccion
Figma: renderizado vectorial en tiempo real
El motor de renderizado principal de Figma es C++ compilado a WASM. Cada forma, gradiente y efecto se calcula en WASM y se dibuja en un elemento Canvas. Esto permite a Figma manejar disenos complejos con miles de capas a 60fps en el navegador - un rendimiento imposible en JavaScript puro.
Adobe Photoshop en la web
Adobe porto filtros y herramientas clave de Photoshop a WASM usando Rust. Sus benchmarks muestran procesamiento de imagenes 4K en 22ms con WASM SIMD frente a 180ms en JavaScript - una mejora de 8 veces que hace posibles las previsualizaciones de filtros en tiempo real.
Cloudflare Workers
Cloudflare ejecuta modulos WASM en isolates V8 en mas de 330 ubicaciones edge. Los cold starts son de 1 a 5ms (comparados con 100 a 500ms del serverless basado en contenedores). En febrero de 2026, desplegaron la inferencia de Llama-3-8b en su red edge usando WASM.
Google Meet
El desenfoque de fondo y los fondos virtuales en Google Meet utilizan WASM con SIMD para el procesamiento de video en tiempo real. El modulo WASM procesa cada frame de video lo suficientemente rapido para mantener un video fluido a 30fps.
Soporte de navegadores (2026)
| Funcionalidad | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
| Core WASM | Completo | Completo | Completo | Completo |
| Threads | Si | Si | Si | Si |
| SIMD (128 bits) | Si | Si | Si | Si |
| WasmGC | 119+ | 120+ | 18.2+ | Si |
| Exception Handling | Si | Si | Si | Si |
| Memory64 | Si | Si | Parcial | Si |
Todos los navegadores principales soportan completamente WASM. Las funcionalidades mas recientes (WasmGC, Exception Handling) han alcanzado una amplia disponibilidad.
Referencia de herramientas
| Herramienta | Proposito | Instalacion |
|---|---|---|
| wasm-pack | Compilar Rust a WASM, generar paquetes npm | cargo install wasm-pack |
| wasm-bindgen | Bindings de interoperabilidad Rust/JS (usado por wasm-pack) | Dependencia en Cargo.toml |
| wasm-opt | Optimizacion del tamano del binario (reduccion del 50%+) | Parte de Binaryen: brew install binaryen |
| wit-bindgen | Generar bindings desde archivos WIT | cargo install wit-bindgen-cli |
| Wasmtime | Runtime WASM del lado del servidor (implementacion de referencia WASI) | brew install wasmtime |
| Wasmer | Runtime WASM alternativo con soporte WASI | curl https://get.wasmer.io -sSfL | sh |
| wasm-feature-detect | Deteccion de funcionalidades del navegador en tiempo de ejecucion | npm install wasm-feature-detect |
Optimizar el tamano del binario
Los binarios WASM pueden ser grandes. Asi es como reducirlos:
# Cargo.toml [profile.release] opt-level = "z" # Optimize for size lto = true # Link-time optimization codegen-units = 1 # Better optimization, slower compile strip = true # Strip debug symbols
# Build in release mode wasm-pack build --release --target web # Further optimize with wasm-opt wasm-opt -Oz pkg/image_processor_bg.wasm -o pkg/image_processor_bg.wasm
Un modulo WASM tipico en Rust pasa de 500KB a menos de 50KB con estas optimizaciones.
Hoja de ruta para empezar
Conclusion
WebAssembly ya no es experimental. Es una tecnologia de produccion utilizada por algunas de las aplicaciones mas exigentes de la web. Rendimiento casi nativo, seguridad en sandbox y portabilidad universal - ningun otro objetivo de compilacion te ofrece las tres cosas.
No necesitas reescribir toda tu aplicacion en WASM. Empieza con una sola funcion intensiva en CPU - un filtro de imagen, un parser de datos, un calculo fisico - compilala a WASM y llamala desde JavaScript. Mide la diferencia. Luego decide donde mas puede ayudar WASM.
Las herramientas son maduras, el soporte de navegadores es universal y el ecosistema esta creciendo. Si escribes en Rust, ya estas a un comando de distancia del navegador.
Checklist para empezar:
- Rust y wasm-pack instalados
- Primer modulo WASM compilado y funcionando en el navegador
- Interoperabilidad con JavaScript funcionando (llamar a WASM desde JS)
- Build de release con optimizaciones de tamano aplicadas
- Rendimiento comparado con el equivalente en JavaScript puro
- WASI explorado con Wasmtime para casos de uso del lado del servidor