Run Python ML Models in the Browser with Pyodide: A Complete Guide
This article explains how to use Pyodide—a WebAssembly‑based Python runtime—to run complex machine‑learning model pre‑ and post‑processing directly in the browser, covering memory layout, JavaScript‑Python interoperation, package loading, OpenCV usage, inference engines, persistent storage, and wheel packaging.
Background
With the growing use of machine learning, many JavaScript frameworks can run inference in the browser. Front‑end developers often want to reuse Python models that normally run on the server, but translating complex pre‑ and post‑processing code to JavaScript is time‑consuming.
Pyodide, a WebAssembly‑based scientific‑computing framework, allows native Python code (including numpy, scipy, etc.) to run directly in the browser, eliminating the need for manual translation.
Principle
Pyodide is a WebAssembly application built from CPython using Emscripten. It bundles many scientific‑computing PyPI packages as WASM modules, enabling Python statements to be executed in the browser. The architecture is shown below.
1 WASM memory layout
This is the linear memory layout of WASM. The data segment starts at 0x400, the function table is also placed there, and the stack and heap have defined start addresses. The key point for developers is the mutual access between JavaScript memory and WASM memory.
2 JavaScript and Python inter‑operation
For security, WASM runs in a sandbox and cannot access JavaScript memory, while JavaScript can access WASM memory (a "one‑way" memory access). Pyodide introduces PyProxy and JsProxy objects that act like pointers, allowing each side to reference objects on the other side.
Creating a PyProxy in JavaScript:
const arr_pyproxy = pyodide.globals.get('arr'); // 'arr' is a global Python objectCreating a JsProxy in Python:
import js
from js import foo # 'foo' is a global JavaScript objectType conversion occurs at three levels:
Automatic conversion : simple types (numbers, strings, booleans) are copied directly.
Semi‑automatic conversion : built‑in containers (list, dict, numpy.ndarray) require explicit pyodide.to_js() or to_py() calls.
Manual conversion : custom classes or functions are accessed via proxy objects; operations are performed through simulated syntax (e.g., let a = new XXX() in JS becomes a = XXX.new() in Python).
Practice
1 Initialize Python
Load required JavaScript libraries dynamically (opencv.js, onnxruntime.js, pyodide.js), then initialize Pyodide and install micropip:
function loadJS(url, callback) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.onload = function() { callback && callback(); };
script.src = url;
document.getElementsByTagName('head')[0].appendChild(script);
}
// Load opencv.js, onnxruntime.js, pyodide.js …
loadJS('https://.../opencv.js', () => console.log('js load ok'));
loadJS('https://.../onnx.min.js', () => console.log('js load ok'));
loadJS('https://.../pyodide.js', () => console.log('js load ok'));
pyodide = await loadPyodide({ indexURL: 'https://.../pyodide/0.18.0/' });
await pyodide.loadPackage(['micropip']);2 Load PyPI packages
Standard library modules are available by default. Third‑party pure‑Python packages can be installed with micropip.install(). Packages with C extensions require pre‑compiled wheels listed in the official Pyodide package repository; otherwise they must be compiled manually.
3 Using OpenCV
Because the official Pyodide build does not include OpenCV, the article demonstrates calling opencv.js from Python via proxies. An example creates a synthetic 1080p image, resizes it with cv2.resize, and converts the result back to a NumPy array.
await pyodide.runPythonAsync(`
# Create a 1080p image
h, w = 1080, 1920
img = np.arange(h * w * 3, dtype=np.uint8).reshape(h, w, 3)
# Resize using opencv.js
h_small, w_small = 108, 192
mat = cv2.matFromArray(h, w, cv2.CV_8UC3, pyodide.to_js(img.reshape(h * w * 3)))
dst = cv2.Mat.new(h_small, w_small, cv2.CV_8UC3)
cv2.resize(mat, dst, cv2.Size.new(w_small, h_small), 0, 0, cv2.INTER_NEAREST)
small_img = np.asarray(dst.data.to_py()).reshape(h_small, w_small, 3)
`);4 Inference engine
Load an ONNX model with onnxruntime.js and run inference from Python:
await pyodide.runPythonAsync(`
model_url = "<model_url>"
session = onnxruntime.InferenceSession.new()
session.loadModel(model_url)
session.run(...)
`);5 Mount persistent filesystem
Pyodide provides a persistent IDBFS filesystem. Create a mount point, write files, and sync to storage so data survives page reloads.
// Create mount point
pyodide.FS.mkdir('/mnt');
// Mount IDBFS
pyodide.FS.mount(pyodide.FS.filesystems.IDBFS, {}, '/mnt');
// Write a file
pyodide.FS.writeFile('/mnt/test.txt', 'hello world');
// Persist changes
pyodide.FS.syncfs(function(err) { console.log(err); });6 Build a wheel package
When a project consists of multiple Python files, package them into a wheel and install via micropip.install(). The wheel must contain an __init__.py file.
micropip.install("https://foo.com/bar-1.2.3-xxx.whl")
from bar import ...Existing Limitations
Python runtime initialization can take several seconds depending on network conditions.
Not all PyPI packages are available; C‑extension packages often need manual compilation.
Common libraries such as OpenCV are not pre‑compiled for Pyodide.
Calling JavaScript libraries from Python may incur memory copy overhead, especially for large arrays.
API mismatches between Python and JavaScript libraries may require additional adaptation.
Conclusion
Despite these drawbacks, Pyodide offers a highly efficient way to port complex machine‑learning models to the browser with near‑one‑to‑one code fidelity, making it suitable for many business scenarios and accelerating the adoption of ML technologies on the front‑end.
Useful links:
Test page: https://test-bucket-duplicate.oss-cn-hangzhou.aliyuncs.com/public/pyodide/test.html
Documentation: https://pyodide.org/en/stable/usage/type-conversions.html
Official compiled packages list: https://github.com/pyodide/pyodide/tree/main/packages
Filesystem manual: https://emscripten.org/docs/api_reference/Filesystem-API.html
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Alibaba Cloud Developer
Alibaba's official tech channel, featuring all of its technology innovations.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
