How to run Java in the browser with WebAssembly
Java WebAssembly tutorial
WebAssembly was created to perform the highly complex and overwhelmingly sophisticated computations that push JavaScript beyond its browser-based capabilities. It is invoked by JavaScript whenever a helping hand is needed.
WebAssembly logic can be coded in any language that can be compiled into a WebAssembly virtual instruction set file. That means, with the help of WebAssembly, your browser can now execute code written in a variety of languages, including:
- Scala
- C/C++
- Rust
- GoLang
- Kotlin
- Clojure
- C#
- and most importantly, Java
W3C, WebAssembly and WASM
Furthermore, WebAssembly (WASM) is a W3C standard supported by all major browsers, including Chrome, Safari, Firefox and Edge. You don’t need to install third-party tools to use it. With WebAssembly, you can run Java in the browser without installing a Mozilla plugin or fighting with a Java applet.
All you need to do is compile your Java code into a WASM binary file, and the browser takes care of the rest.
Java, WebAssembly and the browser
In this tutorial, I will show you how to write logic in Java that compiles into a WebAssembly file and is invoked by JavaScript inside a web browser.
To drive the learning, we will build a Number Guessing Game that offloads complex computational workloads to WebAssembly for processing.
The pretend problem
Let’s pretend the JavaScript based selection of a random number caused a performance issue with our application.
To rectify this issue, we will write a Java class to perform this function instead. The Java class will compile into a WebAssembly binary that gets invoked by JavaScript, rather than make JavaScript do the cumbersome processing itself.
Steps to run Java in the browser with WASM
To run Java in the browser with WebAssembly, follow these basic steps:
- Use a WebAssembly compatible compiler and API to build your Java code.
- Write a Java method that contains your business logic.
- Add a native method reference to your Java code to interact with JavaScript.
- Write a JavaScript method to handle any data returned from the Java program.
- Use the WebAssembly API to invoke your Java code from JavaScript.
- Compile your Java code into a WebAssembly WASM file.
- Package and deploy your HTML and WASM files together and view your webpage.
WebAssembly compatible compiler and API
To run Java in the browser, you must compile your code into a WebAssembly binary file. There are a number of APIs that help you do that, including:
- CheerpJ
- JWebAssembly
- TeaVM
For this example we will use TeaVM. It is well-documented, and has the easiest-to-find examples at the time of this writing.
To include TeaVM WebAssembly capabilities that allow Java to run in the browser with WASM, you’ll need to establish the following Maven configurations:
- A dependency on teavm-classlib Java API for WebAssembly
- A reference to the teavm-maven-plugin to compile Java to WASM
You can find the full Maven pom.xml file to support the Java WASM application on GitHub.
The WebAssembly Java code
The Java code has two main parts:
- An
@Export
annotated method to be invoked through JavaScript - A native,
@Import
annotated method to pass data to a JavaScript method
package com.mcnz.wasm; import org.teavm.interop.*; public class ComplexLogic { /* This method is invoked by the browser. */ @Export(name = "getMagicNumber") public static void getMagicNumber(int range) { /* Pretend this is intense, complex logic. */ int magicNumber = (range/2) + (range % 3); setMagicNumber(magicNumber); } /* This method maps to a JavaScript method in a web page. */ @Import(module = "env", name = "setMagicNumber") private static native void setMagicNumber(int message); }
When you run the mvn package command to build the Java application, this Java source code compiles into a file named complexlogic.wasm and is placed in a subfolder named /wasm. You can change these settings through the pom.xml configuration.
The WebAssembly JavaScript code
To invoke the getMagicNumber method of the Java class, use the following JavaScript method:
WebAssembly.instantiateStreaming( fetch('wasm/complexlogic.wasm'), {env: {setMagicNumber}}) .then(module => { console.log("Calling the Java method from JavaScript.") module.instance.exports.getMagicNumber(10) });
This JavaScript call to the WebAssembly API does three things:
- downloads the complexlogic.wasm file from the web browser;
- tells the Java program the callback method is named setMagicNumber; and
- invokes the getMagicNumber Java method through WebAssembly.
JavaScript WebAssembly callback method
The JavaScript callback method named setMagicNumber is below:
let magicNumber = 0; function setMagicNumber(number) { console.log("Java code has called the JS setMagicNumber method.") magicNumber = number console.log("WASM set the magic number to: " + magicNumber) }
Full code for the webpage with WebAssembly and Java
The full code for the Number Guesser Game HTML page is below:
<html> <head> <title>Java in the Browser with WebAssembly</title> </head> <body> <p>Guess the number between 1 and 10:</p> <button type="button" onclick="playTheGame(5)">5</button> <button type="button" onclick="playTheGame(6)">6</button> <button type="button" onclick="playTheGame(7)">7</button> <script> let magicNumber = 0; function playTheGame(number) { if (number == magicNumber) { alert("You guessed right! It is " + magicNumber); } else { alert("Wrong! Guess again!"); } } function setMagicNumber(number) { console.log("Java code has called the JS setMagicNumber method.") magicNumber = number console.log("WASM set the magic number to: " + magicNumber) } WebAssembly.instantiateStreaming( fetch('wasm/complexlogic.wasm'), {env: {setMagicNumber}}) .then(module => { console.log("Calling the Java method from JavaScript.") module.instance.exports.getMagicNumber(10) } ); </script> </body> </html>
Java WebAssembly deployment
To prepare the application for deployment, run the Maven package command:
mvn package
The Maven POM is configured so that the package
command generates two files for deployment:
- the index.html that represents the webpage, and
- a file named complexlogic.wasm in the /wasm subfolder.
Run the Java WebAssembly application
The easiest way to deploy and test the application is to use a Java Maven Docker image.
To run the application with the source code from GitHub, use these commands:
-
git clone https://github.com/cameronmcnz/java-in-the-browser.git
-
cd java-in-the-browser
-
docker run --rm -it -p 8080:8080 -v ${pwd}:/src maven:3-jdk-8 /bin/bash
-
cd src
-
mvn package
After the maven package command runs, the application will be available on http://localhost:8080.
And that’s how easy it is to invoke code from Java in the browser with JavaScript and WebAssembly.
Java WebAssembly tutorial code is on GitHub
The source code for this Java in the browser with WebAssembly tutorial is available on GitHub.
The pom.xml file is especially long and worth reviewing if you recreate this WebAssembly tutorial with different Java class and package names.