¿Qué es WebAssembly?
WebAssembly (WASM) es un formato de instrucciones binarias ejecutable en el navegador. Puede compilarse desde lenguajes como C/C++, Rust y Go, permitiendo ejecutar código nativo con un rendimiento cercano al de JavaScript.
flowchart LR
subgraph Source["Código Fuente"]
Rust["Rust"]
Cpp["C/C++"]
Go["Go"]
end
subgraph Compile["Compilación"]
LLVM["LLVM"]
WASM[".wasm<br/>Binario"]
end
subgraph Runtime["Entorno de Ejecución"]
Browser["Browser<br/>Runtime"]
JS["Llamada desde<br/>JavaScript"]
end
Source --> LLVM --> WASM --> Browser
WASM --> JS
Características:
- Velocidad de ejecución cercana a la nativa
- Independiente del lenguaje (soporte multilenguaje)
- Ejecución segura en sandbox
- Interoperabilidad con JavaScript
- Soporte para compilación en streaming
Comparación de Rendimiento
| Procesamiento | JavaScript | WASM | Nativo |
|---|---|---|---|
| Multiplicación de matrices (1000x1000) | 2,500ms | 850ms | 700ms |
| Procesamiento de imagen (1920x1080) | 180ms | 72ms | - |
| Cifrado (AES-256) | 320ms | 125ms | - |
※ La diferencia puede reducirse debido a las optimizaciones JIT del motor JS
Rust + WebAssembly
Configuración
# Instalación de wasm-pack
cargo install wasm-pack
# Creación del proyecto
cargo new --lib wasm-example
cd wasm-example
# Cargo.toml
[package]
name = "wasm-example"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
wasm-bindgen = "0.2"
js-sys = "0.3"
web-sys = { version = "0.3", features = ["console"] }
[profile.release]
opt-level = "z" # Optimización de tamaño
lto = true # Optimización en tiempo de enlace
Implementación en Rust
// src/lib.rs
use wasm_bindgen::prelude::*;
// Función llamable desde JavaScript
#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
// Función que maneja strings
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
// Exportar struct
#[wasm_bindgen]
pub struct Calculator {
value: f64,
}
#[wasm_bindgen]
impl Calculator {
#[wasm_bindgen(constructor)]
pub fn new() -> Calculator {
Calculator { value: 0.0 }
}
pub fn add(&mut self, x: f64) {
self.value += x;
}
pub fn subtract(&mut self, x: f64) {
self.value -= x;
}
pub fn multiply(&mut self, x: f64) {
self.value *= x;
}
pub fn divide(&mut self, x: f64) -> Result<(), JsValue> {
if x == 0.0 {
return Err(JsValue::from_str("Division by zero"));
}
self.value /= x;
Ok(())
}
pub fn get_value(&self) -> f64 {
self.value
}
pub fn reset(&mut self) {
self.value = 0.0;
}
}
// Procesamiento de arrays
#[wasm_bindgen]
pub fn sum_array(arr: &[i32]) -> i32 {
arr.iter().sum()
}
// Usar console de JS
#[wasm_bindgen]
pub fn log_to_console(message: &str) {
web_sys::console::log_1(&message.into());
}
// Ejemplo de procesamiento de imagen de alta velocidad
#[wasm_bindgen]
pub fn apply_grayscale(data: &mut [u8]) {
for chunk in data.chunks_exact_mut(4) {
let r = chunk[0] as f32;
let g = chunk[1] as f32;
let b = chunk[2] as f32;
// Coeficientes ITU-R BT.601
let gray = (0.299 * r + 0.587 * g + 0.114 * b) as u8;
chunk[0] = gray;
chunk[1] = gray;
chunk[2] = gray;
// chunk[3] (alpha) se mantiene igual
}
}
// Secuencia Fibonacci (para benchmark)
#[wasm_bindgen]
pub fn fibonacci(n: u32) -> u64 {
if n <= 1 {
return n as u64;
}
let mut a: u64 = 0;
let mut b: u64 = 1;
for _ in 2..=n {
let temp = a + b;
a = b;
b = temp;
}
b
}
Compilación y Uso
# Compilar
wasm-pack build --target web --release
// Uso desde JavaScript/TypeScript
import init, {
add,
greet,
Calculator,
sum_array,
apply_grayscale,
fibonacci
} from './pkg/wasm_example.js';
async function main() {
// Inicialización del módulo WASM
await init();
// Llamadas a funciones básicas
console.log(add(2, 3)); // 5
console.log(greet('World')); // "Hello, World!"
// Uso de clase
const calc = new Calculator();
calc.add(10);
calc.multiply(2);
console.log(calc.get_value()); // 20
// Procesamiento de arrays
const arr = new Int32Array([1, 2, 3, 4, 5]);
console.log(sum_array(arr)); // 15
// Procesamiento de imagen
const canvas = document.getElementById('canvas') as HTMLCanvasElement;
const ctx = canvas.getContext('2d')!;
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
apply_grayscale(imageData.data);
ctx.putImageData(imageData, 0, 0);
// Medición de rendimiento
console.time('WASM Fibonacci');
fibonacci(40);
console.timeEnd('WASM Fibonacci');
}
main();
C/C++ + Emscripten
Configuración
# Instalación de Emscripten
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
Implementación en C++
// src/main.cpp
#include <emscripten/bind.h>
#include <vector>
#include <string>
#include <cmath>
// Funciones básicas
int add(int a, int b) {
return a + b;
}
std::string greet(const std::string& name) {
return "Hello, " + name + "!";
}
// Exportar clase
class Vector2D {
public:
float x, y;
Vector2D() : x(0), y(0) {}
Vector2D(float x, float y) : x(x), y(y) {}
float length() const {
return std::sqrt(x * x + y * y);
}
Vector2D normalize() const {
float len = length();
if (len == 0) return Vector2D();
return Vector2D(x / len, y / len);
}
Vector2D add(const Vector2D& other) const {
return Vector2D(x + other.x, y + other.y);
}
float dot(const Vector2D& other) const {
return x * other.x + y * other.y;
}
};
// Operación de matrices de alta velocidad
std::vector<float> matrixMultiply(
const std::vector<float>& a,
const std::vector<float>& b,
int n
) {
std::vector<float> result(n * n, 0.0f);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
float sum = 0.0f;
for (int k = 0; k < n; ++k) {
sum += a[i * n + k] * b[k * n + j];
}
result[i * n + j] = sum;
}
}
return result;
}
// Bindings con Embind
EMSCRIPTEN_BINDINGS(module) {
emscripten::function("add", &add);
emscripten::function("greet", &greet);
emscripten::function("matrixMultiply", &matrixMultiply);
emscripten::class_<Vector2D>("Vector2D")
.constructor<>()
.constructor<float, float>()
.property("x", &Vector2D::x)
.property("y", &Vector2D::y)
.function("length", &Vector2D::length)
.function("normalize", &Vector2D::normalize)
.function("add", &Vector2D::add)
.function("dot", &Vector2D::dot);
emscripten::register_vector<float>("FloatVector");
}
Compilación
emcc src/main.cpp \
-o build/module.js \
-s WASM=1 \
-s MODULARIZE=1 \
-s EXPORT_NAME="createModule" \
-s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' \
--bind \
-O3
Integración con JavaScript
// Métodos de carga del módulo WASM
// 1. Carga básica
const wasmModule = await WebAssembly.instantiateStreaming(
fetch('/module.wasm'),
importObject
);
// 2. Usando wasm-bindgen
import init, { add } from './pkg/module.js';
await init();
console.log(add(1, 2));
// 3. Memoria compartida
const memory = new WebAssembly.Memory({ initial: 256, maximum: 512 });
const importObject = {
env: {
memory,
// Llamar función JavaScript desde WASM
jsLog: (ptr: number, len: number) => {
const bytes = new Uint8Array(memory.buffer, ptr, len);
const str = new TextDecoder().decode(bytes);
console.log(str);
},
},
};
// 4. Pasar grandes cantidades de datos
function passLargeArray(wasmInstance: WebAssembly.Instance, data: Float32Array) {
const { memory, alloc, dealloc, process } = wasmInstance.exports as any;
// Copiar datos a memoria WASM
const ptr = alloc(data.byteLength);
const wasmArray = new Float32Array(memory.buffer, ptr, data.length);
wasmArray.set(data);
// Ejecutar procesamiento
process(ptr, data.length);
// Obtener resultado
const result = new Float32Array(memory.buffer, ptr, data.length).slice();
// Liberar memoria
dealloc(ptr, data.byteLength);
return result;
}
Casos de Uso Prácticos
flowchart TB
subgraph UseCases["Casos de Uso de WebAssembly"]
subgraph Media["1. Procesamiento de Imagen/Video"]
M1["Figma (Herramienta de diseño)"]
M2["Squoosh (Compresión de imágenes)"]
M3["FFmpeg.wasm (Conversión de video)"]
end
subgraph Games["2. Juegos y 3D"]
G1["Unity WebGL"]
G2["Unreal Engine"]
G3["Godot Engine"]
end
subgraph Tools["3. Herramientas de Desarrollo"]
T1["VS Code (Monaco Editor)"]
T2["Pyodide (Python en navegador)"]
T3["SQLite WASM"]
end
subgraph Security["4. Criptografía y Seguridad"]
S1["1Password (Gestor de contraseñas)"]
S2["Signal (Chat cifrado)"]
S3["Cálculo de hash"]
end
end
Optimización de Tamaño
# Para Rust
# Optimización con wasm-opt
wasm-opt -Oz input.wasm -o output.wasm
# Compresión
gzip -9 output.wasm
# Compresión Brotli (más eficiente)
brotli -9 output.wasm
# Configuración de optimización en Cargo.toml
[profile.release]
opt-level = "z" # Optimización priorizando tamaño
lto = true # Optimización en tiempo de enlace
codegen-units = 1 # Unidad de generación de código a 1
panic = "abort" # Abortar en caso de panic
strip = true # Eliminar información de símbolos
WASI (WebAssembly System Interface)
// Código Rust compatible con WASI
use std::fs;
use std::io::{self, Write};
fn main() {
// Lectura/escritura de archivos
let content = fs::read_to_string("/input.txt").unwrap();
fs::write("/output.txt", content.to_uppercase()).unwrap();
// Salida estándar
println!("Hello from WASI!");
// Variables de entorno
for (key, value) in std::env::vars() {
println!("{}: {}", key, value);
}
}
# Compilar para objetivo WASI
rustup target add wasm32-wasi
cargo build --target wasm32-wasi --release
# Ejecutar con wasmtime
wasmtime target/wasm32-wasi/release/app.wasm
Soporte de Navegadores
| Navegador | Estado de Soporte |
|---|---|
| Chrome | 57+ (desde 2017) |
| Firefox | 52+ (desde 2017) |
| Safari | 11+ (desde 2017) |
| Edge | 16+ (desde 2017) |
| Node.js | 8+ (desde 2017) |