Turn a photo of an empty room into a furnished, listing-ready photo in about a minute. Upload a picture (or paste a link), pick a room type and a furniture style, and get a staged before/after pair back.
- Two staging models, switchable in the UI: PropLabs virtual staging (room type + furniture style + furniture list) and Google Nano Banana Pro (prompt-based, keeps walls, windows and floors in place)
- Sky replacement ("make it sunny"): img2img pass that swaps a grey sky for a bright one
- Upload from device or from a link, with drag and drop and live preview
- Before / after gallery of every staging job, with delete
- Async pipeline: jobs run on Replicate and call back a webhook when done, so the UI polls a status endpoint instead of blocking
- Own storage: originals and results are copied to Cloudflare R2 and served through an image proxy, so nothing depends on Replicate's expiring URLs
- Frontend: Nuxt (Vue 3, compatibility v4), single-page app
- AI: Replicate predictions API (PropLabs virtual staging,
google/nano-banana-pro, img2img for skies) - Storage: Cloudflare R2 for images, Cloudflare KV for job metadata
- Server: Nitro API routes (
/api/stage,/api/stage-banana,/api/sunny,/api/upload,/api/webhook,/api/status/:id,/api/predictions,/api/images/*)
Deployed on Vercel with the Nuxt preset (nitro.preset = 'vercel').
npm install
cp .env.example .env
npm run devEnvironment variables:
| Variable | Purpose |
|---|---|
REPLICATE_API_TOKEN |
Replicate API token |
WEBHOOK_SECRET |
Optional secret for webhook verification |
APP_URL |
Public URL of the app, used for the Replicate webhook callback |
CLOUDFLARE_ACCOUNT_ID |
Cloudflare account |
CLOUDFLARE_R2_API_TOKEN |
Token with write access to the R2 bucket |
CLOUDFLARE_KV_API_TOKEN |
Token with write access to the KV namespace |
CLOUDFLARE_KV_NAMESPACE_ID |
KV namespace that stores job metadata |
Replicate needs a publicly reachable APP_URL to deliver webhooks; use a tunnel (for example ngrok) in development.