← Blog

· 6 min read · Vibeboa

How the Portrait stopped changing people's faces

The app returned someone similar to you, never you. The fault wasn't the prompt — it was three engineering decisions, and the worst was sending the weaker model forward.

The Portrait does one thing: you send a photo, choose a decade or style, and it returns the photo remade in that visual language. It worked — and always returned someone similar to you. Never you.

The obvious suspicion was the prompt. I spent a good amount of time writing longer and longer identity locks, listing trait by trait: jaw format, distance between the eyes, iris color. It improved little. The cause was elsewhere.

Three errors, none of them in the text

The first: the line of models started with the weakest. The flux-2-klein-4b is distilled with four fixed steps of inference — fast and bad at preserving the face. The flux-2-dev accepts steps and is the strongest in the catalog. I was asking for fidelity to the model that least could deliver it.

The second: the output was fixed at 1024×1024. A portrait photo, 900×1200, was forced to fit in a square. The model redraws the face to fit the format before any style enters. The similarity died there, in the first line of the request.

The third is the most interesting. The model accepts a reference image of up to 512 px. This ceiling is hard and non-negotiable. But it accepts FOUR references — and I sent one. A full-body photo reduced to 496 px delivers a face of maybe ninety pixels on the side. The face information was thrown away before leaving the browser.

What changed

Now the whole scene is in a single reference and a closed cut of each face in the others, each occupying the 496 px alone, cut from the full-size photo and not the reduced version. When the browser has a face detector, they are real faces — and a photo with two people returns with both.

I also removed "two people" from the list of negatives. I was removing people from the frame.

The strongest model didn't win

With the queue inverted, the flux-2-dev went to the front. And then came the measurement that changed the decision again: it's charged PER STEP. At twenty steps and one megapixel, it costs around four cents per image — about thirty times the klein-4b. And in testing, twice, it took over four minutes without responding.

modeloaverage timecost per image
flux-2-klein-9b3,6 sUS$ 0.0166
flux-2-dev (20 steps)> 4 min sem resposta~US$ 0.041

The standard became the klein-9b, which is distilled in four steps, responds quickly, and has quality well above the 4b. The dev stayed behind a button that says 'caprichar', with the cost written on the screen. And each model got a deadline: cloud model doesn't fail just by returning an error — sometimes it simply doesn't return, and without a deadline, the person stays looking at the screen for minutes to receive nothing.

The short instruction won over the long one

The last change was to undo my own work. The detailed recipe described the middle with the precision of a photographer: film, lens, color temperature, emulsion defect. The more I described the desired image, the more the model understood that it had a license to build an image that fit the description — and the person is the first thing it rewrites to make fit.

The short instruction does the opposite: it names the theme in one sentence and spends the rest of the text saying what cannot be changed. This is the form that works in conversation editing tools, and has become the standard. The detailed instruction remains in a button, because in cordel, azulejo, and serigraphy, the middle description is precisely what saves.

The part that the model doesn't do

A diffusion model can imagine a scene from 1987. It can't reproduce what the chemistry of a film, the tube of a TV or the screen of a print did to the image signal. That's not imagination, it's signal processing — and signal processing is done with arithmetic.

So the work was divided. The model builds the scene; the browser applies the medium: curve, grain, halation, real halftone reticle, scan line, chromatic aberration, registration error between plates. It's what takes the result out of the territory of "photo with filter". You can turn it off and compare by holding a button.

Measured in production: 896×1184 output from a 900×1200 photo, three references, 2.9 seconds. Before it was 1024×1024 forced, one reference, 35 seconds.