Fluxtion · WebAssembly In development

Write in Java, compile to WASM.

  • Run in the browser
  • Run on the edge
  • Run in Node

Author and test your event-driven logic with the Java stack you already own; the compiled processor is deterministic, no server and no AI on the path. WASM is not a separate programming model — it's another host for the same generated processor, behaviourally equivalent to the JVM across the compatibility suite.

Proven today: a generated processor → TeaVM → WASM-GC, behaviourally equivalent to the JVM, driven from JavaScript with plain objects. The hosted one-click cloud build is a later stage; this is the developer pipeline you run yourself.

New to WebAssembly? A 5-minute grounding for Java developers →

For Java teams
The problem

You're shut out of writing business logic on the edge. Your investment is in Spring, Java, your test suites, your deployment pipeline, your bespoke components — but none of it runs in Node or the browser. Today, edge logic means rewriting it in a stack you don't own.

The solution

Author your event-driven logic in Fluxtion, build and test it with the Java tools you already use, then compile the processor to WebAssembly and deploy the same logic to Node or the browser. The audit log streams back to your Java stack to capture and analyse.

“Why not just…?”

The routes a Java team usually considers to reach the browser or edge — and the friction each one adds that Fluxtion avoids by design.

Generic Java → WASM (J2CL, CheerpJ)

They translate a large slice of the JVM class library — runtime weight, and they tend to trip on the reflection / Unsafe common in frameworks.

With Fluxtion: a small, reflection-free dispatcher compiles cleanly to WASM-GC (via TeaVM, which Fluxtion uses under the hood) — you ship the result of the logic, not the framework machinery.

Native-image frameworks (Quarkus, Micronaut, Spring-native)

GraalVM native images start fast, but they're native binaries built for long-running servers — they don't run inside a V8 isolate (the thin edge: Cloudflare Workers, Fastly, Vercel Edge).

With Fluxtion: the closed-world dispatcher compiles to WASM-GC — the format thin-edge runtimes accept (footprint + support validated per runtime; that work is in progress).

Rewrite in Rust / Go (polyglot edge)

First-class WASM support, but you pay the language tax: rewrite the logic, retrain the team, leave the Java ecosystem and your existing tests behind.

With Fluxtion: the same Java graph — imperative nodes, the functional DSL, or Spring-XML wiring — is what compiles to WASM. No rewrite.

What runs in WASM

The whole event-processing surface survives the compile to WebAssembly — every row below is proven by running live in a browser, not asserted by us.

  • Generic JSON bridge onEvent({type,…}) → typed graph → named sinks; no bespoke @JSExport per app
  • DSL pipelines map / filter / groupBy / sink — closed-world, no runtime reflection
  • Named sinks pull (query) and push (subscribe → DOM)
  • getStreamed read a flow node’s current value by id
  • Signals publishSignal → subscription
  • Audit logging + log level structured event records, retuned at runtime
  • Exported services (@ExportService) host → graph typed calls
  • Injected services (@ServiceRegistered) via a TeaVM ReflectionSupplier (the WASM twin of reflect-config)
  • Callback-as-event reflection-free service delivery
  • JS-implemented service (@JSFunctor) a JS function injected as a Java service — bidirectional JS↔WASM
  • Edge-decoder re-injection decode at the edge, re-inject a typed event — the graph stays typed
  • Connectors / event feeds / threads host-side integration — supplied by the host runtime, not compiled in (by design)
  • ForkJoin parallel triggers WASM is single-threaded (by design)

Explore each one live in the interactive capability grid → (opens the demo; needs a WASM-GC browser)

Or drive the processor yourself in the WASM tester → (send events, watch sinks, step through the audit on the graph)

A real app — the Live Order Desk

A small but complete app running entirely in WASM in your browser: order/price events in, a risk gate that reads + stores position via a JS-backed service, accepted / rejected / market-data named sinks driving the UI, and a live audit log.

Not loading? open it in its own tab →

Benchmark — on your device

We quote no performance numbers. Instead, measure it here: the runtime's bench() drives the DSL pipeline in a tight loop on this device — warm-up discarded, audit off, so it reports the dispatch throughput a hot-path deployment runs (not the cost of logging every event).

What this benchmark measures

End-to-end dispatch throughput of a small Fluxtion DSL pipeline, driven from JavaScript one event at a time, audit off. Each event fans through ~2 map nodes + a filter + a sink. The figure is a whole-trip number — it includes the JS↔WASM boundary on every call, so pure in-wasm dispatch (looping inside the SEP) is faster again. Warm-up is discarded; per-event percentiles are omitted because the browser clock is too coarse for sub-microsecond events.

Fluxtion graph + operators (Java, compiled into the wasm)
// the measured pipeline — wired at build time, compiled into the SEP
// (closed-world: the method refs become direct calls, no runtime reflection)
DataFlowBuilder.subscribe(Integer.class)
        .map(CapFuncs::times2)        // x -> 2x
        .filter(CapFuncs::positive)   // keep > 0
        .sink("dslOut");              // egress, captured by the host

// the same Integer event also drives a second flow
DataFlowBuilder.subscribe(Integer.class).map(CapFuncs::times2).id("doubled");

// the operators — plain statics (this is the whole "business logic")
public final class CapFuncs {
    public static Integer times2(Integer x)   { return x * 2; }
    public static boolean positive(Integer x) { return x > 0; }
}

// the host method the loop calls, once per event
public int dsl(int x) {
    dslOut = null;
    sep.onEvent(Integer.valueOf(x));   // → dispatched through both flows
    return dslOut;                     // the value the sink captured
}
The JS driver (what the “Run benchmark” button does)
const proc = await createProcessor('./classes.wasm');
const host = proc.newHost('CapHost');
host.setLogLevel('NONE');             // measure dispatch, not audit logging
bench((i) => host.dsl((i % 1000) + 1), { events: 1_000_000, warmup: 100_000 });
// → { eventsPerSecond, millis, device }

Full detail — prerequisites, the pipeline, the bridge API — in the fluxtion-wasm getting-started guide.

Get started

Two helper libs do the heavy lifting: a Java one compiled into your processor wasm, and a JS one that loads it and hands you a bridge.

  • fluxtion-wasm-bootstrap (Maven, Apache-2.0) — the generic JSON bridge compiled into your SEP.
  • @telamin/fluxtion-wasm-runtime (npm) — loads the wasm, gives you onEvent / query / subscribe.
$ npm install @telamin/fluxtion-wasm-runtime
import { createProcessor } from '@telamin/fluxtion-wasm-runtime';

const sep = (await createProcessor('./classes.wasm')).jsonBridge('JsonHost');
sep.track('trades');
sep.onEvent({ type: 'trade', symbol: 'EURUSD', qty: 100 });
sep.query('trades');   // → { symbol: 'EURUSD', qty: 100 }

Full walkthrough — prerequisites, the pipeline, a hello-SEP, the bridge API, build config — in the getting-started guide on the repo.

What's true, and what isn't yet

  • Deterministic, on-device. No server and no AI on the execution path; the same logic as the JVM.
  • ⚙️ Generation needs a generator — the Fluxtion cloud (a key) or the local generator. There is no offline-without-a-generator build.
  • 🧵 Single-threaded — WASM has no parallel-trigger equivalent.
  • 🌐 WASM-GC only — recent browsers / Node 20+; older phones are excluded.
  • 📊 Not benchmarked. We quote no performance numbers — the runtime ships a bench() so you measure honest, on-device throughput for your processor.