Skip to main content

What Is Browser-Native Scientific Computing?

· 20 min read

Black Hole Fig above: Computation of a blackhole approach trajectory using EinsteinPy. This trajectory was computed fully in-browser relying on community Python code, demonstrating the power of WebAssembly for easily exploring advanced concepts or calculations.

TL;DR: Browser-native scientific computing means a full compute environment — Python, R, C++, or any language that compiles to WebAssembly — running inside your browser. It arrives as simple static files: no backend, no installation, no setup required. An entire environment can be handed over as a URL, and is highly accessible, secure, and reproducible because WebAssembly, a standardised web technology, was made for safe sharing across devices and time. Since compute happens on the user's own machine, serving one user or a hundred thousand costs about the same, opening up new possibilities — giving nationwide access to code for students, public data portals with embedded compute environments, runnable documentation, and executable blog posts or papers. The technology is used in production and available today.

Introduction

There is a moment almost everyone who has touched code has lived through. Instructions arrive to run an analysis, a tutorial, or a classroom exercise. Following them consumes the next two hours in installing, configuring, and debugging an environment that works perfectly on someone else's machine — all while npm install, pip install, or conda install quietly pulls in hundreds of random packages that now have reach into local files and private data.

In this post we'll talk about a new paradigm that avoids this situation: sharing a complete compute environment — Python, R, C++, and more — entirely inside a web browser, with no installation, no server, no configuration, and strict isolation. In this paradigm you click a link and you have your project set and ready to execute. That's it.

This article explains this new paradigm, how it works, where it fits, and where it doesn't.

The incumbent model: OS-native compute

Today, most scientific compute software, even when written in interpreted languages like Python, boils down to using compiled code. More specifically it is compiled from source code directly into machine instructions for a specific CPU and operating system (OS). While the CPU executes those instructions, memory management, I/O, network access, threading, process lifetime, and more all pass through the operating system. And OSs give code broad power, direct access to the file system, hardware, and network, and generous freedom for the compiler to use liberal memory layouts and apply aggressive optimisations. Their design, from a pre-internet era, largely assumes that responsible users install trusted software, which justifies few restrictions on optimisations or access to machine resources — for the sake of maximum performance and capability. This model — call it OS-native — has powered scientific applications for decades.

Unfortunately, the very properties that make OS-native so fast and powerful also make it inherently unsuited to safe, robust, and easy sharing. Tight coupling to the host machine makes OS-native deeply integrated and frictionless, but ties every program to the specific system it was built on — a different library version, path, or configuration on another machine and pieces break. Flexible memory layouts and liberal control flow permit aggressive performance optimisations, but also open the door to malicious exploits. And the common default of granting unknown, untrusted code full access to user documents, the local network, and other resources may be a convenience on a single trusted machine, but becomes a liability when sharing code in a wider setting. Symptoms of all this include the reproducibility crisis and the recent proliferation of supply chain attacks.

The browser, on the other hand, was designed from the start for safe, massive sharing, and for the distributed execution of untrusted code across devices and time (see e.g. Security Architecture of the Chromium Browser). It imposes safety and compatibility restrictions that, in massive return, yield the power of the internet: publish once, reach anyone on any device without prior installation, heavy centralised infrastructure or trust.

Scientific computing has approached this idea repeatedly. Attempts running compute on the user's machine inside the browser include Java applets, Flash, Google's Native Client, asm.js and more — reaching large audiences through projects like PhET and GeoGebra. But most delivered only narrow slices: click-and-slide simulations, canned demos, or small language subsets. And many of them were non-standardized bolt-on technologies, causing the usual compatibility issues, rather than something integrated in the browser from the ground up. What has never quite arrived is the complete picture — arbitrary user code, in any language, running on the user's own machine, standardized in any browser without a plugin.

Another popular approach, classic Project Jupyter (as opposed to JupyterLite, which we will talk about later), delivers arbitrary code execution in any language, but leaves the compute off the user's machine on a remote server. Users can access a preconfigured compute environment from any device, with no local install to manage. The remote compute environment is what the kernel provider preinstalled; the setup burden hasn't gone away, it has just shifted to whoever runs the service. Compute still runs on someone else's OS-native machine and the model therefore can't deliver the full power of web-like sharing: one remote VM or container per active user, including capacity for peaks, means heavy load at scale on whoever runs the service. Necessary access controls for such remote resources mean no free sharing. The OS-native reproducibility and dependency-fragility problems from earlier still exist, just hidden behind a server. And user data has to leave the user's machine to be operated on. Cloud services like Google Colab or Replit shift the operational burden to a third party — lowering costs at scale — but bring their own trade-offs around data locality, customisation, integration with internal systems, and cost.

What has arrived is that complete picture — the piece neither of these approaches quite reached: arbitrary user code, in any language, running on the user's own machine, standardized in any browser without a plugin. Sharing and using entire compute environments becomes as easy as opening a webpage: the environment is a static file that any web server can deliver, and serving one user or a hundred thousand costs about the same thanks to safe, distributed execution.

The very properties that make OS-native software powerful also make it inherently unsuited to safe, robust, and easy sharing.

The new paradigm: browser-native compute

Browser-native adds a layer between source and OS-native instructions: WebAssembly. The source is first compiled to a .wasm binary, delivered to the browser as a static file, and compiled by the browser's WebAssembly engine into native machine code (V8 in Chrome, SpiderMonkey in Firefox, JavaScriptCore in Safari). The compiled native code runs directly on the CPU, the way any OS-native program does except within the browser process and potentially calling a light WASM runtime at boundaries (imports, host calls, memory growth) in the same way an OS handles syscalls in an OS-native model. Safety comes from validation against the spec before execution, from boundary checks that are compiled into the emitted native code, and from the browser's own process sandbox surrounding all that.

Such intermediate layers, portable bytecodes and in-browser VMs aren't new. What is new is WASM's low level, lightweight, sandboxed and highly standardized approach contrasting strongly with VMs focusing on OS-native or featureful language execution (JVM, JavaScript, ...). It also contrasts with Java applets, Flash, and Native Client that are browser-native in intent, but vendor-specific plugins often built for narrow use cases. WebAssembly itself was designed based on experience with using JavaScript and its engine directly as intermediate layer which hit limitations on parse time for large binaries, file size, cross-browser standardization, the unpredictability of performance and more. Co-designed by all major browser vendors, WASM replaced this workaround with an intermediate layer that is appropriately designed for the use case of performant, safe in-browser code execution — compact binary, structured control flow, linear memory. This design means fast and easy compilation to safe native machine code that runs with minimal runtime overhead, and execution close in shape to what an OS-native compiler would have produced from the same source. For a longer primer, Jakob Meier's Wasm in the Wild series is a good starting point.

Execution through the browser thus enables what operating systems were not built for: running arbitrary code from any origin, safely and identically, on any device. Two consequences follow for scientific compute. Safety: a .wasm can touch nothing beyond what the surrounding page hands it, so running code delivered by a stranger is genuinely safe. Reproducibility: it cannot depend on host state (environment variables, system libraries, ambient configuration) that varies from device to device, and every browser runs the same spec, so the same URL behaves the same on a laptop, a Chromebook, or a phone. Together these enable sharing and instant accessibility at scale.

On top of this foundation, over the last several years the scientific computing community has compiled a large fraction of the scientific software stack to WebAssembly. The effort was pioneered by Pyodide, which started at Mozilla in 2018 by porting CPython and a core set of scientific packages to run in the browser. Since then it has grown to cover the wider Python ecosystem (NumPy, SciPy, pandas, scikit-learn, matplotlib, and hundreds more), the R interpreter, and many C, C++, and Fortran libraries that sit underneath them — today all available for in-browser execution.

Practically, the browser-native paradigm means provisioning a compute environment for 40 users or 40,000 is essentially the same (not considering integrations, persistent storage or related permissions as encountered in more ambitious use cases). Operation and infrastructure costs drop, large-scale use becomes feasible. From the user perspective, access is reduced to a single click — no installation, no waiting for a container to spin up. Any device with a modern browser is a viable target, including mobile (although you may hit memory limits, particularly on iPhone), tablets, and locked-down corporate laptops, because everything the environment needs is safely delivered through the browser.

That this works can be seen in large production deployments leveraging this paradigm to serve hundreds of thousands of users today. Capytale, run by the French Ministry of Education, serves hundreds of thousands of students and tens of thousands of teachers on a very small server footprint, because the servers only deliver static assets while the compute happens in students' browsers. try.jupyter.org offers a public REPL powered by JupyterLite that is used regularly by learners as access patterns show.

Serving one user or a hundred thousand costs about the same — because the compute happens on the user's own machine.

Capabilities and limits

Two natural questions come up: what can a browser-native environment actually do, and how fast does it run. Both benefit from separating what is structural about the paradigm — and will not change — from what is a moving engineering frontier, likely to look different in a year.

Start with what a WebAssembly module can access. In the browser, it can affect the outside world only through capabilities the host provides. In practice that means the standard capabilities any website has: network access through fetch (subject to same-origin rules), persistent storage through the Origin Private File System (a per-site sandbox the browser manages, not general filesystem access), and hardware — camera, microphone, USB, GPU — as well as local network through the same permission-gated APIs any web page uses. There is no direct filesystem access, no arbitrary sockets, no shelling out to system tools. This is structural; it is exactly what makes the sandbox trustworthy, and enables sharing in the first place. It is not going away.

Performance in practice. Once loaded, WebAssembly runs as real native machine code — no interpreter loop between your code and the CPU for calculations that don't involve boundaries. In V8, for example, a baseline compiler (Liftoff) emits machine code in a single pass for fast startup, and a background optimising compiler (TurboFan) patches in higher-quality code for functions that stay hot. At the boundaries, every memory access may add a cheap bounds check if it can't be optimized away (predictable loops) or handled by hardware (on 64 bit machines), and operations like memory growth pass through the WASM engine. These checks are cheap on modern engines but not zero. Beyond that, a small number of compiler optimisations are permanently excluded because they would be unsafe on untrusted code from the open web (structural); others are simply not yet in the WebAssembly specification or not leveraged by WASM-specific build recipes or code paths but can be expected to arrive gradually; and finally some functions hit hard barriers, for example on multithreading that can greatly affect them.

Considering this, many functions from compiled numerical libraries (NumPy, SciPy, matplotlib, and the C/Fortran underneath) run near-native, with benchmarks on numerical kernels showing on average mid 2-digit percent overheads versus native but sometimes up to 2-2.5x (Jangda et al.'s Not So Fast quantified the gap on SPEC CPU and traced most of it to bounds-checking and register pressure — a reasonable ceiling for any WASM stack), and occasionally even matching or exceeding native when the native build uses a less-optimised backend. For interactive small-data analysis, visualisation, and exploratory work this overhead is often imperceptible and less than the overhead of using shared remote resources. However, occasionally a function hits a hard restriction, is not yet optimized well for the WASM context, or hits real structural limitations such as multithreading (see below). Such situations don't happen randomly, though — they can be identified in advance and potentially remediated. Pure Python code — loops, string manipulation, interpreter-level logic — pays a compounded overhead because CPython itself is running inside WebAssembly; the gap is typically a few-fold (2 - 5x) but has been narrowing (e.g. from Pyodide's own benchmarks).

The structural limits are important to keep in mind: sandboxed execution with no direct syscalls, hardware or OS access, and the excluded-for-safety subset of aggressive compiler optimisations. Other limits are engineering frontiers being actively pushed and are likely to evolve:

  • Memory: the 4 GB linear-memory ceiling per module comes from WebAssembly's 32-bit address space; the Memory64 WASM spec proposal removes it and is landing in engines and toolchains. Meaningfully larger single-module datasets are close. Phones may impose tighter runtime limits, whereas modern Android phones can manage a full JupyterLite + scientific Python environment quite well, iPhones seem to be more restrictive on memory (~1 GB), which is a blocker for full mobile access.
  • Threading: shared-memory multi-threading requires COOP/COEP cross-origin isolation headers (a safety mechanism), which not every deployment can set — but even with those in place, making scientific runtimes actually use threads efficiently in the browser remains a genuinely hard engineering problem. Browser-native compute is effectively single-threaded in practice today, and progress here is real but incremental.
  • GPU: WebGPU provides browser-standard access to GPU compute and is now available across major browsers — but integrating it into the scientific WASM stack, so that NumPy-style code or ML frameworks transparently accelerate on the GPU, is a genuinely hard engineering problem still in its early stages. Direct low-level GPU access from WASM works today; drop-in GPU-accelerated scientific libraries at desktop maturity do not.
  • Package coverage: while many packages are available, and pure python or noarch conda packages often run out of the box, there is still a large amount of packages to port so that they are available at all in WASM. And even then, subsets of their functionality may not be immediately available due to the other limitations mentioned above. Right now every use case requires evaluation but the ecosystem has reached critical mass where porting is no longer a daunting task in many cases. Emscripten-Forge alone is at several hundred libraries and expanding.

For a scientist or teacher deciding whether this fits their work: browser-native compute is already the right default for many interactive, exploratory, and educational workloads on small-to-medium datasets. It is not a replacement for OS-native compute if you need full freedom and all the power your machine can provide. Equally it is not a replacement for HPC clusters, GPU training servers, or large-scale distributed data processing — those workloads belong on remote infrastructure by nature. But browser-native environments can offer two complementary roles: acting as an access console to powerful remote machines (through lightweight protocols like Spark Connect), and as an accessible local environment for exploring or visualising predigested data returned from those backends — heavy computation remote, interactive analysis in the browser.

The open source landscape for browser-native compute

Several complementary open source projects make browser-native scientific computing possible — the tour below is representative, not exhaustive: additional xeus kernels and various runtime shims sit alongside these but are outside our scope here. A quick disclosure before the tour: Notebook.link (the product this post is written for) is built on JupyterLite, Emscripten-Forge, and mambajs, all open source under permissive licenses and originated or co-maintained by QuantStack alongside a broader community. What follows aims to map the wider landscape fairly, including projects QuantStack does not contribute to.

JupyterLite is a version of JupyterLab that runs kernels entirely in the browser. It provides the familiar notebook interface — cells, outputs, markdown, visualisations — without any server process. It was created at QuantStack and is now maintained by a community of contributors from across the Jupyter ecosystem.

Pyodide is a port of CPython to WebAssembly, along with a foreign function interface that lets Python call JavaScript and vice versa. It ships a curated set of scientific Python packages and provides an in-browser package installer (micropip) that can pull Python wheels directly from PyPI, including compiled Emscripten wheels now that PyPI accepts the WebAssembly platform tag (PEP 738) — so from the user's perspective it feels much like pip install. Pyodide executes Python code inside the browser in many JupyterLite deployments.

WebR is the R counterpart to Pyodide — a port of the R interpreter to WebAssembly, developed by the r-wasm project (sponsored by Posit), with support for installing many CRAN packages directly in the browser.

PyScript, built by Anaconda on top of Pyodide (with optional MicroPython for lighter-weight cases), exposes Python directly in HTML pages via <py-script> tags. Its focus is embedding Python in web pages, dashboards, and interactive demos rather than notebook workflows — a different authoring surface on the same underlying WebAssembly stack.

xeus family (QuantStack) is a C++ implementation of the Jupyter kernel protocol, plus a set of kernels built on it: xeus-python, xeus-r, xeus-cpp, xeus-lua, and others. These kernels wrap the respective language interpreters with a standardized communication interface, including what a notebook needs beyond a REPL — rich outputs (plots, tables, HTML), execution state, tab completion. Because xeus is compact C++ rather than Python, its kernels compile to WebAssembly and run inside JupyterLite via the jupyterlite-xeus extension, making the notebook UI genuinely multi-language. xeus kernels use language-agnostic Emscripten-Forge packaging.

Emscripten-Forge (originated at QuantStack) is a conda-style multilingual package ecosystem for WebAssembly, with several hundred scientific libraries and growing. Python packages, R packages, C/C++ libraries, Fortran, and standalone binaries fit the same model, which is how xeus-r, xeus-cpp, and xeus-lua get their dependencies. Its dependency solver, mambajs (also QuantStack), runs entirely in the browser, letting users find and install packages cross-language at runtime with no server call. Emscripten-Forge also ships pyjs, a low-level Python ↔ JavaScript foreign function interface for Emscripten-compiled Python — built on pybind11 (Python/C++ bindings) and Emscripten Embind (C++/JavaScript bindings). xeus-python uses it as its FFI (the same role Pyodide's built-in ffi plays on the Pyodide side), but it can also be used directly from a webpage that embeds Emscripten-compiled Python.

To put this in one picture: JupyterLite provides the UI, can be used for the familiar lab or notebook interfaces but is modular enough to be repurposed for other applications, like the REPL on try.jupyter.org; Pyodide and WebR provide monolithic Python and R runtimes with their own package paths and ecosystems (e.g. PyScript). Emscripten-Forge — with xeus kernels, mambajs, and pyjs alongside — provides a wider, multi-language, less monolithic experience.

The stack above is the open in-browser scientific compute foundation. But running it at team, classroom, or enterprise scale usually needs an organisational layer on top: identity, storage, permissions, environment management, hosting, and integrations with the wider systems people already use. Notebook.link — the product this post is written for, built by QuantStack — is a proprietary platform that supplies exactly that. It adds single sign-on and access controls, user and group permission management, versioned compute environments with per-user persistent storage, hosted public environments shareable as a URL in different forms, AI integration inside the notebook experience, autograding and LMS integration (Moodle and others) for teaching workflows, and seamless switching to remote backend kernels — the classic Jupyter server model, reached through the same interface — when a workload needs GPU or larger hardware. It is available as a managed cloud service or as a self-hosted deployment on the customer's own infrastructure, for teams that need data and compute to stay in-house.

A row of examples from the notebook.link gallery — Bayesian inference, orbital mechanics, phylogenetic trees, Conway&#39;s Game of Life, mixed-integer optimization and much more Fig above: A few examples from the notebook.link gallery — Bayesian inference, orbital mechanics, phylogenetic trees, Conway's Game of Life, and mixed-integer optimization, all written, shared and running fully in the browser. Try them yourself!

Where it fits

Today, browser-native compute is often the right default for teaching and workshops, runnable documentation, interactive blog posts or tutorials, sharing quick analyses with a colleague, and exploratory data analysis on small-to-medium datasets. Community libraries increasingly attach JupyterLite or notebook.link badges to their own documentation example galleries as well, letting readers execute code directly next to it.

For workloads that genuinely need the full spectrum of what is possible on one's own machine or remote hardware — large simulations, model training, multi-core parallelism, GPU acceleration — browser-native compute alone is not the right tool. But the two models are complementary rather than competing: browser-native environments can serve as a lightweight front door to backend compute when heavier work is needed, and platforms like notebook.link can seamlessly switch between WASM and backend kernels as needed.


Notebook.link is a scientific computing platform with multilingual browser-native support built by QuantStack, contributors to JupyterLab, Mamba, JupyterLite, and Emscripten-Forge. Try it at notebook.link.