DEV Community

John
John

Posted on • Originally published at hexisteme.github.io

Why Your Vibe-Coded App Looks Worse Than the Showcases: A Forensic Audit

Originally published on hexisteme notes.

I kept circling the same question for weeks: why does my vibe-coded game look worse than the polished demos every other AI-built project seems to post? Am I prompting wrong? I'd nudge, ask for "something more polished," get back a slightly different flavor of mediocre, and assume the gap was somewhere in my phrasing. Eventually I stopped guessing and ran a proper audit — six agents, one doing a local harness audit, one doing transcript forensics across 92 of my own sessions, three doing web research in different directions, and one doing nothing but trying to poke holes in the others' conclusions. The verdict surprised me: I was partially right about my prompting, but it wasn't the main problem. The main problem was that I didn't actually know which parts of my own pipeline were running.

The prompting half was real, but small

The forensic pass over my sessions found exactly what I was afraid it would find. Across my game projects: zero visual references provided, zero style tokens specified — no palette, no size targets, no font choices — and a dominant pattern of asking for something like "make the design high quality" without giving Claude anything to anchor that to. Every color decision, every proportion, every shading choice was left entirely to the model.

The forensics also surfaced a pattern I hadn't noticed about myself: in a different project of mine, a novel I'm writing, I attach five or six reference images per turn, routinely. In my game projects, that number was zero. It wasn't that I don't have the habit of giving visual references. I just never brought it into games specifically.

The transcripts had a few lines that made this concrete. "It's a character game. The design needs to be high quality too" — a result metric with no direction, which is exactly the shape of request that makes a model fall back to its own defaults. Later: "this microbe-level design isn't appealing" — a real critique, delivered after the fact, when a moodboard up front would have caught it before any generation spend. And at one point I asked Claude directly whether the result was fun. Aesthetic and fun judgment isn't something a model can hand back to you honestly if you're the one who's supposed to be judging it.

The bigger problem: pipelines that only look like they're running

Here's what the audit actually surfaced as the dominant cause, and it wasn't a prompting problem at all. I have three separate visual-asset pipelines set up across my projects. Functionally, none of them were working at the time.

PixelLab — the tool that had actually produced my one real success, a character called CodeSprout in my iOS/SpriteKit project — had an account balance of $0.00. Generation had simply stopped being possible partway through that project, and I hadn't noticed, because nothing failed loudly. The higher character tiers, Evolved and Elder, got silently replaced with code-approximated squash-and-stretch shapes instead — a fallback that a note buried in that project's own prototype notes had actually recorded, which is the only reason I found out after the fact rather than never.

A second project, a Godot testbed, has a well-written style.json and four concept PNGs — and then nothing. The full-sheet generation step needed a local ComfyUI server plus a Hunyuan3D model file, and that server was down with the required model missing. The project didn't fail; it most likely just quietly stopped advancing at the cheapest stage that had already succeeded.

The third case is the starkest. A separate Godot project — sound design done, icon done, iOS export pipeline fully ready to ship — still has its main character rendered as a literal ColorRect: an orange rectangle for a body, green dots for eyes. I have an asset-generation pipeline built specifically for this. It had existed for six-plus weeks. It had simply never been run against this project. Not broken, not blocked — never invoked.

Three projects, three different failure shapes, and the common thread across all of them is the same: at no point did any of these pipelines fail in a way that stopped me. Everything silently degraded to the next thing that would still produce an output — a code-approximated shape, a stalled-but-not-erroring concept stage, a default primitive that Godot renders without complaint. From the outside, and even from inside the session transcripts, a silently-skipped generation step and an incompetent one look identical. Both produce "the art still looks bad." Only one of them is a model-quality problem.

And it wasn't that I was asking the model to do something it's structurally bad at, either — the audit checked that too. In the one project where the pipeline was actually running (the iOS/SpriteKit game), the model called the PixelLab generation tool 23 times, correctly, and never once tried to fake the art with code. Tool selection wasn't the failure mode here. Tool state was.

Why "make it prettier" doesn't fix the part that is a prompting problem

The part of this that is genuinely about prompting has a mechanism behind it, and it's one Anthropic states plainly in its own material: without an explicit different instruction, the model reverts to "safe" choices, because the web's training data skews overwhelmingly toward design that offends no one. Asking for "polished" or "pretty" doesn't narrow that down — it just re-samples the training average. The purple-and-indigo gradient that shows up everywhere in AI-generated interfaces has been traced back, credibly, to a single default color in a 2019 Tailwind UI example that ended up saturating years of tutorial content.

Even Anthropic's own official frontend-design skill — installed 500,000-plus times specifically to break this pattern — names three defaults it has to actively suppress: cream background with serif type and terracotta accents, black background with a single acid-toned accent, and newspaper-style typesetting. And the skill's own fix produces a new dominant look of its own once enough people use it. The lesson there isn't "the skill doesn't work" — it's that no single layer is sufficient. You need the skill, explicit style tokens, and an actual reference image, together, or the model has nothing narrow enough to converge on.

The same mechanism shows up in game art specifically. When a model draws pixel art directly in code — SVG paths, GDScript-drawn shapes — the result is structurally programmer art, the same effect Simon Willison's pelican-riding-a-bicycle benchmark keeps demonstrating on every new model release. That's not a reason to give up on AI-generated game art; it's a reason to keep the model out of the drawing step entirely and let it call a dedicated generation tool — which, again, is exactly what it did correctly in the one project where that tool was actually reachable.

What I changed, in order

I didn't try to fix everything at once. The audit gave me a single ranked recommendation, not a checklist: top up PixelLab credits first, because it's the shortest path back to the only pipeline I'd already proven works end to end. Recovering the local ComfyUI server is second, and only if I actually need the higher-resolution mascot tier that PixelLab doesn't cover — that recovery needs its own preflight (confirming disk headroom, since a prior local-model incident on this machine taught me not to skip that check) before I touch it. Replacing the ColorRect placeholder character is third, specifically because that project's deployment pipeline is otherwise done — it's the highest-leverage single asset swap available to me right now.

On the request side, I made exactly three changes, not a longer list:

I now ask for a moodboard before any implementation starts — three or four style candidates as actual images, not descriptions, that I pick from and then freeze into a style file every later asset has to match. Naming one or two existing games I want the look to resemble does more work than any number of adjectives; a name narrows the model's choice space in a way "polished" never will.

I replaced "make it look polished" with a gate I can actually check: a character silhouette has to be legible at a minimum readable size on the target screen, and it has to stay distinguishable if I convert it to grayscale. No completion gets declared until that passes — not because the visual sense behind "polished" was wrong, just because it wasn't checkable.

And I asked the model to close its own screenshot loop: capture its own output after each asset or UI change, review it, and show me before moving on, rather than waiting for me to remember to look. I don't have to change what I do here — I still just correct in text — but the loop closes without depending on my memory of whether I actually looked.

The lesson that generalizes past games

The specific fixes here are game-specific — style files, silhouette gates, generation-tool routing. The lesson underneath them isn't. Before you conclude a model is bad at something, check whether the tool that was supposed to produce the result actually ran. A generation-credit balance that silently hits zero, a local server that's down with a missing model file, a pipeline that exists but was never invoked on this particular project — none of these produce an error you'll notice. They all produce output that looks like "the AI tried and this is what it's capable of." It isn't. It's what happens when the thing you assumed was running wasn't, and nothing was built to tell you.

More notes at hexisteme.github.io/notes.

Top comments (2)

Collapse
 
komo profile image
Reid Marlow

The silent fallback is the part that feels most worth fixing first. Prompt quality can improve the target, but pipeline state decides whether the target is even reachable.For asset flows I like a preflight that emits a boring manifest before the agent spends a token: balance/model file/server up/input refs present/output path writable. If any row is red, the run should stop loudly instead of producing a plausible-looking substitute.

Collapse
 
hexisteme profile image
John

The preflight manifest with explicit red/green rows is a sharper fix than anything I outlined — it turns silent fallback into a hard gate before the agent burns a token. I like the boring checklist: balance, model file, server up, input refs, output path writable. That stops the plausible-looking substitute at the source instead of auditing the damage afterward. Thanks for the concrete pipeline guard.