The MatchBox open beta is live at https://boxlang.ortusbooks.com/boxlang-framework/matchbox, and it brings something genuinely new to the BoxLang ecosystem: a path into WebAssembly.
That means BoxLang code can now move into browser applications, static-site deployments, edge runtimes, and WASI-style containers - without requiring a JVM. The feature is still beta, but the core direction is already useful: write BoxLang, compile it with MatchBox, and ship the generated WASM artifact to wherever a small portable runtime makes sense.
π¦ What Is MatchBox?
MatchBox is a custom BoxLang virtual machine written in Rust. While the standard BoxLang JVM runtime is the right choice for the vast majority of server-side applications, there are deployment targets where carrying a full JVM is not practical: browser applications, edge runtimes, WASM containers, tiny native CLIs, and embedded hardware.
MatchBox fills that gap. It compiles BoxLang source to native bytecode or WebAssembly artifacts and ships a self-contained runtime that can run in any of those environments. You write the same BoxLang you already know - the runtime underneath changes.
Important: MatchBox is a separate runtime from the JVM BoxLang. Code written for the JVM runtime generally runs on MatchBox, but not all JVM BoxLang features are available in every MatchBox target. See the Differences from BoxLang JVM docs for the full compatibility matrix.
π§ Three WebAssembly Modes
MatchBox currently supports three distinct WebAssembly workflows. The right one depends on where and how you want to ship your BoxLang code.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MatchBox WASM Modes β
ββββββββββββββββββββββββ¬βββββββββββββββββββββββ¬βββββββββββββββββββββββ€
β JS Module (AOT) β Raw WASM Binary β Runtime Mode β
β --target js β --target wasm β pkg/ + run_boxlang β
ββββββββββββββββββββββββΌβββββββββββββββββββββββΌβββββββββββββββββββββββ€
β Pre-compiled app as β Raw .wasm binary, β Ship the MatchBox β
β an ES module. Auto- β you control loading. β engine, execute BL β
β bootstraps runtime. β Webpack/Vite/Edge. β source dynamically. β
ββββββββββββββββββββββββΌβββββββββββββββββββββββΌβββββββββββββββββββββββ€
β β
Recommended for β β
Edge platforms, β β
Playgrounds, β
β browser apps β custom bundlers β user scripts β
ββββββββββββββββββββββββ΄βββββββββββββββββββββββ΄βββββββββββββββββββββββ
Mode 1: JavaScript ES Module (AOT)
This is the recommended browser path today. MatchBox compiles your BoxLang script to bytecode, embeds it in a WASM binary, and wraps everything in a JavaScript ES module that bootstraps the runtime automatically.
matchbox --target js app.bxs
Produces:
app.js
app.wasm
The generated module loads the WASM file, initializes MatchBox, and exposes all top-level BoxLang functions as ES module exports. Your BoxLang functions become directly callable from HTML or Node.js:
function greet( name ) {
return "Hello, " & name & "!"
}
function calculate( a, b ) {
return a * b
}
After compiling, your HTML just imports and calls them:
<script type="module">
import { greet, calculate } from './app.js';
const result = await greet( "Developer" );
document.body.textContent = result;
</script>
All top-level BoxLang functions are exported regardless of access modifiers. In WASM, there is no private at the module boundary - every function your script defines becomes accessible.
Mode 2: Raw WASM Binary (AOT)
matchbox --target wasm app.bxs
Produces a .wasm file and hands you full control over the loading environment. This is useful when you are bundling via Webpack or Vite, integrating with a non-standard JS runtime, or targeting edge platforms that have their own WASM loading pipeline.
const response = await fetch( './app.wasm' );
const buffer = await response.arrayBuffer();
const module = await WebAssembly.instantiate( buffer, importObject );
Refer to the MDN WebAssembly docs for the full instantiation API.
Mode 3: Runtime Mode (Dynamic Execution)
In runtime mode, you ship the MatchBox engine itself (pkg/) and execute BoxLang source code dynamically at runtime - similar to a JIT. This is particularly useful for:
- Interactive BoxLang playgrounds
- Applications that allow user-provided BoxLang scripts
- Server-side rendering on WASM-capable edge runtimes
<script type="module">
import init, { run_boxlang } from './pkg/matchbox.js';
await init();
run_boxlang( `
name = "Browser"
println( "Hello from BoxLang running in ##name##!" )
` );
</script>
For apps that call BoxLang functions repeatedly, use the persistent BoxLangVM instance to avoid re-initializing on every call:
import init, { BoxLangVM } from './pkg/matchbox.js';
await init();
const vm = new BoxLangVM();
vm.load_source( `
function add( a, b ) { return a + b }
function greet( name ) { return "Hello, " & name }
` );
const sum = vm.call( "add", [ 10, 20 ] );
const message = vm.call( "greet", [ "BoxLang" ] );
console.log( sum, message );
π Browser Apps: BoxLang Owns the State
The browser target is where MatchBox starts to feel genuinely different.
You can put application state and state transitions in BoxLang, compile it to a JS/WASM bundle, and let the page wire that state into a UI framework. The example todo app in the repo does exactly that: BoxLang owns todos, actions, and snapshots, while the HTML layer imports the generated ES module and uses window.MatchBox.State("todo", ...) to bridge BoxLang state into Alpine.js.
The key insight is separation of concerns at the language level: BoxLang handles data and logic, JavaScript handles the DOM. Both sides of that boundary stay clean because they speak through well-defined function exports.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Browser Architecture β
β β
β ββββββββββββββββββββ compile ββββββββββββββββββββββ β
β β app.bxs β ββββββββββββββΊ β app.js + app.wasm β β
β β (BoxLang Logic) β β (ES Module) β β
β ββββββββββββββββββββ ββββββββββ¬ββββββββββββ β
β β import β
β βββββββββββΌβββββββββββ β
β β index.html β β
β β (Alpine.js / UI) β β
β ββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
JavaScript Interop from BoxLang
When BoxLang runs inside a browser WASM context, it can reach into the JavaScript environment through the js global:
// DOM access
title = js.document.title
js.document.getElementById( "output" ).innerText = "Updated by BoxLang"
// Browser APIs
js.alert( "Hello!" )
js.console.log( "Logged from BoxLang" )
// Location
url = js.window.location.href
js.*is only available in browser WASM. Using it in a native MatchBox build or WASI container throws a runtime error.
Plain JavaScript values cross the WASM boundary as BoxLang values. Browser host objects (DOM nodes, window) remain JS handles so browser APIs continue behaving like browser APIs.
π¦ Static Deployment
The browser output is entirely static: an HTML file, a generated .js file, and a .wasm file. You can deploy that combination to any static host.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Static Deployment β
β β
β Your files: Deploy to: β
β βββββββββββββ ββββββββββββββββββββββββββββββββ β
β β index.htmlββββββββΊ Netlify / Vercel / S3 β β
β β app.js β β Cloudflare Pages / CDN β β
β β app.wasm β ββββββββββββββββββββββββββββββββ β
β βββββββββββββ β
β β
β Only requirement: serve .wasm with β
β Content-Type: application/wasm β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Most modern static hosts set the correct WASM content type automatically. For local development, serve over HTTP - browsers block WASM loading from file://:
npx serve .
# or
python3 -m http.server 8080
Then open http://localhost:8080.
π³ WASM Containers and Edge
MatchBox WASM binaries can also run as standalone server-side workloads using WASM-native runtimes. This deployment model is ideal for serverless functions, edge compute, and hermetically isolated microservices.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Server-Side WASM Targets β
β β
β my_service.bxs β
β β β
β β matchbox --target wasm β
β βΌ β
β my_service.wasm ββββββββββββββββββββββββββββββββββββββββββββββ β
β β β β β
β βΌ βΌ βΌ β
β ββββββββββββ ββββββββββββββ ββββββββββββββββββββββββββββ β
β β Wasmtime β β WasmEdge β β Docker (WASM OCI Image) β β
β β (WASI) β β (Edge/SVC) β β FROM scratch β β
β ββββββββββββ ββββββββββββββ ββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Running with Wasmtime
Wasmtime is the reference WASI runtime:
matchbox --target wasm service.bxs
# Run it
wasmtime service.wasm
# With filesystem access
wasmtime --dir=. service.wasm
Running with WasmEdge
WasmEdge is optimized for cloud-native and microservice workloads:
wasmedge my_service.wasm
Docker OCI Containers
Because the WASM runtime provides the execution environment, your container image needs only the .wasm binary - no OS, no shell, no libc:
FROM scratch
COPY my_service.wasm /my_service.wasm
ENTRYPOINT ["/my_service.wasm"]
Build and run with a WASM-capable runtime shim:
docker build -t my-boxlang-service .
docker run --runtime=io.containerd.wasmtime.v1 my-boxlang-service
Docker Desktop 4.15+ includes native WASM support:
docker run --runtime=io.containerd.wasmtime.v1 \
--platform=wasi/wasm32 \
my-boxlang-service
The resulting image is extremely compact because the WASM runtime supplies everything the binary needs.
β οΈ Beta Boundaries: What to Know
WASM targets are not the same as the standard JVM BoxLang runtime, and MatchBox WASM is not the same as MatchBox native. Here is the current compatibility matrix:
ββββββββββββββββββββββββ¬βββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββ
β Feature β Browser WASM β WASI Container β
ββββββββββββββββββββββββΌβββββββββββββββββββββββΌββββββββββββββββββββββββββββββ€
β Java interop (JNI) β β Not supported β β Not supported β
β Native Fusion (Rust) β β Not available β β
Partial (WASM Rust) β
β DOM / js.* APIs β β
Available β β Not available β
β Filesystem access β β Not available β β
Via WASI --dir grants β
β Network access β β
Browser fetch β β οΈ Experimental (WASI) β
β MatchBox JIT β β Disabled β β Disabled β
β sleep() / async β β
Supported β β
Fiber scheduler β
ββββββββββββββββββββββββ΄βββββββββββββββββββββββ΄ββββββββββββββββββββββββββββββ
The MatchBox JIT (Cranelift-based) is intentionally not active in WASM builds. The browser or WASM runtime provides its own compilation pipeline, and carrying a native JIT into a WASM binary would not make sense.
π Where to Start
The best first experiment is a tiny browser app where BoxLang owns the state and JavaScript handles the rendering.
Step 1: Write your BoxLang logic
// app.bxs
function greet( name ) {
return "Hello from BoxLang, " & name & "! π"
}
function add( a, b ) {
return a + b
}
Step 2: Compile to a JS module
matchbox --target js app.bxs
Step 3: Import and call it from HTML
<script type="module">
import { greet, add } from './app.js';
document.getElementById( "output" ).textContent = await greet( "Developer" );
console.log( await add( 10, 32 ) ); // 42
</script>
Step 4: Serve it locally and open your browser
npx serve .
For edge experiments, compile with --target wasm and run the output with Wasmtime. Keep the application small and explicit while the beta matures.
π The Bigger Picture
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β BoxLang Runtime Landscape β
β β
β βββββββββββββββββββββββββ βββββββββββββββββββββββββββββββ β
β β BoxLang JVM Runtime β β MatchBox (Rust VM) β β
β β β β β β
β β - Web Servers β β - Browser (WASM/JS) β β
β β - AWS Lambda β β - WASI Containers β β
β β - Google Cloud Fns β β - Edge Runtimes β β
β β - Desktop (Electron) β β - Native CLI β β
β β - Full Java interop β β - ESP32 / IoT β β
β β - All BL modules β β - Static deployments β β
β βββββββββββββββββββββββββ βββββββββββββββββββββββββββββββ β
β β
β Same BoxLang language. Different runtimes. β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The important shift is that BoxLang can now run in places that expect WebAssembly artifacts: browsers, static hosts, edge platforms, and WASI runtimes. MatchBox makes that path real.
The language stays the same. You write BoxLang. Where it runs is now a deployment decision, not a language boundary.
Explore the project, try the beta, and open issues at https://github.com/ortus-boxlang/matchbox.
Read the full MatchBox docs at https://boxlang.ortusbooks.com/boxlang-framework/matchbox.
Join the Ortus Community
Be part of the movement shaping the future of web development. Stay connected and receive the latest updates on product launches, tool updates, promo services and much more.
Subscribe to our newsletter for exclusive content.
Follow Us on Social media and don't miss any news and updates:
Add Your Comment