<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: GooDesign AI</title>
    <description>The latest articles on DEV Community by GooDesign AI (@goodesign).</description>
    <link>https://dev.to/goodesign</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4220947%2Fd8f9960d-8d0a-4aa2-be71-3db4ffe9a676.png</url>
      <title>DEV Community: GooDesign AI</title>
      <link>https://dev.to/goodesign</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/goodesign"/>
    <language>en</language>
    <item>
      <title>Why AI Room Renders Look Fake — and the Pipeline Fixes That Actually Work</title>
      <dc:creator>GooDesign AI</dc:creator>
      <pubDate>Mon, 02 Nov 2026 01:18:53 +0000</pubDate>
      <link>https://dev.to/goodesign/why-ai-room-renders-look-fake-and-the-pipeline-fixes-that-actually-work-2f6i</link>
      <guid>https://dev.to/goodesign/why-ai-room-renders-look-fake-and-the-pipeline-fixes-that-actually-work-2f6i</guid>
      <description>&lt;p&gt;Show an AI room render to someone who's never seen one and they'll say "looks great." Show it to anyone who's listed a property, sold furniture, or stood in an actual living room, and you'll often get a squint and a pause. Something is off. They usually can't name what. They're right anyway.&lt;/p&gt;

&lt;p&gt;That squint is the whole problem this post is about. At goodesign we generate photorealistic room designs from a single photo, which means we live or die by that squint. Over the past year we've dissected hundreds of renders that "felt fake" and traced the feeling to a short list of root causes — some fixable in the pipeline, some only manageable, and a few we simply can't fix yet. Here's the full breakdown, including the parts that didn't work.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/acBN7qtAU0M" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Where does the "fake" feeling come from?
&lt;/h2&gt;

&lt;p&gt;Fakeness is almost never one big error. It's the sum of small violations of physics that human vision catches automatically and consciousness reports as a vague unease. A shadow pointing the wrong way. A sofa that's slightly too large for the gap it sits in. A countertop with the specular behavior of a bowling ball.&lt;/p&gt;

&lt;p&gt;We sort every "this looks off" report into four buckets: &lt;strong&gt;light, scale, material, and geometry&lt;/strong&gt;. Each bucket has its own cause and its own fix, and the fixes stack — a render with perfect lighting can still fail on scale, and one that nails both can still be disqualified by a single moved window.&lt;/p&gt;

&lt;p&gt;Before the bucket-by-bucket tour, one framing rule that saves endless argument: the model can only be as honest as its input. A dark, tilted, cluttered phone photo forces the renderer to guess, and guessed geometry is where most artifacts are born — melted chair legs, wavy baseboards, shadows with no owner. If only one room's renders keep drifting, that room is the variable. Reshoot before you touch anything else.&lt;/p&gt;

&lt;p&gt;With that said, here are the four buckets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bucket 1: Lighting — the model lights everything evenly
&lt;/h2&gt;

&lt;p&gt;Real rooms have one dominant light source and everything else is falloff. Sun through the west window, lamps doing the heavy lifting after dark, one bright kitchen under-cabinet run. Real photographs record that hierarchy: bright walls near the source, corners falling into gentle shadow, a single shadow direction shared by every object in the room.&lt;/p&gt;

&lt;p&gt;Diffusion models, by default, light everything evenly. Flat, bright, shadowless-ish. It looks generous in a thumbnail and wrong within seconds, because evenly lit rooms don't exist. Every object casting a soft ambiguous shadow in a slightly different direction is a physics violation stacked on every other one, and the eye sums them.&lt;/p&gt;

&lt;p&gt;The fix that actually moved the needle for us: &lt;strong&gt;every render inherits an explicit light spec derived from the input photo&lt;/strong&gt; — direction, warmth, and time of day — unless the user overrides it. The spec gets phrased concretely in the prompt, because vague light words produce vague light. There's a line from our knowledge hub piece on materials and light that we've basically internalized as an engineering spec: "One named source with one direction reads as photography almost immediately; mixed or sourceless light reads as CGI…"&lt;/p&gt;

&lt;p&gt;In practice the phrases that work are short and physical:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Soft daylight from the left window" — source, direction, quality, seven words.&lt;/li&gt;
&lt;li&gt;"Bright, even, true-to-life daylight" for listing-style shots, where even beats dramatic.&lt;/li&gt;
&lt;li&gt;"Lamps on, overheads off" for evening interiors — lamps sit at eye level and read as lived-in; a bright ceiling pool reads like a hotel lobby at check-in.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second half of the lighting fix is &lt;em&gt;restraint&lt;/em&gt;. The temptation with a dim render is to lift every shadow until the room sparkles. That's exactly how you get the over-bright, flat, showroom look. Real rooms have corner falloff. We generate with the light spec and then explicitly resist shadow-lifting beyond what the spec calls for. One more practical note: when a render "looks right but feels fake," the problem is usually the light, not the furniture — relighting the same scene beats restyling it almost every time. Realism lives in the light.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bucket 2: Scale — the model doesn't know what a centimeter is
&lt;/h2&gt;

&lt;p&gt;A diffusion model has never measured a room. It doesn't know the sofa in your photo is 230 centimeters long or that your doorway is 76 centimeters wide. It knows what sofas look like relative to other things in the average training photo, which is a different and much noisier kind of knowledge.&lt;/p&gt;

&lt;p&gt;The classic failure: you ask for a king bed and get something that reads as a twin against the wall, because the bed's size was resolved relative to a wall whose own scale the model improvised. Or a dining set renders handsomely and is silently too large for the room, which nobody notices until delivery day in the real-world version of the same layout.&lt;/p&gt;

&lt;p&gt;What works, in order of impact:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Name the piece and its size class in the prompt.&lt;/strong&gt; "Full-size king bed, two nightstands" outperforms "bed" by a wide margin, because you've given the model the semantic frame it needs to pull the right prior. This is also our standard advice to users who report wrong-scale furniture: re-render that swap alone, with the piece named.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anchor the scene with architectural facts.&lt;/strong&gt; Doors, windows, and baseboards have roughly standard dimensions, and they survive the render better than furniture does. We use detected architectural elements as scale anchors and generate furniture to be consistent with them, not with the average of training photos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do one change per render.&lt;/strong&gt; Scale errors cluster in overloaded prompts — "new sofa, oak floor, gray walls, bigger window, plants, a dog" is six jobs in one pass, and quality drops on all six. When the model has to resolve conflicts ("bigger window" vs. "same room"), geometry silently loses.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's the honest residue: even with all three in place, a render can be a few centimeters optimistic per piece, and the optimism compounds across a layout. A sofa can look perfect in the render and still be ~20 cm too long for your wall. That's why our accuracy guide says plainly that renders are good enough to &lt;em&gt;inform&lt;/em&gt; decisions and not good enough to &lt;em&gt;place orders&lt;/em&gt; from. The render is a hypothesis. The tape measure is the experiment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bucket 3: Material — the plastic problem
&lt;/h2&gt;

&lt;p&gt;The third bucket is surfaces with the wrong optical behavior: countertops that reflect like wet plastic, floors with a uniform studio sheen, fabrics with no weave at all. Call it the plastic problem. It's the reason a render can have correct light, correct scale, correct layout — and still read as a video game.&lt;/p&gt;

&lt;p&gt;Two causes, two fixes. The first cause is the model's aesthetic prior: glossy renders photograph well at thumbnail size, so the training data rewards shine. The fix is to specify matte where matte belongs. Matte oak, matte black steel, honed stone, washed linen. In our experience, capping the material list at about four named surfaces keeps the render coherent — stack more than roughly four material demands and the model starts averaging them, and the room goes muddy. One material word per surface beats a paragraph of stone adjectives.&lt;/p&gt;

&lt;p&gt;The second cause is mixed lighting on glossy surfaces, which produces highlights in physically impossible places. This is where buckets 1 and 3 meet: a scene with two competing light directions renders plausibly matte surfaces with CGI-looking specular streaks. Fix the light spec first; the plastic often disappears with it. If it doesn't, swap the surface for a matte equivalent and re-render that region alone rather than regenerating the whole frame.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bucket 4: Geometry — the tells that disqualify a frame
&lt;/h2&gt;

&lt;p&gt;The last bucket is the one buyers and clients catch fastest, because it violates structural common sense rather than optics. Our knowledge hub keeps a list of the seven buyer-facing tells, and it's worth reproducing the categories: floating or sinking furniture, wrong-scale pieces, missing or contradictory shadows, warped architecture (bowed walls, crooked frames), rippling hard surfaces like countertops, duplicated or impossible objects (the three-armrest sofa), and furniture that ignores the room's function, like a bed parked across a door.&lt;/p&gt;

&lt;p&gt;Three of those deserve engineering comment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Moved windows and doorways are generation, not editing.&lt;/strong&gt; If a render shows a window where your photo has a wall, the model didn't edit your room — it invented a new one inspired by it. This is the single most serious failure for anything buyer-facing, because it misrepresents the actual property. Our rule is blunt: treat any render that changed a wall line, window position, or doorway as rejected, regardless of how good it looks. This is why edit-in-place modes exist and why anything shown to buyers should come from one: the edit-in-place path preserves wall positions, window shapes, and floor direction by construction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mirrors break depth.&lt;/strong&gt; A mirror doubles the scene and the model's depth reasoning with it — reflections drift out of sync with the room, and the eye catches the disagreement instantly. We don't have a real fix; our working mitigation is to treat mirrors as damaged regions, render around them, and flag rooms with large mirrors as high-attention cases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Text and faces stay unreliable.&lt;/strong&gt; Asked-for signage melts into letterform soup at 100% zoom, and faces in photos or art warp. The fix is editorial, not technical: don't request text in renders, and crop both out of anything publishable.&lt;/p&gt;

&lt;p&gt;The diagnostic tool for all of the above costs nothing: the two-minute audit. Put the render and the original photo side by side, zoom to 100%, and compare — wall lines first, then window geometry, then shadow direction, then every piece of furniture's contact with the floor. Most tells appear within ten seconds of honest looking.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the gallery looks like when this all works
&lt;/h2&gt;

&lt;p&gt;Everything above is easier to see than to read. These pairs are from our public gallery — 35 before/after pairs, 7 rooms across 5 transformation types, each generated from a single input photo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh1xlc3hb3ztiowgobmuv.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh1xlc3hb3ztiowgobmuv.webp" alt="Before: a real room photo from the Goodesign before/after gallery" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F93gfclu95wo3uulsueeb.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F93gfclu95wo3uulsueeb.webp" alt="After: the AI redesign of the same room, from the Goodesign gallery" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn0lkpw2proo0di4dcksw.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn0lkpw2proo0di4dcksw.webp" alt="Before: a real room photo from the Goodesign before/after gallery" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiqow6z8vdnngodgdf4rr.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiqow6z8vdnngodgdf4rr.webp" alt="After: the AI redesign of the same room, from the Goodesign gallery" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa7r38pw7icgbnoftsvab.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa7r38pw7icgbnoftsvab.webp" alt="Before: a real room photo from the Goodesign before/after gallery" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqkahj3fx8p0wkf10me5l.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqkahj3fx8p0wkf10me5l.webp" alt="After: the AI redesign of the same room, from the Goodesign gallery" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What to look for in each pair, in order: wall lines and window positions identical to the before, one consistent shadow direction, furniture touching the floor with real contact shadows, and surfaces that stay matte where matte belongs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest list: what we still can't fix
&lt;/h2&gt;

&lt;p&gt;Any post about fixing fakeness that doesn't include this list is selling something.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mirrors&lt;/strong&gt;, as above — mitigated, not solved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fine text and faces&lt;/strong&gt; — unreliable; crop them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex ceiling angles, staircases, glass furniture, and patterned floors&lt;/strong&gt; — geometry drift concentrates exactly where the interesting architecture is. Flat-walled rectangular rooms are the easy case; if your room isn't one, expect to iterate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Input quality is a hard ceiling.&lt;/strong&gt; Three or four iterations repair most frames. Beyond that, the input photo is the limiting factor and no amount of re-rolling will fix it — motion blur and missed focus can't be recovered by anything downstream.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scale is still estimated, not measured.&lt;/strong&gt; Centimeter-level accuracy from pixels is not on the menu. Renders inform; tape measures decide.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And one warning from outside our walls that we take seriously: a real Massachusetts listing got called out for "fake staging, ambitious proportions, and impossible furniture angles" — a physics-defying rug, an unverifiable built-in cabinet — and the damage wasn't limited to one photo. Once buyers catch one dishonest frame, they discount every honest one in the listing. This is also why disclosure costs nothing: a render labeled "virtually staged" survives scrutiny perfectly well, because scrutiny finds exactly what the label promised — a redesigned photo, not a redesigned room. The unlabeled version is the one gambling the listing's credibility on buyers not looking closely.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you take one thing from this post
&lt;/h2&gt;

&lt;p&gt;Fake-feeling renders are rarely a model-quality problem in the abstract. They're a specific, nameable list: even lighting with no source, unresolved scale, glossy averaged materials, and drifted geometry. Fix them in that order, one change per render, and audit at 100% zoom against the original before anything goes in front of a client or a buyer.&lt;/p&gt;

&lt;p&gt;If you want to run your own room through the pipeline and audit the result yourself: goodesign takes one photo, renders in about a minute, and your first 5 credits are free with no credit card. The full write-up this post draws from — including the complete seven-tell checklist — is in our knowledge hub piece on &lt;a href="https://www.goodesign.io/knowledge-hub/why-does-my-ai-render-look-fake" rel="noopener noreferrer"&gt;why AI renders look fake&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This article was drafted with AI assistance and edited/fact-checked by the Goodesign engineering team.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>engineering</category>
      <category>abotwrotethis</category>
    </item>
    <item>
      <title>Prompt Integrity: We Stopped Letting the LLM Rewrite Your Prompts — Here's What Broke</title>
      <dc:creator>GooDesign AI</dc:creator>
      <pubDate>Mon, 02 Nov 2026 01:18:40 +0000</pubDate>
      <link>https://dev.to/goodesign/prompt-integrity-we-stopped-letting-the-llm-rewrite-your-prompts-heres-what-broke-3jlk</link>
      <guid>https://dev.to/goodesign/prompt-integrity-we-stopped-letting-the-llm-rewrite-your-prompts-heres-what-broke-3jlk</guid>
      <description>&lt;p&gt;The failure report that changed how we build goodesign is the kind you can reconstruct from almost any prompt-rewriting pipeline, because they all break the same way. Picture a completely ordinary session: a user uploads a photo of their living room, types a five-word prompt — "make it cozy, keep the rug" — and gets back a render where the rug was gone, the sofa had visibly grown, and the whole scene had slid from afternoon daylight into warm evening lamplight.&lt;/p&gt;

&lt;p&gt;Nothing about that output was broken in the model sense. The image was sharp, the shadows were plausible, every chair leg touched the floor. It just wasn't what the person asked for. The rug — the one thing they had explicitly protected — was the first thing to go.&lt;/p&gt;

&lt;p&gt;We traced it, and the culprit wasn't the image model. It was us. An LLM sitting in the middle of our pipeline, added months earlier to "help" users who type vague requests, had rewritten those five words into a 140-word template before the render ever started. This post is about what that rewrite broke, why prompt rewriting loses intent in the first place, the two-layer architecture we replaced it with, and how we test for intent loss now. If you build anything where a user types words and a generative model draws pictures — AI room design, virtual staging, photo editing, anything — this is the failure mode I'd put at the top of your list.&lt;/p&gt;

&lt;h2&gt;
  
  
  The prompt that came back wrong
&lt;/h2&gt;

&lt;p&gt;Here is roughly what our expansion layer produced from "make it cozy, keep the rug":&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"A cozy living room interior with warm ambient lighting, plush textiles, soft throw blankets, layered cushions in warm earth tones, an inviting oversized sofa, a soft evening glow from table lamps, photorealistic, professional interior photography."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Read it next to the original and the damage is easy to miss, because nothing in it is wrong. That's what makes prompt rewriting dangerous. Line by line:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The user's only hard constraint — &lt;strong&gt;keep the rug&lt;/strong&gt; — is gone entirely. It isn't paraphrased or weakened. It's just not there.&lt;/li&gt;
&lt;li&gt;"Cozy" got decoded as &lt;em&gt;evening lighting&lt;/em&gt;. That is one interpretation of cozy. It was not this user's interpretation, and it happened to require changing the one thing in the photo the user liked.&lt;/li&gt;
&lt;li&gt;The template stacked enough soft-furniture adjectives that the renderer, averaging all those simultaneous demands, quietly reallocated "softness" — the rug became throw blankets and cushions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The individual edits all looked defensible. Warm light is cozy. Big sofas are cozy. Nothing in the output contradicted the word "cozy." It contradicted the person. And because the rewrite happened silently between the text box and the renderer, the user had no way to know their words were never the words the model saw.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why rewriting loses intent: words are not specifications
&lt;/h2&gt;

&lt;p&gt;The first thing to understand is what a short prompt actually contains. In a room-design prompt, users mix at least three kinds of content:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Intent&lt;/strong&gt; — what they want changed ("make it cozy")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Constraints&lt;/strong&gt; — what must not change ("keep the rug")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rendering parameters&lt;/strong&gt; — the physical facts a renderer needs: light direction, camera height, material names, counts&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Users are good at the first two and usually silent on the third, because the third is our job, not theirs. The expansion layer was written as if all three were missing. So it inflated rendering parameters (lighting adjectives, furniture descriptions), inflated intent into a full aesthetic manifesto, and dropped the constraint — because constraints are the one thing an expansion model has no reason to carry.&lt;/p&gt;

&lt;p&gt;That last point deserves emphasis. "Keep the rug" is negative space in a sentence. It describes what should &lt;em&gt;not&lt;/em&gt; happen. A language model trained to add helpful detail has essentially zero gradient toward preserving an instruction to not add detail. In our own logs, once we went looking, the keep-clause was reliably the first casualty of rewriting. Intent and parameters survived rewrites in various mangled forms; constraints tended to simply vanish.&lt;/p&gt;

&lt;p&gt;The second problem is the word-to-image gap. "Cozy" does not map to any geometry. When a diffusion model reads "cozy," it resolves the adjective through its training priors — the statistical average of every cozy living room in its captioned training data. That average skews warm, textile-heavy, and evening-lit. When our LLM rewrote the prompt, it wasn't translating the user's intent into renderer language, because there is no such translation. It was &lt;em&gt;replacing&lt;/em&gt; the user's private meaning of "cozy" with the training distribution's public meaning. The rewrite didn't add information. It substituted a different person's taste and called it help.&lt;/p&gt;

&lt;p&gt;Third: rewriting has a direction bias. Language models are rewarded for fluent, detail-rich, confident text, so a rewriter normalizes unusual input toward the middle of the distribution. The more specific and unusual the user's request, the more the rewrite sands it down. Vague prompts get inflated; specific prompts get flattened. Both directions move the output away from the user and toward the average.&lt;/p&gt;

&lt;p&gt;And there's a quiet authority transfer nobody signs up for. Once your pipeline decodes "cozy" into a fixed list of signifiers — lamps, throw blankets, earth tones — you have decided, for every user, forever, what cozy means. Some of your users mean candles and rain on the window. Some mean a deep sofa and a bright, crisp winter-lit room. A rewrite can't know. It can only pick one and disguise the choice as neutrality.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: a verbatim layer and a parameter layer
&lt;/h2&gt;

&lt;p&gt;We did not remove the LLM from the pipeline. Truly vague input ("fix this room") genuinely benefits from defaults. What we changed is &lt;em&gt;where&lt;/em&gt; the model is allowed to act, and in what shape.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 1: the user's text is immutable.&lt;/strong&gt; Whatever the user typed goes to the renderer byte-for-byte, first in the composed prompt. No synonym upgrades. No reordering. No cleanup of typos, no expansion of "cozy" into "warm, intimate, inviting" — that triple-synonym move is precisely how you get averaged mush, because each synonym is another vote for the mean. If the user's sentence is short, the render starts from a short sentence, and the fill comes from Layer 2 where we can see it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 2: structured supplement, not prose.&lt;/strong&gt; A small, cheap model call classifies what the user actually specified, then emits a strict JSON object of rendering parameters — light source and direction, time of day, camera height, up to about four material anchors. It may only fill fields the user left empty. The JSON is serialized into a short bracketed hint that gets appended &lt;em&gt;after&lt;/em&gt; the user's text, never interleaved with it, never substituted for it. Our materials guide makes the same point from the user side: one named material per surface beats paragraphs of mood adjectives, because past roughly 30 words of stacked demands, the model starts averaging them. The supplement layer exists to add the technical floor a photo-real render needs — nothing more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Constraints leave prose entirely.&lt;/strong&gt; "Keep the rug" no longer lives in the prompt text, where it competes with forty adjectives and loses. It gets extracted into a machine-readable keep-list that drives the edit-in-place controls directly: locked regions, preserved wall color, preserved window geometry. A constraint expressed as code is a constraint the pipeline cannot paraphrase away.&lt;/p&gt;

&lt;p&gt;The supplement layer runs under four rules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Never contradict.&lt;/strong&gt; If the user said "bright daylight," the layer is forbidden from appending a warm-evening hint. User wins, always.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never touch the user's substring.&lt;/strong&gt; The composed prompt is &lt;code&gt;user_text + hint&lt;/code&gt;, and the boundary is tested.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotent.&lt;/strong&gt; Running the supplement twice changes nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Valid JSON or nothing.&lt;/strong&gt; If the classifier is unsure, it emits an empty object and the render proceeds on the user's words alone. Uncertainty degrades to silence, not to creativity.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you've read our public &lt;a href="https://www.goodesign.io/how-to-prompt" rel="noopener noreferrer"&gt;prompting guide&lt;/a&gt;, this architecture will look familiar, because the user-facing advice describes the same shape: lead with an action verb, name one style direction, list 2–3 anchor pieces with a finish, give placement cues, and finish with an explicit keep list. The pipeline now respects that structure instead of flattening it.&lt;/p&gt;

&lt;p&gt;One honest trade-off: with rewriting gone, a genuinely lazy prompt produces a more literal — sometimes plainer — render than it used to. We think that's correct. The old way felt magical partly because it was making decisions the user never delegated.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we test prompt integrity now
&lt;/h2&gt;

&lt;p&gt;You can't fix intent loss you can't see, so the first thing we built was visibility: the user's raw text is logged separately from the model-facing composed prompt, permanently. Every support conversation about "that's not what I asked for" now starts with a diff.&lt;/p&gt;

&lt;p&gt;Then we built a regression suite around the concept of &lt;em&gt;annotated intent&lt;/em&gt;. Every prompt in the golden set carries tags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;make it cozy, keep the rug&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;must_keep&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rug&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wall color&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;window geometry&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;must_change&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;overall mood&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;must_not_introduce&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;evening lighting&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rug removal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On every change to the model, the hint layer, or the composer, the golden set re-renders and gets scored: a VLM judge checks each tagged constraint, and a rotating human pass spot-checks the judge, because VLM judges are themselves models with their own lazy priors — they will happily grade "the rug is still there" as true if the rug's color changed. The judge scores; humans arbitrate.&lt;/p&gt;

&lt;p&gt;The boundary cases in the set matter more than the happy paths:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Constraints-only prompts.&lt;/strong&gt; "Don't change anything, just brighten it." The pipeline must treat this as a lighting edit, not a redesign invitation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contradictory prompts.&lt;/strong&gt; "Cozy but bright daylight." The user wins; the hint layer stays silent rather than picking a side.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-English prompts.&lt;/strong&gt; The product runs in 8 languages, and user words pass through untouched in all of them. The supplement layer may localize its hints, but it may not translate the user's sentence into English first — that's a rewrite with extra steps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Counting language.&lt;/strong&gt; "Two nightstands, one large piece of art." Exact counts survive; "some" and "a few" from the hint layer never override a stated number.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Junk.&lt;/strong&gt; Empty strings, emoji-only input, 400-word rambles. Ramblers get length-capped from &lt;em&gt;our&lt;/em&gt; side — the user's first N words survive, our words get cut.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, the keep-list is treated as an API contract. If a render violates a tagged keep-constraint, that's a bug with a reproducing case, not a vibe to shrug at.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed after launch — shown with images, not dashboards
&lt;/h2&gt;

&lt;p&gt;I'm suspicious of posts that claim "+38% user satisfaction" without a methodology, so I won't invent one. What I can show is what the pipeline produces now. Our public gallery is 35 before/after pairs — 7 rooms across 5 transformation types, every pair generated from a single input photo. These two pairs are straight from it; notice in each that the fixed features of the room survive the transformation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fruy6myczymdfqz7duv95.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fruy6myczymdfqz7duv95.webp" alt="Before: a real room photo from the Goodesign before/after gallery" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk1w9jrdxflfj29oasccv.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk1w9jrdxflfj29oasccv.webp" alt="After: the AI redesign of the same room, from the Goodesign gallery" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx2bsd5yhetlmbeb5uqvz.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx2bsd5yhetlmbeb5uqvz.webp" alt="Before: a second real room photo from the Goodesign before/after gallery" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjx34yqt9k78salkkty4f.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjx34yqt9k78salkkty4f.webp" alt="After: the AI redesign of the second room, from the Goodesign gallery" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What actually changed, described honestly and qualitatively:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User adjectives now show up as themselves.&lt;/strong&gt; When someone's prompt survives to the renderer intact, the render reflects &lt;em&gt;their&lt;/em&gt; version of "cozy," not the template's.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep-constraints mostly survive.&lt;/strong&gt; Not 100% — on cluttered scenes the model still sometimes fights a locked region — but violations are reproducible from the golden set now, which is the difference between a bug and a ghost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composed prompts got much shorter&lt;/strong&gt;, because a large fraction of their former length was ours, not the user's. Shorter prompts also mean cheaper renders and, usefully, easier diffing when something goes wrong.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What didn't change, because no amount of prompt hygiene fixes it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Renders still can't measure.&lt;/strong&gt; If the actual wall is 3.4 meters and the sofa in the render reads as 2.6, nobody in the pipeline knows. Scale is estimated from pixels; a tape measure is still the final word before anyone buys furniture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The model still drifts on mirrors, fine text, and complex ceiling lines.&lt;/strong&gt; Those are model limitations, not prompt-layer ones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vague input still produces default-flavored output.&lt;/strong&gt; We made the defaults honest and inspectable. We didn't abolish them, and users who type three words and expect a miracle will still get a competent render of somebody's taste — just with their own words, verbatim, at the front of it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Three rules for anyone shipping a similar pipeline
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Log the user's raw text separately from the model-facing prompt — forever.&lt;/strong&gt; The single most useful debugging artifact we have is the pair (what the user said, what the model saw). If you only log the second, every intent-loss bug is invisible by construction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. If you must expand user input, expand into structured parameters, never prose.&lt;/strong&gt; JSON fields can be unit-tested, diffed, suppressed, and audited. Free-text expansion can only be regretted. The entire class of "the LLM overrode the user" bugs shrinks to "the classifier misfiled a field," which is a much smaller animal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Move constraints out of language and into product surfaces.&lt;/strong&gt; A "keep this wall" checkbox beats a keep-clause any rewriter can eat. Every constraint you can express as code instead of tokens is a constraint the pipeline cannot paraphrase away. The prompt is the worst place to store the user's non-negotiables — it's the one part of the stack whose entire job is to change.&lt;/p&gt;

&lt;p&gt;None of this is exotic. The hard part wasn't engineering; it was accepting that "helpful" and "faithful" are different objectives, and that we had optimized for the one users never asked for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it on your own room
&lt;/h2&gt;

&lt;p&gt;If you want to see how the pipeline behaves on your photos: goodesign takes one room photo and renders a redesign in about a minute. Your first 5 credits are free, no credit card required, and 1 credit is 1 render. And if you'd rather write prompts by hand, the five-part pattern — action, style, anchors, placement, keep list — is laid out in the prompting guide linked above.&lt;/p&gt;

&lt;p&gt;This article was drafted with AI assistance and edited/fact-checked by the Goodesign engineering team.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>engineering</category>
      <category>abotwrotethis</category>
    </item>
  </channel>
</rss>
