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 preview
YYYY-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.js
ort.wasm.min.mjs
ort-wasm-simd-threaded.wasm
ort-wasm-simd-threaded.mjs
onnxruntime-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
}
]