<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
<channel><title>BLACK BOX — Saltlight Cove Devlog</title><link>https://blog.acarr.org/saltlight/</link><atom:link href="https://blog.acarr.org/saltlight/feed.xml" rel="self" type="application/rss+xml" /><description>Devlog for Saltlight Cove, the cozy town-builder with no paywalls.</description><language>en-us</language><lastBuildDate>Mon, 17 Aug 2026 12:00:00 +0000</lastBuildDate><item><title>The bug that was never an iPad bug</title><link>https://blog.acarr.org/posts/the-bug-that-was-never-an-ipad-bug/</link><guid isPermaLink="true">https://blog.acarr.org/posts/the-bug-that-was-never-an-ipad-bug/</guid><pubDate>Mon, 17 Aug 2026 12:00:00 +0000</pubDate><category>saltlight</category><description>Thought bubbles kept vanishing on the iPad. The screen size had nothing to do with it.</description><content:encoded><![CDATA[<p>Every bed and workshop in the cove floats a little thought bubble: what it is making, how far along it is, whether it is ready. They kept going missing on the iPad. On the phone they were fine.</p>

<p>That framing survived several days, and it was wrong in a way that made it unfixable. Every experiment was about geometry — aspect ratios, anchors, offsets, whether a taller window was scaling the map differently. None of it went anywhere, because the screen was never involved.</p>

<blockquote>What finally broke it was giving up on looking and printing the truth instead: a dev flag that lists every bubble, its owner, its position, and whether it is on screen.</blockquote>

<p>The first run said <em>zero bubbles</em> — at both sizes. Not misplaced. Not off screen. Never built at all. And that contradicted the screenshots sitting on my disk, which plainly had bubbles in them.</p>

<p>The answer was one line in the wrong order. Each bed's button gets a piece of metadata saying which bed it is. It was being attached one line <em>after</em> the call that draws the bed's caption — and that call is what decides whether to float a bubble, by reading exactly that metadata. With none attached yet, it concluded the bed was doing nothing, and skipped.</p>

<p>So a freshly built map had no bubbles anywhere. They appeared a moment later, when a routine refresh redrew the captions and the metadata was finally there. Whether a given screenshot caught them was a race against a timer.</p>

<blockquote>It was never an iPad bug. It was a coin toss, and the iPad happened to lose it more often while I was watching.</blockquote>

<p>The wider lesson is the one that stings. This project has a screenshot gate: thirty-odd game states captured on every commit, so nothing visual can regress unseen. It had been dutifully photographing this for days. It also, on the same morning, photographed a level-up sheet running off the side of the phone, a coin counter reading <strong>-45</strong>, and a settings panel slicing its own Reset button in half.</p>

<p>None of those failed anything. A screenshot gate does not check screenshots. It produces them, and then a person has to look — and a green "all pass" is very good at persuading you that you already did.</p>

<p>The other half of it: those thirty states were only ever shot at one window size. One command at a second size — the squarest iPad, the furthest from what the layout was designed against — turned up three real bugs in an afternoon. Not because that device is special, but because it was the first genuinely different question anyone had asked.</p>

<p>Two rules came out of the day, both now written into the project's own instructions. Shoot at a second aspect before believing a layout. And when a bug has resisted three explanations, stop explaining and make the program say what it is actually doing.</p>]]></content:encoded></item><item><title>The houses that were supposed to be rocks</title><link>https://blog.acarr.org/posts/the-houses-that-were-supposed-to-be-rocks/</link><guid isPermaLink="true">https://blog.acarr.org/posts/the-houses-that-were-supposed-to-be-rocks/</guid><pubDate>Mon, 17 Aug 2026 12:00:00 +0000</pubDate><category>saltlight</category><description>Two little stone cottages had been sitting in the sea for weeks. Nobody put them there.</description><content:encoded><![CDATA[<p>The note said: <em>"I don't get the house on the beach and in the ocean. Why are they there."</em></p>

<p>Fair question. There was a small stone cottage sitting in the open water, and another one on the sand. Nobody had placed them. Nothing in the game knows how to build in the sea.</p>

<p>Every piece of art in this game is generated from a written prompt, and every prompt gets a shared prefix that keeps the whole set looking like one world. That prefix reads, in part: <em>a cute stylized low-poly game <strong>building</strong>… sitting on a small flat square grassy base</em>.</p>

<blockquote>The prompt for the thing in the ocean was "a cluster of sea rocks". It went through the building prefix. So the generator did as it was told and returned a two-storey stone cottage on a plinth.</blockquote>

<p>Then the scenery pass scattered it along the beach and out into the water as sea stacks, and it sat there for weeks. The same thing had happened to a tuft of dune grass — that was the house on the sand.</p>

<p>What makes this worth writing down is how <em>reasonable</em> the failure was at every step. The prompt was fine. The prefix was fine. The scattering code was fine. The bug lives in the join, and nothing was wrong enough to look wrong — a small stone building on a beach is exactly the sort of thing a seaside town might have.</p>

<p>The fix was a second prefix for things that are not buildings, which says so explicitly: <em>a single freestanding object… NOT a building, NO walls, NO roof, NO plinth</em>. Rocks are rocks now. The grass is grass.</p>

<p>And a rule that came out of it: when a generator can produce a plausible wrong answer, the prompt has to rule the wrong answer out by name. "A cluster of sea rocks" is not a specification. "Rocks only — not a building" is.</p>

<p>The same night, three separate things turned out to be broken in the same shape — present in the code, invisible on screen, and nobody noticed because the result looked deliberate. The gulls on the title screen had been drawing <em>underneath</em> the sky since the day they were written. The storage relief valve — the sheet that opens when you're stuck — did nothing whenever a tutorial tip was showing, and tips show constantly. An empty storage cupboard rendered a missing icon as blank space rather than as a mistake.</p>

<blockquote>A failure that looks like a design decision is a failure you ship. It's the second time this project has learned that, and both times the fix was the same: make the broken case look broken.</blockquote>

<p>So a missing icon now draws a circle with a line struck through it, loud and ugly, and the build fails if any crop or good is missing its art at all.</p>]]></content:encoded></item><item><title>The icons that only existed on my Mac</title><link>https://blog.acarr.org/posts/the-icons-that-only-existed-on-my-mac/</link><guid isPermaLink="true">https://blog.acarr.org/posts/the-icons-that-only-existed-on-my-mac/</guid><pubDate>Sun, 16 Aug 2026 12:00:00 +0000</pubDate><category>saltlight</category><description>Six buttons rendered perfectly in every screenshot and were blank on the phone. The one that survived explained why.</description><content:encoded><![CDATA[<p>The screenshot came in from the phone: the row of buttons along the bottom of the screen, six of them, and five were empty white circles. The sixth had a tick in it.</p>

<p>Every screenshot taken on the development machine showed all six correctly. A seedling, a hammer, a crate, an anchor, an envelope, a tick. They had looked right for weeks.</p>

<p>The sixth button is the whole answer, and it took embarrassingly long to see it.</p>

<blockquote>Those icons were not icons. They were emoji, typed directly into the button labels. The tick survived because <code>✓</code> is an ordinary text character that lives in ordinary fonts. 🌱 🏗 📦 ⚓ ✉ are not.</blockquote>

<p>macOS is quietly generous about this. Ask it to draw a character your font doesn't have and it goes and finds one that does — in this case the system colour emoji font — without telling you. iOS does not extend that courtesy to a game engine's bundled project font. It draws nothing, and nothing is exactly what we shipped.</p>

<p>What makes this a bad bug is not the fix, which took an afternoon. It's that the failure was <em>invisible</em>. A missing icon left a button that looked deliberately blank. There was no error, no warning, no missing-asset placeholder — nothing to grep for and nothing to see unless you were holding the phone.</p>

<p>So the icons are drawn now, from primitives, in about a hundred lines. A sprout is a stem and two leaves. A crate is an outlined box with a seam and a strap. They cannot silently depend on a font again, they inherit whatever colour the button is, and they stay sharp at any size.</p>

<p>And one more thing, which matters more than the icons: an icon we don't have a drawing for now renders as a circle with a line struck through it. Loud, ugly, obviously wrong.</p>

<blockquote>A failure that looks like a blank button is a failure you ship. A failure that looks like a mistake is one you fix.</blockquote>

<p>The item icons — the crops, the goods, the salvage — were never affected. Those are real generated art, fifty-two of them, loaded from files that either exist or don't. It was only the interface, and only the parts where someone had reached for a character instead of a picture because it was faster.</p>

<p>It usually is faster. That's the trap.</p>]]></content:encoded></item><item><title>The crop nobody could cook</title><link>https://blog.acarr.org/posts/the-crop-nobody-could-cook/</link><guid isPermaLink="true">https://blog.acarr.org/posts/the-crop-nobody-could-cook/</guid><pubDate>Sun, 16 Aug 2026 12:00:00 +0000</pubDate><category>saltlight</category><description>Scallops looked right and read right. The economy audit found what playtesting would not have.</description><content:encoded><![CDATA[<p>My wife asked for scallops. Fair request — it is a seaside town and there were mussels and oysters but no scallops. So in they went: level 6, four hours to grow, priced between the mussels and the oysters. Looked right. Read right.</p>
<p>Then we ran the economy simulation, which plays fourteen days as a stubborn free player who never spends a penny, and audits the tables afterwards. Every target passed. And then one line:</p>
<blockquote>crops never used by any recipe (letter/market-only): [scallops]</blockquote>
<p>Nothing in the game could cook them.</p>
<p>Every other crop feeds something. Kelp becomes broth, twine, ash and glass. Mussels become chowder. Oysters become a platter and a jar of brine. Scallops could only be sold or posted away — a cul-de-sac in a game whose entire shape is <em>grow it, cook it, send it</em>.</p>
<h2>Invisible by eye, obvious to the audit</h2>
<p>This is the kind of mistake that survives every playtest. The crop grows. The number is sensible. You would sell a few, post a few, and never once notice that the workshops had no idea what to do with it — because nothing is <em>broken</em>. It is just quietly less of a game than it should be.</p>
<p>The fix took a line of arithmetic. The goods table carries the pricing formula it was built with, so the new dish is not a guess:</p>
<p><code>133 × 1.18 + 38 × (140/60)^0.85 = 235</code></p>
<p>Scallop Skillet. Tide kitchen. Scallops and a couple of handfuls of samphire, a hundred and forty minutes. The audit is clean, and the profit curve still climbs the way it should.</p>
<h2>Why bother simulating a cosy game</h2>
<p>Because "cosy" is a promise about how the numbers behave, not just how it looks. No energy bars, no countdown offers — which means the pacing has to work on its own, without the pressure valves other games reach for. If the fourteen-day curve sags, there is nothing to paper over it.</p>
<p>The simulation is how we find out before you do.</p>
<p style="margin-top:2rem"><a href="https://saltlightcove.com/blog.html">← All posts</a></p>]]></content:encoded></item><item><title>The splash screen that could never work</title><link>https://blog.acarr.org/posts/the-splash-that-could-never-work/</link><guid isPermaLink="true">https://blog.acarr.org/posts/the-splash-that-could-never-work/</guid><pubDate>Sun, 16 Aug 2026 12:00:00 +0000</pubDate><category>saltlight</category><description>Every idea we had for the launch screen was impossible. So we stopped using it.</description><content:encoded><![CDATA[<p>Our launch screen looked wrong and we knew why: a small square of artwork stranded in a field of flat colour. The obvious fix was to make it prettier. Add the title. Add a menu. Maybe a sign-in button.</p>
<blockquote>None of that was possible. The engine draws a launch splash <em>before any of the game exists</em> — no code is running, so nothing can move, and nothing can be tapped.</blockquote>
<p>It is a single image and a background colour, and that is the whole feature. Every idea we had for it — floating text, a menu, accounts — required something the splash fundamentally cannot do.</p>
<p>So the splash stops trying. It fills the screen with one colour and hands straight over to a real screen, which does all the things:</p>
<img src="https://saltlightcove.com/assets/shot-title.jpg" alt="The Saltlight Cove title screen">
<p>The lighthouse, the title drifting gently, the promise the whole game is built on, and a menu. The <em>Sign in</em> button is deliberately there and deliberately disabled — accounts arrive with the co-op work, and we would rather show the shape now than surprise you with a new button later.</p>
<h2>It moves like the game</h2>
<p>The title bobs. The line beneath it breathes. Gulls drift across. All of it is the same idle motion the town itself uses — pennants, chimney smoke, birds over the water — so the first thing you see already feels like the game instead of a poster of it.</p>
<p>It is also deliberately cheap: it draws its own sky and its own gulls without loading the town at all, so the first frame does not wait on a whole village.</p>
<p style="margin-top:2rem"><a href="https://saltlightcove.com/blog.html">← All posts</a></p>]]></content:encoded></item><item><title>Ask for a sheaf, not a plant</title><link>https://blog.acarr.org/posts/ask-for-a-sheaf-not-a-plant/</link><guid isPermaLink="true">https://blog.acarr.org/posts/ask-for-a-sheaf-not-a-plant/</guid><pubDate>Sat, 15 Aug 2026 12:00:00 +0000</pubDate><category>saltlight</category><description>Fifty-odd item icons, generated. The trick was describing the harvest, not the crop.</description><content:encoded><![CDATA[<p>Saltlight Cove needs a picture for everything you can hold — ten crops, forty goods. Drawing fifty icons by hand is weeks. Generating them is an afternoon, <em>if</em> you can get the machine to make the right thing.</p>
<p>It could not make plants. Ask a 3D generator for "sea oats" and it hands back a hut. Ask for mint and you get a hut with a green roof. We already knew this — the palm trees in the game are hand-built in Blender for exactly that reason.</p>
<blockquote>Then the obvious thing: the icons in a farming game aren't growing plants. They're the harvest. A sheaf. A bundle. A jar.</blockquote>
<p>A sheaf is an <em>object</em> — it has volume, weight, a silhouette. That's what these tools are good at. Every prompt got rewritten from the plant to the bundle:</p>
<p><code>a tied sheaf of golden sea-oat grain stalks, pale straw-yellow heads, bound with a thin twine band</code></p>
<p>All ten crops came back clean on the first pass, foliage included.</p>
<div class="strip">
<img src="https://saltlightcove.com/assets/models/item_sea_oats.png" alt="Sea oats"><img src="https://saltlightcove.com/assets/models/item_samphire.png" alt="Samphire">
<img src="https://saltlightcove.com/assets/models/item_mint.png" alt="Mint"><img src="https://saltlightcove.com/assets/models/item_meadowbloom.png" alt="Meadowbloom">
<img src="https://saltlightcove.com/assets/models/item_golden_squash.png" alt="Golden squash"><img src="https://saltlightcove.com/assets/models/item_oysters.png" alt="Oysters">
</div>
<h2>Kelp took two goes</h2>
<p>Kelp came back as a coil of brown rope. Perfectly good rope. The fix was to name the failure inside the prompt:</p>
<p><code>broad flat ribbon-like leafy fronds with wavy frilled edges … NOT a rope, NOT a coil, NOT brown cord</code></p>
<p>Green, bladed, correct. Telling it what you <em>don't</em> want turns out to matter as much as telling it what you do.</p>
<p><a href="https://saltlightcove.com/gallery.html">See all 47 of them →</a></p>
<p style="margin-top:2rem"><a href="https://saltlightcove.com/blog.html">← All posts</a></p>]]></content:encoded></item><item><title>The day touch stopped working</title><link>https://blog.acarr.org/posts/the-day-touch-stopped-working/</link><guid isPermaLink="true">https://blog.acarr.org/posts/the-day-touch-stopped-working/</guid><pubDate>Sat, 15 Aug 2026 12:00:00 +0000</pubDate><category>saltlight</category><description>A bug that only existed on the phone, and the four wrong answers before the right one.</description><content:encoded><![CDATA[<p>The report was three words: <em>touch doesn't work</em>. Not one button — nothing. On the phone only; fine on the desktop.</p>
<p>Four things looked guilty and weren't. A tap-versus-drag guard. A screen-scaling change. A crash in an animation. A stray finger left registered by iOS. Each one was a real bug, each one got fixed, and none of them was <em>the</em> bug.</p>
<blockquote>The answer arrived from the phone itself: sixty frames a second, steady, touches landing exactly where they should. The app was fine. It was throwing the taps away on purpose.</blockquote>
<p>The ground was eating them. In this engine, every visual element grabs input unless told not to — and the grass, the sand, the road and the sea are all visual elements. Anything you tapped that wasn't a building was swallowed before it reached the game. Dragging only worked if you happened to start on a building, which is precisely what the earlier report had said, in different words.</p>
<p>One line per layer: <em>scenery never eats a touch</em>.</p>
<h2>What we changed after</h2>
<p>The lesson wasn't "make fewer mistakes". It's that a broken machine and broken code looked identical for hours. The test harness now says <strong>could not run</strong> when the Mac can't render, instead of reporting twenty failures that aren't real — and the build gate can no longer pass by accident, which is how a bad build got out in the first place.</p>
<p style="margin-top:2rem"><a href="https://saltlightcove.com/blog.html">← All posts</a></p>]]></content:encoded></item><item><title>Less reading, more pictures</title><link>https://blog.acarr.org/posts/less-reading-more-pictures/</link><guid isPermaLink="true">https://blog.acarr.org/posts/less-reading-more-pictures/</guid><pubDate>Sat, 15 Aug 2026 12:00:00 +0000</pubDate><category>saltlight</category><description>The game played like a settings app wearing a farm. So we took the words out.</description><content:encoded><![CDATA[<p>Every action had drifted into the same shape: tap a building, a sheet opens, read a list of sentences, tap the button on the right row. Correct, complete, and all writing.</p>
<blockquote>Nobody reads a sentence to plant wheat. They pick up wheat and put it on the field.</blockquote>
<p>So: pick a seed up and it rides under your finger. Compatible beds light green, wrong ones red. Let go and it's planted — and the strip stays up, so one pick-up sows a whole row. Workshops work the same way; drag a recipe onto the building. Drop bread on the windmill and it refuses, because the windmill doesn't bake.</p>
<img src="https://saltlightcove.com/assets/shot-fields.jpg" alt="Merged fields in Saltlight Cove">
<p>Buildings stopped explaining themselves in text too. A workshop shows <em>what it is making</em> as a picture with a progress bar under it. Tap that bubble and it tells you what finishing it now would cost — in coins you already earn, never a premium currency. Money makes the cove prettier, never stronger.</p>
<img src="https://saltlightcove.com/assets/shot-storage.jpg" alt="Storage as a grid of pictures">
<p>Storage used to be a stack of sentences — <em>"1× Kelp / sells 19 coins each — the market is the relief valve"</em>. It's a grid of pictures with a number under each. Fields that touch now merge into one field instead of tiling like separate boxes.</p>
<p>The economy underneath didn't change at all. This was only ever about the verbs.</p>
<p style="margin-top:2rem"><a href="https://saltlightcove.com/blog.html">← All posts</a></p>]]></content:encoded></item></channel></rss>
