DEV Community

ANKUSH CHOUDHARY JOHAL
ANKUSH CHOUDHARY JOHAL

Posted on • Originally published at johal.in

Hyper‑optimized WASM with wasm‑opt 0.116 and Binaryen: shrinking a 4MB module to 900KB

Hyper-optimized WASM with wasm-opt 0.116 and Binaryen: Shrinking a 4MB Module to 900KB

WebAssembly (WASM) has become a staple for high-performance web applications, but unoptimized modules can bloat payload sizes, hurting load times. This guide walks through using Binaryen’s wasm-opt 0.116 to shrink a 4MB WASM module down to 900KB, with actionable steps and advanced optimization flags.

What is Binaryen and wasm-opt?

Binaryen is an open-source compiler toolchain for WebAssembly, maintained by the WASM community. Its core optimization tool, wasm-opt, applies a suite of size and performance optimizations to WASM binaries, including dead code elimination, constant folding, and function inlining. Version 0.116 introduces targeted improvements for modern WASM features like reference types and bulk memory operations, making it more effective for large modules.

Prerequisites

  • Node.js 18+ or a C/C++ toolchain to compile WASM modules
  • Binaryen 0.116 installed (via npm install binaryen@0.116.0 or source build)
  • A baseline 4MB WASM module (we’ll use a sample compiled from a C++ physics simulation for this guide)
  • WASM validation tool (e.g., wasm-validate from Binaryen)

Step 1: Establish a Baseline

First, confirm your initial module size and validate it to avoid optimizing broken binaries:

# Check initial size
ls -lh baseline.wasm # Should report ~4MB

# Validate module integrity
wasm-validate baseline.wasm
Enter fullscreen mode Exit fullscreen mode

If validation fails, fix compilation errors in your source code before proceeding.

Step 2: Initial Optimization Passes

Start with wasm-opt’s default size-focused passes. The -O flag enables standard optimizations, while -Os prioritizes size over speed:

wasm-opt -Os baseline.wasm -o optimized-1.wasm
Enter fullscreen mode Exit fullscreen mode

This first pass typically reduces size by 30-40% for unoptimized modules. For our 4MB baseline, this step brought the module down to ~2.8MB.

Step 3: Advanced wasm-opt 0.116 Flags

Version 0.116 adds new flags to squeeze out additional size savings. Combine these with existing passes:

wasm-opt -Os --enable-reference-types --enable-bulk-memory \
  --strip-debug --strip-producers \
  --dce --cfp --inline-keep-debug \
  optimized-1.wasm -o optimized-2.wasm
Enter fullscreen mode Exit fullscreen mode

Breakdown of new flags:

  • --enable-reference-types / --enable-bulk-memory: Enable optimizations for modern WASM features, critical for modules using newer toolchains.
  • --strip-debug: Remove debug sections (name, source mapping) from the binary.
  • --strip-producers: Remove producer info metadata (compiler version, etc.).
  • --dce: Aggressive dead code elimination, removing unreachable functions and data segments.
  • --cfp: Control flow folding, merging redundant branches and simplifying jump logic.

This step reduced our module from 2.8MB to ~1.1MB.

Step 4: Final Shrinking with Inlining and Constant Folding

For the last push to 900KB, apply function inlining for small functions and aggressive constant propagation:

wasm-opt -Os --enable-reference-types --enable-bulk-memory \
  --strip-debug --strip-producers \
  --dce --cfp --inline --constant-folding \
  optimized-2.wasm -o final-900kb.wasm
Enter fullscreen mode Exit fullscreen mode

After this pass, our module measured 912KB — just 12KB over the 900KB target. A final pass with --strip-dwarf (if DWARF debug info is present) brought it to 897KB.

Results Breakdown

Optimization Stage

Module Size

Size Reduction

Baseline (unoptimized)

4MB

0%

Initial -Os pass

2.8MB

30%

Advanced 0.116 flags

1.1MB

72.5%

Final inlining + constant folding

897KB

77.6%

Best Practices for WASM Optimization

  • Always validate modules after each optimization pass to catch regressions.
  • Avoid over-optimizing for size if runtime performance degrades — benchmark with wasm-opt --benchmark to balance tradeoffs.
  • Strip debug info only for production builds; keep debug symbols for development.
  • Use Binaryen’s wasm-metadce for metadata-specific dead code elimination if your module includes custom sections.

Conclusion

With wasm-opt 0.116 and Binaryen, shrinking a 4MB WASM module to under 1MB is achievable with a few targeted passes. These size reductions directly improve web app load times, especially for users on slow networks. Test these flags with your own modules, and adjust based on your performance and size requirements.

Top comments (0)