Serverless Workflows in Wasm Across Browser, Edge, and Cloud
One Wasm artifact running a serverless workflow in a browser, at the edge, and in the cloud — what the comparative research shows about AOT vs JIT, cold starts, and payload sizes, plus the component model, orchestration patterns, and today's real limitations.
Published on • November 15, 2026
AI Assistant

The promise is seductive: compile once, run the same serverless workflow in a browser tab, on an edge node, and in a cloud VM — same binary, same ABI, same results. WebAssembly is the first binary format with a credible claim to that portability, and a 2025 comparative study (arXiv:2512.04089, presented at the ScaleSys ‘25 workshop in Vienna) puts real numbers behind both the promise and its limits. This post unpacks what the research found, then maps it onto the engineering reality: runtimes, component-model composition, orchestration patterns, and the places where “one runtime everywhere” still falls short.
The experiment: same workflow, three tiers
The paper’s design is exactly what you would want from a portability claim: a five-step DAG workflow (ingest → preprocess → map with fan-out 4 → reduce with fan-in 4 → serialize), implemented in Rust and compiled to wasm32-wasi modules with a single toolchain, executed unchanged in three environments:
| Tier | Host | Runtime |
|---|---|---|
| Browser | Ubuntu workstation, Firefox 143.0.4 | SpiderMonkey Wasm engine, tasks in a Web Worker |
| Edge | Raspberry Pi 4 (arm64, 4 GiB) | WasmEdge v0.13.5 in Docker 28.5.1 |
| Cloud | VM, 4 vCPU / 24 GiB | WasmEdge v0.13.5, same container setup |
Data moves as CBOR frames entirely in memory; the transport differs per tier (loopback WebSocket + postMessage in the browser, HTTP multipart/form-data via a shim at the edge and in the cloud), while the invocation ABI stays identical. Payloads are fixed-seed synthetic frames of 16 KiB, 1 MiB, and 4 MiB, and each step’s kernel is sized from the payload (a 64×64 to 1024×1024 matrix multiply in the map step).
Three findings matter for anyone designing multi-tier Wasm systems:
- Warm starts and AOT dominate cold JIT. Across every environment, warming pre-initialized instances cut startup latency, and ahead-of-time compilation removed the compile/validate phase from cold starts — at the cost of artifacts 125–135% larger than the original Wasm modules (e.g., 0.152 MB → 0.342 MB for step S1). Cache size is the price of predictable startup.
- The browser wins when payloads are small, then loses. At 16 KiB, the browser (JIT) posted the best makespan and throughput — everything is in-memory, orchestration is a
postMessageaway, and startup cost is negligible. At 1 and 4 MiB the regime flips: once the workflow is compute- and memory-bound, AOT on the edge and cloud wins on makespan, throughput, and stability. - Runtime shape explains the resource numbers. The browser showed higher average/peak RSS (JS engine stack, worker, GC management) while edge and cloud stayed compact thanks to a native Wasm runtime — but the edge’s cold JIT startup was the worst of all configurations, which is precisely the case AOT caching exists to fix.
The honest caveat the authors state themselves: one hardware set, one browser, one workload — the trends are transferable, the absolute numbers are not.
One artifact everywhere: what it takes
Portability at this level is a stack of decisions, not a switch:
- A single target and toolchain. Rust →
wasm32-wasiwith identical flags and features (SIMD on, threads on in the study) across all tiers. The moment the browser build diverges from the edge build, you are maintaining two products. - A filesystem-free ABI. The browser has no shared filesystem to lean on, so the workflow does all I/O through in-memory buffers. This constraint — designed for portability — also removes the most common source of nondeterminism.
- Per-tier transports behind one interface. WebSocket +
postMessage(with transferableArrayBuffers to avoid copies) in the browser; HTTP shims with the Wasm runtime on edge/cloud. The orchestrator stays outside the workflow, driving the DAG identically in all three cases and correlating timings via arun_id. - AOT where the runtime allows it. WasmEdge’s AOT compilation produced the edge/cloud wins above; the browser relies on its engine’s own tiered compilation plus warm workers.
From wasm32-wasi to the component model
The study runs on WASI preview 1 with a JS shim in the browser — the pragmatic present. The architectural future is the component model: Wasm modules with typed interfaces defined in WIT (Wasm Interface Type), which compose into larger components without glue code. A component declares that it imports wasi:http/incoming-handler and exports a typed function; the same component runs under a browser-compatible host, WasmEdge, Wasmtime, or a workflow engine, with the host providing the interface.
Concretely, the component model changes three things for workflows:
- Shared code becomes shared components, not shared modules. A parsing component compiled once can be linked into a browser workflow, an edge filter, and a cloud aggregator —
jco(the JavaScript component tool) can even transpile it to JS for browser hosts that cannot load components natively. - Composition replaces orchestration glue. Fan-out/fan-in becomes component composition with typed channels instead of hand-rolled CBOR envelopes between steps.
- The supply chain shrinks. One signed component artifact with declared dependencies travels through one registry — the same “compile once, run anywhere” economics, one abstraction up.
Orchestration patterns
Who drives the DAG? The research uses an external orchestrator — isolated from the executors, feeding each task’s output to its successors, and recording metrics. That maps onto three deployable patterns:
- Thin orchestrator + stateless tasks (the paper’s pattern). The orchestrator runs wherever you like — control-plane VM, browser main thread, edge gateway — and tasks are pure functions over buffers. Simple, resumable, and trivially parallelizable; the cost is a hop per edge (WebSocket or HTTP), which is why the browser’s in-memory advantage appears only when payloads are small.
- Event-driven step chaining. Steps publish results to a bus (HTTP webhook, NATS, WebSocket frames); each step is an independently invocable function. Best when steps have different resource profiles — the browser can host the interactive step while compute-heavy steps live at the edge.
- Host-managed workflows. Engines like Spin and wasmCloud wrap the Wasm runtime with triggers (HTTP, cron, messaging) so the platform owns invocation and retries. wasmCloud v2 even brings this to Kubernetes: a runtime operator schedules workloads onto host groups via CRDs, with a per-workload “service” acting as supervisor and health check.
The cold-start economics select the pattern: if a step’s warm-start is sub-millisecond and a cold start is a few milliseconds (as in the AOT/warm results above), per-invocation orchestration overhead is fine; if steps are long-lived and chatty, co-locate them in one host and compose in-process.
Where it breaks: current limitations
“One runtime everywhere” is directionally true and practically incomplete:
- Threads. The browser and WASI have different threading stories (web workers +
SharedArrayBuffervs. WASI threads proposals); the study enabled threads across tiers, but portable shared-memory code remains the sharpest edge in cross-tier Wasm. - Async I/O. WASI 0.2’s pollable-based async is a major step, but hosts implement it unevenly — a component written against one host’s async semantics can stall or busy-poll on another.
- Storage. There is no portable filesystem. Key-value and SQL interfaces (
wasi:kv,wasi:sqlite-style proposals) are stabilizing, but a workflow needing durable state today needs a host-specific adapter per tier — the exact thing portability was supposed to eliminate. - Ecosystem maturity. Observability (traces across the orchestrator/host boundary), debugging (source-level stepping in AOT-compiled instances), and library support (HTTP, crypto, serialization) all trail containers. AOT artifacts are also platform-specific — cache them per architecture, or you rediscover cross-compilation.
- Memory model. 32-bit Wasm addresses cap linear memory near 4 GB (with multi-memory and 64-bit memory proposals still moving), which bounds how much state a single instance can hold — fine for the study’s 4 MiB payloads, a design constraint for in-browser LLM-scale data.
Practical takeaways
If you are building a workflow that spans tiers today: keep the ABI filesystem-free and buffer-based, AOT-compile and cache on edge/cloud, keep a warm instance pool (the paper’s pre-warming was worth more than any micro-optimization), treat payload size as a placement signal — small and interactive near the browser, large and compute-bound at the edge or cloud — and design the orchestrator to be swappable. Adopt the component model for new code where the host supports it, because it is the piece that turns “the same binary runs in three places” into “the same components compose into workflows in three places.”
The research says the continuum is real: one artifact, honest measurements, and a clear boundary where the browser stops winning. The engineering says the boundary moves every release — which is the best possible reason to keep the portable part of your system genuinely portable.
References
- Serverless Everywhere: A Comparative Analysis of WebAssembly Workflows Across Browser, Edge, and Cloud (arXiv:2512.04089) (cited source; Colosi, Farahani, Lovén, Prodan, Villari — ScaleSys ‘25, Vienna)
- WASI — WebAssembly System Interface · WebAssembly Component Model
- WasmEdge · Spin (Fermyon) · wasmCloud
- wasmCloud v2 on Kubernetes: Runtime Operator, wash host & WASI P3
- Bytecode Alliance — jco, the JavaScript component tool