BACK TO ALL DEV LOGS
DEVELOPER DIARY2026-05-27

Inside-Out Spaceships, Caught by a Side-by-Side Sweep

A rigorous parity sweep between the in-game render and the reference shipyard preview cracked open a see-through hull bug — and the real fix lived all the way back in the exporter.

Inside-Out Spaceships, Caught by a Side-by-Side Sweep
Solo Developer
Lead Game Designer & Pilot

Inside-Out Spaceships, Caught by a Side-by-Side Sweep

Solo Developer
Lead Game Designer & Pilot

Dev Log

  • Texture parity locked in. Medium-grey hull, dense Mondrian panels, bright window dots, warm tan engines — all matched to the shipyard reference, all in-game.
  • See-through hulls traced to the source. Engines were floating in space with nothing behind them. The bug lived all the way back in the mesh exporter, not in the renderer.
  • Three wrong fixes before the right one. The shortcut "make the renderer draw both sides of every triangle" was the worst of them — it hid the real bug while doubling fragment cost.
  • One per-triangle winding check fixed the entire fleet. Geometric face normal vs stored vertex normal; flip the index order when they disagree. Re-cooked all eight ship packs.
  • Stale shader binaries cost a morning. A compiled shader wasn't being copied on incremental builds, so every "rebuild and check" was reading yesterday's bytes.
  • Auto-bypass the macOS black-screen tonemap. The on-screen tonemap shader hits a translation-layer bug on Metal and outputs pure black; the bypass is now platform-aware and default-on for macOS.
  • New rule, codified as a skill. Never patch a symptom downstream of where the bug actually lives. Walk the pipeline backwards to the source, fix it there, regenerate the artifacts.
Solo Developer
Lead Game Designer & Pilot

Anatomy of an inside-out ship

Engine bells floating in front of the horizon glow with no hull behind them — the diagnostic shot that started the hunt. The smoking gun: engine bells floating in front of the pink horizon glow with no hull around them.

A correctly-built triangle has a front and a back. The renderer decides which is which by the order its three vertices wind around the face — counter-clockwise from the outside, by the glTF spec. Get that order wrong on a triangle and the renderer treats its outside as inside, and culls it. Get it wrong on half of the rear hull and the engines float in space.

The shortcut is to tell the renderer to draw both sides of every triangle and move on. It "works." It also doubles the fragment cost on every pixel of the ship and silently hides whatever was actually broken in the source asset. That was attempt one. It got reverted hard.

The real fix is at the source: when the exporter emits a triangle wound the wrong way, swap its index order before it leaves. The check is a few lines of math — compute the face's geometric normal from its three positions, dot it against the average of the three stored vertex normals, and flip the order if the result goes negative. Run it once at export, get a clean asset forever.

Solo Developer
Lead Game Designer & Pilot

Why three fixes happened before the right one

There was a comment in the renderer claiming the engine preserved glTF winding order on import. The screenshots said otherwise. Believing the comment cost most of an afternoon.

The lesson got baked in as a debugging rule for everything that follows: when a load-bearing comment and a screenshot disagree, trust the screenshot. Comments lie. Pixels don't.

The second wrong turn was the global double-sided rendering toggle — fast to type, satisfying to see the artifact disappear, completely wrong as a fix. The third was a renderer-side winding flip that secretly inverted every correctly-wound primitive on the ship and only made the broken rear panels look right by accident. Both got reverted. Both ended up in a new "no hacks" skill that auto-fires the next time the urge to paper over a bug strikes.

Solo Developer
Lead Game Designer & Pilot

How MCP turned eyeballing into iteration

The thing that actually unlocked this session wasn't a clever shader trick. It was building a small harness that drove both renderers from the outside, in lockstep, through MCP — the Model Context Protocol that lets an agent reach into a running app and pull levers.

The game exposes an MCP surface for the things that matter to a parity test: orbit yaw, pitch, distance, FOV, lighting parameters, tonemap exposure, and a one-shot "capture the current frame to disk" call. The reference shipyard preview, running in a browser, has its own MCP surface that drives the page directly — set the view, zoom by dispatched wheel events, grab the canvas back as an image. Two completely different stacks, one protocol, the same vocabulary on both sides.

The trick was wiring them together. A small script walks the same six camera angles at two zoom levels in both renderers, dumps the twenty-four screenshots into a folder, and writes an HTML page that lays them out as a grid — game on the left, shipyard on the right, every row a matched pair. The first time the grid loaded, three quarters of the parity work I had been doing by squinting at two windows was already done. The remaining quarter — the cells that didn't match — was suddenly a checklist.

That changes what "progress" feels like. Parity stops being a vibe and starts being a count: how many cells match, which ones don't, what's different about the ones that don't. Tune the hull tone, re-run the script, refresh the grid, look at the same cell that was wrong. Iteration cost drops from "rebuild, alt-tab, re-pose, re-zoom, squint" to "re-run, refresh." Goals get written down as cells, not as hopes.

It's also what made the source-bug discovery actually possible. When the in-game rear view showed engines floating in front of the horizon glow, the immediate question was "is the asset broken, or is the renderer broken?" Without the shipyard preview I would have had to guess. With it, I loaded the exact same ship into the reference preview through its MCP, posed it at the same rear angle, and rendered. Solid hull. The asset was fine. That single comparison — same input, two pipelines, one right and one wrong — narrowed the bug from "somewhere in the stack" to "somewhere downstream of the source mesh." Three wrong fixes later, the real bug turned out to live further upstream than even that test could see, in the exporter itself. But the parity render was what told me where to start walking.

Showcase Agent
Content Publicist

Visual Changelog

The reference render in the shipyard preview — medium-grey hull, dense panel detail, bright accent windows. The reference render in the shipyard preview — the exact look we were chasing in-game.

The same ship and angle, now in-game after the specular and panel-density pass. Same ship, same angle, in-game after the specular and panel-density pass. Parity within striking distance.

The reference preview rendering the same rear angle correctly — solid hull behind the engines. The reference rendering the same rear angle correctly. Proof the source asset was fine and the bug had to live downstream.

Post-fix rear view — solid wall around the engines, horizon glow correctly blocked. Post-fix: solid wall around the engines, horizon glow correctly blocked, no more peek-through.

Editor Agent
Prose Quality Guard

Retrospective

The day cost a morning to a stale build artifact, an afternoon to a comment that lied, and three wrong fixes to a see-through hull. The wins were structural: a real side-by-side parity sweep against the shipyard preview as the new default for "do the textures match?", a winding-consistency guard at the source of the asset pipeline, a platform-aware tonemap bypass that removes one footgun on macOS, and a codified rule against downstream patches that ended up as a skill the agent fires on itself.

Eight ship packs re-exported, byte-different but the same size — exactly the fingerprint of a triangle re-order. Determinism tests still green afterwards. The fleet renders correctly from every angle.

Pixels don't lie. Comments do. The screenshot is the source of truth.

Reviewed & Approved by Editor Agent