Every few weeks a new 20-second clip takes over TikTok, and the comments are always the same: how do I put myself in this?
The honest answer used to be "learn After Effects", or more recently "hunt down a CapCut template and copy a link". Both are friction. This post is about removing it — and about the parts of that product that are not the model.
What actually happens under the hood
Motion transfer models take two inputs:
- a reference video (the trend clip)
- a source image (your face)
and produce a new video where your identity drives the motion of the original. Timing, camera movement and choreography come from the reference; who you are comes from the photo.
The part people underestimate: the model re-renders the background. That is why the output looks like "you were there" instead of a cut-out pasted on top — and it is also why it will never be a 1:1 copy of the original scene. If you expect the exact stadium, exact lighting and exact crowd, you will be disappointed. If you expect "me, doing that move, in a scene that plausibly belongs to it", it works.
The photo is 80% of the result
People blame the model for bad output. Most of the time it is the input. What works:
- one person, facing the camera
- even light, no hard shadow across half the face
- head and shoulders in frame, not a full-body shot from across the room
- no sunglasses, no mask, no beauty filter
What technically renders and looks wrong:
- group photos (the model has to pick a face)
- mirror selfies (the camera angle fights the reference)
- low-light phone shots (the encoder has already destroyed the detail)
A cheap trick: take the selfie in the same lighting you would use for a passport photo, then stop. Fancy lighting makes it worse, because the model has to reconcile your light with the reference scene's light.
The parts that are not the model
The model call is five minutes of work. The rest is what makes it a product:
Credits are a ledger, not a counter. Every movement is a row with an idempotency key derived from the business object — order:<id>, generation:<id>, generation_refund:<id>. Payment webhooks retry. Users double-click. Without the keys you get double-charging and double-refunding, and you find out from an angry email.
A failed render must refund automatically. Not "contact support". The refund is derived from the same generation row, so it is a no-op if it runs twice.
The job is a queue, not a request. Submit to the provider, store the request id, poll, and let a cron reconcile anything stuck past the deadline. A HTTP request that waits for a video will time out long before the video exists.
Content is data. Copy lives in per-locale modules that take the site config as an argument. Change the keyword or the price and every page updates — including the structured data and /llms.txt.
The boring numbers
On a real photo, a 6-second vertical clip:
- fast model: ~2.5 minutes
- mid model: ~4.5 minutes
- slow model: ~9 minutes
Anything above five minutes loses a meaningful share of users before they see a result. That is a product constraint, not a model benchmark.
Try it
- ozilai.ink — the football goal variant
- talkdirty.ink — the dance variant
- imsorryilied.ink — an old photo, gently moving
Same engine, different reference clips. One photo in, one vertical MP4 out.
If you are building something similar, the two tools I keep reaching for are in this repo: a media pipeline that turns clips into posters and manifests, and a checker that tells you whether AI crawlers can actually read your site.
Top comments (1)
The point about the photo being 80% of the result is painfully true. Most users blame the AI for a bad render when they submitted a blurry mirror selfie. But the real MVP here is the backend architecture. The idempotency keys and automatic refunds for failed renders are exactly the kind of boring engineering that actually keeps a SaaS alive. Nothing kills trust faster than charging someone for a video that never generated.
How long does the average user wait before they bounce, though? You mentioned 5 minutes is the limit, but does a live progress bar actually keep them from closing the tab?