vellum-outpaint
๐ Quick Links
๐ Project Details
- Primary Language: TypeScript
- Languages Used: TypeScript, CSS, Dockerfile, HTML
- License: MIT License
- Created: July 15, 2026
- Last Updated: July 21, 2026
๐ About
**[โ Launch the studio at outpaint.aaroncollins.info](https://outpaint.aaroncollins.info)**
    
Why Vellum
Most "AI outpainting" tools send your image to a server. Vellum doesn't have one. The entire
diffusion pipeline โ text encoder, U-Net, and VAE โ is downloaded once and then runs locally on
your graphics card through onnxruntime-web. After the
first load it works fully offline, and nothing you paint is ever uploaded, logged, or seen by
anyone but you.
- ๐จ Infinite canvas โ pan, zoom, and frame any region to extend. Outpaint in every direction.
- โ๏ธ One-click expand โ press โ โ โ โ and Vellum snaps the frame onto the picture's edge and paints the scene onward, so the image genuinely grows instead of being overwritten in place.
- ๐ฉน Erase & repaint โ brush away any spot (a stray object, a bad patch), then Outpaint regenerates just that area from its surroundings (inpainting).
- โฆ Suggest a prompt โ a small captioning model reads the current scene on-device and proposes a prompt to continue it. Or leave the prompt blank and let the model simply continue what's there.
- ๐ผ๏ธ Stamp your own photos โ drop, paste, or import an image, then drag & scale it onto the canvas and outpaint around it. The natural starting point for extending a real photograph.
- ๐ Truly private โ no server, no account, no telemetry. It's a static site plus your browser.
- โก Two engines, one click โ pick instant single-step SD-Turbo or the classic, fully controllable SD 1.5.
- ๐ช Seamless seams โ feathered latent compositing blends new pixels into the existing image so joins disappear.
- ๐ Bring your own backend (optional) โ point it at a local Automatic1111 / ComfyUI server if you'd rather run those weights.
- ๐พ Cached weights โ models are stored in the browser Cache API, so the multi-GB download happens only once. Install Vellum (address-bar install icon) so the browser grants persistent storage and the cache is never evicted โ see Persistent weights.
Engines
Choose per-session from the engine picker โ no reload required.
| Engine | Runs on | Speed | Control | Download |
|---|---|---|---|---|
| SD-Turbo | Your GPU (WebGPU) | โก ~1 step, seconds | Fixed sampler | ~2.5 GB, once |
| SD 1.5 | Your GPU (WebGPU) | ๐ข multi-step | Steps + guidance scale | ~2.3 GB, once |
| Remote WebUI | Your own A1111/ComfyUI | Depends on host | Full | โ |
| Atelier demo | CPU, no download | Instant | Procedural preview | none |
SD 1.5 is the original, non-distilled model: it runs a real multi-step Euler sampler with classifier-free guidance for finer, more prompt-faithful results. SD-Turbo trades that control for one-step speed. Both stay 100% on-device.
Requirements
- A browser with WebGPU +
shader-f16โ recent Chrome or Edge (113+) on a discrete GPU is the reliable path. The engine picker will tell you if your browser can't run local inference and suggests the demo or a remote backend instead. - For local development: Node.js 24+ and npm.
Quick start
Other scripts:
npm run build # type-check (tsc -b) + production build โ dist/
npm run preview # serve the production build locally
npm run lint # oxlint
How it works
prompt โโถ CLIP text encoder โโ
โโโถ U-Net (denoise, N steps) โโถ latent โโถ VAE decoder โโถ pixels
frame pixels โโถ VAE encoder โโ โ
โผ
feathered composite back onto the canvas
- On-device inference via
onnxruntime-webon the WebGPU execution provider (threaded WASM as a fallback). fp16 weights are packed/unpacked in JS around the GPU tensors. - Outpainting encodes the existing frame into latent space, injects seeded noise scaled by the fidelity control, denoises, then feather-composites the result so new and old pixels blend.
- UI state is a single Zustand store; the canvas, engines,
and remote backend all sit behind one
DiffusionProviderinterface.
Placing photos
The Stamp tool is the quickest way to start from a real image:
- Click Stamp a photo in the right rail (or the โ tool, or Import) โ or just drag an image onto the canvas, or paste one with โ/Ctrl-V.
- The photo floats on the canvas. Drag to position, drag a corner to scale (aspect-locked), Flip to mirror, or Fit to frame to snap it into the current outpaint window.
- Press Place (โ) to commit it โ or Cancel (Esc) to discard.
Once placed, move the frame past the photo's edge and Outpaint to extend it.
Persistent weights
The engines cache ~2.5 GB of model weights in the browser Cache API, so the download is a one-time
cost. That cache survives page reloads โ but browsers keep it as best-effort storage by
default, and Chrome only exempts an origin from eviction (navigator.storage.persist()) once the
site is installed, bookmarked, or has enough engagement. On a brand-new visit that grant is
usually declined, so under disk pressure the browser can reclaim the cache and the weights
re-download on a later visit.
Vellum requests persistence on every load and ships an installable PWA manifest to earn the grant; if the browser still declines, it tells you so (and how to fix it) instead of silently re-downloading. The reliable fix: install Vellum (the install icon in the address bar, or โฎ โ Install) โ installed origins get persistent storage, and the cache then sticks forever.
Deployment
Vellum is a static SPA (Vite output in dist/) with no server runtime. It ships as a two-stage
Docker image (Node build โ nginx) behind a central Caddy reverse proxy. See
deploy/README.md for the full runbook.
Two things this class of app gets wrong easily, handled here:
- Cross-origin isolation โ
onnxruntime-web's threaded/WebGPU backends needcrossOriginIsolated === true, which requiresCross-Origin-Opener-Policy: same-originplus aCross-Origin-Embedder-Policyheader. Vellum usescredentiallessCOEP so cross-origin model downloads (e.g. from huggingface.co) still succeed. .wasmMIME type โ served explicitly asapplication/wasm; a wrong content type makes the browser refuse to instantiate the module.
Tech stack
React 19 ยท TypeScript ยท Vite ยท Zustand ยท onnxruntime-web (WebGPU) ยท @huggingface/transformers (CLIP tokenizer) ยท Stable Diffusion Turbo & 1.5 (fp16 ONNX)
License
MIT โ see LICENSE. Model weights are distributed by their respective authors on
Hugging Face under their own licenses.