Open-source project
i-net-software/JWebAssembly avatar
i-net-software/JWebAssembly

JWebAssembly: compiling Java bytecode to WebAssembly

Java bytecode to WebAssembly compiler

1,058 stars62 forksJavaApache-2.0

At a glance

What is it?
JWebAssembly is a Java bytecode to WebAssembly compiler that takes class files as input and writes a .wasm or a .wat file, so anything that compiles to the JVM, including Clojure, Groovy, JRuby, Jython, Kotlin and Scala, can run in a browser. Its distinguishing claim is that it is a one to one conversion rather than a port of a virtual machine, and milestone one of the roadmap is complete while milestones two and three are not.
Who is it for?
Adopt JWebAssembly if you have Java or JVM language code that has to run in a browser, if you want a one to one translation rather than a virtual machine port, and if you are prepared to test the compiled output yourself against the Java behaviour, which is what the project asks of users of the release candidate.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 22 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A compiler rather than a virtual machine

The README states the difference from similar projects in one sentence, and it is the sentence that decides whether this project is for you. The difference to similar projects is that not a complete VM with GC and memory management should be ported. It is more like a 1: 1 conversion, and the generated WebAssembly code is similar in size to the original Java class files.

That is a genuinely different approach from the alternative of putting a Java virtual machine into WebAssembly. A VM port gives you the whole language at once, including the garbage collector, the class loader and the reflection machinery, at the price of a large module and a runtime that has to be downloaded and initialised. A one to one conversion produces a module roughly the size of the class files it came from, and it works only for the subset of Java that the compiler has implemented.

So the trade is size and startup against language coverage. A subset that covers the collection framework, strings, enums, static constructors, simple class objects, and the various kinds of method invocation gives you a lot of ordinary Java, and it excludes the parts that need runtime support.

The input is Java class files, and the consequence is that the compiler is not limited to Java itself. The README points out that it can compile any language that compiles to Java bytecode, naming Clojure, Groovy, JRuby, Jython, Kotlin and Scala. The output is either the binary format, a .wasm file, or the text format, a .wat file, which is the readable form and the one you want when you are trying to understand what the compiler produced. The stated target is to run Java natively in the browser with WebAssembly.

The licence is Apache 2.0, the last push was on 2026-10-01, and the published releases are v0.2 from 2020-02-18, v0.3 from 2020-05-01 and v0.4 from 2022-03-20.

The annotations are the interface between Java and JavaScript

The usage section is built around two annotations, and they are the entire API surface you need to know to start.

To export a Java function so that it is accessible from JavaScript, you add the annotation de.inetsoftware.jwebassembly.api.annotation.Export:

java
import de.inetsoftware.jwebassembly.api.annotation.Export;

@Export
public static int add( int a, int b ) {
    return a + b;
}

The mirror image is the Import annotation, which makes a JavaScript function accessible from Java. The README adds a detail that saves a confusion: the method can be declared native or can have a Java implementation which will be ignored on compiling.

java
import de.inetsoftware.jwebassembly.api.annotation.Import;

@Import( module = "global.Math", name = "max" )
static int max( int a, int b) {
    return Math.max( a, b );
}

So the module and name are resolved in JavaScript, in this case global.Math and max, and the Java side is a stub. Writing a body for the stub is allowed and is what the example does, but that body is not what runs. Anyone reading the Java source of an imported function should know that the implementation they see is not the implementation in play.

The compiler's public API is a single class, JWebAssembly, and the class files in the repository live under the de.inetsoftware.jwebassembly package. There is a separate Gradle plugin in its own repository, JWebAssembly-Gradle, so the plugin is versioned outside this one.

There is also an Emulator, linked from the wiki's debugging page, and the repository carries an Eclipse project configuration alongside the Gradle build, with build.gradle, settings.gradle, a src/ directory, a test/ directory and a separate test11/ directory, plus separate build workflows for Java 8 and Java 11.

Milestone one is done, the later milestones are not

The roadmap is the most informative part of the README, because it tells you exactly how much of Java the compiler covers.

Every item under version 1.0, milestone 1, is checked. The Java byte code parser, the test framework, the public API, the Gradle plugin, the Emulator, the binary and text format file writers, support for native methods, memory management with a polyfill on the JavaScript side, the five kinds of method invocation including dynamic calls for lambdas and default methods, string support, simple Class object support, static constructors, enum support, an optimizer that optimises the WASM output of a single method after transpiling and before writing, a Hello World sample, and compilation of the collection framework.

That list is a good description of a usable subset. The collection framework compiling means ordinary Java code that uses lists and maps is in scope, and enum support and lambdas cover a lot of idiomatic modern Java.

Milestone 2 is untouched. It wants a full featured library for accessing the DOM, which lives in a separate JWebAssembly-API repository, and embedding of resources such as properties and images. Without the DOM library, the browser is a JavaScript host you call into, not a place your Java code manipulates the page.

Milestone 3 is where the hard language features sit, and every one of them is unchecked: exception handling, multiple threads, memory management with a built-in GC without the JavaScript polyfill, reflection, and more optimisation such as tail calls, removing asserts and inlining of functions. The README is explicit that exception handling and multiple threads are required the next version of WebAssembly, so those two are not a lack of effort in the compiler but a lack of a platform feature.

The memory management item is the subtle one. Milestone one delivers memory management with a polyfill on the JavaScript side, so the garbage collector exists but is implemented outside the WebAssembly module, which means the module has to call out to JavaScript to do its collection.

The WebAssembly feature table is the honest part

The README publishes a table of the status of future WebAssembly features required by JWebAssembly, measured in nightly builds across V8/Chrome, SpiderMonkey/Firefox and WABT, and it is a good example of a project stating its constraints plainly.

Garbage collection has medium importance, and the table shows a dash for V8, partly for SpiderMonkey and a dash for WABT. Exceptions have low importance, partly for V8, a dash for SpiderMonkey and partly for WABT. Threads have low importance, yes for V8, a question mark for SpiderMonkey and yes for WABT. Tail call has very low importance, yes for V8, a question mark for SpiderMonkey and yes for WABT.

The footnotes explain the measurement, that the V8 column is based on the node.js integration builds, the SpiderMonkey column on the nightly jsshell build, and the WABT column on libwabt.js through the node module wabt@nightly. The README also notes that to use it, some flags and switches are currently needed.

The last row of the explanation is the one to keep in mind. Importance: all with high marked features are required for a hello world sample, for a first version that can be used for production. Read carefully, that says the features marked high importance are what a hello world needs, and the table shows none of the four rows marked high, which is consistent with the roadmap having milestone one complete.

The practical consequence for an adopter is that the browser support question is not rhetorical. The README points at a feature detection page so you can check your own browser, and for a production deployment that check belongs in your evaluation, not in a release announcement.

A release candidate, and a release history that stopped in 2022

Two facts about the release state point in different directions, and both matter.

The first is in the roadmap section. The compiler is feature complete for milestone 1, and there is a release candidate, and the request is explicit: please test it and report bugs if the compiled code has the same behavior as Java.

That request tells you what the project wants from users. The definition of success is behavioural equivalence with Java, which is a much higher bar than the code running at all. A compiler that produces a module which loads and prints something has demonstrated little. A compiler whose output matches the original semantics for the subset it claims to support has demonstrated the thing that is actually hard.

The second fact is the tag history. The releases are v0.2 from February 2020, v0.3 from May 2020 and v0.4 from March 2022, and there is no GitHub release since. The repository is not archived and the last push was on 2026-10-01, so the code is being worked on, but the published artefacts on Maven Central, where the artifact is de.inetsoftware:jwebassembly-compiler, and on JitPack are old.

That is a meaningful gap for anyone planning to depend on it. A project that publishes a release candidate but does not publish release tags leaves a consumer choosing between the last tag, which is four years old, and the trunk, which is untagged. The Maven Central badge and the JitPack badge in the README exist for exactly this reason, and the JitPack link points at a master-SNAPSHOT, so both a released and a snapshot build are available.

For a project where the documentation itself is in a wiki rather than in the repository, the wiki and the roadmap are where the details live, and the roadmap's unchecked boxes are the most reliable statement of the current scope.

Where a Java to WebAssembly compiler is the wrong tool

The limits here are sharper than in most projects, because the subset is finite and the exclusions are listed.

The first is that anything needing exceptions is out. Milestone three puts exception handling behind a WebAssembly feature that is not universally available, and a Java program that throws exceptions is a Java program that will not compile to something usable today. The same applies to threads, and to reflection.

The second is the DOM. Without the milestone two DOM library, Java code running in the browser cannot manipulate the page, so the shape of what you can build is constrained to computation and communication with JavaScript through the Import and Export annotations. That is a perfectly good shape for an algorithm, a codec or a parser, and it is not a shape for an application with a user interface written in Java.

The third is the alternative approach. If your requirement is Java semantics in the browser, there is a different family of solutions, the one that compiles Java to JavaScript rather than to WebAssembly, and the difference is substantial. A JavaScript emitting toolchain produces a module the browser runs natively with no runtime at all, at the price of a translation layer that is not the Java language, where the class file semantics of threading, of the garbage collector and of reflection do not survive the trip. The WebAssembly route keeps the class file semantics for the supported subset and pays for it in size, in startup and in the missing features.

Which is right depends on what you are porting. Code that is a computation, a data format, or a piece of business logic, where startup time and payload size matter, is a reasonable fit for a one to one WebAssembly conversion. Code that is an application with a UI, threads or heavy exception use is not.

Editorial conclusion

Adopt JWebAssembly if you have Java or JVM language code that has to run in a browser, if you want a one to one translation rather than a virtual machine port, and if you are prepared to test the compiled output yourself against the Java behaviour, which is what the project asks of users of the release candidate. Do not adopt it if you need exception handling, threads or a garbage collector without a JavaScript polyfill, since all three sit in milestone three and depend on WebAssembly features that are not universally available. Verify first that the browsers you target report the needed WebAssembly features, that the class files you compile stay within the supported subset, and that the milestone one feature list covers the language features your code actually uses.

Frequently asked questions

What does JWebAssembly compile and what does it produce?

It takes Java class files as input and generates the binary format, a .wasm file, or the text format, a .wat file. Because the input is bytecode, it can compile any language that compiles to Java bytecode, and the README names Clojure, Groovy, JRuby, Jython, Kotlin and Scala.

How do I call a Java method from JavaScript with JWebAssembly?

Annotate the method with de.inetsoftware.jwebassembly.api.annotation.Export, and it becomes accessible from JavaScript. The mirror operation is de.inetsoftware.jwebassembly.api.annotation.Import with a module and a name, where the Java method can be declared native or can have a Java implementation which will be ignored on compiling.

Which Java features does JWebAssembly support?

The milestone 1 checklist in the README is fully checked, covering the byte code parser, the test framework, the public API, native method support, memory management with a polyfill on the JavaScript side, static, instance, interface, dynamic and default method invocation, strings, simple Class objects, static constructors, enums, an optimizer and the collection framework. Exception handling, threads, reflection and a built-in garbage collector are listed under milestone 3 and are not done.

What Java version does the JWebAssembly compiler need?

The compiler requires Java SE 8 or higher, and the README says it is tested with OpenJDK 8 on travis-ci.com. The repository carries separate build workflows for Java 8 and Java 11, and a separate test11/ directory alongside test/.

Official sources

  1. i-net-software/JWebAssembly on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/i-net-software-jwebassembly.svg)](https://hysenlabs.com/projects/i-net-software-jwebassembly)

Was this useful?