Skip to content

Repository files navigation

Wearly — AI virtual try-on

See how a specific garment, in a specific size and colour, would actually look on your body — including when it does not fit.

Most virtual try-on tools quietly render a flattering result whatever size you pick. Wearly does the opposite: it computes the fit numerically first, then requires the render to show the consequences. Ask for an XS on a body that needs XXL and you get a garment that will not close.

Bring your own OpenAI key

Try-ons are generated with your OpenAI API key, so you only ever pay for your own images.

  1. Create a key at platform.openai.com/api-keys.
  2. Open AI Profile and paste it into Your OpenAI API Key.
  3. Upload front, side and back photos, fill in your measurements, then try something on from the catalog.

The key is stored in your browser's localStorage, sent only with generate requests, and never written to the server. Remove it from the same panel at any time. Because localStorage is readable by any script on the origin, use a restricted key you can revoke.

Your reference photos never leave your device except as part of a generate request, and are stored locally in IndexedDB.

Renders always show the subject in ordinary underwear — plain, opaque and full-coverage, plus a bra where the reference photos show a woman — so an ill-fitting garment can be drawn honestly without the image becoming revealing. Bare midriff, arms and legs are expected wherever a garment genuinely does not cover; the private areas never are.

Running it

npm install
npm run dev

Copy .env.example to .env.local. For local development you can skip the in-app key entry and use one from the environment instead:

OPENAI_API_KEY=sk-...
ALLOW_SHARED_KEY_FALLBACK=true

Deploying publicly

Leave OPENAI_API_KEY and ALLOW_SHARED_KEY_FALLBACK unset. The key resolution fails closed: without ALLOW_SHARED_KEY_FALLBACK=true a visitor who has not supplied their own key gets a clear "add your key" message rather than silently spending yours.

Variable Default Purpose
OPENAI_API_KEY unset Only used when ALLOW_SHARED_KEY_FALLBACK=true.
ALLOW_SHARED_KEY_FALLBACK false Lets keyless visitors render on your key. Bills you.
TRY_ON_QUALITY medium low / medium / high. high roughly doubles the wait.
TRY_ON_STREAMING true Progressive previews while rendering. Falls back automatically if a model rejects it.
TRY_ON_RATE_LIMIT 12 Generates allowed per window per client. 0 disables.
TRY_ON_RATE_WINDOW_SECONDS 600 Length of that window.

Deploys to Vercel with no configuration — it is a standard Next.js App Router project. Set the variables above in Project Settings → Environment Variables.

Rate limiting is in-memory and therefore per serverless instance — it stops one person hammering the endpoint, not coordinated abuse. Back lib/rateLimit.ts with Vercel KV or Upstash if you need more.

The try-on route declares maxDuration = 60, the Hobby ceiling. A render takes roughly a minute at medium, so it fits but with little headroom; on Pro you can raise it to 300 and use TRY_ON_QUALITY=high.

Generating

Renders run in a registry (lib/tryOnJobs.ts) keyed by variant — product, size, colour and cut — rather than being owned by the page. Start one, browse to another product while it works, and it keeps going; the panel simply shows whichever job matches what is currently selected, and a strip lists anything rendering elsewhere. Cancelling targets one render, not all of them.

Finished images are written to IndexedDB (try-on-results, 24 most recent). Coming back to a variant you rendered before — this session or a previous one — restores it instantly with no regeneration and no API call. IndexedDB rather than localStorage because these are 1–2 MB binary blobs; localStorage caps out around 5 MB and stores strings only.

How the fit engine works

lib/fitEngine.ts is dependency-free arithmetic that turns a body and a garment into a verdict:

  • Body estimation — chest, waist, hip and shoulder from height, weight and body type, plus vertical landmarks from standing-anatomy ratios.
  • Graded size chart — the body measurements each size is drafted for, the wearing ease each cut adds, and per-garment pattern specs (length, sleeve, neckline, armhole, rise, inseam, leg opening).
  • Fabric behaviour — usable stretch per material, so "too small" is judged against what the cloth can absorb. Rigid leather fails where jersey stretches.
  • Consequences — ride-up, exposed midriff, closure gaps, dropped shoulder seams, hem and cuff placement as a percentage of figure height.

lib/tryOnPrompt.ts renders that into the brief, leading and closing with a directive that names the required reading of the image. Severity is derived from how far the wearer sits from the body the size was drafted for, independently of the chosen cut — so deliberately picking Oversized in your correct size is not reported as a bad fit.

The same report drives the How This Will Fit You panel, so the numbers shown to you are the numbers the renderer was given.

Tests

npm test          # everything
npm run test:engine   # fit engine, prompt, responsive CSS, BYO-key security
  • tests/fit-engine.test.mjs — sizing, fabric limits, landmark ordering, prompt structure and cross-references.
  • tests/byo-key.test.mjs — key never logged or echoed, upstream errors scrubbed, fail-closed key resolution, catalog-derived inputs.
  • tests/responsive.test.mjs — parses the stylesheet: dvh fallbacks, safe-area insets, breakpoint cascade, grid arithmetic.
  • tests/try-on-jobs.test.mjs — concurrent renders, blob-URL lifetimes, cache restore.
  • tests/rendered-html.test.mjs — server-render smoke tests.

Known limits

  • Product shots are 1024×1024 JPEGs, sent to the model untouched so weave and trim survive. Replacing them with real studio photography at the same size or larger is a drop-in improvement — see productImagePath in lib/catalog.ts.
  • Body measurements are estimated from height, weight and body type, not measured. The brief tells the model to trust the photograph over the estimate for body shape while keeping garment dimensions fixed.
  • A generation takes roughly a minute at medium, against a 60s function limit on Vercel Hobby. high roughly doubles that and needs Pro with a raised maxDuration. Streaming previews keep bytes flowing throughout.

About

A fabric aware virtual try on of clothes ecommerce website developed using Claude Ops

Resources

Stars

23 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages