---
title: "Zig e WebAssembly: Guia Prático para Compilar WASM"
url: "https://ziglang.com.br/tutoriais/zig-webassembly-wasm/"
markdown_url: "https://ziglang.com.br/tutoriais/zig-webassembly-wasm.MD"
description: "Aprenda a compilar código Zig para WebAssembly (WASM). Tutorial completo com exemplos práticos, integração com JavaScript, e deploy de módulos WASM no browser."
date: "2026-02-09"
author: ""
---

# Zig e WebAssembly: Guia Prático para Compilar WASM

Aprenda a compilar código Zig para WebAssembly (WASM). Tutorial completo com exemplos práticos, integração com JavaScript, e deploy de módulos WASM no browser.


WebAssembly (WASM) é um formato binário que permite executar código de alto desempenho em navegadores web. Com Zig, você pode compilar código para WebAssembly de forma trivial — sem toolchains complexos ou configurações extensas.

Neste tutorial, vamos criar um módulo WebAssembly em Zig, integrá-lo com JavaScript, e fazer deploy de uma aplicação funcional no browser.

> **O que você vai aprender:**
> - Configurar Zig para compilar para WebAssembly
> - Criar funções exportadas para JavaScript
> - Alocar e gerenciar memória compartilhada
> - Trabalhar com strings entre Zig e JavaScript
> - Criar uma aplicação web completa com Zig + WASM

## Pré-requisitos

- [Zig instalado](/tutoriais/como-instalar-zig/)
- Conhecimento básico de HTML e JavaScript
- Um navegador moderno (Chrome, Firefox, Edge, Safari)

## O que é WebAssembly?

WebAssembly é um formato binário de instruções de máquina projetado para ser executado em navegadores web. Ele oferece:

- **Performance próxima de nativa** — executa em velocidade comparável a C/C++
- **Segurança** — sandboxed, sem acesso direto ao sistema
- **Portabilidade** — roda em qualquer navegador moderno
- **Interoperabilidade** — trabalha junto com JavaScript

### Por que usar Zig para WASM?

| Feature | Benefício |
|---|---|
| **Cross-compilation trivial** | `zig build -Dtarget=wasm32-freestanding` |
| **Sem runtime** | Binários WASM mínimos |
| **Interop nativa com C** | Reutilizar bibliotecas C existentes |
| **Controle de memória** | Gerenciar a memória linear WASM |
| **Sem dependências** | Toolchain completo em um único binário |

## Configuração do Projeto

Crie a estrutura do projeto:

```bash
mkdir zig-wasm-demo
cd zig-wasm-demo
zig init
```

Estrutura:
```
zig-wasm-demo/
├── build.zig
├── build.zig.zon
├── src/
│   └── main.zig
└── web/
    ├── index.html
    └── app.js
```

## Passo 1: Configurar o Build para WASM

Modifique o `build.zig` para adicionar o target WASM:

```zig
// build.zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    // Target: WebAssembly freestanding (sem sistema operacional)
    const target = b.resolveTargetQuery(.{
        .cpu_arch = .wasm32,
        .os_tag = .freestanding,
    });

    // Otimização para release pequena
    const optimize = .ReleaseSmall;

    const wasm = b.addExecutable(.{
        .name = "zig-wasm",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    // Exportar símbolos para JavaScript
    wasm.entry = .disabled;
    wasm.rdynamic = true;

    b.installArtifact(wasm);
}
```

## Passo 2: Criar Funções Exportadas

Crie o código Zig que será chamado do JavaScript:

```zig
// src/main.zig

// Exportar funções para JavaScript
export fn add(a: i32, b: i32) i32 {
    return a + b;
}

export fn multiply(a: i32, b: i32) i32 {
    return a * b;
}

export fn factorial(n: u32) u64 {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}
```

Compile:
```bash
zig build
```

O arquivo gerado estará em `zig-out/bin/zig-wasm.wasm`.

## Passo 3: Integrar com JavaScript

Crie a interface HTML e JavaScript:

```html
<!-- web/index.html -->
<!DOCTYPE html>
<html lang="pt-BR">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Zig + WebAssembly Demo</title>
    <style>
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
            background: #1a1a2e;
            color: #eee;
        }
        h1 { color: #ffd700; }
        .demo-box {
            background: #16213e;
            border-radius: 8px;
            padding: 20px;
            margin: 20px 0;
        }
        input {
            padding: 8px 12px;
            margin: 5px;
            border: 1px solid #0f3460;
            border-radius: 4px;
            background: #1a1a2e;
            color: #eee;
        }
        button {
            padding: 10px 20px;
            margin: 5px;
            background: #e94560;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        button:hover { background: #ff6b6b; }
        .result {
            margin-top: 10px;
            padding: 10px;
            background: #0f3460;
            border-radius: 4px;
            font-family: monospace;
        }
    </style>
</head>
<body>
    <h1>⚡ Zig + WebAssembly</h1>
    
    <div class="demo-box">
        <h2>Operações Matemáticas</h2>
        
        <div>
            <input type="number" id="a" placeholder="A" value="5">
            <input type="number" id="b" placeholder="B" value="3">
            <button onclick="calcAdd()">Add (Zig)</button>
            <button onclick="calcMultiply()">Multiply (Zig)</button>
            <div class="result" id="math-result">Resultado aparecerá aqui</div>
        </div>
    </div>

    <div class="demo-box">
        <h2>Fatorial</h2>
        
        <div>
            <input type="number" id="n" placeholder="N" value="10" min="0" max="20">
            <button onclick="calcFactorial()">Calcular Fatorial (Zig)</button>
            <div class="result" id="factorial-result">Resultado aparecerá aqui</div>
        </div>
    </div>

    <script src="app.js"></script>
</body>
</html>
```

```javascript
// web/app.js

let wasmModule = null;

// Carregar o módulo WASM
async function loadWasm() {
    try {
        const response = await fetch('../zig-out/bin/zig-wasm.wasm');
        const bytes = await response.arrayBuffer();
        
        const wasm = await WebAssembly.instantiate(bytes, {
            env: {
                // Funções importadas do JavaScript (se necessário)
                memory: new WebAssembly.Memory({ initial: 256, maximum: 256 })
            }
        });
        
        wasmModule = wasm.instance.exports;
        console.log('WASM carregado com sucesso!');
        console.log('Funções exportadas:', Object.keys(wasmModule));
    } catch (error) {
        console.error('Erro ao carregar WASM:', error);
    }
}

// Funções de cálculo
function calcAdd() {
    if (!wasmModule) return;
    
    const a = parseInt(document.getElementById('a').value) || 0;
    const b = parseInt(document.getElementById('b').value) || 0;
    
    const result = wasmModule.add(a, b);
    document.getElementById('math-result').textContent = `${a} + ${b} = ${result}`;
}

function calcMultiply() {
    if (!wasmModule) return;
    
    const a = parseInt(document.getElementById('a').value) || 0;
    const b = parseInt(document.getElementById('b').value) || 0;
    
    const result = wasmModule.multiply(a, b);
    document.getElementById('math-result').textContent = `${a} × ${b} = ${result}`;
}

function calcFactorial() {
    if (!wasmModule) return;
    
    const n = parseInt(document.getElementById('n').value) || 0;
    
    const start = performance.now();
    const result = wasmModule.factorial(n);
    const end = performance.now();
    
    document.getElementById('factorial-result').textContent = 
        `${n}! = ${result} (calculado em ${(end - start).toFixed(3)}ms)`;
}

// Carregar WASM quando a página iniciar
loadWasm();
```

## Passo 4: Trabalhar com Memória Compartilhada

Para dados mais complexos (strings, arrays), precisamos gerenciar a memória linear do WASM:

```zig
// src/main.zig

// Alocador simples para WASM
var wasm_allocator_state = std.heap.WasmPageAllocator.init;
const wasm_allocator = wasm_allocator_state.allocator();

// Buffer para comunicação
var buffer: [1024]u8 = undefined;
var buffer_len: usize = 0;

// Exportar ponteiro para o buffer
export fn getBufferPtr() [*]u8 {
    return &buffer;
}

export fn getBufferLen() usize {
    return buffer_len;
}

// Função que processa string
export fn reverseString(ptr: [*]u8, len: usize) void {
    buffer_len = len;
    
    // Copiar para buffer
    @memcpy(buffer[0..len], ptr[0..len]);
    
    // Reverter in-place
    var i: usize = 0;
    while (i < len / 2) : (i += 1) {
        const temp = buffer[i];
        buffer[i] = buffer[len - 1 - i];
        buffer[len - 1 - i] = temp;
    }
}

// Calcular Fibonacci
export fn fibonacci(n: u32) u64 {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

// Soma de array
export fn sumArray(ptr: [*]const i32, len: usize) i64 {
    var sum: i64 = 0;
    for (0..len) |i| {
        sum += ptr[i];
    }
    return sum;
}
```

Atualize o JavaScript para trabalhar com memória:

```javascript
// web/app.js (atualizado)

let wasmMemory = null;

async function loadWasm() {
    try {
        wasmMemory = new WebAssembly.Memory({ initial: 256, maximum: 512 });
        
        const response = await fetch('../zig-out/bin/zig-wasm.wasm');
        const bytes = await response.arrayBuffer();
        
        const wasm = await WebAssembly.instantiate(bytes, {
            env: {
                memory: wasmMemory
            }
        });
        
        wasmModule = wasm.instance.exports;
        console.log('WASM carregado!');
    } catch (error) {
        console.error('Erro:', error);
    }
}

// Converter string para WASM
function stringToWasm(str) {
    const encoder = new TextEncoder();
    const bytes = encoder.encode(str);
    
    // Alocar na memória WASM (simplificado)
    const ptr = 1024; // Offset na memória
    const mem = new Uint8Array(wasmMemory.buffer);
    mem.set(bytes, ptr);
    
    return { ptr, len: bytes.length };
}

// Ler string do WASM
function wasmToString(ptr, len) {
    const mem = new Uint8Array(wasmMemory.buffer, ptr, len);
    const decoder = new TextDecoder();
    return decoder.decode(mem);
}

// Reverter string usando WASM
function reverseString() {
    const input = document.getElementById('string-input').value;
    const { ptr, len } = stringToWasm(input);
    
    wasmModule.reverseString(ptr, len);
    
    const resultPtr = wasmModule.getBufferPtr();
    const resultLen = wasmModule.getBufferLen();
    
    const result = wasmToString(resultPtr, resultLen);
    document.getElementById('string-result').textContent = result;
}

// Calcular soma de array
function sumArray() {
    const input = document.getElementById('array-input').value;
    const numbers = input.split(',').map(n => parseInt(n.trim())).filter(n => !isNaN(n));
    
    // Copiar array para memória WASM
    const ptr = 2048;
    const mem = new Int32Array(wasmMemory.buffer);
    
    for (let i = 0; i < numbers.length; i++) {
        mem[ptr / 4 + i] = numbers[i];
    }
    
    const result = wasmModule.sumArray(ptr, numbers.length);
    document.getElementById('array-result').textContent = 
        `[${numbers.join(', ')}] → Soma = ${result}`;
}
```

## Passo 5: Exemplo Completo — Processamento de Imagem

Vamos criar um exemplo mais avançado — converter imagem para escala de cinza:

```zig
// src/main.zig

// Processar pixels (RGBA)
export fn grayscale(input: [*]const u8, output: [*]u8, len: usize) void {
    var i: usize = 0;
    while (i < len) : (i += 4) {
        const r = @as(u16, input[i]);
        const g = @as(u16, input[i + 1]);
        const b = @as(u16, input[i + 2]);
        
        // Fórmula de luminância
        const gray = @as(u8, @intCast((r * 299 + g * 587 + b * 114) / 1000));
        
        output[i] = gray;
        output[i + 1] = gray;
        output[i + 2] = gray;
        output[i + 3] = input[i + 3]; // Alpha inalterado
    }
}

// Aplicar brilho
export fn brightness(input: [*]const u8, output: [*]u8, len: usize, factor: i16) void {
    var i: usize = 0;
    while (i < len) : (i += 4) {
        inline for (0..3) |c| {
            const val = @as(i16, @intCast(input[i + c])) + factor;
            output[i + c] = @intCast(std.math.clamp(val, 0, 255));
        }
        output[i + 3] = input[i + 3]; // Alpha
    }
}
```

```html
<!-- Adicionar à página HTML -->
<div class="demo-box">
    <h2>Processamento de Imagem</h2>
    <input type="file" id="image-input" accept="image/*">
    <button onclick="processImage()">Converter para Escala de Cinza</button>
    <canvas id="canvas"></canvas>
</div>
```

```javascript
async function processImage() {
    const fileInput = document.getElementById('image-input');
    const file = fileInput.files[0];
    if (!file) return;
    
    const img = new Image();
    img.onload = () => {
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');
        
        canvas.width = img.width;
        canvas.height = img.height;
        ctx.drawImage(img, 0, 0);
        
        // Obter dados da imagem
        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        const pixels = imageData.data;
        
        // Processar com WASM
        const inputPtr = 4096;
        const outputPtr = 4096 + pixels.length;
        
        const mem = new Uint8Array(wasmMemory.buffer);
        mem.set(pixels, inputPtr);
        
        const start = performance.now();
        wasmModule.grayscale(inputPtr, outputPtr, pixels.length);
        const end = performance.now();
        
        // Ler resultado
        const result = new Uint8Array(wasmMemory.buffer, outputPtr, pixels.length);
        imageData.data.set(result);
        
        ctx.putImageData(imageData, 0, 0);
        
        console.log(`Processado em ${(end - start).toFixed(2)}ms`);
    };
    
    img.src = URL.createObjectURL(file);
}
```

## Deploy da Aplicação

### Opção 1: Servidor Local

```bash
# Python 3
python -m http.server 8080

# Node.js
npx serve .

# PHP
php -S localhost:8080
```

Acesse `http://localhost:8080/web/`

### Opção 2: GitHub Pages

1. Crie um repositório
2. Ative GitHub Pages nas configurações
3. Faça push dos arquivos web

### Opção 3: Netlify/Vercel

1. Faça deploy do diretório `web/`
2. Configure o build para copiar o `.wasm`

## Dicas de Performance

### 1. Compile com otimizações adequadas

```bash
# Tamanho mínimo (recomendado para web)
zig build -Doptimize=ReleaseSmall

# Performance máxima
zig build -Doptimize=ReleaseFast

# Com safety checks (para debug)
zig build -Doptimize=ReleaseSafe
```

### 2. Minimize o tamanho do WASM

```zig
// build.zig - adicione:
wasm.strip = true; // Remove símbolos de debug
```

### 3. Use SharedArrayBuffer para dados grandes

```javascript
// Para processamento de grandes volumes de dados
const sharedMemory = new WebAssembly.Memory({
    initial: 256,
    maximum: 512,
    shared: true
});
```

## Exercícios Práticos

### Exercício 1: Criptografia Simples

Implemente cifra de César em Zig e exporte para WASM:

```zig
export fn caesarCipher(input: [*]u8, len: usize, shift: i8) void {
    // Implementar cifra de César in-place
}
```

<details>
<summary>Ver solução</summary>

```zig
export fn caesarCipher(input: [*]u8, len: usize, shift: i8) void {
    for (0..len) |i| {
        const c = input[i];
        if (c >= 'a' and c <= 'z') {
            input[i] = @intCast(((c - 'a' + @as(u8, @intCast(shift))) % 26) + 'a');
        } else if (c >= 'A' and c <= 'Z') {
            input[i] = @intCast(((c - 'A' + @as(u8, @intCast(shift))) % 26) + 'A');
        }
    }
}
```

</details>

### Exercício 2: Sorting

Implemente quicksort em Zig e compare performance com JavaScript nativo.

### Exercício 3: Simulação Física

Crie uma simulação simples de partículas em Zig e renderize com Canvas.

## Limitações do WASM

| Limitação | Solução |
|---|---|
| Sem acesso direto ao DOM | Use JavaScript como glue |
| Apenas tipos numéricos | Serializar dados na memória |
| Sem threads (ainda) | Use Web Workers |
| Sem acesso a I/O | Use APIs do browser via JS |
| Tamanho máximo de memória | Configure no Memory object |

## Conclusão

Neste tutorial, você aprendeu a:

✅ Compilar Zig para WebAssembly  
✅ Exportar funções para JavaScript  
✅ Gerenciar memória compartilhada  
✅ Processar strings e arrays  
✅ Criar aplicações web com Zig + WASM  

### Próximos Passos

- 📦 [Como Instalar o Zig](/tutoriais/como-instalar-zig/) — revise o básico
- ⚡ [Comptime em Zig](/tutoriais/comptime-em-zig/) — otimize código em tempo de compilação
- 🎮 [Criando Jogos com Zig](/tutoriais/jogos-zig/) — use WASM para jogos no browser

---

*Criou algo interessante com Zig + WebAssembly? Compartilhe com a comunidade!*
