A browser-based “photo booth” that guides a user through capturing a fixed set of poses/emotions, writes PNGs to a user-chosen local folder (via the File System Access API), and post-processes them to transparent cutouts using an ONNX portrait matting model running in a Web Worker.
Design inspired by https://thomasmcinnis.com/posts/teenage-engineering-calculator/
Quick Start
bun install (or bun i).bun run dev then open the printed URL.bun run build • Preview: bun run previewYYYY-MM-DD/cutout/.Architecture
src/routes/+page.svelte with small components in src/lib/components/.src/lib/DateState.svelte.ts centralizes selected day and the list of days.YYYY-MM-DD/<emotion>.png.YYYY-MM-DD/cutout/<emotion>.png.src/lib/SegmentationEngine.ts wraps a Web Worker (src/lib/segmentation/worker.ts).static/models/rmbg14.onnx (default). You can override via PUBLIC_RMBG_MODEL_URL or comma-separated PUBLIC_RMBG_MODEL_URLS; the local file remains as a fallback. Worker prefers WASM-only provider for stability.static/onnxruntime-web/ and configured via ort.env.wasm.wasmPaths.vite.config.ts for crossOriginIsolated so WASM can use threads.src/hooks.server.ts sets COOP/COEP when !dev.@sveltejs/adapter-cloudflare).Repository Status (Assessment)
'$lib/DateState.svelte' vs '$lib/DateState.svelte.ts'.static/models/rmbg2016.onnx is currently unused; only rmbg14.onnx is referenced.vite-plugin-devtools-json is included but not used in the codebase.' appears at repo root (likely accidental).bun.lock is committed but package.json does not declare a package manager. Consider standardizing on one (Bun, pnpm, or npm).convertToBlob() may vary across browsers; this project targets Chromium where it’s supported.ORT Runtime Files (WASM-only minimal set)
static/onnxruntime-web/ for WASM-only execution (no WebGPU/WebGL/bundles). Kept files:ort.wasm.min.jsort.wasm.min.mjsort-wasm-simd-threaded.wasmort-wasm-simd-threaded.mjsonnxruntime-web fetches with env.wasm.proxy = false.What’s Extraneous (post-trim)
static/models/rmbg2016.onnx is intentionally kept as-is per request but remains unused by default.vite-plugin-devtools-json dependency and plugin registration if there’s no JSON devtools usage.Recommended Next Steps
src/routes/+page.svelte status area.'$lib/DateState.svelte' (or the explicit .svelte.ts) consistently across files.vite-plugin-devtools-json if not needed.pnpm@…/npm@…) to package.json and commit the corresponding lockfile only.bun run lint and bun run check on PRs.+page.svelte into small $lib modules/stores for testability.Deploying
static/ (models and ORT files) are uploaded and cached. COOP/COEP are set by hooks.server.ts in production.static/onnxruntime-web/ reduces upload time and cache footprint.Troubleshooting
/onnxruntime-web/ and /models/.?wasmonly=1 in the URL to force the WASM-only path (also the worker already prefers WASM).crossOriginIsolated in dev, make sure dev server printed the COOP/COEP headers (Vite config sets them). Some extensions or proxies can strip them.File Map (selected)
src/routes/+page.svelte: Main flow, camera, session orchestration, and calling segmentation.src/lib/components/*: Small UI components (camera controls, video capture, gallery, status).src/lib/DateState.svelte.ts: Central day selection/list state.src/lib/SegmentationEngine.ts: Worker wrapper and model lifecycle.src/lib/segmentation/worker.ts: ONNX runtime setup and matting pipeline.static/models/*: ONNX models (currently using rmbg14.onnx).static/onnxruntime-web/*: Minimal ORT browser runtime files for WASM.This README reflects the new minimal ORT set and keeps rmbg2016.onnx untouched. I can also fix the small code nits (typo, imports) in a follow-up if you want.
Cloudflare R2 CORS (exact JSON)
[
{
"AllowedOrigins": ["*"],
"AllowedMethods": ["GET", "HEAD"],
"ExposeHeaders": ["ETag", "Content-Length", "Accept-Ranges", "Content-Range"],
"MaxAgeSeconds": 86400
}
]