Real programs, sandboxed in your browser.
Each run below is a whole Linux machine, packaged from a Dockerfile and emulated in WebAssembly right in this tab. No server runs it, its only way out is your browser's own requests, and closing it throws the machine away.
Run one here to type into it, or open it in a window of its own. It is a 64-bit ARM CPU in emulation, so it is slower than native; every number and transcript below was measured and recorded when this site was built.
Compare the runs
The snapshot is what a run downloads before the machine resumes. Preloaded files follow in the background: everything the run read while booting, or while its commands were exercised. The rest is fetched only if you touch it.
| Run | Snapshot | Ready at build | Preloaded | On demand | Files |
|---|
Make your own
Every run on this page came out of one command. snowglobe is a single Go binary; Docker is all else it needs, and the result is a folder of static files you can host anywhere.
Install
$ go install github.com/filipecabaco/snowglobe/cmd/snowglobe@latest
Describe
Any arm64 Alpine image works. Its CMD, ENV and WORKDIR are what the
visitor's terminal gets. Labels say when the program is ready, and which commands to exercise
so their files are preloaded. The Python run's Dockerfile, exercise list shortened:
# examples/python/Dockerfile
FROM alpine:3.24.2
RUN apk add --no-cache python3 && \
python3 -m venv /opt/venv && \
/opt/venv/bin/pip install --no-cache-dir rich==15.0.0
COPY startup.py /root/startup.py
ENV LANG=C.UTF-8 PATH=/opt/venv/bin:$PATH \
PYTHONSTARTUP=/root/startup.py
CMD ["python3"]
LABEL snowglobe.title="Python + Rich in the browser" \
snowglobe.ready=">>> " \
snowglobe.exercise='["inspect(str.split)", "1 / 0"]'
Build
It adds a kernel and a 9p boot layer, converts the image, boots it headless until the ready text appears, snapshots it, then types the exercises and measures what they read. An excerpt of the real log from this site's build:
$ snowglobe build examples/python --out dist/python
==> Resolve source image
command: 'python3'
ready: ">>> "
memory: 512 MB
network: none
==> Add boot layer
==> Convert to a 9p filesystem
5600 entries, 107 MB uncompressed, 4437 unique files
==> Boot and snapshot
App ready on hvc0 after 19.966s
Saved /out/system/state.bin.zst (13 MB)
Exercising: t = Table("language", "year")… (0 files, 3.3s)
Exercising: Syntax("def fib(n):…", "python") (4 files, 3.5s)
==> Write warm pack
warm.pack: 415 files, 8.7 MB
read while booting 409 files 8.5 MB
first read by: Syntax… 3 files 0.0 MB
never read 4018 files 36.9 MB
dist/python is ready to deploy.
Ship
Preview it locally, then put the folder on any static host. Make it an app people can install and run offline, and let several runs on one host share one blob store. This site is GitHub Pages, built by a workflow running the same commands; every run on it is installable, and each one shares to X as a post that boots it.
# preview at http://localhost:8000 $ snowglobe serve dist/python # installable, and offline once it has run $ snowglobe pwa dist/python # several runs on one host: share one blob store $ snowglobe pool dist
Run
The same site runs without a browser, in a sandboxed container: a session in your terminal, lines piped
in as commands, or a long-lived instance that scripts and agents drive with exec. It runs from
a folder, from a site's URL (try snowglobe run https://filipecabaco.github.io/snowglobe/python/),
or from one tarball on a CDN.
# a session in your terminal; ctrl-] quits $ snowglobe run dist/python # from where it's hosted: a site's URL, or one tarball $ snowglobe pack dist/python -o python.tar.gz $ snowglobe run https://cdn.example.com/python.tar.gz # one instance, driven command by command $ snowglobe run dist/python --name py --detach $ snowglobe exec py 'sum(range(10))' 45
Commands
| Command | Does |
|---|---|
| build <dir | image> | package a Docker image (a directory with a Dockerfile, or an image) into a static site; the flags are below |
| serve [dir] [--port N] | preview a built site at localhost, port 8000 by default |
| pwa <site> | make it an installable app that works offline once it has run; --name and --short-name override the title |
| pool <dir> | let every site under a directory share one blob store, for hosting several together |
| pack <site> [-o file] | write a site as one tarball (.tar.gz or .tar.zst) to publish on a CDN |
| run [site | url] | run a site without a browser, in a sandboxed container: a session in a terminal, or piped lines typed in as commands. Takes a folder, a tarball, or a URL to either; --detach --name N keeps it running, and -p 8000:8000 forwards a port into a guest built with a network |
| exec <name> <command> | type a command into a running instance and print only its output |
| attach <name> | join a running instance's session; ctrl-] detaches and leaves it running |
| ps | list running instances |
| stop <name> | throw a running instance away |
Build flags
| Flag | Label | Sets |
|---|---|---|
| --ready TEXT | snowglobe.ready | terminal output that means the program is ready to snapshot |
| --exercise CMD | snowglobe.exercise | commands typed after the snapshot; their files get preloaded |
| --cmd CMD | run something other than the image's own CMD | |
| --memory MB | snowglobe.memory | guest RAM, 512 by default |
| --title TEXT | snowglobe.title | the page title |
| --warm REGEX | snowglobe.warm | extra guest paths to preload |
| --network fetch | snowglobe.network | give the guest a network: HTTP(S) and WebSockets leave as the visitor's own browser requests, so CORS applies; none by default |
| --out DIR | where the site is written, dist by default |