QuantStack brings Numba JIT compiler to the browser via JupyterLite
QuantStack has released the first working version of the Numba JIT compiler running entirely inside a web browser. The project pairs JupyterLite, which runs Jupyter kernels locally through WebAssembly instead of on a remote server, with emscripten-forge, a WebAssembly package distribution built on the conda/mamba stack. Until now, Numba had been a missing piece of the browser-based scientific Python stack: support for it in WebAssembly had been requested since 2018, in a Numba GitHub issue (numba/numba#3284), and the Pyodide project had separately tracked the packaging problem in its own issue tracker (pyodide/pyodide-recipes#192).
The obstacle was architectural, not just a packaging gap. Numba is a compiler that depends on llvmlite and LLVM, and on a normal machine it places freshly generated machine code straight into executable memory and calls it. Browsers deliberately block applications from creating or modifying executable memory that way, so QuantStack needed a WebAssembly-aware execution engine for llvmlite, a way to run the LLVM linker inside the browser process itself, and a mechanism for loading freshly compiled code into a persistent Python runtime.
The approach reuses work QuantStack had already done for Xeus-Cpp, its C++ interpreter for JupyterLite, which runs into the same restriction with the Clang-Repl interpreter. That pipeline compiles LLVM IR into a WebAssembly object file, links it with wasm-ld into a WebAssembly 'side module', the WebAssembly analogue of a shared library, loads the side module into the running application, and resolves and calls its symbols, with each new piece of code extending the running program and sharing memory with everything loaded before it. QuantStack built a matching WebAssembly execution engine for llvmlite: it takes the LLVM modules llvmlite produces, emits WebAssembly objects, invokes LLVM's LLD linker through its in-process, re-entrant driver, since spawning a separate wasm-ld process is not possible inside a browser, and loads each result as an Emscripten side module, keeping all modules alive globally so they can resolve each other's symbols. The underlying Xeus-Cpp work is also the subject of a QuantStack talk at FOSDEM 2026 on interactive C++ workflows.
With that engine in place, an ordinary Numba-decorated Python function, written with @jit or @njit, compiles the same way it would outside the browser: Numba turns the Python bytecode into its own intermediate representation, infers types, transforms the program, and lowers it to LLVM IR through llvmlite. In JupyterLite, that module goes to the new WebAssembly engine instead of LLVM's native JIT, gets compiled and linked as a side module, and is loaded into the running Xeus-Python kernel, where Numba calls it through the WebAssembly function table. A single compiled function can draw in several such modules: Numba may first load runtime support such as the Numba Runtime (NRT), then compiler-generated helpers, and finally the module with the user's own function, and they must load in the correct order so later modules can resolve symbols the earlier ones provide. QuantStack also added persistent caching: with @njit(cache=True), Numba restores a previously cached compilation from JupyterLite's persistent filesystem when the same function is used again in a later browser session, and llvmlite reuses the cached WebAssembly object while relinking and loading a fresh side module into the new kernel process.
In a demonstration example, Numba delivered a 249× speedup over standard Python in the browser, which the post rounds to 'roughly 250×'; the same kind of speedup measured natively, outside the browser, was about 90×. QuantStack attributes the larger relative gain in the browser to the fact that bypassing the Python interpreter's overhead matters more there. Numba is now packaged in emscripten-forge and can be installed into a running JupyterLite deployment with a Mamba install command typed into the browser terminal.
QuantStack frames the packaging step as unlocking more than one library. PyTensor can compile symbolic numerical graphs through its own Numba linker; PyMC, built on PyTensor, brings probabilistic modeling into the same browser environment; interpolation.py provides Numba-accelerated interpolation routines used in numerical economics; and Dolo.py builds on the stack to provide JIT-aware routines for economists, covering stochastic processes and decision rules, plus optimized solution methods for dynamic-programming problems. All four depend on Numba, so getting Numba working in the browser opens browser access to that whole chain.
The team describes the current release as an end-to-end architecture rather than a finished product: next steps include moving the work upstream into the Numba and llvmlite projects, expanding test coverage across both test suites, improving performance and caching, and validating more packages from the Numba ecosystem, none of it on a stated timeline. The post is bylined to Anutosh Bhat, a scientific software developer at QuantStack, an LLVM maintainer, and a co-author of the Xeus-Cpp Jupyter kernel, who led the WebAssembly integration work for llvmlite and Numba.
Key facts
- QuantStack shipped the first working version of the Numba JIT compiler running entirely in the browser, using JupyterLite together with its emscripten-forge WebAssembly package distribution.
- The team built a WebAssembly execution engine for llvmlite that runs LLVM's LLD linker in-process, since spawning a separate wasm-ld process is not possible inside a browser, reusing an architecture from QuantStack's earlier Xeus-Cpp/Clang-Repl work.
- In QuantStack's demo, Numba delivered a 249× speedup over standard Python in the browser, rounded to 'roughly 250×' in the post, versus about 90× for the same kind of speedup measured natively.
- Support for Numba in WebAssembly had been an open request since 2018, tracked in Numba issue numba/numba#3284 and separately in the Pyodide project's pyodide/pyodide-recipes#192.
- Numba's browser availability is meant to unlock dependent packages, including PyTensor, PyMC, interpolation.py, and Dolo.py, for use inside JupyterLite without a server.
Why it matters
This is the first time Numba, the just-in-time compiler that speeds up numerical Python code, has run entirely inside a browser instead of through a remote server. JupyterLite already lets a static website host a full Python notebook environment by running kernels locally through WebAssembly, which removes the need to provision a server for every user and makes notebooks cheaper to share for documentation, teaching, or interactive demos. Numba had been the missing piece of that browser-based scientific Python stack, and closing the gap had been requested since 2018. It required more than repackaging: Numba compiles Python functions into real machine code, and browsers deliberately forbid creating or modifying executable memory the way a normal machine does, so QuantStack had to build a new WebAssembly execution engine for llvmlite, the LLVM-binding library Numba depends on. The same engine also runs LLVM's LLD linker in-process and renders Graphviz control-flow graphs without a subprocess, which points at other LLVM- or JIT-based tools being able to follow the same route into the browser.
Who it affects
The direct audience is scientific Python users: researchers, students, and engineers who already use Jupyter notebooks for exploratory work, plus anyone who publishes notebooks as static, server-free material through JupyterLite for documentation or teaching. It also matters to maintainers of the packages built on top of Numba: PyTensor, PyMC, interpolation.py, and Dolo.py can now be evaluated for browser support instead of being excluded from WebAssembly builds by default. The Numba and Pyodide projects, which had both tracked the browser-compilation problem as an open request for years, are direct beneficiaries of the underlying engine.
How to use it
Numba is packaged for emscripten-forge, so it can be added to a running JupyterLite deployment with a Mamba install command typed into the browser terminal. The code looks the same as outside the browser: decorate an ordinary Python function with @jit or @njit, and Numba compiles it through its normal pipeline, from Python bytecode to Numba's own intermediate representation, to typed code, to LLVM IR, and finally to WebAssembly. Adding cache=True to the @njit decorator persists the compiled WebAssembly object in JupyterLite's own filesystem: in a later browser session, Numba restores the cached compilation data, and llvmlite reuses the WebAssembly object while relinking and loading a fresh side module into the new kernel process. QuantStack's post links to several live demos: a Numba and ecosystem notebook, a lower-level llvmlite-and-Graphviz notebook, and a combined walk-through covering Numba, PyTensor, PyMC, interpolation.py, and Dolo.py.
How solid is it
The claims come from QuantStack's own engineering blog post about its own project, and the post gives no hardware, browser, or workload details behind the 249× and 90× speedup figures, so they cannot be checked independently. QuantStack does have a track record with the underlying technique: it says the WebAssembly execution engine reuses an approach already shipping in Xeus-Cpp, its C++ interpreter for JupyterLite, work that is also the subject of a QuantStack talk at FOSDEM 2026. The post itself frames the Numba release as an end-to-end architecture rather than a finished, fully tested product, listing upstreaming, wider test coverage, and further package validation as still to come. On Hacker News, the story had drawn 29 points and 5 comments at last check, a modest discussion for a topic this specialized.
Risks and caveats
QuantStack frames this explicitly as a starting point: the post lists moving the work upstream into the Numba and llvmlite projects, expanding test coverage across both test suites, improving performance and caching, and validating more packages from the Numba ecosystem, all as open work with no committed dates. The one demonstrated speedup, roughly 250× in the browser versus about 90× natively, comes from a single example with no disclosed benchmark methodology, so it should not be read as a general result across workloads. The audience is inherently narrow: Numba, llvmlite, and the browser-compilation architecture behind this release are specialist tools for numerical and scientific Python, not something aimed at general developers, and getting value from it requires already working in that ecosystem.
“Using LLD in process is essential: spawning a wasm-ld subprocess is not an option inside the browser.”
— Anutosh Bhat, QuantStack