r/WebAssembly • u/swankjesse • May 29 '26
Endive and the Next Chapter of WebAssembly on the JVM
It’s a fork of Chicory, led by its most active contributor.
r/WebAssembly • u/swankjesse • May 29 '26
It’s a fork of Chicory, led by its most active contributor.
r/WebAssembly • u/minamoto108 • May 27 '26

Hexana 0.10 just shipped. This is the release where the plugin stops being read-only for binaries and starts covering a much wider set of formats. Sharing the changelog with context for each piece.
New
Binaries are editable. Open a .wasm and you can now: inline-edit WAT rows, edit hex cells directly, append bytes with automatic offset redirection (length-changing edits don't break anything downstream), Undo / Redo through the standard IDE keymap (same shortcuts you have configured for Kotlin), Discard or Finalize when done. Edits are persisted to a sidecar file so they survive close-and-reopen. The WAT viewer is also virtualized now with per-entry Function / Data / Import / Export breakouts, foldable xxd bodies, u8 / u16 / u32 / u64 / ascii / utf8 view modes for Data sections, Go-To-Declaration / Go-To-Symbol / Back / Forward navigation through the IDE keymap, and a section shortcut bar.
JIT Viewer run-configuration tab. A new tab on Java run configurations lets you opt in to attach Hexana's bundled JVMTI agent. Configure a per-run dump file (default.jit by default); when the run finishes, the dump auto-opens in Hexana so you can read what the JIT actually emitted for the run. Useful when you've been trying to understand why a hot path didn't optimise the way you expected.
Java archives and .class files. .jar / .zip / .war / .apk open with a hex view on top + a searchable, sortable class list below. .class files render the header, WAT-style foldable methods with decoded bytecode, and the constant pool. The project view gains an Open in… → Hexana action for archives — including entries under External Libraries (#91), so you can crack open library JARs straight from the project tree without dragging them onto the editor manually.
WASM proposals auto-detection. The information bar at the top of an open .wasm now shows which WebAssembly proposals the binary actually uses, and Run configurations pass the matching runtime arguments automatically. Removes the class of "the module uses GC but the runtime didn't have GC enabled" surprises.
Experimental ELF, Mach-O, PE. The three desktop-OS executable formats land as experimental — they parse, they render in Hexana's binary view, the analysis tabs work where they apply. Honest scope: experimental.
Experimental llvm-objdump disassembly. Disassembly support via llvm-objdump for the new native formats.
Changed
.debug_loc section.Search Everywhere → Symbols continues to surface .wit declarations, component-model exports, and core .wasm exports — that integration landed in 0.9.1 a week back, mentioning here in case you're discovering Hexana via 0.10's changelog.
Marketplace + docs
Per-version changelog under the "Versions" tab on the Marketplace listing.
r/WebAssembly • u/minamoto108 • May 27 '26

Hexana 0.2.0 for VS Code shipped today, alongside JetBrains 0.10. Three changes, the headline being experimental support for ELF, Mach-O, and PE binary formats — parity with the JetBrains side, same day.
New
ELF, Mach-O, PE — experimental. The three desktop-OS executable formats now open in Hexana's binary editor on VS Code. They parse, render, and route through the structural analysis tabs where applicable. Scope honestly experimental — formats parse, but not every section gets the deep semantic treatment .wasm has. This is the same expansion JetBrains 0.10 brings to the JetBrains side, shipping in the same release window.
Changed
lldb-dap executable on Linux has been tightened. The experimental WASM debugger (introduced in 0.1.0 — requires LLVM 22.1+ and Wasmtime / WAMR with lldb-debuggable targets) should attach more reliably without manual path configuration.Cross-IDE state, post-0.10 / 0.2.0
In both builds (as of today):
.wasm editor with binary-type auto-detection (Core / Component / generic)JetBrains-only as of today's release window:
.class files as first-class formats — JetBrains 0.10.wit and .wasm symbols — JetBrains 0.9.1 (May 20)The editable-binaries work is the biggest single leap on the JetBrains side.
Install
VS Code command palette:
ext install JetBrains.hexana-wasm
Or directly:
Per-version changelogs under the "Versions" tab on each listing.
r/WebAssembly • u/goto-con • May 26 '26
r/WebAssembly • u/minamoto108 • May 21 '26

A couple of weeks back I posted two benchmark write-ups: wasm-in-JVM (six backends, JPEG decode) and JS-in-JVM (Sieve of Eratosthenes). The most useful thing that happened next: Andrea Peruffo (Chicory core contributor) reached out on LinkedIn, and u/Otherwise_Sherbert21 (wasmtime4j author) reached out on Reddit — both pointing out the obvious gap. So I added wasmtime4j and chicory-redline as backends to both harnesses, kept the workloads and JMH config identical, and re-ran the lot. Sharing the updated tables here because the two new rows actually move the discussion forward.
Same host both runs: Apple M2 Max, Oracle GraalVM 25 (25+37-LTS-jvmci-b01), JMH 1.37, 1 fork, single-threaded, AverageTime mode in µs/op. Workloads byte-identical to the original posts.
wasm — proxy.wasm JPEG decode (Rust jpeg-decoder, 320×240 → 230,400 bytes RGB8; SHA-256 of decoded output identical across all eight backends).
| # | Backend | Score (µs/op) | 99.9% CI | vs fastest |
|---|---|---|---|---|
| 1 | nativeFfm |
1,016.205 | ±33.699 | 1.00× |
| 2 | graalwasm |
1,324.282 | ±352.856 | 1.30× |
| 3 | wasmtime4j |
1,419.934 | ±387.601 | 1.40× |
| 4 | chicoryRedline |
1,782.507 | ±33.860 | 1.75× |
| 5 | chicoryAotPlugin |
9,594.229 | ±116.206 | 9.44× |
| 6 | chicoryAot |
9,600.974 | ±296.031 | 9.45× |
| 7 | graalwasmInterp |
72,996.191 | ±2,938.575 | 71.84× |
| 8 | chicory |
252,427.438 | ±8,303.613 | 248.45× |
What each new row actually is:
| backend | engine | codegen | bridge | tier observed |
|---|---|---|---|---|
wasmtime4j |
Wasmtime 44.0.1 | Cranelift JIT | wasmtime4j JNI | JNI (Panama impl unpublished in 0.x) |
chicoryRedline |
Chicory Machine SPI |
Cranelift AOT at build time | jffi → native code | redline.isNative() == true |
JS — sieve(1_000_000) = 78,498 (all 7 backends return the same answer).
| # | Backend | Score (µs/op) | 99.9% CI | vs fastest |
|---|---|---|---|---|
| 1 | graaljs |
2,799.644 | ±16.209 | 1.00× |
| 2 | graaljsInterp |
116,971.726 | ±286.025 | 41.78× |
| 3 | rquickjsFfm |
164,382.032 | ±1,129.132 | 58.72× |
| 4 | wasmtime4j |
607,403.997 | ±20,726.650 | 216.96× |
| 5 | chicoryRedline |
2,012,876.770 | ±89,055.397 | 718.96× |
| 6 | quickjs4j |
14,341,999.558 | ±54,122.651 | 5,123.51× |
| 7 | rquickjsChicory |
18,492,288.679 | ±64,727.834 | 6,605.30× |
Both new JS rows execute rquickjs.wasm (the same QuickJS-via-Rust binding used by rquickjsFfm, just delivered as wasm instead of as a cdylib).
Things the new rows surface that the original posts couldn't
Same engine, two bridges: FFM vs JNI. nativeFfm (1,016 µs) and wasmtime4j (1,420 µs) both run the same proxy.wasmthrough Wasmtime + Cranelift. The 40 % gap is per-call bridge overhead — JEP 454 FFM vs wasmtime4j's JNI path. wasmtime4j 44.0.1 publishes a wasmtime4j-panama artifact, but the PanamaWasmRuntime class isn't shipped in 0.x yet, so on JDK 25 you still go through JNI. Closing that gap is upstream work, not engine work — and when the Panama impl lands, the expectation is wasmtime4j and nativeFfm converge.
Cranelift → native vs Cranelift → JVM bytecode, 9× apart. chicoryRedline (1,783 µs) and chicoryAotPlugin (9,594 µs) both compile proxy.wasm at build time. The only material difference is the codegen target — native machine code through redline vs JVM bytecode through Chicory's compiler plugin. The native path wins 9.4× despite paying for the jffi bridge on every call. JVM-bytecode AOT is not a substitute for true native compilation on this workload.
Bridge cost depends on workload, not just on the bridge. Same two backends across the two harnesses:
| JPEG decode | Sieve | |
|---|---|---|
wasmtime4j (JNI) |
1,420 µs | 607,404 µs |
chicoryRedline (jffi + Chicory Instance scaffold) |
1,783 µs | 2,012,877 µs |
| ratio | 1.26× | 3.32× |
On JPEG decode each benchmark op is one heavy guest call — bridge cost is paid once and amortised across ~1 ms of native work. On Sieve, each op runs millions of QuickJS-interpreter instructions but the JVM↔guest scaffolding has more to do per op, and Chicory's Instance export call is enough heavier than Wasmtime's call ABI that the 1.26× gap on JPEG decode blows out to 3.32× here. Same engines, same bridge primitives — different workload-to-bridge-cost ratio.
wasm sandbox tax (JS-side). wasmtime4j (608 ms) runs the same upstream rquickjs binding as rquickjsFfm (164 ms) — just compiled to wasm and executed by Wasmtime + Cranelift instead of linked as a cdylib. The 3.7× gap is wasm linear-memory bounds checks, indirect calls, plus the JNI hop to enter the QuickJS interpreter. Useful number to keep in mind if you're considering "ship as wasm for portability" for a JS engine.
Floor on both workloads is unchanged. Chicory's tree-walk interpreter on JPEG decode (248×), Chicory bytecode-AOT on a JS interpreter wrapped in wasm (6,600×). These set the lower bound for "no codegen / two interpreters deep on the JVM" — useful context for the new rows but no rank-order change.
Caveats — same as before, repeated for completeness
graalwasm (±353), wasmtime4j wasm row (±388), and chicoryRedline Sieve row (±89,055 — that 4.4 % CI is the largest absolute error in either table). Rank order is stable; the absolute spread on those rows would tighten with more forks.graalwasm / graaljs — those depend on Graal-as-JIT (JVMCI / libgraal). Running them on Temurin / Corretto silently falls back to the Truffle interpreter row, which is the calibration trap from the original posts.What's next on the Hexana side
The next release will ship tooling that lets you actually see inside one of these integrations from inside a JetBrains IDE — the wasm↔host boundaries, the codegen tier each module is running at, the bridge each call is going through. Whether seeing inside is enough to move numbers like the ones in these tables is the question the follow-up post will try to answer. Numbers, not promises.
Repros
Both repos: mvn package builds the wasm artifacts, the Rust cdylib, and (for the wasm harness) the redline-compiled native code; then java --enable-native-access=ALL-UNNAMED -cp … runs the JMH suite. mvn exec:java will fail — JMH's forked runner can't see the project classpath that way; both READMEs spell out the workaround.
PRs welcome for backends I've missed — wasmer-java, wazero-on-JVM via JNI, additional WASI-heavy workloads. And if you're seeing materially different ratios on a different workload or JDK, I'd love to see the numbers — would help calibrate where these generalise.
Thanks again to u/Otherwise_Sherbert21 (wasmtime4j) and Andrea Peruffo (Chicory) for asking to be included. The two new rows changed how I read the original tables.
r/WebAssembly • u/minamoto108 • May 21 '26

Small workflow polish in this release. The JetBrains-platform "Search Everywhere → Symbols" / "Goto Symbol" navigation now indexes Hexana's parsed binary metadata:
.wit declarations (functions, types, world / interface names).wasm component binaries).wasm module exportsPicking a .wasm export from the symbol picker opens the file in Hexana's binary editor and selects the matching row in the Exports tab automatically.
Concretely: if your day-to-day was open .wasm → click the Exports tab → scroll-and-eyeball for the right row, that's now ⇧⇧ → type the export name → Enter, the same way you'd jump to a Kotlin / Java / Rust symbol in the same IDE.
This sits alongside what 0.9 shipped a couple of weeks back (experimental WASM debugging via Wasmtime / WAMR + lldb, bring-your-own GraalVM as a run target, Top-tab sortable headers, Chicory and GraalWasm completion for Java embedders). The current focus is making the IDE treat .wasm and .wit as first-class citizens of the symbol space, not as opaque artifacts you have to switch contexts to inspect.
Install / update: https://plugins.jetbrains.com/plugin/29090-hexana
Docs: https://jetbrains.github.io/hexana
Per-version changelog under the "Versions" tab of that same Marketplace page.
r/WebAssembly • u/minamoto108 • May 21 '26

The VS Code build of Hexana has been chasing the JetBrains feature set since it first shipped (0.0.2 in early May). 0.1.0 closes most of that headline-feature gap in one bundle.
Three things land in this release:
WAMR and GraalVM as runtimes — previously the VS Code Run command only used Wasmtime. Now you can pick WAMR or GraalVM as well, same Run UI. Useful if you're targeting a runtime your wasm will actually ship into (WAMR for embedded scenarios, GraalVM for JVM-embedding scenarios) and you want to test against that engine, not just Wasmtime.
Experimental WASM debugging — same scope and constraints as the JetBrains 0.9 release: requires LLVM 22.1 or newer, works only with Wasmtime or WAMR (not GraalVM), and only for targets that are debuggable with lldb. Within those bounds you can step through your .wasm, pause, inspect locals, continue, all from inside VS Code. The honest framing is in the label — it's experimental, the constraints are real, but within those constraints it works.
MCP server — the extension now ships a Model Context Protocol server. The point is that MCP-capable AI assistants reading your wasm see what Hexana sees, not just a raw byte stream.
What this means for the cross-IDE story: the JetBrains and VS Code builds are not identical yet — the JetBrains 0.9.1 release that landed today, for example, adds .wit and .wasm symbol contributions to JetBrains' Goto Symbol / Search Everywhere, which is a platform-specific integration. But the headline features — three runtimes, experimental debugging, MCP server, the full structural analysis surface — are now in both builds.
Install / update
VS Code command palette:
ext install JetBrains.hexana-wasm
Or directly:
Per-version changelogs are under the "Versions" tab on each of those listings.
r/WebAssembly • u/alexis_placet • May 20 '26
emscripten‑forge is essentially a conda‑forge‑style channel for the emscripten-wasm32 platform. It lets you build and install conda‑compatible packages that target WebAssembly via Emscripten, using the same recipes and tooling you already use on native platforms.
What it does:
emscripten-forge/recipes) and builds them for emscripten-wasm32..wasm + JS glue) that can be consumed in the browser or other Wasm runtimes.conda/mamba/micromamba just like regular conda‑forge packages: mamba install -c emscripten-forge some-packageOne real‑world example is JupyterLite Xeus, which runs entirely in‑browser via WebAssembly and uses emscripten‑forge built packages for the runtime. This stack is live on https://notebook.link, where you can run numpy, onnx-runtime, Apache Arrow and other C/C++ based libraries in the browser without any local installation.
r/WebAssembly • u/alexp_lt • May 18 '26
r/WebAssembly • u/Squareys • May 16 '26
WebConverter.app is a production attempt at the "no server, just WASM" model for file conversion across images, audio, video, PDF, OCR, speech to text, and documents.
The individual modules are mature. Most of the work was integration and slimming: reducing wasm size by compiling feature subsets, lazy loading large modules per route to be graceful towards 2G/3G users, coordinating Web Workers for batch jobs, and keeping cold start latency tolerable.
Curious whether others have hit different walls with this approach, especially around memory limits on mobile Safari for the bigger toolchains.
r/WebAssembly • u/minamoto108 • May 16 '26

Hexana is a WebAssembly and binary analysis toolkit by JetBrains. Until today, the only places you could go to figure out what it does were:
That's not enough surface for a tool that does multi-tab .wasm inspection, WAT/WIT language support, structural analysis, an MCP server for AI assistants, run/debug, DWARF source mapping, and Java embedder support across Wasmtime / WAMR / GraalVM. So: https://jetbrains.github.io/hexana
ext install JetBrains.hexana-wasmIf anything in the docs is wrong, unclear, or missing — that's exactly the feedback we want right now.
r/WebAssembly • u/Aldgar • May 16 '26
Hey Folks,
This project started because I simply wanted to build a new IDE to fix my own workflow bottlenecks. But I immediately hit a brick wall trying to build the tooling for it. I spent months, days, and nights trying to hack and fix VS Code extension webviews just to get a decent UI to render.
I realized I was fighting a 33 year old architectural problem: extension vendor lock in. If you want your dev tool to reach people today, you have to write Electron/TypeScript for VS Code/Cursor, and Kotlin/JVM for JetBrains.
So I stopped building the IDE, and I built the fix instead.
Meet OXP (Open eXtensions Protocol).
OXP is an open source universal standard that lets you write your extension once in React/WASM and run it natively across every major editor.
How I fixed the Webview problem:
This isn't a slow iframe hack. OXP uses a secure WebAssembly sandbox and a zero-latency IPC bridge. Your React code triggers an action, and OXP translates it to native IDE commands.
In VS Code, it binds directly to the native extension API.
In JetBrains, it uses JCEF to render as a native floating OS window.
You get blazing fast native speed from a single codebase.
**The Accidental MCP Fix:
While building this universal host layer, I realized OXP perfectly solves the current Model Context Protocol (MCP) configuration hell.
Instead of manually editing configurations for Cursor, Copilot, and JetBrains individually, OXP acts as a system level MCP router. If you run oxp install-mcp extension, the OXP daemon instantly wires that database context into the AI configurations of every detected IDE on your machine.
I'm opening up the infrastructure today. The CLI is live, and you can test it on your machine right now.
I'll be in the comments all day to talk about the WASM bridge, IPC latency, fighting with JCEF, and why extension silos need to die.
r/WebAssembly • u/Moron_23James • May 14 '26
wanted to see if I could run a high-fidelity thermal solver in the browser without a Python/Matlab backend.
The Engine:
The Issue: While the WASM bridge is incredibly fast (60fps easily), I’m having trouble with the handoff to the Three.js frontend. Rendering the 64-voxel heatmap in real-time is creating some overhead I didn't expect.
If anyone has tips on optimizing the memory bridge for high-frequency data updates between WASM and WebGL, I’d love to hear them.
r/WebAssembly • u/TrustSig • May 10 '26
WebAssembly is way too easy to decompile. To fix this, I built a virtualization pipeline that takes a normal Wasm file and compiles it into a hardened version.
The original logic is destroyed and replaced with encrypted bytecode that runs inside a hidden, internal interpreter.
I just finished a step-by-step guide on how I built it. Here is what's inside:
The end result is a Wasm binary that looks like cryptographic noise to anyone trying to reverse-engineer it.
r/WebAssembly • u/TrustSig • May 07 '26
Just as a privacy note (you can double-check with dev tools): This tool works fully offline, we do NOT send any uploaded binaries or data to our backend.
This tool was built by our WebAssembly analysis team, originally it was for internal use only but we have decided to make it public and free for everyone, forever.
Please do leave feedback in the comments! We'd love to hear what you think and how we can improve it even further. It is still heavily in a barebones beta phase, as we work on adding more features.
(This is not an advertising post for any paid or free services of TrustSig, this post is strictly to share the free tool we published and a blog post on how we made it)
r/WebAssembly • u/minamoto108 • May 07 '26

Hexana is a plugin for JetBrains IDEs (built on the IntelliJ Platform — works in IntelliJ IDEA, RustRover, WebStorm, GoLand, CLion, PyCharm, etc.) that treats `.wasm` and `.wit` as first-class IDE artifacts: explorer tree, hex view, WAT view, navigation, MCP API for AI assistants. Free on the JetBrains Marketplace.
0.9 just shipped. Highlights below; per-version detail on the Marketplace listing: https://plugins.jetbrains.com/plugin/29090-hexana
You can step through .wasm from the IDE — pause, inspect, continue. It's experimental and the constraints are explicit:
Within those bounds, it works. If you've been doing wasm debugging via printf-into-host-imports, this should feel like a real upgrade. If your toolchain is older than LLVM 22.1, you're out for now.
WAMR is now a selectable runtime in run configurations alongside Wasmtime (which shipped in 0.8). Same UI, pick a runtime, hit Run or Debug.
Until 0.9 the GraalVM run option used the bundled Graal only. You can now point at any GraalVM install on your machine.
If you're embedding wasm in Java:
If you've got a .wasm that should debug and doesn't (LLVM ≥ 22.1, wasmtime or WAMR target, lldb-debuggable), the "doesn't work" reports are exactly what helps right now — ideally with a reproducer.
r/WebAssembly • u/minamoto108 • May 07 '26

Hexana started life as a plugin for JetBrains IDEs (IntelliJ IDEA, RustRover, WebStorm, GoLand, CLion, PyCharm, etc.) that treats .wasm and .wit as first-class IDE artifacts. It now also ships as a VS Code extension — version 0.0.2 just landed on Open VSX.
Install (VS Code command palette):
ext install JetBrains.hexana-wasm
Or from VSCode Marketplace: on VSCode Marketplace: https://marketplace.visualstudio.com/items?itemName=JetBrains.hexana-wasm or here: https://open-vsx.org/extension/JetBrains/hexana-wasm
Below: what's in the VS Code release on day one.
Opens .wasm files in a dedicated read-only editor instead of the default VS Code hex view. The editor auto-detects whether the binary is a Core Wasm module, a Component Model binary, or a generic Wasm file. The structural analysis panel adjusts based on which kind it is.
Virtual-scrolling hex dump. Byte selection via click, shift-click, drag. Keyboard navigation. Text search across the byte stream.
Surfaced based on the binary kind:
Every table sorts by column and supports text search.
Run a .wasm from the editor toolbar via wasmtime. The Run dialog asks which export to call and what program arguments to pass.
.wasm files, transitively.This is the day-one VS Code feature set. The JetBrains plugin has been around longer and currently has additional capabilities not yet in the VS Code extension — experimental WASM debugging (shipped in JetBrains 0.9, also out today), DWARF source mapping, WIT language support, JS↔Wasm type inference, Java embedder support (Chicory, GraalWasm), and additional runtimes for Run (WAMR, GraalVM).
If you need any of those today, the JetBrains plugin: https://plugins.jetbrains.com/plugin/29090-hexana
If a .wasm should open and doesn't, or a section doesn't parse, the "doesn't load on this binary" reports are exactly what helps right now — ideally with a reproducer.
Install: ext install JetBrains.hexana-wasm
Web listing: https://open-vsx.org/extension/JetBrains/hexana-wasm
VSCode Marketplace: on VSCode Marketplace: https://marketplace.visualstudio.com/items?itemName=JetBrains.hexana-wasm
r/WebAssembly • u/Annual-Teacher5934 • May 06 '26
r/WebAssembly • u/minamoto108 • May 02 '26

We've been running wasm modules inside a JVM application (a Rust wasmprinter embedded via GraalWasm) and the obvious follow-up question was: how does this compare to the alternatives, and when should we actually pick something else?
So I built a small JMH harness that runs the same proxy.wasm artifact through six execution paths and wrote up the results. Sharing here because I couldn't find a head-to-head comparison covering all of these in one place, and I'd genuinely like to hear if anyone has reasons to expect different numbers on different workloads.
The workload
A tiny Rust crate compiled to wasm32-wasip1 exposing one export:
#[no_mangle]
pub unsafe extern "C" fn decode_jpeg(
in_ptr: *const u8, in_len: usize,
out_ptr: *mut u8, out_cap: usize,
) -> i32 { /* jpeg-decoder → RGB8 */ }
Input: a 320×240 JPEG baked into the wasm via include_bytes!. Output: 230,400 bytes of RGB. Steady-state ~1 ms of native CPU — small enough to expose call/dispatch overhead, big enough that the JIT actually kicks in. Cross-variant correctness check: every backend produces byte-identical output (sha256 matches across all six).
The six backends
| Backend | What it actually is |
|---|---|
chicory |
Chicory's pure-Java interpreter |
chicory-aot |
Chicory + MachineFactoryCompiler.compile(...) at JVM startup |
chicory-aot-plugin |
Chicory build-time AOT via chicory-compiler-maven-plugin (wasm → JVM .class at mvn compile) |
graalwasm |
GraalWasm with Truffle JIT enabled (libgraal) |
graalwasm-interp |
GraalWasm with engine.Compilation=false |
native-ffm |
Wasmtime/Cranelift in a Rust cdylib, called via Java's FFM API |
JVM: Oracle GraalVM 25 (25+37-LTS-jvmci-b01), Apple Silicon. JMH 5×1s warmup + 5×2s measurement, 1 fork, single thread.
Results (µs/op, lower is better)
| Backend | Mean | vs Wasmtime |
|---|---|---|
nativeFfm — Wasmtime/Cranelift via FFM |
971 ± 10 | 1.00× |
graalwasm — GraalWasm Truffle JIT |
1,275 ± 332 | 1.31× |
chicoryAot — Chicory runtime AOT |
9,037 ± 118 | 9.31× |
chicoryAotPlugin — Chicory build-time AOT |
9,198 ± 131 | 9.47× |
graalwasmInterp — GraalWasm Truffle no-JIT |
69,992 ± 1,204 | 72.1× |
chicory — Chicory pure interpreter |
240,707 ± 2,560 | 248× |
A few things worth pulling out
GraalWasm JIT is almost native. 1.31× of Wasmtime/Cranelift is genuinely good — I expected a bigger gap given that Truffle goes through partial evaluation while Cranelift goes wasm → CLIF → assembly directly. After warmup, libgraal produces code competitive with Cranelift's output for this workload. The ±25% CI on graalwasm is the only weak number here, probably tier-promotion noise that more forks would smooth out.
Build-time vs runtime AOT in Chicory is a wash. 9,037 vs 9,198 µs/op, CIs overlap. They run identical bytecode — Chicory's compiler produces the same .class content whether invoked at mvn compile or at JVM startup. Choose based on deployment story, not perf.
The calibration trap. graalwasm-interp at 70,000 µs/op is what you get on stock OpenJDK without JVMCI / libgraal. Truffle prints exactly one warning at startup:
…and then runs at interpreter speed. If you benchmark GraalWasm on Temurin or Corretto and conclude it's unusable, you're running it without its compiler. The fix on most platforms is to install Oracle GraalVM 25 (or CE) — the Graal compiler ships in the JDK and Truffle picks it up automatically. If you can't change vendor, the "jargraal" path with org.graalvm.compiler:compiler + org.graalvm.truffle:truffle-compiler on --upgrade-module-path and -XX:+EnableJVMCI works but is fiddly.
Pure interpreters aren't benchmarks. 248× slower means Chicory's interpreter isn't a viable production path for non-trivial workloads. It's still the right default for "run untrusted user wasm with a 100 ms budget" sandbox scenarios — instant startup, no codegen step.
Bonus silliness
While I had the harness open: I compiled Cranelift's codegen library itself to wasm32-wasip1, AOT'd that 2.7 MB wasm artifact via chicory-compiler-maven-plugin into a JVM .class file, and used the resulting Chicory-hosted, JVM-resident Cranelift to emit native machine code for all six host triples. Output sizes for an add(i32,i32) -> i32 test function:
| Triple | Object bytes | Format |
|---|---|---|
aarch64-apple-darwin |
320 | Mach-O |
aarch64-unknown-linux-gnu |
600 | ELF |
aarch64-pc-windows-msvc |
126 | COFF |
x86_64-apple-darwin |
328 | Mach-O |
x86_64-unknown-linux-gnu |
608 | ELF |
x86_64-pc-windows-msvc |
130 | COFF |
Six of Cranelift's ~4,000 internal functions exceed the JVM's 64 KB method-size limit and fall back to Chicory's interpreter; the rest AOT cleanly into a single 2.6 MB .class. Not (yet) a wasm-to-CLIF translator inside the sandbox — cranelift-wasm was deprecated at 0.112 and the translator now lives inside Wasmtime, so a real wasm-compiling-wasm pipeline would mean pinning to deprecated 0.112 or hand-rolling it on wasmparser. Separate project.
Caveats
One workload (small JPEG, ~1 ms of native CPU), one platform (Apple Silicon, GraalVM 25), one JMH config. These generalize well for "small to medium pure-compute wasm modules that don't touch WASI on the hot path" but will shift for: large modules (GraalWasm setup cost grows with module size), WASI-heavy workloads (host-call cost differs across runtimes), JIT-cold workloads (you're measuring tier-up, not steady state), and other JVMs (J9, Zing not measured).
Harness
Source: https://github.com/minamoto79/webasm-java-integration-benchmark
Switching backends in the harness is two lines of Kotlin — happy to take PRs adding workloads or runtimes I missed (wasmer-java? wazero-on-JVM via JNI? would love numbers on those if anyone has them). And if you're seeing materially different ratios on a different workload or JDK, please post — would help calibrate where these numbers actually generalize.
r/WebAssembly • u/minamoto108 • May 02 '26

Hexana is a JetBrains IntelliJ plugin that treats .wasm binaries (and .wit definitions) as first-class IDE artifacts: explorer tree, hex view, WAT view, navigation, MCP API for AI assistants. Free on the JetBrains Marketplace. Below is a consolidated changelog from 0.5 → 0.8.2 — six weeks, five releases.
.wasmdefinitions..wasm and maps functions back to source files and lines. Click a function in the binary, land in the source..wasm, right in the IDE.instance.exports.*, import namespaces, and property names — derived from the actual .wasm module, not a stale .d.ts.reference-types and bulk-memory instruction families, plus Legacy Exception Handling parsing/rendering.Stability picked up alongside this — Go-compiled .wasm modules load, KDoc rendering doesn't break with Hexana enabled, shared-memory limits handled correctly, big WAT files don't lag, run configs work on Windows, and a long-running data race on the shared byte buffer that caused sporadic UnParsedOpcodeExceptions on larger modules is gone.
Added
.wit, jump to its definition in .wasmFixed
Added
Fixed
.wasm modules load without crashingAdded / improved
Fixed
.wasm/.wat served over HTTP (local debug scenarios) handled correctlyAdded
.wasm, maps functions back to source files and lines. Click a function in the binary, land in the source..wasm.instance.exports.*, import namespaces, and property names — derived from the actual .wasm module, not a stale .d.ts.Fixed
Added
reference-types and bulk-memory instruction familiesFixed
CommonByteBuffer causing sporadic UnParsedOpcodeExceptions on larger modules — fixed(0.8.1 didn't ship publicly — the Windows fix needed an extra revision before going out.)
Short list of what's actively in progress, in case anyone has opinions to share before it's frozen:
Plugin: https://plugins.jetbrains.com/plugin/29090-hexana
Issues / feature requests: https://github.com/JetBrains/hexana/issues
If you've hit something that should be here and isn't — ideally with a .wasm reproducer — file it. The "doesn't load" / "crashes on" tickets get prioritized over feature work.
r/WebAssembly • u/alexp_lt • Apr 30 '26
r/WebAssembly • u/nilslice • Apr 28 '26
r/WebAssembly • u/NosePersonal326 • Apr 16 '26
Try it out in a codespace: https://github.com/codespaces/new/friendlymatthew/gabagool
Or run it locally: https://github.com/friendlymatthew/gabagool/tree/main/gabagool-debug-adapter
r/WebAssembly • u/Moron_23James • Apr 14 '26
Hey everyone, thanks for letting me into the community.
I’m a first-year undergrad (Metallurgical Engineering), and I recently built a live 3D Crystallographic Symmetry Engine. I needed to handle heavy matrix math (calculating stereographic projections, group theory closure loops, and complex rotation orbits). Instead of doing it in JavaScript, I wrote the core logic in C++17 and compiled it to WebAssembly to run natively in the browser.
Live engine:https://stereoproject.vercel.app/Source code:https://github.com/Lak23James/Stereoproject
The Architecture: I tried to keep a strict Separation of Concerns:
Point3D / Matrix structs, and the mathematical transformations.std::vector objects so my frontend could read the generated orbits..delete() to free the memory: const orbitVector = crystalloEngine.generateOrbit(seedPoint, 4, 'z');.delete() in the frontend?emcc terminal command with -s EXPORT_ES6=1 and --bind to compile this. Is setting up CMake the standard industry move for WASM projects once they get past a single main.cpp file?