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
- Review the patterns and examples below
- Apply the relevant patterns to your implementation
- 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:
| Step | Command | Expected Result | Verify |
|---|---|---|---|
| 1. Release build | cargo build --release | Debug symbols stripped | ⬜ |
| 2. Strip WASM | wasm-strip module.wasm | Size reduced 5-10% | ⬜ |
| 3. Optimize | wasm-opt -Oz module.wasm -o opt.wasm | Size reduced 20-30% | ⬜ |
| 4. Validate | wasm-validate opt.wasm | "Valid" output | ⬜ |
| 5. Compress | brotli -Z opt.wasm -o opt.wasm.br | Size reduced 30-40% | ⬜ |
| 6. Gzip fallback | gzip -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:
| Metric | Baseline | Optimized | Target | Pass |
|---|---|---|---|---|
| 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-Pattern | Problem | Solution |
|---|---|---|
| Over-optimization too early | Wastes time, hard to debug | Start with basic build, profile first, then optimize |
| Skipping validation | Ships broken modules | Always run wasm-validate after optimization |
| No compression fallback | Breaks on unsupported browsers | Serve .br with .gz fallback, then raw .wasm |
| Optimizing debug builds | Removes debug symbols needed | Use separate dev/prod build configurations |
| Ignoring build cache | Slow CI/CD pipeline | Implement layer caching with cache-from: type=gha |
| Wrong optimization level | -O3 can increase size | Use -Oz for size, -O3 only for speed-critical code |
| Missing MIME type | Browser refuses to compile | Ensure Content-Type: application/wasm header |
| No performance baseline | Can't measure improvement | Capture 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