Framework
mirkosertic/Bytecoder avatar
mirkosertic/Bytecoder

Bytecoder turns JVM bytecode into JavaScript, WebAssembly or OpenCL

Framework to interpret and transpile JVM bytecode to JavaScript, OpenCL or WebAssembly.

963 stars64 forksJavaApache-2.0

At a glance

What is it?
Bytecoder is a Java framework that models JVM bytecode as a rich domain object graph and then compiles it, through an intermediate representation, to JavaScript, WebAssembly or OpenCL. The ambition is large, and the shipped release history shows the gap between that ambition and the current pace of work.
Who is it for?
Bytecoder makes sense for teams that already hold JVM code they want to run in a browser or on a GPU, and that can accept an experimental tool with no releases since 2024-05-10. It does not suit production JavaScript bundles that need predictable performance, since the readme offers no benchmark data and describes the goals rather than the achieved state.
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 33 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 November 7, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • mirkosertic/Bytecoder963 ★ · +1
Star history
Repository2026-10-212026-11-07
mirkosertic/Bytecoder962963

1 Stars in the last 17 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Bytecode is treated as a domain model, not as a file to parse

Bytecoder starts from an unusual premise for a compiler project. Instead of treating class files as opaque binary input, it describes JVM bytecode as a rich domain model in Java. The readme calls it both a rich domain model for Java bytecode and a framework to interpret and transpile it to other languages such as JavaScript, OpenCL or WebAssembly.

That framing has consequences for what you can do with the library. If bytecode is an object graph you can construct, inspect and rewrite in memory, then the framework is usable as more than a one shot compiler. You can walk a method body, reason about instructions, and transform them before code generation happens. That is a different starting point from a tool that reads a jar and emits a bundle.

The project states its goals in the readme and they are worth reading as intentions rather than as a feature list. It wants the ability to cross compile JVM bytecode to JavaScript, WebAssembly, OpenCL and other languages. It names JavaScript and WebAssembly as the primary compile targets, states support for Java 8 up to Java 20, and says the work should fit debugger toolchains and source maps and allow integration with other UI frameworks such as vue.js. The topic list of the repository also mentions kotlin, llvm and llvm-frontend, which describes the direction of the codebase rather than a set of user facing features.

Bytecode becomes an intermediate representation before a backend sees it

The compilation pipeline has three stages, and the readme describes them directly. JVM bytecode is parsed and transformed into an intermediate representation. That intermediate representation is passed through optimizer stages. It is then handed to a backend implementation for target code generation.

This is a conventional compiler shape, and it is the reason the project can plausibly grow more targets later. Backends do not parse bytecode, they consume a common form, so the cost of adding a language is paid in the backend rather than across the whole pipeline. The optimizer stages also sit in one place, which is where any future performance work would land.

The three documented backends differ in ambition. The JavaScript backend transforms the intermediate representation into JavaScript. The WebAssembly backend transforms it into WebAssembly text and binary code, which covers both the human readable format and the binary module a browser actually loads. The OpenCL backend is narrower: it is used to compile single algorithms into OpenCL and execute them on the GPU, and it is described as a way to enhance existing programs running on the JVM so they can use modern GPUs.

That OpenCL design is worth pausing on, because it is not a general purpose transpilation target. It targets a single algorithm at a time rather than a whole program, which means the natural workflow is finding a hot loop in existing JVM code and moving that piece to the device. That is a different proposition from shipping a Java application to the browser, and it is the framing in which the GPU backend is most credible.

The classlib is backed by OpenJDK 20, and the supported range starts at Java 8

The readme states that the project is backed by OpenJDK 20 as its JRE class library. In other words, the runtime library that the compiled output relies on is taken from a specific, named JDK build rather than from whatever runtime happens to be present. That is the kind of decision that determines reproducibility, and it also sets a ceiling on what the transpiled code can assume about available classes.

The supported source range is stated separately: Java 8 up to Java 20. A project written for a newer Java release, or one using APIs introduced after Java 20, is outside the documented range. The repository layout reinforces that there is a dedicated classlib module alongside the core, the command line interface, the manual and the tests, which matches the picture of a library with its own runtime class set rather than a thin compiler.

Combining the two numbers is the practical check. The class library comes from OpenJDK 20 while source support runs from Java 8 to Java 20, so a project built against an older language level still gets to run against a modern class library, and a project built for Java 20 gets the same one. Anything newer is simply outside what the readme claims.

The important caveat is that the readme gives these numbers as goals and stated support, not as a compatibility matrix you can audit. It does not enumerate which of the thousands of OpenJDK classes are implemented, so the real question of whether your code will transpile cannot be answered from the readme alone. That is what the example applications in the manual are for.

Getting to a first run means going through the manual, not the readme

There is no install command in the readme, and there is no usage snippet either. The readme's pointer to running something is the Bytecoder User Manual, which it says is available along with a full set of example applications at mirkosertic.github.io/Bytecoder. If you want to know how to invoke the compiler or how to write a custom backend, that manual is the place the project sends you.

The distribution channel is Maven Central, which the build status badge points at through the parent artifact coordinates.

xml
de.mirkosertic.bytecoder/bytecoder-parent

The repository is a Maven multi-module build, with a pom.xml at the root and a maven directory beside it, and the .mvn directory at the root holds the Maven wrapper configuration. The module list is visible in the top level entries: core, cli, classlib, manual, integrationtest and languagefeatures, plus the .github directory for workflows and the LICENSE file.

The cli module is the interesting one for a first experiment, because it is the command line entry point, while integrationtest and languagefeatures are where the real coverage lives. The languagefeatures module in particular tells you that the project is organised around verifying that specific language features work, which is the right way to answer the question of whether your own code is safe to transpile.

There is also a project homepage at mirkosertic.de/blog/2017/06/compiling-bytecode-to-javascript/, which points back at the original motivation for compiling bytecode to JavaScript. The repository also carries a manual directory, so the documentation is versioned alongside the code rather than living only on the web.

Debugger toolchains, source maps and vue.js are goals, not confirmed features

The high level goals include working well with debugger toolchains and source maps, and allowing integration with other UI frameworks such as vue.js. These are the two entries that reveal what kind of project this is trying to be. A transpiler that emits unreadable code is a curiosity. One that can map a browser debugger back to your original Java source is a tool someone might use for real work.

The readme does not describe how any of this works. It does not name a source map format version, it does not say whether debugging goes through the standard JavaScript source map mechanism, and it does not describe what integration with vue.js means in practice. It is also unclear whether the JavaScript backend supports the module system a bundler would expect, or whether output is assembled for direct inclusion.

My reading is that these goals describe a roadmap for a tool that would like to be usable in a normal web development stack, rather than a set of behaviours you can rely on today. The honest way to treat them is as questions to ask of the manual and the example applications. If your workflow depends on stepping through transpiled code in a browser debugger, that is the feature to verify first, because a transpiler without it will frustrate anyone debugging a production issue.

The same caution applies to the OpenCL goal. The readme frames it as compiling single algorithms to the GPU to enhance existing JVM programs, which is an integration task where data marshalling between JVM objects and GPU buffers is usually the hard part, and the readme says nothing about how that transfer is handled.

Release history says the work slowed after 2024

The recent releases are dated, not numbered, and they are spaced far apart. The three most recent are 2023-06-15, 2023-12-01 and 2024-05-10, each published under its date as the version identifier. The repository is not archived, and the last push was on 2026-10-05.

That combination is the main thing an adopting team should weigh. A repository that is not archived and was pushed recently suggests someone is still working in it, yet the released artefacts stop in May 2024. Code in the repository can therefore be newer than anything you can pull from Maven Central, which matters if you depend on versioned releases rather than building from source.

The date based version scheme is unusual for a library that people will place in a pom.xml, and it is worth asking how a downstream project handles upgrades when the version identifier is a calendar date. It also makes it harder to reason about which release corresponds to which set of goals.

The licence is Apache-2.0. For an organisation that ships compiled JavaScript or WebAssembly output to browsers, the practical questions are attribution and notice obligations for the distributed output, and how the Apache patent grant interacts with your own licensing. That is a matter for your legal team, and this is not legal advice, but it is worth raising early because the output of a transpiler is a derivative artefact in a way that a normal dependency is not.

The build status badge and the manual both suggest a project that has been worked on with care over a long period. The release history is the constraint that cuts against that impression, and it should be part of the adoption conversation rather than a footnote.

The alternative path is an interpreter, and the trade-off is real

There is a different way to get JVM code into a browser, and it is worth naming the difference rather than declaring one approach best.

An interpreter runs the bytecode at runtime. A transpiler converts it ahead of time. The interpreter path ships the original classes plus an interpreter written in JavaScript, so nothing about your build changes, but the program you download is larger and the work happens on the user's machine. The transpiler path ships generated JavaScript or WebAssembly, so the output is native to the platform and starts faster, but you accept a compile step and any gap between what your JVM code does and what the backend supports.

Bytecoder sits firmly on the transpiler side, and its pipeline is built for that. The OpenCL backend is a further variation on the same idea, moving one algorithm to a GPU at compile time instead of running it on the CPU.

The honest limitation follows from that choice. A transpiler only covers the language features and library calls it has implemented, and the readme gives you the source range and the class library version but not a coverage list. If your application depends on reflection, on class loading at runtime, or on a third party library with native calls, the output may compile and still fail in ways that are hard to predict from the documentation.

That is why the languagefeatures module is the right place to look before committing, and why the example applications in the manual matter more than the goal list.

Editorial conclusion

Bytecoder makes sense for teams that already hold JVM code they want to run in a browser or on a GPU, and that can accept an experimental tool with no releases since 2024-05-10. It does not suit production JavaScript bundles that need predictable performance, since the readme offers no benchmark data and describes the goals rather than the achieved state. Before adopting it, read the user manual and example applications at mirkosertic.github.io/Bytecoder, confirm that the language features your code relies on are covered by the classlib based on OpenJDK 20, and check the Apache-2.0 licence terms against how you intend to distribute the output.

Frequently asked questions

What does Bytecoder do with JVM bytecode?

It parses the bytecode and transforms it into an intermediate representation, passes that through optimizer stages, and hands it to a backend that generates target code. The documented backends are JavaScript, WebAssembly and OpenCL, with JavaScript and WebAssembly named as the primary compile targets.

Which Java versions does Bytecoder support?

The readme states support for Java 8 up to Java 20, and it says the project is backed by OpenJDK 20 as its JRE class library. The readme does not list which individual OpenJDK classes are implemented.

How do I get Bytecoder and try it out?

The readme does not give an install command. It points to the Bytecoder User Manual and a full set of example applications at mirkosertic.github.io/Bytecoder, and the project is published to Maven Central under the parent artifact de.mirkosertic.bytecoder/bytecoder-parent.

Can Bytecoder run a Java program on a GPU?

The OpenCL backend is described as compiling single algorithms into OpenCL and executing them on the GPU, to enhance existing programs running on the JVM. It targets individual algorithms rather than transpiling a whole program, and the readme does not describe how data is transferred to the device.

Is Bytecoder still being worked on?

The repository is not archived and the last push was on 2026-10-05, but the released versions are dated and the most recent is 2024-05-10, preceded by 2023-12-01 and 2023-06-15. That means repository code is newer than anything published to Maven Central.

What licence does Bytecoder use?

The project is released under the Apache-2.0 licence, with the LICENSE file at the root of the repository. The readme does not say how the licence applies to generated output such as JavaScript or WebAssembly bundles, which is worth checking against your own distribution plans.

Official sources

  1. License: Apache-2.0
  2. mirkosertic/Bytecoder on GitHub
  3. Project website
  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/mirkosertic-bytecoder.svg)](https://hysenlabs.com/projects/mirkosertic-bytecoder)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/mirkosertic-bytecoder.svg)](https://hysenlabs.com/projects/mirkosertic-bytecoder)

Was this useful?