npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@coffeetales.net/wasm2lang

v2026.4.109

Published

CLI tool that transpiles WebAssembly modules into equivalent asm.js, PHP, or Java source code.

Readme

wasm2lang

Compile once to WebAssembly. Ship everywhere as source code.


Launch the Playground -- no install, no server. Pick a sample, choose a backend, and see generated asm.js, PHP, or Java code instantly in your browser.


wasm2lang reads WebAssembly modules and emits equivalent, human-readable source code. Not an interpreter. Not a runtime bridge. Actual source code that compiles, runs, and optimizes like anything else written in the target language.

One logic base. Multiple ecosystems. Zero runtime dependencies.

The problem

You already compile to WebAssembly. That investment is real: tested algorithms, validated correctness, portable IR. But then you hit a wall.

Your target environment refuses to run WASM. A WordPress plugin on shared hosting cannot load a WebAssembly module. A managed PHP platform forbids native extensions. An enterprise Java application server locks down its classloader. Your portable bytecode is suddenly not portable at all.

Your WASM runtime is the bottleneck. Running WebAssembly inside a host-language interpreter adds an abstraction layer the platform optimizer cannot see through. In Java, a WASM runtime is a JIT-over-JIT -- HotSpot never touches your actual logic. In PHP, it is an opaque extension the OPcache/JIT cannot reason about. For compute-intensive workloads -- cryptography, codecs, compression, numerical kernels -- the performance gap between interpreted WASM and natively optimized host code can be substantial.

You are rewriting the same logic for every platform. Without a transpilation path, each target language gets its own hand-rolled implementation. Bugs are fixed in one place and rediscovered in another. Behavior drifts. Maintenance compounds.

The solution

wasm2lang treats WebAssembly not just as a runtime format, but as a portable intermediate representation for source-code generation.

Write once. Compile to .wasm. Run wasm2lang. Get native-feeling source code that each platform's toolchain can compile, inline, and optimize directly.

How it works

        ┌──────────────────────────────────────┐
.wasm ─>│ WASM2LANG                            │─> source
.wast   │ parse ─> normalize ─> passes ─> emit │    code
        └──────────────────────────────────────┘
  1. Parse -- reads .wasm binary or .wast text via the Binaryen API.
  2. Normalize -- optional Binaryen optimization passes restructure the IR (flatten, simplify locals, reorder, vacuum).
  3. Passes -- wasm2lang's own structural passes analyze and transform the control flow graph: loop simplification, block-loop fusion, switch dispatch detection, local usage analysis, and drop-const elision.
  4. Emit -- a traversal-based code emitter walks each function body and produces target-language source, applying type-aware coercion elimination and identifier mangling along the way.

No intermediate AST is constructed. The emitter produces output chunks directly from the traversal visitor callbacks, keeping memory overhead minimal.

Features

  • Three production backends -- asm.js, PHP, Java; CRC32-validated byte-identical output across all runtimes.
  • SIMD128 -- Java backend emits IntVector v128 ops that HotSpot auto-vectorizes natively.
  • Typed coercion elimination -- expression categories eliminate redundant |0, Math_fround, and type casts.
  • Cast-module imports -- "cast" module functions lowered to native type casts instead of calls.
  • Spec-compliant truncation trapping -- NaN and out-of-range inputs trap instead of silently producing wrong results.
  • Structural passes -- loop simplification, block-loop fusion, switch dispatch detection, local init folding.
  • Deterministic identifier mangling -- Feistel-round permutation; same key = same output.
  • Exported mutable globals -- getter/setter accessors across all backends.

Backends

| Backend | --language-out | Strength | Status | | ---------- | ---------------- | --------------------------------------------------- | ----------------------------------------------------------------------- | | asm.js | ASMJS | Closest semantic match to WASM; AOT-compiled by V8 | Active -- full function-body emission, validated by V8 and SpiderMonkey | | PHP | PHP64 | Runs on shared hosting with no extensions | Active -- full function-body emission, validated by PHP CLI | | Java | JAVA | HotSpot/Graal optimize the output directly; SIMD128 | Active -- full function-body emission, validated by jshell |

Why asm.js? WebAssembly was designed as the binary evolution of asm.js -- they share the same linear memory model, integer coercion semantics, and structured control flow. asm.js is not merely a JavaScript subset; it is a formally specified typed bytecode that happens to be syntactically valid JavaScript, and engines such as V8 still recognize the "use asm" directive and apply ahead-of-time compilation. This makes asm.js the most semantically natural transpilation target for WASM: the mapping is nearly a round-trip.

The project covers WebAssembly MVP features plus post-MVP extensions where they map well to host capabilities. The Java backend already emits SIMD128 operations as IntVector expressions via the Vector API, turning WASM SIMD intrinsics into code that HotSpot auto-vectorizes natively. The longer-term ambition is broader still: more WASM proposals, more backends, and deeper optimization passes.

Installation

npm install @coffeetales.net/wasm2lang

Or run directly without installing:

npx @coffeetales.net/wasm2lang --input-file module.wast --emit-code

For development, clone the repo and build from source (see Building).

Quick start

# Inline a .wast module and emit PHP:
npx @coffeetales.net/wasm2lang                                                                                    \
 --language-out php64                                                                                             \
 --input-data '(module (func (export "add") (param i32 i32) (result i32) (i32.add (local.get 0) (local.get 1))))' \
 --normalize-wasm binaryen:min                                                                                    \
 --mangler secret                                                                                                 \
 --emit-code

CLI reference

wasm2lang [options]

After npm install, the wasm2lang command is available in your project. When developing from a local clone, use node wasm2lang.js --dev to load source files directly from src/.

Options

| Flag | Type | Description | | ---------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | --input-file <path> | string | Path to a WebAssembly file. Files ending in .wat/.wast (or prefixed with wast:) are read as text; all others as binary. Use wast:- to read text from stdin. | | --input-data <string> | string | Inline WebAssembly text to compile (alternative to --input-file). | | --language-out <lang> | enum | Output backend: ASMJS (default), PHP64, JAVA. | | --normalize-wasm <bundles> | list | Comma-separated normalization bundles (see below). Default: binaryen:min. | | --emit-code [name] | string | Emit generated source code. The name becomes the output variable/class name (default: code). | | --emit-metadata [name] | string | Emit static memory initialization. The name becomes the output variable name (default: metadata). | | --emit-web-assembly [text] | string | Emit the (normalized) WebAssembly module. Defaults to binary format; pass text for WAT output. | | --define <K=V> | string | Set a compile-time define (repeatable). Used to configure backend constants. | | --mangler <key> | string | Enable deterministic identifier mangling. Same key = same output; different keys = different names. | | --out-file <path> | string | Write output to a file instead of stdout. | | --help | -- | Print option descriptions to stderr and exit. |

Normalization bundles

Bundles are passed as a comma-separated list to --normalize-wasm and control how the WebAssembly IR is transformed before code emission.

| Bundle | Phase | Description | | ------------------- | --------- | ------------------------------------------------------------------------------------------------------------------- | | binaryen:none | binaryen | No Binaryen normalization; raw input is used as-is. | | binaryen:min | binaryen | Lightweight Binaryen passes: flatten, simplify-locals, merge-blocks, reorder-locals, vacuum. | | binaryen:max | binaryen | Full post-lowering optimization (constant propagation, inlining, local coalescing, DCE) for smaller, faster output. | | wasm2lang:codegen | wasm2lang | Internal wasm2lang passes (loop simplification, block-loop fusion, switch dispatch detection, etc.). |

Common combinations:

  • binaryen:none -- useful when the input is already in the shape you want.
  • binaryen:min,wasm2lang:codegen -- recommended for general code generation.
  • binaryen:none,wasm2lang:codegen -- skip Binaryen but still apply wasm2lang's structural passes.

Backend defines

Each backend reads specific defines from --define to control output parameters.

| Define | Backend | Default | Description | | ----------------- | ------- | ------- | --------------------------------------- | | ASMJS_HEAP_SIZE | asm.js | 65536 | Size of the ArrayBuffer heap (bytes). | | PHP64_HEAP_SIZE | PHP | 65536 | Size of the binary string heap (bytes). | | JAVA_HEAP_SIZE | Java | 65536 | Size of the ByteBuffer heap (bytes). |

Usage examples

Emit asm.js with memory initialization

wasm2lang                            \
  --input-file module.wast           \
  --normalize-wasm binaryen:min      \
  --language-out ASMJS               \
  --define ASMJS_HEAP_SIZE=524288    \
  --emit-metadata memBuffer          \
  --emit-code module > output.asm.js

The output will contain a var memBuffer = new ArrayBuffer(...) block followed by var module = function asmjsModule(stdlib, foreign, buffer) { ... }.

Emit PHP code

wasm2lang                                          \
  --input-file module.wast                         \
  --normalize-wasm binaryen:none,wasm2lang:codegen \
  --language-out PHP64                             \
  --define PHP64_HEAP_SIZE=524288                  \
  --emit-metadata memBuffer                        \
  --emit-code module > output.php

PHP output is a closure-based module: $module = function(array $foreign, string &$buffer): array { ... }. Functions are emitted as PHP closures with use clauses capturing the heap buffer and other function references by reference.

Emit Java code

wasm2lang                                          \
  --input-file module.wast                         \
  --normalize-wasm binaryen:none,wasm2lang:codegen \
  --language-out JAVA                              \
  --define JAVA_HEAP_SIZE=524288                   \
  --emit-metadata memBuffer                        \
  --emit-code module > output.java

Java output is a class wrapping all exported functions as methods, with a ByteBuffer-based heap.

Inline WebAssembly text

wasm2lang                                                                            \
 --language-out java                                                                \
 --input-data '(module (func (export "f") (param i32) (result i32) (local.get 0)))' \
 --normalize-wasm binaryen:min                                                      \
 --mangler secret                                                                   \
 --emit-code

--input-data passes the WAT source directly as a CLI argument -- no pipe or temp file needed.

--input-file wast:- can also read WAT from stdin, but note that piping may fail on some platforms (e.g. MINGW/Git Bash on Windows reports "stdin is not a tty").

Re-emit normalized WebAssembly

# Emit normalized WAT (text):
wasm2lang                               \
  --input-file module.wasm              \
  --normalize-wasm binaryen:min         \
  --emit-web-assembly text

# Emit normalized WASM (binary):
wasm2lang                               \
  --input-file module.wasm              \
  --normalize-wasm binaryen:min         \
  --emit-web-assembly > normalized.wasm

Use identifier mangling

wasm2lang                                          \
  --input-file module.wast                         \
  --normalize-wasm binaryen:none,wasm2lang:codegen \
  --language-out JAVA                              \
  --mangler my-secret-key                          \
  --emit-code module

Internal identifiers are replaced with short, opaque names derived from the key. The same key always produces the same output.

Combine metadata and code emission

--emit-metadata and --emit-code can be used together. The metadata (memory initialization) is emitted first, followed by the code.

wasm2lang                         \
  --input-file app.wast           \
  --normalize-wasm binaryen:min   \
  --language-out ASMJS            \
  --define ASMJS_HEAP_SIZE=131072 \
  --emit-metadata heapData        \
  --emit-code myModule

Output:

var heapData = new ArrayBuffer(131072);
var i32_array = new Int32Array(heapData);
var myModule = function asmjsModule(stdlib, foreign, buffer) {
  'use asm';
  // ...
};

Multiple defines

--define is repeatable:

wasm2lang                         \
  --input-file app.wast           \
  --define ASMJS_HEAP_SIZE=262144 \
  --define CUSTOM_FLAG=true       \
  --emit-code

Compile from .wasm binary

wasm2lang                       \
  --input-file module.wasm      \
  --normalize-wasm binaryen:min \
  --language-out PHP64          \
  --emit-code module

Binary .wasm files are detected automatically (no wast: prefix needed).

Building

yarn closure-make # produces dist_artifacts/wasmxlang.js

The project targets Closure Compiler ADVANCED_OPTIMIZATIONS (ES5 strict).

Testing

export SPIDERMONKEY_JS=/path/to/js
export PHP_CLI=/path/to/php
export JSHELL_CLI=/path/to/jshell

mkdir test_artifacts && cd test_artifacts
../scripts/wasm2lang_build_tests.sh
./wasm2lang_run_tests.sh

The test harness runs 14 tests covering MVP ops, control flow, arithmetic, memory types, algorithms, i64 ops, type casts, and SIMD:

  1. Generates .wast test fixtures from tests/*.build.js scripts.
  2. Builds each fixture in two variants -- codegen (with wasm2lang:codegen passes + mangling) and none (raw, no codegen passes).
  3. For each variant, transpiles to all enabled backends (asm.js, PHP, Java). Per-test .build.languages files can restrict which backends run.
  4. Runs the original .wasm through V8 as a reference.
  5. Runs each backend's output through its runtime (V8, SpiderMonkey, PHP CLI, jshell).
  6. Compares stdout output and a CRC32 memory snapshot across all backends.

All backends must produce byte-identical output and matching memory checksums to pass.

Browser Playground

Live version: https://coffeetales.github.io/wasm2lang/

The playground includes selectable WAT samples (including data-segment examples that showcase metadata output), backend and normalization selectors, identifier mangling, and shows both the generated metadata + code and normalized WAT output. No install, no server -- everything runs client-side in your browser.

Changelog

Release history, new features, and breaking changes are tracked in CHANGELOG.md. Each version documents what was added, changed, and fixed -- useful for understanding what the generated output looks like at a given release.

Contributing

Bug reports, issues, and pull requests are welcome. Good starting points:

  • Backend emission correctness and coverage
  • Traversal, schema, and pass behavior
  • Test fixtures and validation coverage

Support the project

wasm2lang is built and maintained as an independent, self-funded project. There is no company behind it, no venture backing, no grants -- just focused engineering work, sustained over time.

Every backend, every optimization pass, every test fixture that validates byte-identical output across three runtimes represents hours of careful design. Sponsorship is what makes that level of rigor sustainable.

Your sponsorship directly funds:

New backends and language targets -- expanding where WebAssembly can ship as native source code.

Deeper optimization passes -- better loop recognition, smarter coercion elimination, tighter generated code.

Broader WebAssembly coverage -- building on the SIMD128 foundation toward threads, exception handling, and advanced proposals.

Validation infrastructure -- the cross-runtime test harness that guarantees correctness is not free to build or maintain.

If wasm2lang saves you from rewriting logic across languages, if it unlocks a deployment target that a WASM runtime cannot reach, or if you simply believe this kind of tool should exist -- consider sponsoring.

Become a sponsor