Developers' Guide: How Wax Lost Weight 🏋️
Published on HivePostify by @thebeedevs · Mon Feb 09 2026
Developers' Guide: How Wax Lost Weight 🏋️
Developers' Guide: How Wax Lost Weight 🏋️
Hey Hive community! 👋
As promised in this [article](hive-139531/@thebeedevs/afj7u-decentralized-google-login) below is detailed info how we did it. Enjoy !
🤓 For the Nerds: Technical Deep Dive
We imagine you might be interested in how we achieved such a significant size improvement in a project as complex as the Wax library (which mixes core Hive C++ code with TypeScript/JavaScript interfaces). Below, we share the key steps and ideas behind this "magic" change.
Our old motto is: It's not a big deal to achieve complex goals by using lots of source code and dependencies—the real trick is simplicity and a minimal amount of code (both source and generated binary) to limit maintenance, resource consumption, and execution costs...
Since the size of the Wax NPM package was mostly dominated by the internally held WASM code, focusing on reducing this part was the obvious decision.
Step 1: Improved emSDK Toolchain
The [Emscripten](https://emscripten.org/) toolchain we use to build the C++ portion of Wax targeting WASM has been improving significantly lately. No surprise there—WASM is one of the cutting-edge technologies these days. The obvious approach was to upgrade from the 3.0 series and early 4.0 releases we used in the past, which hadn't provided any significant improvements. But...
Yes—there were some changes that allowed us to go further, where previously it was very troublesome.
How did we do it? Well, our infrastructure is mostly based on Docker images, which allow us to efficiently manage our development environments by combining external tool upgrades with our own changes.
We updated the [common-ci-configuration](https://gitlab.syncad.com/hive/common-ci-configuration/-/commit/1fbe7147a58e5938e4dda5ba25953054b428fe62) module to use a new emsdk Docker image (version 4.0.22 series). Although it didn't immediately lead us to low-hanging fruit and instant benefits after the upgrade, it offered—in my humble opinion—something much more important: the ability to analyze WASM binaries using other WASM tools. We had tried a few months ago and it was impossible—the produced WASM binary was simply incompatible with WASM targets built by Rust or other tools (like WABT).
---
Step 2: emsdk 4.0.22 with -Oz Optimized System Libraries
What changed in common-ci-configuration:
- Now all system libraries (especially Boost, OpenSSL) are compiled with the -Oz optimization level. Some of these libraries are built on our side to provide better compatibility with the underlying C++ code shared directly with the Hive protocol library. - This propagates size savings across ALL linked code, not just our source.
The above trivial step—which, honestly, we had missed when we first discovered how much space could be saved (in the direct Wax code build) by using the -Oz switch instead of the default -O3 in Release WASM builds—gave us benefits: close to hundreds of kilobytes. But the results were still disappointing. We still had a binary exceeding 5MB. 😥
---
Step 3: Additional wasm-opt Binaryen Passes
HA ‼️ The new compiler, thanks to newer underlying Binaryen tools (responsible for binary code generation), also offered the ability to perform post-build optimizations at low risk. This is the big one! We added a custom set of Binaryen optimization passes that run after the initial WASM compilation:
diff ts/wasm/src/CMakeLists.txt
DEFINEWASMTARGETFOR( wax TARGETENVIRONMENT "web" LINKLIBRARIES hiveprotocol LINKOPTIONS -sNODYNAMICEXECUTION=1 -sALLOWMEMORYGROWTH=1 + -sBINARYENEXTRAPASSES="--all-features,-Oz,--strip-debug,--strip-dwarf,--strip-producers,--remove-unused-names,--remove-unused-module-elements,--vacuum,--duplicate-function-elimination,--merge-similar-functions" )
DEFINEWASMTARGETFOR( wax TARGETENVIRONMENT "node" LINKLIBRARIES hiveprotocol LINKOPTIONS -sALLOWMEMORYGROWTH=1 + -sBINARYENEXTRAPASSES="--all-features,-Oz,--strip-debug,--strip-dwarf,--strip-producers,--remove-unused-names,--remove-unused-module-elements,--vacuum,--duplicate-function-elimination,--merge-similar-functions" )
That was the first breath of fresh spring! (Even though winter was coming. 😊) 500 kB less! 🍾
Breakdown of each pass:
| Pass | Description | | ------ | ------------ | | --all-features | Enable all WASM features for maximum optimization opportunities | | -Oz | Optimize aggressively for size | | --strip-debug | Remove debug information | | --strip-dwarf | Remove DWARF debugging data | | --strip-producers | Remove producer section (compiler metadata) | | --remove-unused-names | Strip unnecessary function/variable names | | --remove-unused-module-elements | Dead code elimination at module level | | --vacuum | Remove obviously unneeded code | | --duplicate-function-elimination | Merge identical functions into one | | --merge-similar-functions | Merge functions that differ only slightly |
Result: 500 kB reduction from this single change! 🎯
But what to do next? The black box.
Yes, this was one of our biggest problems: how could we look inside the WASM binary package, how could we split it logically, and—most importantly—how could we associate it with the source code to answer the question: what led the compiler to produce so much binary code?
---
Step 4: Black Magic: What's Under the Black Box Cover? Simple Answer: The Evil.
Here we could offer some not-so-kind words about the code generated by the Emscripten compiler—it was guilty, producing tons of trash. Not exactly. We needed an exorcist—a.k.a. a hacker. :-) OK, let's get back down to earth: we needed tools. And we got them—thanks to the compiler upgrade mentioned above.
Due to the standardized format of the produced WASM binary, analysis by third-party tools became possible. twiggy is our hero...
It was the tool that allowed us to peer into the produced binary. And what we saw... was a big surprise. 😃
🔬 How to Analyze WASM Binary Size (Developer Guide)
To make things easier for everyone doing Wax development in our group, we added professional binary analysis tools to our emsdk Docker image to help with optimization efforts.
Available Tools
| Tool | Description | Best For | | ---- | ----------- | -------- | | twiggy | WASM-specific size profiler | Function-level analysis, dominators | | bloaty | Google's binary size profiler | Compile units, DWARF analysis | | wasm-objdump | WABT disassembler | Section breakdown, raw details | | wasm-opt | Binaryen optimizer | Metrics, optimization passes |
Investigation Workflow
Step 1: Get section breakdown
bash wasm-objdump -h wax.common.wasm
Step 2: Get overall metrics
bash wasm-opt --all-features --metrics wax.common.wasm
Example output:
[funcs] : 9405 [memory-data] : 1777632 🔴 Static data - often the biggest culprit! Try : 31448 🔴 Exception handling blocks Rethrow : 24515 🔴 Exception rethrowing
Step 3: Find top size consumers
bash twiggy top -n 50 wax.common.wasm
Example output:
Shallow Bytes │ Shallow % │ Item ───────────────┼───────────┼────────────────────── 1114324 ┊ 24.42% ┊ data[83] 🔴 1.1MB data segment! 185555 ┊ 4.07% ┊ data[0] 28393 ┊ 0.62% ┊ code[174] Largest function 14475 ┊ 0.32% ┊ code[4568]
Tags: #hivedev#wax#optimization#size#nerd#developer#hack#wasm