Living Document Notice
Published 2026-09-14. The evolving architecture, revisions, and connected notes for this dispatch live in the Stax Digital Garden.

Compiling the Engine to WebAssembly

Compiling the Engine to WebAssembly: Abstract monochrome amber phosphor CRT hexagonal core and linear memory bus pathways over coordinate graticule grid

Summary

Executing the Bosun parsing and graph engine directly inside browser runtimes requires cross-compiling the Rust codebase to the wasm32-unknown-unknown target. Relying on heavy runtime shims or POSIX emulation layers degrades startup performance and expands binary payloads.

The Bosun WebAssembly compilation pipeline strips standard library dependencies that depend on host operating system syscalls. By utilizing linear memory exchange buffers, link-time optimization, and targeted binary stripping, the compiled WASM engine runs in modern browser workers with minimal bundle weight.

Zero-Copy Linear Memory Exchange

Transferring multi-megabyte Markdown strings across JavaScript and WebAssembly boundaries often triggers duplicate string allocations. Bosun eliminates this memory duplication by mapping strings directly into WASM linear memory.

#[no_mangle]
pub extern "C" fn bosun_alloc(size: usize) -> *mut u8 {
    let mut buffer = Vec::with_capacity(size);
    let ptr = buffer.as_mut_ptr();
    std::mem::forget(buffer);
    ptr
}
 
#[no_mangle]
pub extern "C" fn bosun_parse_buffer(ptr: *const u8, len: usize) -> u32 {
    let slice = unsafe { std::slice::from_raw_parts(ptr, len) };
    let cst = parse_markdown_slice(slice);
    cst.node_count() as u32
}

The browser JavaScript client allocates space inside the WebAssembly instance memory buffer, writes UTF-8 bytes directly via TextEncoder, and passes only pointer and length values to the parsing function.

After parsing completes, the WASM engine returns a 64-bit packed integer containing memory offset and payload length for serialized AST results. The JavaScript client reads this buffer directly through typed array views without intermediary serialization overhead.

Binary Size Reduction and Symbol Stripping

Unoptimized Rust WebAssembly outputs often exceed 2.5 MB due to formatting strings, panic handling machinery, and symbol tables. Bosun enforces strict release profile flags in Cargo.toml.

[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"
strip = true

These configuration flags instruct the LLVM compiler to optimize purely for code size, inline across crate boundaries, and discard debugging symbols. Applying wasm-opt -Oz in CI post-processing strips dead functions and reorders functions to maximize gzip compression efficiency.

Replacing the default memory allocator with a compact 2KB slab allocator trims an additional 28 KB from the compiled output.

Web Worker Isolation and Parallel Dispatch

Parsing syntax trees must not block browser layout or UI rendering. The compiled WASM binary loads inside a dedicated HTML5 Web Worker.

Communication between the UI thread and the Web Worker uses postMessage with transferable ArrayBuffer instances. Transferring buffer ownership hands memory across thread contexts without cloning memory pages.

This architecture keeps main-thread frame rates locked at 60 FPS while processing background document imports and complex graph index queries.

WASM Engine Performance and Bundle Profile

The table below details binary size reductions and execution throughput across different optimization levels for the WebAssembly build.

Build Stage / ProfileRaw WASM SizeGzipped SizeCold Instantiation TimeParsing Throughput
Debug Target4.82 MB1.12 MB42.0 ms18 MB/s
Standard Release (-O3)1.45 MB382 KB12.4 ms142 MB/s
Size Optimized (-Oz + LTO)480 KB142 KB4.1 ms136 MB/s
wasm-opt -Oz Stripped318 KB98 KB2.8 ms134 MB/s

  • Directus Target: bosunpkm-blog
  • Garden Source Reference: MOC - Bosun PKM Engine, MOC - Bosun PKM Tools, MOC - The Plain-Text Longevity Standard