ARM64JS
ARM64js boots unmodified 64-bit Arm Linux in the visitor's browser. It can run shells, TUI apps, servers and even Docker images with no server behind it.
For example, below is barebones Alpine Linux emulated by ARM64js.
ARM64js is an ARMv8-A emulator that runs in a web browser via WebAssembly. It emulates the minimal hardware the virtual CPU needs to run:
The vCPU itself comes with user and kernel privilege levels, vector maths, and the newer atomic and AES / SHA / CRC instructions. In the browser, each vCPU runs in a separate worker. Up to 4 vCPUs are supported, but more is not always better: communication between vCPUs in a browser is expensive, so extra vCPUs only help multithreaded software that does a lot of waiting. A single app runs faster on one vCPU.
Docker and Docker Compose are supported but might be slow.
There is no good way to test server (or TUI) software without installing it, even though most of it is lightweight enough to run (at least as a demo) in a web browser. The initial idea came from frustration with TUI apps, the majority of which are lighter than a standard React website, yet you need to install them to see if there is a spark.
Below are a few example VMs showcasing what it can do.
| Alpine | Alpine with Python | Alpine with PHP |
|---|---|---|
| Micro | Midnight Commander | Helix |
|---|---|---|
| Docker running nginx |
|---|
Measured in Chrome, 1 vCPU, plain Alpine snapshot. Latency figures are medians of 120 key presses.
| Boot to a shell from a snapshot | 2.5 s on 50 Mbit/s, 1.3 s with no download |
| Typing lag in the shell | 8 ms |
| Typing lag in vim | 37 ms |
So it's fine for shells, TUI apps, scripts and small servers โ not for compilers or heavy compute.
The ARM64js SDK provides a convenient way to create, set up and manage your VMs. It's an npm package that encapsulates all the inner workings of the wasm build and provides a high-level interface for it:
import { Arm64JS } from 'arm64js';
const vm = await Arm64JS.boot('alpine'); // boot the base Alpine image
const { output, exitCode } = await vm.exec('ls -la'); // run `ls -la` in the home folder
console.log(output); // log the output
vm.dispose(); // stop the VM and free up resources.
You can find more information and the full SDK spec on its GitHub page.
ARM64js is the engine. Demoshell is the product built on it: you describe a VM with a build recipe, Demoshell bakes it into a snapshot, hosts it on a CDN and gives you a link, a README badge or an embed for your own site. The demos on this page are Demoshell embeds.
.wasm files and supporting JS) are licensed under PolyForm Strict 1.0.0. TL;DR: they may be used as is for non-commercial purposes โ research, testing, education and personal use โ without sharing, modifying or redistributing them. See the full license text for the complete terms.A VM powered by ARM64js runs Alpine Linux on musl with an optimised (patched) apk and some kernel tweaks to make it play nicely in a WebAssembly-based setup.
It is not possible to make HTTP(S) requests without CORS from a web page, so apk cannot work out of the box. To overcome this, apk goes via a proxy (apk.arm64js.com) which pretends to be an apk mirror while actually just proxying requests to dl-cdn.alpinelinux.org and adding CORS headers.
A browser tab cannot open TCP connections, so when the guest Linux needs to send TCP to anything on the internet, something outside the browser has to provide the real sockets. That's what the relay does:
The relay cannot read HTTPS. TLS is done by the guest and terminated at the real server; the relay only sees encrypted bytes going by, not the data itself.
Note: using the relay requires a demoshell.com account, to avoid it being used as an anonymous proxy and to be able to limit the traffic.
At any point in its lifecycle the VM can be snapshotted, which means it's possible to do a setup (install software, change the Linux config, etc.) and then snapshot the result. A snapshot includes:
Loading from a snapshot is quick: assuming the size of the VM hasn't grown much, a snapshot loads as quickly as the original guest OS (which is technically also a snapshot).
A VM starts from a base image, for example plain Alpine or Alpine with PHP. See all base images and their versions.
You can find more examples of TUI and server apps running in the ARM64js VM in the Demoshell Gallery.
How fast is it? Fast enough for a shell or a TUI app to feel local: typing lag is 8 ms at the shell prompt and 37 ms in vim. A VM boots to a shell from a snapshot in about 2.5 s on a 50 Mbit/s connection (see Performance). Raw CPU throughput is of course lower than native: ARM64js is an interpreter, so pure compute runs at a small fraction of native speed. That is fine for shells, TUI apps, scripting languages, package installs and small servers, which spend most of their time waiting for you. It is not the right tool for compilers, heavy number crunching or big JVM stacks.
Why arm64 and not x86?
Because the modern image ecosystem is arm64-first (Apple Silicon, Graviton, Raspberry Pi), and an ARMv8-A machine is far simpler to emulate faithfully than x86: fewer instruction encodings, a cleaner MMU and a standard interrupt controller, which also means better emulation performance. Most software already ships arm64 builds, and apk, pip, npm and Docker Hub all serve them.
Can I run my own Docker image?
Docker runs inside the VM, so docker pull and docker run work for arm64 images (see the nginx demo). Doing it live is slow because the whole pull runs on the emulated CPU, but pulling it once and then storing the snapshot would work, it would take some time to build it but then once loaded snapshot resumes from already saved state. This is also what Demoshell does.
Publishing your own base images is coming, but at the moment the SDK boots only images from arm64js.com/images or your own local snapshots.
Is it open source? The SDK is MIT. The emulator builds are free for non-commercial use under PolyForm Strict. Commercial licences are available โ reach out to info[at]arm64js.com.
Does it need a server? No. The VM runs entirely in the visitor's browser. The only things fetched are the wasm build and the image chunks from the CDN when the VM is loaded.
Two optional services exist:
apk add works despite CORS): publicly available.Why does the relay need an account? Because a relay that opens arbitrary TCP sockets on request is an anonymous proxy, so it needs an account for us to be able to rate-limit and shut off abuse. The relay only forwards encrypted bytes โ TLS is terminated inside the guest.
Which browsers work?
The VM needs SharedArrayBuffer, which browsers only grant to cross-origin-isolated pages. If your page sends the COOP/COEP headers, the VM runs inline in all modern browsers. Without the headers, the SDK falls back to a hidden frame on the ARM64js CDN, which works in Chrome and Edge 137+. See the SDK README for details.
Does it work on a phone? Yes, if it has sufficient memory. A VM uses up to 1 GiB of guest RAM, so mobile browsers work but may evict the tab under memory pressure.
Is it safe to run untrusted code in it?
The guest runs inside the browser's WebAssembly sandbox and has no access to the host beyond what the page gives it (files you mount or writeFile, and the network via the relay if enabled). It inherits the browser's security boundary.
How does it compare to WebVM / CheerpX, v86, container2wasm, WebContainers and BrowserPod? CheerpX (WebVM) and v86 emulate 32-bit x86. container2wasm converts containers via Bochs/TinyEMU and is experimental. WebContainers and BrowserPod are not emulators: they port specific runtimes (Node.js; Node, Rust, Python) to wasm, so they are much faster on those runtimes but cannot run arbitrary Linux binaries or Docker images. ARM64js runs unmodified 64-bit Arm Linux, including Docker, at interpreter speed.
What happens to a visitor's data? Everything stays in their tab. Snapshots the SDK makes are stored in the browser's origin-private file system on their machine. Nothing is uploaded unless your page uploads it.
Can the VM talk to my page?
Yes: vm.exec runs commands and returns their output, writeFile/readFile/mount move data in and out, and onOutput streams the console. Exposing a port the guest serves on to the page is on the roadmap.
Blog ยท ARM64js on X
Copyright 2026