Skip to main content

WebAssembly Optimization Patterns

WebAssembly Optimization Patterns

When to Use This Skill

Use this skill when implementing wasm optimization patterns patterns in your codebase.

How to Use This Skill

  1. Review the patterns and examples below
  2. Apply the relevant patterns to your implementation
  3. Follow the best practices outlined in this skill

Level 1: Quick Reference (Under 500 tokens)

Core WASM Optimization Patterns

1. Module Splitting & Code Splitting

# Split large WASM module
wasm-split module.wasm -o split/ --split-funcs

# Lazy loading pattern
wasm-opt module.wasm --asyncify --optimize -o optimized.wasm

2. Compression Strategies

# Brotli compression (best for WASM)
brotli -Z module.wasm -o module.wasm.br

# Gzip fallback
gzip -9 module.wasm

# Comparison
ls -lh module.wasm*

3. Streaming Compilation

// Streaming instantiation (fastest)
WebAssembly.instantiateStreaming(fetch('module.wasm'), importObject)
.then(result => result.instance.exports);

// With compression header detection
fetch('module.wasm').then(response => {
const contentEncoding = response.headers.get('Content-Encoding');
return WebAssembly.instantiateStreaming(response, importObject);
});

4. Build Optimization Flags

# Rust → WASM optimization
cargo build --target wasm32-unknown-unknown --release
wasm-opt -Oz target/wasm32-unknown-unknown/release/module.wasm -o optimized.wasm

# C/C++ → WASM with Emscripten
emcc -O3 -s WASM=1 -s MODULARIZE=1 -s EXPORT_ES6=1 \
-s ALLOW_MEMORY_GROWTH=1 --closure 1 \
source.c -o module.js

5. Edge Deployment Configuration

# Cloudflare Workers WASM deployment
name = "wasm-service"
main = "worker.js"
compatibility_date = "2025-12-01"

[wasm_modules]
MODULE = "optimized.wasm"

Quick Wins:

  • Brotli compression: 30-40% size reduction
  • wasm-opt -Oz: 20-30% additional reduction
  • Streaming compilation: 2-3x faster load time
  • Module splitting: Load only what's needed

WASM Optimization Checklist

Pre-Optimization (Before You Start):

  • Baseline measurements captured (size, load time, compile time)
  • Target environment identified (browser, edge worker, Node.js)
  • Compression support verified (Brotli preferred, gzip fallback)
  • Module size >100KB (optimization worth the effort)
  • Not in development/debugging phase (use unoptimized builds)

Build Optimization:

StepCommandExpected ResultVerify
1. Release buildcargo build --releaseDebug symbols stripped
2. Strip WASMwasm-strip module.wasmSize reduced 5-10%
3. Optimizewasm-opt -Oz module.wasm -o opt.wasmSize reduced 20-30%
4. Validatewasm-validate opt.wasm"Valid" output
5. Compressbrotli -Z opt.wasm -o opt.wasm.brSize reduced 30-40%
6. Gzip fallbackgzip -9 -k opt.wasm.gz file created

Deployment Checklist:

  • Content-Type header: application/wasm
  • Content-Encoding header set correctly (br/gzip)
  • Cache-Control configured (immutable for versioned files)
  • CORS headers if cross-origin loading
  • Streaming compilation tested in target browser
  • Fallback for non-streaming browsers (Safari < 15)

Performance Validation:

MetricBaselineOptimizedTargetPass
File size (raw)___KB___KB<50% baseline
File size (br)___KB___KB<35% baseline
Load time___ms___ms<2s on 3G
Compile time___ms___ms<500ms
Instantiate time___ms___ms<100ms

Quick Decision: Optimization Level

What's your priority?
├── Smallest size (mobile, edge) → wasm-opt -Oz + Brotli
├── Fastest execution → wasm-opt -O3 (may increase size)
├── Balanced → wasm-opt -O2 + Brotli
├── Debug needed → wasm-opt -O0 --debuginfo
└── SIMD support → wasm-opt -O3 --enable-simd

Level 2: Implementation Details (Under 2000 tokens)

Advanced Optimization Workflows

1. Complete Build Pipeline

#!/bin/bash
# build-optimize-wasm.sh

set -e

SOURCE_FILE="$1"
OUTPUT_NAME="${2:-module}"

echo "Building WASM module..."

# Rust compilation with optimizations
cargo build --target wasm32-unknown-unknown \
--release \
-Z build-std=std,panic_abort \
-Z build-std-features=panic_immediate_abort

# Strip debug symbols
wasm-strip target/wasm32-unknown-unknown/release/*.wasm

# Optimize with wasm-opt (multiple passes)
wasm-opt target/wasm32-unknown-unknown/release/*.wasm \
-Oz \
--enable-bulk-memory \
--enable-mutable-globals \
--enable-nontrapping-float-to-int \
--enable-sign-ext \
--strip-debug \
--vacuum \
-o "${OUTPUT_NAME}.wasm"

# Compress with Brotli
brotli -Z "${OUTPUT_NAME}.wasm" -o "${OUTPUT_NAME}.wasm.br"
gzip -9 -k "${OUTPUT_NAME}.wasm"

# Size report
echo "Size comparison:"
ls -lh "${OUTPUT_NAME}.wasm"*

# Analyze module
wasm-objdump -x "${OUTPUT_NAME}.wasm" | head -50

2. Module Splitting Strategy

// wasm-loader.js - Dynamic module loading
class WASMModuleLoader {
constructor() {
this.modules = new Map();
this.cache = new Map();
}

async loadCore() {
// Load essential core module first
const core = await this.loadModule('core.wasm');
this.modules.set('core', core);
return core;
}

async loadFeature(featureName) {
// Lazy load feature modules
if (this.modules.has(featureName)) {
return this.modules.get(featureName);
}

const module = await this.loadModule(`features/${featureName}.wasm`);
this.modules.set(featureName, module);
return module;
}

async loadModule(url) {
// Check cache first
if (this.cache.has(url)) {
return this.cache.get(url);
}

// Streaming compilation with compression detection
const response = await fetch(url);
const contentType = response.headers.get('Content-Type');

let module;
if (contentType === 'application/wasm') {
const result = await WebAssembly.instantiateStreaming(response, {
env: this.getImportObject()
});
module = result.instance;
} else {
// Fallback for unsupported streaming
const bytes = await response.arrayBuffer();
const result = await WebAssembly.instantiate(bytes, {
env: this.getImportObject()
});
module = result.instance;
}

this.cache.set(url, module);
return module;
}

getImportObject() {
return {
memory: new WebAssembly.Memory({ initial: 256, maximum: 512 }),
table: new WebAssembly.Table({ initial: 0, element: 'anyfunc' }),
// Additional imports
};
}
}

3. Edge Deployment Patterns

Cloudflare Workers:

// worker.js
import moduleWasm from './optimized.wasm';

export default {
async fetch(request, env, ctx) {
// Instantiate WASM module
const instance = await WebAssembly.instantiate(moduleWasm, {
env: {
memory: new WebAssembly.Memory({ initial: 256 }),
}
});

// Process request
const url = new URL(request.url);
const input = url.searchParams.get('input');

// Call WASM function
const result = instance.exports.process(input);

return new Response(JSON.stringify({ result }), {
headers: { 'Content-Type': 'application/json' }
});
}
};

Fastly Compute@Edge:

// src/main.rs
use fastly::http::{Method, StatusCode};
use fastly::{Error, Request, Response};

#[fastly::main]
fn main(req: Request) -> Result<Response, Error> {
match req.get_method() {
&Method::GET | &Method::HEAD => {
// WASM module already compiled in edge runtime
let result = process_request(req.get_url().path());

Ok(Response::from_status(StatusCode::OK)
.with_body_text_plain(&result))
}
_ => Ok(Response::from_status(StatusCode::METHOD_NOT_ALLOWED)),
}
}

fn process_request(path: &str) -> String {
// Business logic executed as WASM
format!("Processed: {}", path)
}

4. Performance Monitoring

// wasm-performance-monitor.js
class WASMPerformanceMonitor {
constructor(moduleName) {
this.moduleName = moduleName;
this.metrics = {
loadTime: 0,
compileTime: 0,
instantiateTime: 0,
executionTimes: []
};
}

async measureLoad(wasmUrl) {
const start = performance.now();
const response = await fetch(wasmUrl);
const loadEnd = performance.now();

this.metrics.loadTime = loadEnd - start;

const compileStart = performance.now();
const module = await WebAssembly.compileStreaming(response);
const compileEnd = performance.now();

this.metrics.compileTime = compileEnd - compileStart;

const instantiateStart = performance.now();
const instance = await WebAssembly.instantiate(module, {});
const instantiateEnd = performance.now();

this.metrics.instantiateTime = instantiateEnd - instantiateStart;

return instance;
}

measureExecution(fn, ...args) {
const start = performance.now();
const result = fn(...args);
const end = performance.now();

this.metrics.executionTimes.push(end - start);
return result;
}

report() {
return {
module: this.moduleName,
loadTime: `${this.metrics.loadTime.toFixed(2)}ms`,
compileTime: `${this.metrics.compileTime.toFixed(2)}ms`,
instantiateTime: `${this.metrics.instantiateTime.toFixed(2)}ms`,
avgExecutionTime: this.metrics.executionTimes.length > 0
? `${(this.metrics.executionTimes.reduce((a, b) => a + b) / this.metrics.executionTimes.length).toFixed(2)}ms`
: 'N/A'
};
}
}

Level 3: Complete Reference (Full tokens)

Production-Grade WASM Optimization

1. Multi-Target Build System

#!/bin/bash
# build-multi-target-wasm.sh

set -e

PROJECT_NAME="${1:-wasm-project}"
OUTPUT_DIR="${2:-dist}"

mkdir -p "$OUTPUT_DIR"

# Build configurations
declare -A TARGETS=(
["web"]="wasm32-unknown-unknown"
["wasi"]="wasm32-wasi"
["emscripten"]="wasm32-unknown-emscripten"
)

declare -A OPTIMIZATIONS=(
["size"]="Oz"
["speed"]="O3"
["balanced"]="O2"
)

build_target() {
local target_name="$1"
local target_triple="$2"
local opt_level="$3"

echo "Building ${target_name} with ${opt_level} optimization..."

# Rust build
cargo build \
--target "$target_triple" \
--release \
--manifest-path Cargo.toml

local wasm_file="target/${target_triple}/release/${PROJECT_NAME}.wasm"
local output_file="${OUTPUT_DIR}/${PROJECT_NAME}-${target_name}-${opt_level}.wasm"

# Optimize
wasm-opt "$wasm_file" \
-"$opt_level" \
--enable-bulk-memory \
--enable-mutable-globals \
--enable-nontrapping-float-to-int \
--enable-sign-ext \
--enable-simd \
--strip-debug \
--vacuum \
-o "$output_file"

# Compress
brotli -Z "$output_file" -o "${output_file}.br"
gzip -9 -k "$output_file"

# Report
echo " Original: $(wc -c < "$wasm_file") bytes"
echo " Optimized: $(wc -c < "$output_file") bytes"
echo " Brotli: $(wc -c < "${output_file}.br") bytes"
echo " Gzip: $(wc -c < "${output_file}.gz") bytes"
}

# Build matrix
for target_name in "${!TARGETS[@]}"; do
for opt_name in "${!OPTIMIZATIONS[@]}"; do
build_target "$target_name" "${TARGETS[$target_name]}" "${OPTIMIZATIONS[$opt_name]}"
done
done

# Generate size report
echo "Size Report:" > "${OUTPUT_DIR}/size-report.txt"
ls -lh "${OUTPUT_DIR}"/*.wasm* >> "${OUTPUT_DIR}/size-report.txt"

echo "Build complete. Output: $OUTPUT_DIR"

2. Advanced Caching Strategy

// wasm-cache-manager.js
class WASMCacheManager {
constructor(cacheName = 'wasm-modules-v1') {
this.cacheName = cacheName;
this.dbName = 'wasm-compiled-db';
this.storeName = 'modules';
}

async openDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, 1);

request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);

request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains(this.storeName)) {
db.createObjectStore(this.storeName);
}
};
});
}

async cacheCompiledModule(url, module) {
const db = await this.openDB();
const tx = db.transaction(this.storeName, 'readwrite');
const store = tx.objectStore(this.storeName);

// Cache compiled module (faster than recompiling)
await store.put(module, url);

return new Promise((resolve, reject) => {
tx.oncomplete = () => resolve();
tx.onerror = () => reject(tx.error);
});
}

async getCompiledModule(url) {
const db = await this.openDB();
const tx = db.transaction(this.storeName, 'readonly');
const store = tx.objectStore(this.storeName);
const request = store.get(url);

return new Promise((resolve, reject) => {
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}

async loadWithCache(url) {
// Check compiled cache first
const cachedModule = await this.getCompiledModule(url);
if (cachedModule) {
console.log('Using cached compiled module');
return await WebAssembly.instantiate(cachedModule, this.getImports());
}

// Load and compile
const response = await fetch(url);
const module = await WebAssembly.compileStreaming(response);

// Cache compiled module
await this.cacheCompiledModule(url, module);

// Instantiate
return await WebAssembly.instantiate(module, this.getImports());
}

getImports() {
return {
env: {
memory: new WebAssembly.Memory({ initial: 256, maximum: 512 }),
abort: () => { throw new Error('WASM abort'); }
}
};
}
}

3. CDN Deployment Configuration

Cloudflare R2 + Workers:

// wasm-cdn-worker.ts
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
const path = url.pathname;

// Serve WASM from R2 bucket
const object = await env.WASM_BUCKET.get(path.slice(1));

if (!object) {
return new Response('Not Found', { status: 404 });
}

// Headers for optimal WASM delivery
const headers = new Headers({
'Content-Type': 'application/wasm',
'Cache-Control': 'public, max-age=31536000, immutable',
'Access-Control-Allow-Origin': '*',
});

// Detect and serve compressed version
const acceptEncoding = request.headers.get('Accept-Encoding') || '';

if (acceptEncoding.includes('br')) {
const brObject = await env.WASM_BUCKET.get(`${path.slice(1)}.br`);
if (brObject) {
headers.set('Content-Encoding', 'br');
return new Response(brObject.body, { headers });
}
}

if (acceptEncoding.includes('gzip')) {
const gzObject = await env.WASM_BUCKET.get(`${path.slice(1)}.gz`);
if (gzObject) {
headers.set('Content-Encoding', 'gzip');
return new Response(gzObject.body, { headers });
}
}

return new Response(object.body, { headers });
}
};

4. Debugging & Profiling

#!/bin/bash
# wasm-profile.sh

WASM_FILE="$1"

echo "Analyzing WASM module: $WASM_FILE"

# Module structure
echo "=== Module Structure ==="
wasm-objdump -x "$WASM_FILE" | head -100

# Function analysis
echo "=== Exported Functions ==="
wasm-objdump -x "$WASM_FILE" | grep -A 20 "Export\["

# Memory analysis
echo "=== Memory Sections ==="
wasm-objdump -s "$WASM_FILE" | head -50

# Code size breakdown
echo "=== Code Size ==="
wasm-objdump -d "$WASM_FILE" | wc -l

# Validation
echo "=== Validation ==="
wasm-validate "$WASM_FILE" && echo "Valid" || echo "Invalid"

# Performance profiling with Node.js
cat > profile-wasm.js <<'EOF'
const fs = require('fs');
const { performance } = require('perf_hooks');

const wasmBuffer = fs.readFileSync(process.argv[2]);

async function profile() {
const start = performance.now();
const module = await WebAssembly.compile(wasmBuffer);
const compileEnd = performance.now();

const instance = await WebAssembly.instantiate(module, {});
const instantiateEnd = performance.now();

console.log('=== Performance ===');
console.log(`Compile: ${(compileEnd - start).toFixed(2)}ms`);
console.log(`Instantiate: ${(instantiateEnd - compileEnd).toFixed(2)}ms`);
console.log(`Total: ${(instantiateEnd - start).toFixed(2)}ms`);
}

profile();
EOF

node profile-wasm.js "$WASM_FILE"
rm profile-wasm.js

Best Practices Summary:

  • Always use streaming compilation in production
  • Cache compiled modules in IndexedDB
  • Serve pre-compressed .br files with fallback to .gz
  • Use module splitting for large applications
  • Profile startup time and optimize critical path
  • Deploy to edge CDN for global low-latency access
  • Monitor WASM execution metrics in production

Success Output

When this skill is successfully applied, output:

✅ SKILL COMPLETE: wasm-optimization-patterns

Completed:
- [x] WASM build pipeline configured with optimization flags
- [x] Brotli compression applied (size reduction: X%)
- [x] Streaming compilation implemented
- [x] Module splitting/lazy loading configured
- [x] Edge deployment setup complete
- [x] Performance benchmarks recorded

Outputs:
- optimized.wasm (size: X KB)
- optimized.wasm.br (size: X KB, compression ratio: X%)
- build-optimize-wasm.sh
- wasm-loader.js (if module splitting)
- Performance report (load time: Xms, compile time: Xms)

Completion Checklist

Before marking this skill as complete, verify:

  • WASM module compiles without errors
  • wasm-opt optimization applied successfully
  • File size reduction achieved (target: 20-40%)
  • Brotli compression working (target: 30-40% additional reduction)
  • Streaming compilation tested in browser
  • Module loads and executes correctly
  • Performance metrics captured (load, compile, instantiate times)
  • Edge deployment configuration validated
  • All output files exist at expected locations
  • Size comparison report generated

Failure Indicators

This skill has FAILED if:

  • ❌ wasm-opt crashes or produces invalid output
  • ❌ Compiled WASM module fails validation (wasm-validate)
  • ❌ Brotli compression increases file size
  • ❌ Streaming compilation throws errors in browser
  • ❌ Module splitting breaks functionality
  • ❌ Performance is worse than unoptimized version
  • ❌ Edge deployment returns 500 errors
  • ❌ Build pipeline missing required dependencies
  • ❌ Output files corrupted or unreadable

When NOT to Use

Do NOT use this skill when:

  • Simple WASM module under 100KB (optimization overhead not worth it)
  • Development/debugging phase (use unoptimized builds with debug symbols)
  • WASM not performance-critical to application
  • Target environment doesn't support streaming compilation (use basic-wasm-patterns instead)
  • No CDN/edge infrastructure available for deployment
  • Module used only server-side (Node.js) where size less critical
  • Time-to-market more important than optimization (optimize later)

Use alternatives:

  • basic-wasm-patterns - Simple compilation without optimization
  • wasm-debugging-patterns - Development builds with source maps
  • server-side-wasm - Node.js-specific WASM patterns

Anti-Patterns (Avoid)

Anti-PatternProblemSolution
Over-optimization too earlyWastes time, hard to debugStart with basic build, profile first, then optimize
Skipping validationShips broken modulesAlways run wasm-validate after optimization
No compression fallbackBreaks on unsupported browsersServe .br with .gz fallback, then raw .wasm
Optimizing debug buildsRemoves debug symbols neededUse separate dev/prod build configurations
Ignoring build cacheSlow CI/CD pipelineImplement layer caching with cache-from: type=gha
Wrong optimization level-O3 can increase sizeUse -Oz for size, -O3 only for speed-critical code
Missing MIME typeBrowser refuses to compileEnsure Content-Type: application/wasm header
No performance baselineCan't measure improvementCapture metrics before optimization

Principles

This skill embodies CODITECT core principles:

#1 Recycle → Extend → Re-Use → Create

  • Reuse existing build scripts and optimization tools
  • Extend wasm-opt with custom flags for specific needs
  • Build upon proven compression strategies

#5 Eliminate Ambiguity

  • Clear size metrics (before/after optimization)
  • Explicit performance benchmarks (load, compile, instantiate)
  • Concrete compression ratios and targets

#6 Clear, Understandable, Explainable

  • Each optimization flag documented with purpose
  • Build pipeline steps clearly separated
  • Performance metrics explained with context

#8 No Assumptions

  • Always validate optimized output with wasm-validate
  • Verify browser compatibility with streaming compilation
  • Test compressed variants in target environment

#10 Research When in Doubt

  • WASM optimization evolves rapidly (check latest wasm-opt flags)
  • Browser WASM API updates frequently
  • Edge platform capabilities vary (verify before deployment)

Full Standard: CODITECT-STANDARD-AUTOMATION.md