Quick Answer: AI virtual try-on sends a reference garment image and a person's photo to an image-generation model, which renders the person wearing the garment. The open-source project paipiange/nvzhuang pairs a static gallery of 1,407 clothing references with an optional GoFrame backend that calls OpenAI Images, Gemini, Doubao Seedream, or a local ComfyUI workflow.

AI virtual try-on sounds like a single feature, but it's really three systems stacked together: a reference library, a browser that prepares images, and a backend that talks to a generative model. The paipiange/nvzhuang repository packages all three in one project under the MIT License. I spent time reading its structure, config, and deployment notes, and the design choices are more interesting than the gallery itself. Here's what it ships, how the pieces connect, and what you should copy or avoid if you run something similar.

What the Repo Actually Ships

The core is a dark-themed static site with 1,407 AI-generated clothing references at a 2:3 ratio. They're grouped into eight categories, and the site supports Chinese and English keyword search, so "白" and "white" both find the same images. The data lives in a single data.js file, with each record holding an ID, category, keywords, and image paths.

The try-on feature is separate and optional. The frontend is a pure static site with no build step, which means you can serve it with python3 -m http.server 3000 and it works without any keys. Try-on only activates when you connect the backend/ service, a GoFrame v2 server listening on port 8000 by default.

That split matters. The gallery costs you nothing to host, and the expensive part, model inference, only runs when someone explicitly asks for it. Most projects in this space blur that line, and the cost surprises come later.

How the Try-On Pipeline Moves an Image

Here's the actual sequence when a user clicks "try on." The browser first compresses the uploaded portrait: anything under 256 px on a side is scaled up, and high-definition mode caps the longest edge at 2048 px, then re-encodes as WebP at quality 0.90 to 0.75. The compressed bytes go to the backend, which probes the real file format rather than trusting the extension. It accepts only JPEG, PNG, and WebP, with a single-image cap of 8 MiB and a combined cap of 16 MiB.

The backend then checks dimensions (256–8192 px per side) and total pixels (under 20 million). The pixel check is the one people skip, and it's the one that matters most. A small compressed file can decompress into a huge bitmap, which is a classic decompression-bomb attack that can exhaust server memory. Only after validation does the request reach a model adapter.

Here's where most self-hosters get tripped up. The browser doesn't call the model directly, and the model key doesn't have to live in the frontend. The backend holds the server-side keys, while personal settings are sent with each request and not stored. That's a deliberate trust boundary, and it's the right one.

Choosing a Model Backend: Trade-Offs That Actually Matter

The backend uses pluggable adapters, so you can swap models by editing config.yaml without touching code. The table below summarizes what the project documents.

Backend aliasModelProtocolPractical trade-off
comfyuiQwen-Image-Edit-2511 (4-step Lightning LoRA), MiniMax H3 fallbackLocal ComfyUI APIZero per-call cost and no key, but needs a capable GPU or a LAN host
geminiGemini gemini-3.1-flash-imageGoogle generateContentProject's recommended cloud default; you pay per call
doubaoDoubao Seedream via Volcano ArkVolcano Ark APIReachable from mainland China without a proxy
gpt-imagegpt-image-2OpenAI ImagesWorks with OpenAI-compatible relays and proxies

The counter-intuitive point is that the "free" option isn't free in engineering time. A local ComfyUI setup removes the per-call bill but adds model downloads, VRAM planning, and a fallback path when the local node isn't ready. The repo handles that fallback automatically, which is useful, but you should test it before you trust it.

The cloud options trade cost for predictability. If you're comparing Gemini against an OpenAI-compatible endpoint, measure latency and failure rates on your own images. Vendor moderation behavior on real-person edits varies, and the project's own notes say the Gemini path was chosen partly for that reason. Treat that as a claim to verify, not a guarantee.

Security Controls You Should Copy

This is the section most worth reading if you plan to expose a try-on backend publicly. The project ships several controls by default, and each one closes a specific abuse path.

  1. Bearer token authentication. The server stores only a SHA-256 digest of the access token and compares values in constant time, which prevents timing side channels.
  2. Strict CORS allowlist. Only exact domain matches are accepted, so other sites can't use your backend as a free API.
  3. Rate and quota limits. A token bucket allows 10 requests per minute by default, with a 100-call daily quota on a UTC calendar day. Overruns return HTTP 429.
  4. Concurrency caps. The default is 2 concurrent generations globally and 1 per caller, so generation jobs don't pile up behind a single heavy user.

The last point deserves a closer look. Personal Base URLs are blocked from pointing at localhost, internal networks, and reserved addresses. Without that rule, a public backend becomes a proxy into your own network. Most tutorials on custom API endpoints never mention this, and it's the main risk when you let users supply their own URL.

Here's the catch with the browser key storage. Saving keys in the browser is convenient, but it's only safe on a personal device. The project's guidance is to clear local configuration on shared computers, and you should follow it.

This section is the one the source material underplays. Virtual try-on takes a real person's photo and redraws it wearing different clothing. That is an image-manipulation workflow, and it can cause real harm when the photo is someone else's.

The project's documentation says uploaded photos are kept in page memory for custom garments, and that generation sends only compressed image content to the backend you configured. That's a sound design. But it doesn't answer the consent question. If you run this, you're responsible for whether the person in the photo agreed to be edited, and you should refuse to use the tool on images of people who didn't.

The dataset also includes categories such as lingerie, swimwear, and "sexy" styles. Whether that's appropriate depends on where you publish it and who sees it. If you're deploying publicly, check your platform's rules on adult imagery and consider age and consent gates. The repo's own audit documents exist for this reason, and reading them before launch is worth the time.

Most people stop here, and they shouldn't. A try-on tool with no consent policy isn't a neutral feature. It's a product decision, and you own it.

Deploying It Yourself Without Surprises

The simplest path is Docker Compose. Copy the environment template, add the key for whichever model you chose, and run docker compose up -d. The backend listens on http://localhost:8000, and the frontend connects automatically when served from localhost.

cp backend/.env.example backend/.env
docker compose up -d

For a public deployment, the repo's DEPLOY.md covers Nginx reverse proxying and hardening. Put HTTPS in front of the backend, since the settings center requires HTTPS for cross-origin public backends. Don't rely on the ?tryon= URL parameter for external servers, because the project restricts it to same-origin or loopback addresses to prevent phishing that steals keys and photos.

Start with the cheapest reliable setup. Run the gallery alone first to confirm search and category filtering work for your audience. Add try-on only after you've set rate limits and confirmed your consent policy.

Pass this to someone deciding whether to self-host an AI virtual try-on tool: before they enable the backend, have them set a daily quota, an allowlisted CORS origin, and a written rule about whose photos are allowed. Then try the local gallery for a week and note which model returns usable results for your images.

Frequently Asked Questions

what is AI virtual try-on?

AI virtual try-on is a process where a generative model renders a person wearing a chosen garment, using a portrait and a clothing reference image as inputs. In the paipiange/nvzhuang project, the browser compresses the images and the GoFrame backend forwards them to a configured model such as Gemini, OpenAI Images, Doubao Seedream, or a local ComfyUI workflow.

how do I set up the try-on backend?

Copy backend/.env.example to backend/.env, add the API key for your chosen model, and run docker compose up -d from the project root. The backend listens on port 8000. For a public server, put HTTPS in front of it and set a bearer token and a CORS allowlist before you open it to anyone.

why does the backend block some base URLs?

Personal base URLs that point to localhost, private networks, or reserved addresses are blocked. Without that check, a public backend could be used to send requests into your internal network. It's a server-side request forgery risk, and the restriction closes it.

is it safe to upload my own photo?

The browser keeps custom garment images in page memory only, and the backend doesn't store personal keys or photos in its database or logs, according to the project's documentation. The real risk is not storage but consent: only use photos of people who agreed to be edited, and review your platform's rules before you publish any output.