A unified Java API for executing WebAssembly across multiple runtimes.
Write your code once against a stable API and swap runtimes without changing application code. WebAssembly4J supports Wasmtime, WAMR, GraalWasm, and Chicory out of the box.
Add the API and a provider to your project:
<dependency>
<groupId>ai.tegmentum.webassembly4j</groupId>
<artifactId>webassembly4j-runtime</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>ai.tegmentum.webassembly4j</groupId>
<artifactId>wasmtime4j-provider</artifactId>
<version>1.3.0</version>
</dependency>Run a WebAssembly function in three lines:
byte[] wasm = Files.readAllBytes(Path.of("add.wasm"));
int result = WasmRuntime.call(wasm, "add", Integer.class, 2, 3);
// result == 5The low-level API gives you full control over the engine lifecycle:
try (Engine engine = WebAssembly.newEngine()) {
Module module = engine.loadModule(Path.of("math.wasm"));
Instance instance = module.instantiate();
Function add = instance.function("add").orElseThrow();
Object result = add.invoke(2, 3);
}Avoid boxing overhead with typed function wrappers:
Function fn = instance.function("add").orElseThrow();
TypedFunction.I32_I32_I32 add = fn.typed(TypedFunction.I32_I32_I32.class);
int result = add.call(2, 3);Inspect a module's exports and imports before instantiation:
Module module = engine.loadModule(wasmBytes);
for (ExportDescriptor export : module.exports()) {
System.out.println(export.name() + " : " + export.type());
}Provide host functions to a WebAssembly module via LinkingContext:
LinkingContext ctx = LinkingContext.builder()
.define("env", "log", new ValueType[]{ValueType.I32}, new ValueType[]{},
args -> { System.out.println("wasm says: " + args[0]); return null; })
.build();
Instance instance = module.instantiate(ctx);WasmRuntime provides a static facade for common operations:
// One-shot call
int result = WasmRuntime.call(wasmBytes, "factorial", Integer.class, 10);
// Interface proxy -- bind a Java interface to WASM exports
try (Calculator calc = WasmRuntime.load(Calculator.class, wasmBytes)) {
int sum = calc.add(2, 3);
}
// Pre-compile for reuse
WasmModule compiled = WasmRuntime.compile(wasmBytes);
try (WasmInstance inst = compiled.instantiate()) {
inst.call("run");
}| Module | Description | Java |
|---|---|---|
webassembly4j-api |
Stable user-facing API (Multi-Release JAR: 8/11/22) | 8+ |
webassembly4j-spi |
Provider contracts and discovery | 11+ |
webassembly4j-runtime |
High-level runtime with proxy binding and marshalling | 11+ |
webassembly4j-bindgen |
WIT binding generator (Maven plugin + CLI) | 11+ |
webassembly4j-testing |
JUnit 5 multi-engine test support | 11+ |
webassembly4j-pool |
Thread-safe instance pooling | 11+ |
webassembly4j-spring |
Spring Boot auto-configuration | 17+ |
webassembly4j-benchmarks |
JMH benchmarks across all engines | 17+ |
| Provider | Engine | Java | Priority |
|---|---|---|---|
wasmtime4j-provider |
Wasmtime (via wasmtime4j) | 11+ | 200 |
graalwasm4j-provider |
GraalWasm (Polyglot API) | 17+ | 150 |
wamr4j-provider |
WAMR (via wamr4j) | 17+ | 100 |
chicory4j-provider |
Chicory (pure Java) | 11+ | 50 |
Providers are discovered automatically via ServiceLoader. When multiple providers are on the classpath, the one with the highest priority is selected. To select a specific provider, use the builder:
Engine engine = WebAssembly.builder()
.provider("chicory") // "wasmtime", "wamr", "graalwasm", or "chicory"
.build();For languages that compile to WasmGC (Kotlin/Wasm, Dart, OCaml, Java via J2Wasm), the GC bridge provides automatic marshalling between Java objects and WebAssembly GC struct instances -- no linear memory management needed.
Annotate a Java class or record with @GcMapped:
@GcMapped
public record Point(double x, double y) {}
@GcMapped
public record Line(Point start, Point end) {}Supported field types: int, long, float, double, boolean, and nested @GcMapped types.
GcExtension gc = instance.extension(GcExtension.class)
.orElseThrow(() -> new UnsupportedOperationException("GC not supported"));
GcMarshaller marshaller = GcMarshaller.forExtension(gc);
// Java -> GC struct
Point p = new Point(3.0, 4.0);
GcStructInstance struct = marshaller.marshal(p);
// GC struct -> Java
Point result = marshaller.unmarshal(struct, Point.class);Nested @GcMapped fields are recursively marshalled as GC struct references. Null references are preserved in both directions.
GcProxyFactory creates interface proxies that automatically marshal @GcMapped parameters and return values through GC structs:
interface Geometry extends AutoCloseable {
@WasmExport("rotate_point")
Point rotate(Point p, double angle);
@WasmExport("distance")
double distance(Point a, Point b);
}
GcExtension gc = instance.extension(GcExtension.class).orElseThrow();
Geometry geom = GcProxyFactory.create(
Geometry.class, engine, module, instance, gc);
Point rotated = geom.rotate(new Point(1, 0), Math.PI / 2);
double dist = geom.distance(new Point(0, 0), new Point(3, 4));Primitive parameters pass through directly. This is the GC counterpart to ProxyFactory which marshals through linear memory.
For fine-grained control, use the GC extension directly:
GcStructType pointType = GcStructType.builder("Point")
.addField("x", GcFieldType.f64(), true)
.addField("y", GcFieldType.f64(), true)
.build();
GcStructInstance point = gc.createStruct(pointType,
GcValue.f64(3.0), GcValue.f64(4.0));
double x = point.getField(0).asF64();
point.setField(0, GcValue.f64(5.0));The webassembly4j-testing module provides JUnit 5 support for running tests against every available engine:
@WasmTest
@WasmModule("math.wasm")
void addFunction(Instance instance) {
Function add = instance.function("add").orElseThrow();
assertEquals(5, add.invoke(2, 3));
}Each test method runs once per discovered engine. The extension handles engine and instance lifecycle automatically.
For high-throughput scenarios, pool and reuse instances:
PoolConfig config = PoolConfig.builder()
.minSize(2)
.maxSize(16)
.build();
try (WasmInstancePool pool = WasmInstancePool.create(wasmBytes, config)) {
try (PooledInstance inst = pool.borrow()) {
inst.function("handle").orElseThrow().invoke(request);
} // instance returned to pool on close
}Add webassembly4j-spring and a provider to your Spring Boot application:
<dependency>
<groupId>ai.tegmentum.webassembly4j</groupId>
<artifactId>webassembly4j-spring</artifactId>
<version>1.3.0</version>
</dependency>An Engine bean is auto-configured and available for injection. Configuration properties:
webassembly4j.enabled=true
webassembly4j.engine=wasmtimeHealth indicator and actuator endpoint (/actuator/wasm) are registered when Spring Boot Actuator is present.
Generate Java bindings from WIT (WebAssembly Interface Types) definitions:
<plugin>
<groupId>ai.tegmentum.webassembly4j</groupId>
<artifactId>webassembly4j-bindgen</artifactId>
<version>1.3.0</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>Supports both modern (Java 17+ records) and legacy (Java 8+ POJOs) code styles.
./mvnw clean installThe API module targets Java 8+ as a Multi-Release JAR (8/11/22). Core modules require Java 11+. The full build including all providers requires JDK 22+ (for the MRJAR Java 22 overlay compilation).