AI demos are everywhere. A single Python script can swap a face in a static photo, and the result looks magical. But building something that real people can actually upload to, walk away from, and come back to? That is an entirely different job. I recently built a web tool that takes an animated GIF and a reference face, then returns the same animation with the face swapped across every frame. The model does the visual heavy lifting, yet the real engineering effort went into the architecture surrounding it: keeping connections alive, surviving page refreshes, and making sure a two-minute inference job did not vanish into a 504 Gateway Timeout.

This is the gap between a prototype and a product. Engineers love to talk about diffusion architectures and inference parameters. But when a user uploads a dense, two-hundred-frame GIF, nobody cares about your model if the browser tab gives up after thirty seconds. The infrastructure around the inference matters just as much as the inference itself.

The Problem with Waiting

Standard web architecture assumes fast responses. A user clicks a button, the server answers, and the page updates. Face swapping across dozens of GIF frames breaks that assumption immediately. The model runs on Replicate's GPUs, not on my server, and a long GIF can easily take a minute or more to process. If you try to hold an HTTP request open that long, you are asking for trouble. Load balancers cut idle connections. Browsers assume the network failed and retry. Users stare at a frozen spinner and assume the app is broken.

I avoided this entirely by decoupling the submission from the result. When a user uploads a GIF and a face image, my Next.js backend starts a prediction on Replicate and instantly returns a job ID. The user gets immediate confirmation. The actual result arrives later through a webhook once the GPU work finishes. This pattern is not exotic, but for long-running media jobs it is absolutely essential. It turns an unpredictable wait into a confident handshake: the job is accepted, and you will be notified when it is done.

Building a State Machine You Can Trust

Once you go asynchronous, you need visibility. Users will refresh the page. They will close the tab and reopen it. They will copy the link and send it to a coworker who checks it two hours later. Without a durable record of what happened, you have chaos.

I used Supabase as the single source of truth. Every upload creates a row with a unique job ID, and that row moves through specific states: queued, processing, succeeded, failed, or expired. When the user first hits submit, the row is queued. The moment Replicate accepts the prediction, it shifts to processing. The webhook pushes it to succeeded or failed. I added expired for jobs that sit too long without a callback, so the system does not chase ghosts indefinitely.

The frontend, built in TypeScript, polls Supabase on a short interval and renders whatever the current state says. This polling looks primitive, but it solves the refresh problem completely. A user can close their laptop, open it tomorrow, and see exactly where things stand because the database—not the browser memory—owns the progress. Supabase also tracks credits, so the accounting stays tied to the same job record that tracks the state. Everything lives in one place.

Handling Files Without Crushing the Browser

GIFs are heavier than people think. A file that is perfectly sized for the AI pipeline might still weigh several megabytes. Rendering that as a looping preview inside the browser would wreck performance, especially on lower-end devices. I needed two entirely separate file pipelines: one for the AI, and one for the user interface.

For previews, I convert large GIFs to animated WebP using FFmpeg compiled to WebAssembly. This runs entirely client-side in the browser. The result is a lightweight preview that keeps the interface snappy without touching the original file. The GIF sent to Replicate stays untouched. That separation matters. You do not want compression artifacts from a preview sneaking into your training data or your final render, and you do not want the user interface stalling on a multi-megabyte blob while the AI is still thinking.

FFmpeg WASM also handles other GIF tasks before the upload ever leaves the browser. I use it to parse frame counts, validate dimensions, and catch corrupted files early. Catching a problem before it consumes GPU credits saves both money and user patience.

Bir Demoyu İnsanların Güvendiği Bir Yazılıma Dönüştürmek

Burada, piyasadaki hemen hemen her üretken yapay zeka aracı için geçerli olan daha geniş bir ders var. Modelin kendisi işin belki yüzde otuzudur. Geri kalan yüzde yetmiş ise kimsenin tweet atmadığı o gösterişsiz altyapı işleridir: durum kurtarma, webhook imzaları, dosya dönüştürme, kredi takibi ve kontrollü hata yönetimi.

Teknoloji yığınım basit ve bilinçlidir. Next.js ve TypeScript arayüzü ve API rotalarını yönetir. Replicate modelleri çalıştırır. Supabase durumları, verileri ve kredileri yönetir. FFmpeg WASM istemci tarafındaki medya işlerini halleder. Animated WebP, kullanıcı arayüzünü (UI) hızlı tutar. Her parçanın tek bir görevi vardır ve bunlar kırılgan, uzun süreli istekler yerine açık durum geçişleri aracılığıyla birbirine bağlanır.

Bir kullanıcı yüz değiştirme işlemi için kredi harcadığında, bir araştırma makalesi değil, güvenilirlik bekler. Eğer bir tahmin başarısız olursa, sistem bunu bilmeli ve kullanıcıya bildirmelidir. Eğer kullanıcı beklemek zorundaysa, bakabileceği hafif bir önizlemesi ve tarayıcı yeniden başlatıldığında bile korunan bir durum göstergesi olmalıdır. Bu detaylar işler yolunda gittiğinde görünmezdir, ancak işler ters gittiğinde kritik hatalara yol açar.

Yüz değiştirme işleminin kendisi hoş bir tekniktir. Ancak araç, ancak kullanıcı yükleme yapıp ayrıldıktan sonra kaldığı yerden devam edebildiği için gerçek hissettirir. Bir API çağrısını, insanların gerçekten güvendiği bir yazılıma dönüştüren şey budur.