New Java to WASM GC Transpiler: Run Java in the Browser, No Plugin Required
teavm.org
teavm.org
TeaVM ( https://teavm.org/ ) is an Apache-licensed project, free for use in commercial projects. It is mature and used widely, including in frameworks like Flavour ( https://frequal.com/Flavour/book.html ) and more projects listed here: https://www.frequal.com/TeaVM/TeaVmBasedSites.html
TeaVM compiles Java code to run in the browser, efficiently and quickly. It features short build times, small downloadable files, and a batteries-included toolset (including minification, tree-shaking, and packaging). Previous releases have conquered all similar open-source tools (that support threading) in independent testing: https://renato.athaydes.com/posts/comparing-jvm-alternatives...
I made a general guide for migrating from Swing to TeaVM here: https://frequal.com/TeaVM/migration/MigratingFromSwingToTeaV...
For a more specific example, I ported an open-source Java applet and described the process here: https://frequal.com/java/RestoringA19YearOldGameWithTeaVm.ht...
There are at least two GUI toolkits that run on TeaVM: 1. Codename One. Similar to swing, but focused on mobile https://www.codenameone.com 2. SnapKit. Targeted at desktop. Also a light-weight toolkit, like Swing. https://github.com/reportmill/SnapKit
It could work though by drawing to canvas.
That's what Avalonia does in C#: https://avaloniaui.net/
Here's a clone of Visual Basic 6 written with Avalonia and compiled to WebAssembly:
I think TeaVM is more focused on letting you share code with the web instead of porting entire apps.
I emailed Alexey (the creator of TeaVM) to ask if he had plans for thread support, and he replied that, while "proper" threads weren't possible, he had some ideas for supporting green threads. Over the following 6 months or so, Alexey would share updates on progress he had made on the thread front. In March 2015, he shared that he had found a new approach for async code that wouldn't affect the performance of single-threaded code, and would support natural stack traces. We released a preview of our web port using TeaVM a month later, and it exceeded all of our expectations, both in how easy it ended up being to do the port (because of TeaVM), and on the performance.
Alexey's solution for supporting threads, was truly a piece of genius. Had he not created TeaVM, and added threading support, I don't think we would have ended up with our web port - or, at the very least it wouldn't have ended up performing nearly as well.
How much trouble was it to get working? Not sure how doppio handles the absence of raw TCP connections and a standard filesystem in the browser, but if it works in doppio I would think it could work in TeaVM too.
Additionally I really dislike the anti-Java discourse in Kotlin circles, as if Kotlin was able to stand on its own, without the Java ecosystem, or Google's making it Java replacement on Android.
I think that for all language discussion online that there is a very small percentage of people that get over-inundated by the emotional feeling of “investing their time” into a language and wanting to not feel like they made a mistake or that their time was a waste.
I wish f# got the same treatment Kotlin did tooling wise, because if I were to compare the “interoperability” of the two with the incumbent on their respective VMs, my experience has shown that developing with Kotlin incurs much less friction on a day-to-day basis.
I have no doubt that part of that friction is due to the radically differing foundations that f# vs c# were built on, which as a positive for f# gives it some very cool features that Kotlin won’t have, but it leads one to ponder the RoI of diverging from the primary language.
I do agree with you that googles choice to adopt Kotlin for android rather than invest further into modernizing the jvm capabilities to allow for Java to be able to keep up better was definitely a boost to Kotlin, I assume they made that choice in a calculated manner. I also wonder if Kotlin would be seeing as much server side use at places like Google, was, Airbnb, etc if not for that move on googles part.
For example, compared to GP comment on coroutines - starting with version 6, F# natively supports writing asynchronous code with task CEs that are based on the same Task<T> type as async methods in C#. Asynchronous sequences (IAsyncEnumerable<T>) can be consumed with first-party FSharp.Control.TaskSeq package and work both ways too (ironically, it's what prompted me to look into F# in more detail, noticing how much terser working with them was compared to C#).
F# mainly lacks the kind of push Kotlin gets by being first-class on Android. But at the same time F# has excellent tooling support with Ionide and FSAC which can be consumed from VS Code, Neovim/Emacs and anything else that works with LSP, and there are two separate commerical offerings with first-class support - Rider and Visual Studio. It is also shipped out of box with .NET SDK including F# interactive for scripting which I find very productive.
If I were to employ it in my team for a task F# is particularly well-suited for, it would be a low-risk change that would not require other contributors to take any extra steps to work with it - it slots right into the same solutions and most F#-declared types can be consumed as is from C#. The adaptation effort required for experienced developers should be minimal too.
However, there are still some huge gaps that Kotlin doesn’t experience. Source-generation based tooling created a divide between c#/f# libraries (see ef.core.fsharp), mixed solutions have problems with aot, he’ll I spent 2 hours trying to get z3 to work with f# for an old advent of code problem before just writing it in typescript.
F# would be the language I would default to for 99% of things if the friction wasn’t ever present. I just somehow hit it on every project I use it on
I agree, source-generation is particularly unfortunate. I don't think it's a deal-breaker per se however - most of the time a small bindings project in C# is more than enough - I just accept it as a matter of life and get on with it, luckily it has not been particularly troublesome. Perhaps it's a bigger issue for people who would like to keep their projects C#-free (which is unfortunate, because C# is an awesome and productive language too).
The bigger issues for me personally is support for low-level primitives. Because F# has full HM type inference and of course gets to enjoy the same performance of struct generics as C# does - it is tempting to write HPC code for which C# can't infer the full generic signature the way F# and Rust can.
Unfortunately, however, in this specific area F# is outmatched because it has surprisingly less flexible pattern matching - list and array patterns in F# are bound to lists and arrays and you can't as easily match opaque objects, abstract classes or interfaces to a specific type with specific state inside, something that C# lets you do easily, where you can match, deconstruct, inline slice and unbox on any type that simply has the necessary "shape" without being bound to specific core types or interface implementations. You can work around most limitations with active patterns, but there is nothing that you can do that would give you `span is [var x, ..var rest]`.
Continuing the last point - C# has really pushed forward in making span-based and general performance-oriented code simple and idiomatic. In F#, the use of ref structs is much more restricted (please do not hold it against the team that works on it - it's a complex piece of functionality, just look at https://em-tg.github.io/csborrow/ as a start). As a result, F# is an extremely powerful and productive language that sits in an awkward spot where it is frustratingly close to being "the OCamlish Rust for .NET" thanks to its incredible type system, but at the same time if you do care about maximum performance with heavy data manipulation - you will be better served by C# still simply because it would take a lot of effort to make these specific areas in F# work just as well.
Important - these issues are _luxury_. Java ecosystem simply does not provide any alternatives due to limitations of the type system offered by JVM. So while indeed things could have been better, they do not have a proper alternative in Kotlin/Clojure/Scala at all, disqualifying them from certain performance domains completely.
On AOT - as long as you avoid printfn in F# and use string interpolation with Console.WriteLine and such you should be getting no AOT warnings. With library code it can be tricky, but it's usually caused by the abuse of un-analyzable reflection. You can always ping authors to perhaps take a look at it or use something better if available :) I'm enjoying AOC with F# and FSharpPacker too, but sadly don't have time to continue.
I’m curious in what industry you’re utilizing those features. I imagine you must be using mostly your own code since much of nuget contains things that don’t take performance as a first priority. I imagine it would be similar to the trading companies using Java where they toss out most of the standard library and most things on maven and create their own faster versions (which is also not dissimilar to gamedevs tossing out STL and making their own low-allocation libraries).
Performance engineering for CDN systems!
> I imagine you must be using mostly your own code since much of nuget contains things that don’t take performance as a first priority.
This was my assumption too. While "much of nuget" trend continues to hold, there are actually quite a few performance-oritned libraries nowadays.
Bindings and binging generators (that are near-zero-cost), managed allocator implementations, high-performance parsers and serializers (which completely wipe the floor with alternatives written in flimsy languages like Go), higher-level APIs for graphics libraries (DX12, Vulkan, ...), high-performance primitive libraries, game engines, and more.
You are correct to point out that performance-sensitive code tends to avoid standard library containers and certain APIs, but CoreLib itself has been exposing quite a few APIs (like ArrayPool) and tends to practically always offer Span<T>-based method overloads so the data can reside in any memory if it needs so (stackalloc, array pool, unmanaged, or anything in between).
Sure, if you have a hot path you will probably avoid using List<T> and StringBuilder in favor of something less allocation-heavy, but it's generally the Unity land that is subjected to pain of fighting with every allocation rather than .NET itself.
Also while .NET is great and I favour it over Java when having the option, it is a guest on mobile OSes constrained by platforms FFI API surface, or subpar APIs (Catalyst on macOS), and Meadow isn't at the same level as PTC, Aicas, microEJ and many other embedded implementations.
Could you list 3 or 4 of them please?
I would consider this for a project where I already have Java code on the backend and rewriting it for the front end in JS is a bigger barrier than adding TeaVM to the stack.
By contrast, TeaVM is Apache-licensed, available for commercial use without any gotchas, limits, or entanglements.
* Grab B, some software that can run wasm [2].
* Run A on B.
Just one example, since this "algorithm" can generate endless combinations! You could create some game logic in Kotlin, Clojure or Scala, compile it to WASM, and run it on Taca [3] (a runtime for multimedia wasm apps that runs native and in browsers).
--
1: from TeaVM front page: "the source code is not required to be Java, so TeaVM successfully compiles Kotlin and Scala"
2: There are many projects out there embedding some WASM runtime, not just browsers.
(For the Python version see https://simonwillison.net/2024/Dec/10/chatgpt-canvas/)
Several use cases where TeaVM shines:
* A developer or team has strong Java skills and wants to develop single-page apps
* A Java desktop project wants to move to the web and has lots of validation logic they don't want to double-maintain in Java and ECMAScript/WASM
* A Java-based game developer wants to make a Canvas-based version of their game for the web (potentially full-screen)
* A Java developer wants a batteries-included framework for web development. TeaVM comes with a minifier, tree-shaker, packager, and more, out of the box, with familiar 'mvn clean install' semantics (And Gradle support too).
For more details on use cases and reasons for picking TeaVM, read chapter 1 of my book here: https://frequal.com/Flavour/book.html
Would this allow me to play Puzzle Pirates (a Java game) in the browser, or would the developers still need to do some work for the game to be actually playable in the browser?
While Java Kernel did improve download times against Flash, the latter tooling was much better, and still downloaded faster.
Had it not been for Apple and iOS forbidden, mostly due to app store than security concerns, and classical plugins would be around.
Since browsers never contended themselves with being document only, leaving the rest for native apps, now we have plugins back.
Back in the '90s I tried hard, and succeeded, in getting Java applets to work in a browser
If I recall correctly it was clunky and quite annoying.
Flash succeeded, sort of, but was also clunky.
But I think both were ahead of their time.
Gluon also has a cool JavaFX in the browser demo but I think it's dead: https://gluonhq.com/developer-preview-for-javafx-inside-a-we...
Of course you could also just distribute such apps as desktop apps... I will happily sell you a product that makes doing that easy :) https://hydraulic.dev/
We might have lost a decade since Unreal was running on Flash Crossbridge, yet thanks to Web trying to be a app platform to replace native apps, we are back full circle.
Java, .NET have their plugins back, even if with additional turtles along the stack, and stuff like Flutter are the new Flash.
I guess, thanks to all WebGL, WebGPU and WebAssembly folks
And of course this cannot be replicated with a Java plugin running in a (browser) WASM engine.
I don't see such fears around ECMAScript/WASM. Since the dominant players have a vested interest in users feeling safe leaving those technologies turned on for all websites, they'll keep investing to maintain that safety (and the marketing of that safety).
This is a massive insurance policy for building a TeaVM app -- the foundational web technologies it builds on are receiving incredible investments from industry heavyweights. It's not just security that is maintained, new Web APIs are regularly developed too. Want to use USB from your web app? No need to wait for Oracle to roll out a Java API for it -- browser vendors have made a new API and you can call it from you TeaVM app right away.
Furthermore, since the browser vendors never want browser code to have public security issues, they work hard to find and fix problems in their sandboxes and runtimes. This of course benefits TeaVM apps as well, since they play in those same sandboxes, and gives them a level playing field with other web apps in perpetuity.
Plugin-based applets were only safe as long as Sun had clout and the browser vendors were investing less in security than the Java team.