Node.js · npm · tsc · ESLint · Prettier — in a tab

Run Node.js and your dev tools
in the browser.

NanoVM runs the real, unmodified Node.js v25 binary — plus npm, TypeScript, ESLint and Prettier — entirely client-side. No server, no container, no install. An open-source alternative to WebContainers — and Node is just one of four runners on the platform; the wasm and sandboxed-JS demos run live below.

real Node v25.4.0·2.3 MB core·100% client-side·open source
Run it now

Real Node, running in this tab.

Pick a demo and hit run — the unmodified Node.js v25 binary executes it in the real terminal below, instruction by instruction, with npm packages, an HTTP server and a React build all served from inside the VM.

booting nanovm…
A different tier

The wasm runner: image processing with Photon.

Node runs on the RISC-V VM; photon — a Rust image CLI compiled to wasm32-wasip1 — runs on the wasm runner, a faster, capability-scoped tier on the host wasm engine. Same kernel, same shared filesystem. Pick a filter; it decodes, transforms and re-encodes the PNG inside the tab.

booting nanovm…
Sandboxed JavaScript

The Boa runner: scripting with only the powers you grant.

The Boa runner is a Rust→wasm JavaScript interpreter that drives the VM with no guest process and no ambient authority — a fresh engine can touch nothing until you expose a capability. This script is granted read-only files and run, nothing else.

booting nanovm…
One kernel, many engines

Cross-tier: Node on the host engine, BusyBox on the RISC-V VM, one filesystem.

The kernel routes node to the fast host JS engine (its native arch), while the RISC-V VM runs a Linux userland — the same platform, two execution tiers, chosen per program by the router.

booting nanovm…
Not a reimplementation

Other in-browser runtimes ship a reimplemented Node. NanoVM runs the actual Node.js executable — the same binary you’d install on a Linux server — instruction by instruction on an emulated RISC-V CPU. If it works on Node, it works here: the same APIs, the same versions, the same edge cases.

The toolchain

Your everyday dev tools, unmodified.

Node.js
v25.4.0
The real upstream runtime — require, fs, crypto, http, streams, Buffer, EventEmitter and async/await.
npm
v11.18.0
Install and run packages; dependencies are pulled over HTTPS through the host-fetch bridge.
TypeScript
v5.9.3
tsc type-checks and compiles your project, completely unmodified, inside the VM.
ESLint
v10.0.0
Lint real codebases with the actual ESLint binary and your own configuration.
Prettier
v3.8.1
Format files with the real Prettier — byte-for-byte the same output as your terminal.
BusyBox + catalog
fd · ripgrep · …
Coreutils out of the box, plus extra Linux tools installed on demand from the signed catalog.
How it works

Four lines, from boot to output.

01
Boot the VM
createNano() loads the RISC-V VM wasm (a BusyBox shell built in), then installs the real Node binary on demand from the signed catalog. No server, no container — static assets plus the catalog CDN.
02
Write your files
Seed the in-memory filesystem with your sources via nano.fs.writeFile — synchronously, with no round-trips.
03
Run the toolchain
nano.shExec("npx tsc && node dist/main.js") or nano.node([…]) runs the real binaries and streams stdout as it appears.
04
Serve & preview
Run an HTTP server inside the VM and render it in a live iframe through the service-worker bridge.
In code

Boot, write, run.

javascript
import { createNano } from "@userland-run/nano-sdk";

// boot the RISC-V VM (a BusyBox shell is built in), then install the real Node
// + TypeScript toolchain on demand from the signed catalog — 100% in the browser
const nano = await createNano({ image: { wasm: "/nano/console.wasm" } });
await nano.installApp("node");        // the riscv64 Node build, fetched + OPFS-cached
await nano.installApp("typescript");  // tsc, the same way

// write a TypeScript file into the in-memory filesystem
nano.fs.writeFile(
  "/app/main.ts",
  'const who: string = "RISC-V";\nconsole.log(`hello from ${who}`);',
);

// type-check + run it with the real tsc and node binaries
const { stdout } = await nano.shExec("cd /app && npx tsc main.ts && node main.js");
console.log(stdout); // hello from RISC-V

Need a tight run loop? Snapshot V8 once and restore it per run with the Node fast path.

javascript
// snapshot V8 once, then restore it per run for a fast loop
const rt = nano.nodeRuntime();
await rt.warmup();
await rt.run("console.log(process.version)"); // → v25.4.0
userland.run vs WebContainers

An honest head-to-head.

userland.run
WebContainers
Cloud sandbox
Node.js engine
real Node binary
custom Node reimpl.
real Node (remote)
Runs fully client-side
yes
yes
no — server
Core download
2.3 MB
several MB
Other Linux tools
yes — fd, ripgrep…
limited
yes
Open source
AGPL / MPL
proprietary
varies
Cross-origin isolation
required
required
n/a
Execution speed
emulated — slower
near-native
native (remote)
WebContainers is a trademark of StackBlitz. Comparison reflects public behavior at the time of writing.
The honest trade-offs
NanoVM emulates RISC-V, so heavy compute runs slower than a native or near-native runtime — it’s excellent for editors, linters, type-checks, scripts and demos, and is not a production compute engine. Networking is brokered through the host’s fetch() (HTTP/HTTPS only, subject to CORS), so there are no raw TCP sockets yet. And, like any shared-memory tool, the page must be cross-origin isolated (COOP/COEP) — the SDK ships a service worker that arranges this for you.

Put real Node in your app.

The SDK is a typed, ESM, zero-dependency package. Install it, point it at the wasm, and you have a Linux dev environment running in your users’ browsers.