Python on the Zipp VM
Zipp's Python frontend is its own implementation of Python 3, validated on 13 September 2026. Python source is parsed with the RustPython parser and lowered by Zipp's compiler straight to register bytecode, so Python and JavaScript run on one virtual machine, natively or in the browser.
How Python reaches the VM
There is no second interpreter and no transpilation to JavaScript source. The vendored RustPython parser produces an AST; Zipp's own compiler (symtable.rs for CPython scoping rules, emitter.rs for code generation) lowers it to the same FuncProto bytecode the JavaScript compiler produces. Python's object model lives in a fixed JavaScript-shaped runtime that the VM compiles once per program, and guest Python never sees the host's JavaScript globals.
Every Python code object has a fixed ABI: register 0 holds the function object and register 1 the array of bound positional values that the runtime's bind produced. Locals are registers, captured locals are cells, and module globals are a Map. A Python call is a direct VM Call, so recursion lives on the VM's explicit frame stack and ends in a catchable RecursionError.
zipp py main.py # one file
zipp py ./project # a folder whose entry is main.py
zipp py lab/train.py --steps 20 # a script inside a folder, with arguments
zipp run --lang=python - # read standard inputWhat is supported
The frontend is validated differentially: the programs in tests/python_corpus run under CPython and under Zipp, and standard output must match byte for byte. The recorded CPython outputs are committed, so continuous integration needs no CPython.
- Arbitrary-precision
int,float,bool, the full operator set, and the fullstrmethod set with%,formatand f-strings. list,tuple,dict,set,frozenset,range, slices and comprehensions.- Every function signature form, closures with
nonlocalandglobal, decorators, and generators withsend,throw,closeandyield from. - Classes with C3 multiple inheritance,
super(), properties, descriptors,__slots__, metaclasses,__init_subclass__and__set_name__. - The builtin exception hierarchy with
raise ... from, chained__cause__and__context__, and CPython-style tracebacks. - Modules and packages by folder with CPython-like import cycles, full
matchpattern support, anddataclasses,enum,typing,itertools,functools,collections,json,re,struct,hashlib,pathlib,argparseand more from the standard library.
A project folder is loaded into a virtual filesystem (8 MiB per file, 64 MiB total) that open(), os.path and pathlib see. Files the program writes are reported back to the host; the CLI copies them to disk while the browser only reports them.
Performance, honestly
Python on Zipp is currently an interpreter over a JavaScript-shaped VM with the JIT disabled, and integers are BigInts. On a release build fib(25) takes about 0.09 s, a million-iteration total += i % 7 loop about 0.5 s and 200,000 dict insertions with string keys about 0.7 s; CPython does the same in 0.005 s, 0.05 s and 0.03 s. Dedicated Python bytecodes are the documented next step. There are no published Python benchmark tables, and this page will not invent any.
Python in the browser
The all WebAssembly build variant includes the Python frontend: 7,773,932 bytes raw, 1,777,382 bytes after Brotli, about 43% more than the JavaScript-only module. A browser host calls engine.initSource(source, "python") for one file or engine.initPythonProject(files, entry, argv) for a folder, then drains output and host requests.
import init, { Engine } from './zipp_wasm.js'
await init()
const engine = new Engine()
engine.initSource('print(sum(range(10)))', 'python')
console.log(engine.takeOutput()) // 45
engine.dispose()The playground runs exactly this: load a folder of .py files from disk into the browser's virtual filesystem, pick an entry, run, and watch a canvas. Files never leave the browser.
GPU compute from Python
The bundled zipp_gpu module records float32 compute graphs (add, sub, mul, ReLU, transpose, matmul, sum and a toroidal Game of Life step) and submits them to the host. The graph leaves the engine as a gpu.execute request; the host validates it and runs it on WebGPU, WebGL2, compiled WASM kernels or a JavaScript reference backend, then delivers the named outputs to a Python callback. Nothing inside the engine touches a GPU.

The GPU page covers the graph protocol, backends, limits and the experimental torch.compile path.
The Torch subset
Zipp bundles an experimental Python implementation of a Torch API subset. It is not the native PyTorch package, TorchInductor, CUDA or a pip environment. Eager CPU support is broad: tensors over typed-array storage, creation ops, indexing, shape ops, broadcasting elementwise and reduction ops, matmul and einsum, reverse-mode autograd, nn modules including Linear, Embedding, Conv1d, Conv2d, GRUCell, LSTMCell and LayerNorm, common losses, optim.SGD, Adam, AdamW and RMSprop, and torch.save / torch.load in PyTorch's zip checkpoint format.
The training example reproduced native PyTorch's ten rounded losses (0.085254 down to 0.010812), and the Life rules matched an independent PyTorch implementation across 1×1, 2×2, 7×7 and 96×96 grids. Those are correctness checks, not performance claims, and device="cuda" is rejected rather than silently ignored.
Calling JavaScript from Python
With the opt-in python-js-interop feature, import javascript; javascript.eval(source) runs JavaScript in the same VM instance, never in the browser's engine. Data is copied across, not proxied: numbers, strings, booleans, BigInts, lists and plain dicts convert, with None mapping to null. Cycles, accessors, functions, symbols and non-plain objects are rejected, conversion is capped at 10,000 values and depth 32, and source at 32,768 UTF-16 code units.