<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Wreckwing devlog</title><description>A Mac game about flying a small gunship at street level through procedurally generated cities you can destroy.</description><link>https://trilithon.games/</link><language>en-us</language><item><title>Fact-checking the game while writing about it</title><link>https://trilithon.games/devlog/fact-checking-the-game/</link><guid isPermaLink="true">https://trilithon.games/devlog/fact-checking-the-game/</guid><description>The Claude Code session that writes this devlog checks every claim against the game&apos;s records, and it has found errors the game&apos;s own checks missed.</description><pubDate>Sun, 18 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Wreckwing is built by Claude Code sessions working in the game’s repository. This site and its devlog are run by a different Claude Code session in a different repository. That session writes posts from the game’s own records: its development journal, its changelog, its commit messages and its tracker. Its rules say every claim in a post traces to one of those records and every image shows what its caption says, and before a draft comes to me, a separate agent checks each number in it against the sources.&lt;/p&gt;
&lt;p&gt;I set that up to keep the devlog accurate. It turned out to check the game as well.&lt;/p&gt;
&lt;h2 id=&quot;checking-the-work-four-ways&quot;&gt;Checking the work four ways&lt;/h2&gt;
&lt;p&gt;The game’s work now goes through four kinds of check, and each has caught something the others missed:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The game session’s own tests.&lt;/strong&gt; Every feature gets a test that it changes what a player would see, a problem I report closes only when my case is kept as a test, and a smoke test flies a clean build before anything a player reaches is closed. The smoke test flagged enemies spawning inside buildings in two game modes the same night I ran into it in play. &lt;a href=&quot;/devlog/keeping-the-project-out-of-vibe-coding/&quot;&gt;Keeping the project out of vibe coding&lt;/a&gt; describes these rules.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reviewers that didn’t build the change.&lt;/strong&gt; A separate model judges visual work from recordings. The first version of the shockwave from a ship’s death stretched the image about eight times in thin bands, and that review caught it. Side agents raise knock-on effects. One pointed out that a new sound for near misses would carry into the next run’s ratings of the enemies, so the sound got a switch to turn it off for a fair comparison.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;My play.&lt;/strong&gt; I fly what was built, and my verdicts go back to the sessions on a checklist. A new set of enemies that tested as dangerous as the old ones felt soft in my hands, and the flight records showed why.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The site session.&lt;/strong&gt; It comes to the work after it is done, with no part in building it, and it has to describe what the work does in plain sentences a reader can check.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;catching-a-flash-check-that-was-too-lenient&quot;&gt;Catching a flash check that was too lenient&lt;/h2&gt;
&lt;p&gt;Wreckwing’s blasts flash and warp the screen, and rapid flashing can cause seizures in people with photosensitive epilepsy. The game checks its effects with a flash audit that reads a recording and counts flashes a second against WCAG 2.3.1, the guideline games follow. The game’s journal, its changelog and the audit itself took the guideline’s area to be a quarter of the screen.&lt;/p&gt;
&lt;p&gt;On October 17 the agent fact-checking a post about the shockwave compared that with W3C’s own explanation of the guideline. There, the limit applies to 25% of any 10-degree part of the viewer’s field of view, and W3C estimates that field as a 341 by 256 pixel rectangle on a 1024 by 768 screen. The game’s records had read that 25% as a quarter of the whole screen. The note went to the game session, which confirmed it and changed the audit that morning. It also added a test clip that the old math passed and the new math fails: 15% of the screen flashing five times a second. The game’s changelog credits the note to this site’s session.&lt;/p&gt;
&lt;p&gt;The fact check made an error of its own. It took the area to be the whole rectangle, about 11% of the screen, from a sentence in the explanation’s intent section, and the game session used that figure. On October 18 a review of the MipYip version of this post found that W3C’s page disagrees with itself, and that the guideline’s definition and its technique G176 limit flashing to a quarter of the rectangle: 21,824 square pixels, about 2.8% of the screen. The original audit had been about nine times as lenient as the rule it cited. The game session changed the audit again and audited every clip it had kept. Two effects failed at the corrected area and were changed that day: a hurt ship’s view at the strongest damage setting, and the title screen’s falling buildings, whose blasts now go off in pairs at least a second apart. Two of the title screen’s camera shots, which run fast past bright detail, still fail. The shockwave and the blast flare still pass on W3C’s reference screen. The audit covers the game’s own effects, and play itself can still flash over the limit, for example when the camera tumbles through a collapsing building, so the game shows a photosensitivity warning at every launch.&lt;/p&gt;
&lt;h2 id=&quot;catching-numbers-that-were-never-built&quot;&gt;Catching numbers that were never built&lt;/h2&gt;
&lt;p&gt;The game’s design documents hold proposals as well as decisions, and a proposal reads like a fact. The ship classes document lists the cannon’s stream at 16 rounds a second. That was a proposal, and the game fires 12. The fact check on &lt;a href=&quot;/devlog/rounds-that-never-showed-up/&quot;&gt;The muzzle flashed, but the rounds never showed up&lt;/a&gt; caught it before the post went out.&lt;/p&gt;
&lt;h2 id=&quot;reviewing-the-game-sessions-themselves&quot;&gt;Reviewing the game sessions themselves&lt;/h2&gt;
&lt;p&gt;On October 11 I asked the site session to review the game session’s work from September 29 to October 11. Four agents each read a slice of the game session’s transcripts. They found about 15 issues closed as fixed and then seen again in play, and the patterns behind them. The site session doesn’t write to the game’s repository, so it handed the findings over as an issue, and the game session put them into its rules the same day. Those are the rules in the post on vibe coding.&lt;/p&gt;
&lt;h2 id=&quot;where-it-falls-short&quot;&gt;Where it falls short&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;It checks records, not code.&lt;/strong&gt; When a piece of code and its record agree and both are wrong, the fact check passes them. The flash audit was caught because its rule comes from a public standard that can be checked from outside the game.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It doesn’t see what I see.&lt;/strong&gt; The first still and clip for a post about erupting rubble were early physics tests, captioned in the game’s journal as the effect itself. I caught that, not the check, and the rule that every image shows what its caption says came from it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A correction helps only if it gets back to the game.&lt;/strong&gt; The flash audit was fixed that morning, but the design document still listed 16 rounds a second, two journal entries still described a quarter of the screen, and the rubble entry still captioned its physics tests as the eruption. Those went back to the game session as notes later the same day, and it corrected all four.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Vibe coding means accepting what an AI writes without checking it. Here, the work is checked by tests, by reviewers that didn’t build it, by my play, and by an agent whose job is to describe it accurately in public.&lt;/p&gt;
</content:encoded><category>Studio note</category><category>claude-code</category><category>tooling</category><author>Alex van Rossum</author></item><item><title>Darkening Wreckwing without losing the blasts</title><link>https://trilithon.games/devlog/darkening-wreckwing/</link><guid isPermaLink="true">https://trilithon.games/devlog/darkening-wreckwing/</guid><description>Wreckwing has a darker, grimier look, with crushed blacks, a tint and film grain. It made big blasts feel smaller, so they now flare through the dark.</description><pubDate>Sat, 17 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Wreckwing got a new look this week: darker, with crushed blacks, a tint and film grain, for a grimmer and tenser feel. Shaded streets are deep, and the city feels heavier. A dark look has costs, though. It made the biggest moment in a fight, an enemy ship’s fusion core going up, feel smaller, and it showed up a flaw in how the game draws worn edges.&lt;/p&gt;
&lt;h2 id=&quot;building-the-look-from-filters&quot;&gt;Building the look from filters&lt;/h2&gt;
&lt;p&gt;The look is a set of filters applied after the scene is lit:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Darkness&lt;/strong&gt; lowers the midtones and leaves black and white where they are.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Black crush&lt;/strong&gt; pulls dark tones toward black.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contrast&lt;/strong&gt; and &lt;strong&gt;Color&lt;/strong&gt; set contrast and saturation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tint&lt;/strong&gt; colors the shade and the light: Cold steel, Sodium, Rust or Bruise.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vignette&lt;/strong&gt; darkens the corners, and &lt;strong&gt;film grain&lt;/strong&gt;, new each frame, is strongest in the midtones.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Haze&lt;/strong&gt; thickens the fog up to five times, and &lt;strong&gt;Bloom&lt;/strong&gt; spreads bright light up to 3.5 times.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Edge softening&lt;/strong&gt; and &lt;strong&gt;edge wear&lt;/strong&gt; work on the edges between faces. Trilithon Engine builds the city from convex pieces, and their edges can look too sharp. Softening blends those pixels toward their neighbors. Wear adds grime to inside corners and pale wear to outside edges.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The edges come from depth. Across a flat face, depth changes at an even rate on the screen, so its second difference is zero there and not where two faces meet or at a silhouette.&lt;/p&gt;
&lt;p&gt;The game now starts with Black crush on Heavy, Color at 50%, the Rust tint, a medium vignette and grain, smoggy haze, much more bloom, soft edges and heavy wear. Darkness is off.&lt;/p&gt;
&lt;h2 id=&quot;softening-the-black-crush&quot;&gt;Softening the black crush&lt;/h2&gt;
&lt;p&gt;The first black crush was a hard clip: everything under a threshold went to black, and the deepest shade became unreadable. It is now a soft toe. A display value &lt;em&gt;g&lt;/em&gt; becomes &lt;em&gt;g&lt;/em&gt;²(1 + &lt;em&gt;k&lt;/em&gt;)/(&lt;em&gt;g&lt;/em&gt; + &lt;em&gt;k&lt;/em&gt;), darker the darker it was and black only at black. On Heavy, a pixel at 10% brightness stays at 5.5% where the clip took it to 2%, and one at 5% stays at 1.8% where the clip took it to black.&lt;/p&gt;
&lt;h2 id=&quot;finding-why-big-blasts-felt-small&quot;&gt;Finding why big blasts felt small&lt;/h2&gt;
&lt;p&gt;A screen can’t show anything brighter than white. Games sell a bright flash with what the eye does: after a flash the eye adapts, so the world looks dim for a moment. Wreckwing already did this. A big flash near the camera dimmed the picture by up to a third at once, and it recovered over about a second.&lt;/p&gt;
&lt;p&gt;The new look made that backfire. The dim came at the same moment as the flash, and the black crush took the dimmed street down with it. So the moment of the blast was darker than the moment before it.&lt;/p&gt;
&lt;h2 id=&quot;flaring-first-then-letting-the-eye-adapt&quot;&gt;Flaring first, then letting the eye adapt&lt;/h2&gt;
&lt;p&gt;Now a big blast in view gets a flare before the eye adapts. For about a twentieth of a second the picture brightens by up to 80%, the crush and the darkening let go, and the vignette drops by 70%. Then everything eases back over about a third of a second, and only then does the old dim take over.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;Three frames of a night test scene with two gray blocks on an open floor under a starry sky: dark before a blast, lifted out of the dark at the flare’s peak, and settling back 0.4 s later&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;./flare-test.jpg&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;How much flare a blast gets depends on three things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;How big it is.&lt;/strong&gt; Every fusion core gets the full flare. A missile gets none, since there are many of them and a flare for each would wear the effect out.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Where it is on screen.&lt;/strong&gt; A blast in the middle half of the view counts fully. One near the edge counts less, and one behind the camera not at all.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How near it is.&lt;/strong&gt; It counts fully within eight times the blast’s own size.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the test scene, a core 40 m ahead at night lifts a shaded street far from the blast from 29 to 67 on a scale of 255. The same core behind the camera changes nothing.&lt;/p&gt;
&lt;h2 id=&quot;limiting-the-flare-to-one-flash-at-a-time&quot;&gt;Limiting the flare to one flash at a time&lt;/h2&gt;
&lt;p&gt;A flash that lights the whole screen is a photosensitivity concern. The guideline games follow, WCAG 2.3.1, allows no more than three flashes a second over more than a quarter of any 10-degree part of the viewer’s field of view, about 2.8% of a 1024 by 768 screen, and a dogfight can set off several cores in a second. So a new flare can start at most once every half second, and a blast held back by that limit never starts one later. The first version let the fading tail of a held-back blast start a flare of its own, and a test that sets off a chain of cores caught it.&lt;/p&gt;
&lt;p&gt;The flash audit described in &lt;a href=&quot;/devlog/shockwave-you-can-see/&quot;&gt;Visualizing pressure when a fusion core blows&lt;/a&gt; checks every new flash. A worst-case chain of four cores at the strongest setting, three of them within a third of a second, makes no more than one flash a second over WCAG’s area. At the strongest damage setting, a stream of rounds through a broken shield made a hurt ship’s view flash four times a second over that area. Since October 18, its glitches, the color split and the camera’s kick on each hit share one budget of three flash starts a second, and the view now passes on W3C’s reference screen at every strength audited. The game’s Reduce flashes setting turns the flare off.&lt;/p&gt;
&lt;h2 id=&quot;keeping-edge-wear-off-the-smoke&quot;&gt;Keeping edge wear off the smoke&lt;/h2&gt;
&lt;p&gt;Edge wear finds creases from the depth of the nearest surface at each pixel. Smoke and dust write no depth, so over a pile of debris seen through smoke, the wear drew its lines on top of the smoke and the debris looked cel-shaded, like a cartoon.&lt;/p&gt;
&lt;p&gt;The smoke is drawn in a pass of its own, and that pass records how much smoke covers each pixel. The edge pass now reads it and fades the wear by that much. In the test, wear under a thick cloud in front of a block’s corner changed 88 of the 90 pixels it changes in clear air before the fix, and none after.&lt;/p&gt;
&lt;h2 id=&quot;seeing-ships-in-the-dark&quot;&gt;Seeing ships in the dark&lt;/h2&gt;
&lt;p&gt;A darker city makes enemy ships harder to see, most of all at night. A far hull came out as dark as the street behind it, and smoggy haze fogged a ship at 250 m by 62%. Enemy ships now carry a red glow at each side of the hull and a dimmer one atop the tail. It is a few pixels across at any distance, brighter the farther the ship is, and at full strength at night and golden hour and a quarter of it by day. At night, a dark Raven-sized target’s contrast against what is behind it rises from 0.09 to 0.40 at 250 m and from 0.20 to 0.38 at 100 m.&lt;/p&gt;
&lt;h2 id=&quot;showing-damage-on-the-screen&quot;&gt;Showing damage on the screen&lt;/h2&gt;
&lt;p&gt;The look also carries the player’s damage. Every hit swells the film grain for about a second, more with the shield down. A hit that gets past the shield glitches the view like a failing drone camera: thin bands slide sideways, colors split, blocks freeze, and a large hit tears the picture. Between hits the view keeps glitching, more often the more damage the hull has taken, and never on an undamaged ship. Reduce flashes keeps the glitches at a quarter and never tears the view, and a hurt ship under fire passes the flash audit as described above.&lt;/p&gt;
&lt;p&gt;All of this is in the development build as of October 17, the fix to a hurt ship’s view as of October 18, and none of it is in a tagged release yet.&lt;/p&gt;
</content:encoded><category>Dev journal</category><category>rendering</category><author>Alex van Rossum</author></item><item><title>Finding why the new enemies felt soft</title><link>https://trilithon.games/devlog/new-enemies-felt-soft/</link><guid isPermaLink="true">https://trilithon.games/devlog/new-enemies-felt-soft/</guid><description>Wreckwing&apos;s new enemy roster tested as dangerous as the Ravens and played at a sixth of that. Flight records showed why, and the roster is now five kinds.</description><pubDate>Sat, 17 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;On October 12 Wreckwing got a roster of eight kinds of enemy, each with its own weapon, to sit beside the original enemy fighter, the Raven. &lt;a href=&quot;/devlog/eight-kinds-of-enemy/&quot;&gt;Pitting a dodging test player against eight enemies&lt;/a&gt; described how we built and balanced it. On the night of October 15 we flew a full run on Hard against the roster, and it felt too easy. For the first four or five levels the player seemed to be the only one fighting. Three runs against Ravens alone the same night came much closer to the fight we want.&lt;/p&gt;
&lt;h2 id=&quot;reading-the-two-nights-side-by-side&quot;&gt;Reading the two nights side by side&lt;/h2&gt;
&lt;p&gt;Every fight the game plays is recorded ten times a second: where each enemy is, what it is trying to do, and how much its weapons have taken off the player. The game counts that damage in blows, and a fresh ship stands about two and a half.&lt;/p&gt;
&lt;div class=&quot;tl-table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table, scrolls sideways&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;The roster&lt;/th&gt;
&lt;th&gt;The Ravens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Time the enemies spent attacking&lt;/td&gt;
&lt;td&gt;23%&lt;/td&gt;
&lt;td&gt;20%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Damage their weapons did, a minute&lt;/td&gt;
&lt;td&gt;0.25 blows&lt;/td&gt;
&lt;td&gt;1.54 blows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;of which from guns&lt;/td&gt;
&lt;td&gt;0.13&lt;/td&gt;
&lt;td&gt;0.09&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;of which from missiles&lt;/td&gt;
&lt;td&gt;0.12&lt;/td&gt;
&lt;td&gt;1.45&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;The obvious explanation was wrong. The new enemies attacked as much as the Ravens did, and guns did little on either night. The difference was missiles. Each time one was fired, a Raven’s missile did about ten times what a roster missile did, and the roster’s easiest kinds carried none at all.&lt;/p&gt;
&lt;h2 id=&quot;finding-where-the-tests-went-wrong&quot;&gt;Finding where the tests went wrong&lt;/h2&gt;
&lt;p&gt;The roster was balanced before anyone flew it. Each kind fought a scripted stand-in player in headless fights, which run the simulation with no screen, and each was tuned until it did about what a Raven does to that stand-in. Measured against our own flights, the stand-in understates a Raven’s harm to a real player four to five times, and it overstated the new kinds’ harm by two to fifteen times. So “as dangerous as a Raven” in the test came out at about a sixth of that in play.&lt;/p&gt;
&lt;p&gt;The stand-in still shows whether something changed. It can’t say whether the change feels right, and the project’s rules now say so. Every flight also records how each enemy missile ended (a hit, a near miss, dodged, or into a wall), so the next comparison can tell a weak missile from one that misses.&lt;/p&gt;
&lt;h2 id=&quot;cutting-the-roster-to-five&quot;&gt;Cutting the roster to five&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Raven&lt;/strong&gt; joins the roster as the general fighter and is the most common enemy at every level: 42,117 of 49,990 escorts over 2,000 Hard runs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Picket&lt;/strong&gt;, the easy long-range kind, now carries one missile on a long reload.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Lancer&lt;/strong&gt;, the medium long-range kind, fires its wall-piercing missile with a Raven’s warhead.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Salvo&lt;/strong&gt; is now a missile boat with no gun and a battery of eight light missiles, shown above fanned out across a park. One of them does little, and the whole barrage does a lot.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Warden&lt;/strong&gt;, the boss, takes the first moves of the Salvo and the Lancer the player met on the way, then the Dozer’s ram.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Wasp, the Bruiser, the Stinger and the Dozer are no longer drawn into runs. Against the juking stand-in, the one comparison it can still make fairly, the Picket does 0.55 of an easy Raven’s harm a minute, the Salvo 1.06 of a medium Raven’s, and the Lancer 1.1 on the same measure. Our next flights decide whether that holds in play.&lt;/p&gt;
&lt;h2 id=&quot;adding-mosquitoes&quot;&gt;Adding mosquitoes&lt;/h2&gt;
&lt;p&gt;The Wasp failed in a way worth learning from. It was a fast ship with a single cannon that waited its turn to attack, and in the roster run it did 0.01 blows per enemy-minute. Mosquitoes are built against that failure. They are small gun ships that come on top of each city’s enemies and never wait for a turn. A mosquito circles the player at 20 to 40 m, dodging side to side, and fires a spray of 12 rounds a second, curving in, whenever its nose is near where the player will be. Twenty of the player’s rounds bring one down. Easy brings one or two a level, and Hard two or three.&lt;/p&gt;
&lt;p&gt;With two mosquitoes beside a medium and an easy Raven, the stand-in lost 3.9 blows to enemy rounds, against 1.16 with the two Ravens alone. One or more enemies attacked it 92% of the time, against 42%.&lt;/p&gt;
&lt;p&gt;A miss should press the player too. An enemy round that passes within 4 m of the player’s ship without hitting it now makes a snap, a tick and a short whiz falling in pitch, from where it passed. A mosquito’s spray is a run of snaps.&lt;/p&gt;
&lt;p&gt;Mosquitoes are on by default in the development build as of October 17. The five-kind roster is still a choice beside the Ravens, and both wait on our next flights.&lt;/p&gt;
</content:encoded><category>Dev journal</category><category>ai</category><category>balance</category><category>weapons</category><category>audio</category><author>Alex van Rossum</author></item><item><title>Visualizing pressure when a fusion core blows</title><link>https://trilithon.games/devlog/shockwave-you-can-see/</link><guid isPermaLink="true">https://trilithon.games/devlog/shockwave-you-can-see/</guid><description>A ship&apos;s death in Wreckwing now sends a shockwave over the city and past the camera, and a new check tests every flash against the photosensitivity limit.</description><pubDate>Sat, 17 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When an enemy ship is shot down in Wreckwing, its fusion core goes up: a blue-white flash, a ring of sparks, then fire. &lt;a href=&quot;/devlog/since-0-15-5/&quot;&gt;0.15.5 and since&lt;/a&gt; described that death. We wanted it to hit harder, with a shockwave that visibly bends the view and more force behind the blast.&lt;/p&gt;
&lt;h2 id=&quot;finding-a-shockwave-that-was-already-there&quot;&gt;Finding a shockwave that was already there&lt;/h2&gt;
&lt;p&gt;The game already bent the picture around big blasts, the way a pressure wave bends light. A shell grows out from the blast, and whatever lies behind its edge is pushed outward on the screen. A research pass into how games and films make a blast read as big pointed at that shell first. Working from the code, its arithmetic said a ship’s shell lived about a twentieth of a second.&lt;/p&gt;
&lt;p&gt;A recording of a ship’s death at 60 frames a second confirmed it before anything changed. The shell ran outward at 300 m/s, reached the camera in three frames, and was dropped once the camera was inside it. In the eight frames after the blast, no bending could be seen at all.&lt;/p&gt;
&lt;h2 id=&quot;slowing-the-wave-and-passing-it-through-the-camera&quot;&gt;Slowing the wave and passing it through the camera&lt;/h2&gt;
&lt;p&gt;The shell now grows the way a real blast wave does, fast at first and slowing as it goes. Its radius rises as time to the power 0.4, the law physicists use for a point explosion, and it reaches at least 36 m over 0.55 s, fading as it goes. When it reaches the camera it no longer simply vanishes. A ring runs across the screen from where the blast is, as if the wave were passing over you.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;Two frames of the same moment, a twelfth of a second after an enemy ship’s core goes up at a street crossing. On the left, as it was: the fireball and a starburst of blue sparks. On the right, now: the same, with the crosswalk and a painted line bent outward where the wave is passing over the view&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;./before-after.jpg&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;In a test with a core 12 m from the camera, the new ring bends 667 pixels of a wall 0.05 s after the blast, where the old one had already gone. It is gone by 0.85 s.&lt;/p&gt;
&lt;p&gt;The first version went too far. Its review found that at its first strength the thin shell stretched the image about eight times in narrow bands for two frames, enough to drip a parked car’s roof onto the sidewalk. The shell now runs at half the strength and is at least 3 m thick, twice the first version’s 1.5 m. It bends the scene as far with about a quarter of the stretch, and the second review measured about two times at worst.&lt;/p&gt;
&lt;h2 id=&quot;adding-force-behind-the-blast&quot;&gt;Adding force behind the blast&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The kick.&lt;/strong&gt; The view kicks away from the blast by the square of how near it is, about half a degree for a kill 40 m away and at most 1.4 degrees of pitch and 1 of roll. It eases back in about 0.4 s.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The sound.&lt;/strong&gt; Under a core going up within about 65 m, the sounds that start and the rumble, gravel and hum beds drop to half and come back over 0.45 s, so the core’s own blast stands out. Other blasts and the lock and missile warnings are never lowered.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Three small blasts.&lt;/strong&gt; They trail the core at 0.14, 0.27 and 0.41 s, 4 to 5 m from it, spread round it on the screen and smaller each time. In the first version they sat 1.5 to 3 m out, and the review couldn’t pick them out inside the core’s fireball.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Slow motion.&lt;/strong&gt; At first, every core in view stopped the game for 60 ms and then ran it at 0.4 speed for half a second. With mosquitoes in a city, small gun ships added this week, that happened several times a level, so it now plays only for the level’s last enemy and for the player’s own ship. Every core keeps its ring, kick, sound and trailing blasts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The last kill.&lt;/strong&gt; When a level is won, the camera turns onto the last enemy’s blast over 0.6 s and holds there for 3 s before the results.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;checking-every-flash&quot;&gt;Checking every flash&lt;/h2&gt;
&lt;p&gt;Brighter flashes and screen warps raise a real risk. Some people with photosensitive epilepsy can have a seizure from rapid bright flashing. The usual guideline, WCAG 2.3.1, allows no more than three flashes in any one second over more than a small area: a quarter of any 10-degree part of the viewer’s field of view. W3C estimates that 10-degree field as a 341 by 256 pixel rectangle on a 1024 by 768 screen at a typical viewing distance, so the area is a quarter of that rectangle, 21,824 square pixels or about 2.8% of the screen. The game shows a warning at launch, and every new flash or warp goes through a flash audit before we play it.&lt;/p&gt;
&lt;p&gt;The audit reads a recording pixel by pixel and counts a flash as a pair of opposite brightness changes of 10% or more, with the darker state below 80%. It fails a clip with more than three flashes in any second over WCAG’s area, and it checks red flashes separately. It also reports the largest share of the frame that flashed more than three times in a second. It tests itself on nine made-up clips with known answers. A full screen flashing five times a second fails, and so do a red one, 15% of the screen at five times a second, a tenth of the screen at ten times a second, and 3.75% of the screen at five times a second. Twice a second passes, 1.9% of the screen at ten times a second passes, and a slow fade and a single blast pass.&lt;/p&gt;
&lt;p&gt;Two independent checks found errors in the audit’s math. While this post was being fact-checked against the game’s records on October 17, the comparison with WCAG showed that the audit counted a flash only once it covered a quarter of the screen, about nine times the area the guideline allows. We changed the area that day to the whole 341 by 256 rectangle, about 11% of the screen, which was still four times too large. A second review on October 18 found that the guideline’s definition limits flashing to a quarter of that rectangle. The audit now uses that area, and every clip it had passed was audited again. In a recording of a ship’s death in front of the chase camera, the fusion core flashes three times in its worst second, within the limit of three on W3C’s reference screen.&lt;/p&gt;
&lt;p&gt;The audit covers the game’s own effects. Play itself can flash over the limit, for example when the camera tumbles through a collapsing building, so the game does not claim to meet WCAG 2.3.1. It shows a photosensitivity warning at every launch, and its accessibility settings include Reduce flashes.&lt;/p&gt;
&lt;p&gt;All of this is in the development build as of October 17, the corrected area as of October 18, and none of it is in a tagged release yet.&lt;/p&gt;
</content:encoded><category>Dev journal</category><category>rendering</category><category>audio</category><category>tooling</category><author>Alex van Rossum</author></item><item><title>Pitting a dodging test player against eight enemies</title><link>https://trilithon.games/devlog/eight-kinds-of-enemy/</link><guid isPermaLink="true">https://trilithon.games/devlog/eight-kinds-of-enemy/</guid><description>Wreckwing&apos;s enemies were one ship at different strengths. We built eight kinds, balanced them against a test player that dodges, and retuned two after play.</description><pubDate>Thu, 15 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Until October 12, every enemy in Wreckwing’s dogfight run was the same ship, the Raven. Its tier made it harder or easier: how fast it fired, how well it aimed, and how much its shield held. We wanted enemies that differ in what they do, some faster and some slower. The player’s three ships, the Skyhammer, the Brute and the Sniper, gave us the shape for it. Each enemy kind takes one of those three and changes one thing about it.&lt;/p&gt;
&lt;h2 id=&quot;the-roster&quot;&gt;The roster&lt;/h2&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;The eight enemy kinds as box stand-ins, side by side on a city street, each labeled with its name and each with a lit engine; the Bruiser, Salvo, Dozer and Warden carry rust-colored plates&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;./roster-lineup.jpg&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;tl-table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table, scrolls sideways&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Enemy&lt;/th&gt;
&lt;th&gt;Built on&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;How to beat it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wasp&lt;/td&gt;
&lt;td&gt;Skyhammer&lt;/td&gt;
&lt;td&gt;The smallest and fastest; closes in with its cannon&lt;/td&gt;
&lt;td&gt;Lead it and shoot; it dies quickly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bruiser&lt;/td&gt;
&lt;td&gt;Brute&lt;/td&gt;
&lt;td&gt;Slow and armored, with a scatter gun for close range&lt;/td&gt;
&lt;td&gt;Stay more than 60 m away&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Picket&lt;/td&gt;
&lt;td&gt;Sniper&lt;/td&gt;
&lt;td&gt;Holds 220 m off and fires bursts that curve onto you&lt;/td&gt;
&lt;td&gt;Leave its line of sight and close in by the side streets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stinger&lt;/td&gt;
&lt;td&gt;Skyhammer&lt;/td&gt;
&lt;td&gt;Two quick-lock missiles 2.4 s apart, then away&lt;/td&gt;
&lt;td&gt;Two timed dodges&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Salvo&lt;/td&gt;
&lt;td&gt;Brute&lt;/td&gt;
&lt;td&gt;Four slow missiles fanned out on one lock&lt;/td&gt;
&lt;td&gt;Break the line behind a building&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lancer&lt;/td&gt;
&lt;td&gt;Sniper&lt;/td&gt;
&lt;td&gt;A long lock that holds behind cover, and a missile that goes through walls&lt;/td&gt;
&lt;td&gt;Watch for its beam and get close&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dozer&lt;/td&gt;
&lt;td&gt;Brute&lt;/td&gt;
&lt;td&gt;Boosts straight through glass, wood and brick&lt;/td&gt;
&lt;td&gt;Hide behind concrete or steel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Warden&lt;/td&gt;
&lt;td&gt;Brute&lt;/td&gt;
&lt;td&gt;The boss&lt;/td&gt;
&lt;td&gt;Whatever the run taught you&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;The Wasp, Bruiser and Picket are the easy kinds and carry no missiles. The Stinger, Salvo, Lancer and Dozer are the medium kinds. A run draws its enemies from the roster by its seed, so every run is different and the same seed repeats exactly. Two of the medium kinds fight from long range, and a level holds at most one of those until the fifth. Before the Dozer joined, the fifth level was the same three enemies in every run. With it, 2,000 runs give four different fifth levels and eleven different fourth ones.&lt;/p&gt;
&lt;p&gt;The Warden borrows from the run itself. Under two thirds of its hull, its armor plates burst off and it takes the move of the first medium enemy the player met. Under a third, it adds the move of the last one and flies 15% faster. A run that taught the Stinger’s quick pair and the Lancer’s wall-piercing missile ends with a boss that uses both.&lt;/p&gt;
&lt;p&gt;Each kind flies a stand-in made of boxes in its family’s shape until its model is made: white wingtips for the Skyhammer’s family, rust armor plates for the Brute’s, and a long needle with a gold stripe for the Sniper’s. A mark beside each enemy’s health bars also shows its family: a chevron, a square or a diamond.&lt;/p&gt;
&lt;h2 id=&quot;fixing-the-first-numbers-then-balancing&quot;&gt;Fixing the first numbers, then balancing&lt;/h2&gt;
&lt;p&gt;Each kind should be as dangerous as a Raven of the same tier, in its own way. The test flies each kind against a scripted stand-in for the player, on 24 seeds, beside a Raven, and adds up how much the player loses a minute.&lt;/p&gt;
&lt;p&gt;The first run said the close-range kinds dealt five to seven times the Raven’s damage, and the Warden six times the old boss’s. Most of that turned out not to come from their weapons. Three things were wrong with the measurement, and each was found by measuring again.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Units.&lt;/strong&gt; Shields and hulls take damage differently. A shield loses the whole blow, while the hull’s share is multiplied by how tough the ship is. The first count of each kind’s weapon damage left that multiplier out, so it read eight times too small. Most of what looked like extra damage from an unknown cause was the weapons all along.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Falling buildings.&lt;/strong&gt; Splitting the rest by cause found the real extra: buildings around the fight coming down on the player. A scatter gun’s nine pellets a shot wear walls down as fast as the player’s own Brute does. Near the Warden, falling buildings caused more than eight of every ten points of damage the player took. That is the game’s destruction working as intended, so we left it alone.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A player that never dodged.&lt;/strong&gt; The stand-in flew straight through every missile. Against it, the Stinger’s 0.8 s lock landed nearly every time, and its missiles did 19 times the Raven’s damage. A real player dodges a missile at the last moment with the juke, the game’s quick sideways boost, and a juke made at that moment beats a missile. Once the stand-in juked as well, the Raven’s missiles almost never landed. We then tuned each kind’s weapons so that its rounds and missiles together match the Raven of its tier.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The missiles taught us one more thing. Making the Stinger’s warhead weaker barely changed the damage it did. A missile hurts a ship through how far its blast reaches, not how much energy it carries, so a smaller warhead now also means a smaller blast.&lt;/p&gt;
&lt;p&gt;After tuning, every kind lands within about 12% of its tier’s Raven. The exception is the Salvo, whose four missiles land together or not at all, so its result swings by 30% from one set of runs to the next.&lt;/p&gt;
&lt;p&gt;Flown as whole levels against the same juking stand-in, the roster’s levels 5 to 7 match the Ravens’ difficulty. Levels 2 to 4 come out about half as hard. In a group, an easy Raven’s missiles land far more often than they do one on one, and the roster’s easy kinds carry none.&lt;/p&gt;
&lt;h2 id=&quot;seeing-it-coming&quot;&gt;Seeing it coming&lt;/h2&gt;
&lt;p&gt;The long-range kinds give a warning. A Picket winds up for 0.8 s before each burst, with a thin red beam from its gun to the player. A Lancer shows the same beam for the whole of its 2.4 s lock, the longest warning in the game.&lt;/p&gt;
&lt;p&gt;The first beam drew nothing. It was built from the same streak as a cannon round’s tracer, and a tracer’s streak is never longer than the distance the round has flown. A beam that stays put has flown nowhere, so it drew as a stub half a meter long. It is now drawn the way engine exhaust is, at full length from its first frame. A recording from behind a Picket still lost it, because a beam aimed away from the camera is only a few pixels long. The player sees the beam nearly end on, which is worse, so it now comes with a small red glow at the gun that is a few pixels across at any distance.&lt;/p&gt;
&lt;h2 id=&quot;changes-after-the-first-play-through&quot;&gt;Changes after the first play-through&lt;/h2&gt;
&lt;p&gt;The roster’s first real test came on October 14, in a full play-through on Easy and Hard. The kinds read as different fights, and the Warden’s borrowed moves stayed as built. Two of the kinds didn’t read as fights at all. The Bruiser and the Dozer seemed to do nothing.&lt;/p&gt;
&lt;p&gt;A measurement set two of each kind against a hunting stand-in for 60 s, on three seeds. The Bruiser and Dozer did engage. They took their turns attacking, spent 64% and 43% of the time in attack, and came close: the Dozer was within 30 m a third of the time. But their scatter guns barely hurt. Over the three fights they did 0.94 and 0.62 blows, the least of the seven kinds, against 5.5 for the Stinger. An enemy that comes up close and does almost no harm looks idle. Their guns now do 3.5 and 4 times the damage. In the same test the Bruiser now does 5.9 blows and the Dozer 1.9 to 2.5.&lt;/p&gt;
&lt;p&gt;That change has a cost to watch. The hunting stand-in took 15.3 blows from two Bruisers, against 7.0 from two Stingers, and a Bruiser can turn up in the first level. A whole-run measurement comes next, to check that the early levels didn’t get too hard.&lt;/p&gt;
&lt;p&gt;Other changes came out of the same night:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Difficulty.&lt;/strong&gt; The old Hard is the new Easy. The new Hard adds one enemy to each of levels 3 to 7. On level 5, three or more enemies now attack at once 19% of the time, up from 5%, and the stand-in loses 6.3 blows a minute, up from 3.2.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The player’s Brute.&lt;/strong&gt; Its missiles rarely hit at close range. They now leave along the line to the target, turn at 110° a second instead of 65°, lock out to 65 m instead of 220 m, and fall to the ground past 65 m. Against a live Raven in open air, 30 to 90 m off and up to 20° off the nose, 82% of them hit, up from 58%. The Skyhammer’s hit 81% in the same test.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wave defense.&lt;/strong&gt; Wave defense now draws its fighters from the roster too. Each wave has one more fighter and a third fewer drones, 71 enemies in all instead of 87. With a scripted player hunting, 4 of 6 cities stood through the fourth wave, where none did before.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The boss falling first.&lt;/strong&gt; When the Warden goes down before its escorts, the game now says so and tells the player to finish the rest.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flight recordings&lt;/strong&gt; now name each enemy’s kind, so the next play-through can be read kind by kind.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of this is in the development build as of October 15 and waits on the next play-through. The roster is still behind a switch on the run’s difficulty screen, and every kind still flies its stand-in.&lt;/p&gt;
&lt;p&gt;After the next play-through, the roster was cut to five kinds and its missiles were brought near the Raven’s. &lt;a href=&quot;/devlog/new-enemies-felt-soft/&quot;&gt;Finding why the new enemies felt soft&lt;/a&gt; covers that change.&lt;/p&gt;
</content:encoded><category>Dev journal</category><category>ai</category><category>balance</category><category>weapons</category><author>Alex van Rossum</author></item><item><title>Keeping the project out of vibe coding</title><link>https://trilithon.games/devlog/keeping-the-project-out-of-vibe-coding/</link><guid isPermaLink="true">https://trilithon.games/devlog/keeping-the-project-out-of-vibe-coding/</guid><description>Wreckwing changes faster than I can read it. Where that drifted toward vibe coding, the rules we added, and what the first play-through under them showed.</description><pubDate>Thu, 15 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Wreckwing is built almost entirely by Claude Code sessions. I design the game, direct the work and play it. I don’t read the code. On October 10 the game’s repository took more than 26,000 new lines in one day, and no one read them line by line.&lt;/p&gt;
&lt;p&gt;Vibe coding usually means accepting what an AI writes without checking it by any means. By that definition this project isn’t vibe coding. Every piece of work starts as an issue with a stated condition for when it’s done, changes are judged by measurement, and when two fixes for enemies hitting walls made the numbers worse, we took them out. But speed makes it easy to drift. When the checking is done by tests and by play instead of by reading, anything the tests don’t measure goes unchecked. On October 11 I found out how much that could hide.&lt;/p&gt;
&lt;h2 id=&quot;recognizing-the-drift&quot;&gt;Recognizing the drift&lt;/h2&gt;
&lt;p&gt;Every enemy pilot in the game has a personality: how aggressive it is and how cautious. Personalities were drawn for every enemy and written to the logs. But only the experimental trained pilot read them, and the game flies the scripted one. For a week, every enemy in a level flew the same way, and nothing failed. We found it while measuring why enemies so rarely boosted or chased (&lt;a href=&quot;/devlog/enemies-werent-using-their-ships/&quot;&gt;Enemies weren’t using everything their ships can do&lt;/a&gt;). Code that nothing reads looks alive and isn’t.&lt;/p&gt;
&lt;p&gt;That led to a review of the game’s sessions from September 29 to October 11. It found a pattern:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;About 15 issues had been closed as fixed and then seen again in play. Enemies spawning inside buildings had been closed three times. Floating rubble was closed with 8 pieces left, and a replay eight hours later held 72. The minimap closed on a test render that came out blank, then crashed on another Mac.&lt;/li&gt;
&lt;li&gt;Some problems took many rounds of fixes before they were solved or set aside: floating rubble 6, wall bumps 6, stalls in large cities 5, the water splash 4. Four stills the sessions had approved themselves turned out to be wrong.&lt;/li&gt;
&lt;li&gt;Of ten notes from a side session over three days, six were knock-on effects of a change that nobody had raised. Doubling ship toughness also halved what a falling building did to a ship, night arrived in one city in four with no way to turn it off, and cars skated across the new ponds.&lt;/li&gt;
&lt;li&gt;I was asked about 36 questions on one evening, and about 15 of them didn’t matter to me either way. On October 11, 33 issues sat waiting for me to play them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of that is accepting code without checking it. It is checking that had gaps, at a speed where the gaps added up.&lt;/p&gt;
&lt;h2 id=&quot;what-i-had-to-change&quot;&gt;What I had to change&lt;/h2&gt;
&lt;p&gt;These went into the game’s rules on October 11:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Every feature gets a test that it changes behavior.&lt;/strong&gt; The test runs the game with the feature and without it, or at two settings, and checks that what a player would see differs in the expected direction. A test that only shows the code runs doesn’t count. For the personalities, aggressive pilots now have to deal more than twice the damage cautious ones do. This rule came from an outside reviewer’s suggestion.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A problem I report closes on my own case.&lt;/strong&gt; My flight, my city or my scene is kept in the repository as a test that fails before the fix and passes after. Without it, the fix is called unconfirmed, the issue stays open with a “Needs play” label, and it goes on my play checklist.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A smoke test before anything a player reaches is closed.&lt;/strong&gt; A clean build of the game flies a run level, the sandbox with a wave of enemies and wave defense, with no one at the keyboard, and reports spawns, frame rates, the HUD and crashes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Two attempts, then measure.&lt;/strong&gt; After two failed fixes for the same problem, the next step is a measurement that isolates the cause and a research pass, not a third fix. On visual work, a separate model that didn’t make the change compares the stills with reference photos.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check the consequences of shared numbers.&lt;/strong&gt; A change to a default or a balance value is run with and without it first, and its effect on the rest of the game is counted.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fewer questions.&lt;/strong&gt; At most five at a time, each with a recommendation. Anything waiting on my play goes on one checklist a session, where I mark each item and the sessions read my verdicts back.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-its-working&quot;&gt;How it’s working&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The night glow.&lt;/strong&gt; Measuring the new neon city showed the same number of bright pixels by day and by night. Every glow had been clipped at the brightness of its own color, so the night lighting we had designed had never reached the screen. It was the same kind of failure as the personalities, found the same afternoon the rules went in. A test now checks it: a lit sign cabinet went from 2 bright pixels to 902.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enemies inside buildings.&lt;/strong&gt; On October 14 I flew into a city where an enemy started inside a building. This had been closed as fixed three times. The two-attempts rule meant the next step was a measurement. The flight recordings showed 5 of the run’s 19 enemies starting at exactly 158.4 m from me, the distance the game’s unchecked fallback uses. Rebuilding my city without a screen found the cause: the elevated freeway counted as one box around the whole road, 502,000 m², and every spawn point not near me lay inside it. Now each piece of road has its own box. My city keeps 37 of 160 spawn points open, where it had none, and it is kept as the test. The smoke test had flagged the same problem in two other modes that night. Once those spawns worked, it caught a new one: a fighter warping in on top of a drone.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The house windows.&lt;/strong&gt; An issue about breakable house windows and doors stayed open because one part, a warehouse door, couldn’t be reproduced without a screen. Before, it would have been closed. It closed after I confirmed it in play.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first play-through under the new rules was on October 14. I flew 31 items on the checklist, and 22 held up. Of the other nine, seven were design and tuning, such as more impact for ship deaths and a role for the Sniper. Enemies flying into walls was better but not gone, and lit windows that change as you approach a building were a new defect. Separately, one old fix came back: the spawns above, closed three times under the old rules.&lt;/p&gt;
&lt;h2 id=&quot;still-unproven&quot;&gt;Still unproven&lt;/h2&gt;
&lt;p&gt;This is one play-through against two weeks of history. The count that matters is how many fixes come back over the next several play-throughs, against about 15 in the first two weeks.&lt;/p&gt;
&lt;p&gt;The rules also missed something. Two enemy kinds, the Bruiser and the Dozer, were built to fight up close. In my play they seemed to do nothing. A measurement found they did fight, but their guns did the least harm of any enemy. The balancing tests hadn’t flagged it. It was found two days after it was built, where the personalities took a week, but it was found by me in play, not by a test. The fix also went in without my flights kept as its case; a measurement stood in for them. &lt;a href=&quot;/devlog/eight-kinds-of-enemy/&quot;&gt;Pitting a dodging test player against eight enemies&lt;/a&gt; has the numbers.&lt;/p&gt;
&lt;p&gt;Some of these rules are enforced by scripts, such as the commit checks, the smoke test and the release gate. Most are followed by the sessions because they are written down, and nothing stops a session from skipping one. The checklist and the closing comments are how I see whether they did.&lt;/p&gt;
&lt;p&gt;I’ll write again after more play, with the number of fixes that came back.&lt;/p&gt;
</content:encoded><category>Studio note</category><category>claude-code</category><category>tooling</category><author>Alex van Rossum</author></item><item><title>Skipping pixels and tearing down a 4K frame</title><link>https://trilithon.games/devlog/skipping-pixels-and-tearing-down-a-4k-frame/</link><guid isPermaLink="true">https://trilithon.games/devlog/skipping-pixels-and-tearing-down-a-4k-frame/</guid><description>We took the largest step of a 4K frame apart term by term, then made each pixel skip the small lights that can&apos;t reach it, saving 2 ms.</description><pubDate>Tue, 13 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We play and test Wreckwing on a 4K display. In the largest city at 4K, on the High preset, in Low Power Mode, the graphics card takes about 30 ms to draw a frame in our benchmark, which is about 33 frames a second. The biggest single step of that frame is the one that works out what every pixel looks like: what surface it shows, and how that surface is lit. It took 13.1 ms of the 30.&lt;/p&gt;
&lt;h2 id=&quot;taking-the-frame-apart&quot;&gt;Taking the frame apart&lt;/h2&gt;
&lt;p&gt;That step runs in two stages. First every pixel records its surface (color, roughness, which way it faces, how far away), which graphics programmers call the G-buffer. Then a second pass lights each pixel from those records: the sun and its shadows, the sky, the street’s light bounced up onto walls, light from rooms behind broken windows, and the small moving lights of missiles, engines and fires.&lt;/p&gt;
&lt;p&gt;To find where the 13.1 ms went, the benchmark can now leave one part out at a time. The picture comes out wrong, but the timing shows what that part costs. Each run flies the camera down the same avenue for 20 seconds, twice, in alternating order.&lt;/p&gt;
&lt;div class=&quot;tl-table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table, scrolls sideways&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Left out&lt;/th&gt;
&lt;th&gt;Time for the step&lt;/th&gt;
&lt;th&gt;So that part costs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nothing&lt;/td&gt;
&lt;td&gt;13.1 ms&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;All the lighting&lt;/td&gt;
&lt;td&gt;8.7 ms&lt;/td&gt;
&lt;td&gt;4.5 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The small moving lights&lt;/td&gt;
&lt;td&gt;10.7 ms&lt;/td&gt;
&lt;td&gt;2.5 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How much sky each spot sees&lt;/td&gt;
&lt;td&gt;12.3 ms&lt;/td&gt;
&lt;td&gt;0.9 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The street’s bounced light&lt;/td&gt;
&lt;td&gt;12.7 ms&lt;/td&gt;
&lt;td&gt;0.5 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Room lights, and softening the sun’s shadow edges&lt;/td&gt;
&lt;td&gt;13.2 ms&lt;/td&gt;
&lt;td&gt;nothing measurable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;More than half of the lighting’s cost was the small moving lights. There are up to twelve of them, and every one of the eight million pixels on the screen checked each of them three times: for the light itself, for its glint on shiny surfaces, and for its glow in smoky air.&lt;/p&gt;
&lt;h2 id=&quot;most-pixels-are-nowhere-near-a-light&quot;&gt;Most pixels are nowhere near a light&lt;/h2&gt;
&lt;p&gt;Each of these lights reaches from a few meters out to 16 m, a missile’s light the farthest. Seen from the street, almost every pixel is far outside every one of those reaches, so each pixel checked twelve lights and found that none of them touched it.&lt;/p&gt;
&lt;p&gt;A light’s reach is a ball around it. Before the frame is drawn, the game now works out the rectangle on the screen that the ball could cover, padded a little so that it never misses a pixel. A pixel checks only the lights whose rectangle it falls inside.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;The same night street twice. On the left as the game draws it. On the right each pixel is tinted by how many small lights it checks: none over the left part of the picture, and a blue block over the right part, which is the one light’s rectangle&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;./light-rectangles.jpg&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;A light added nothing to the pixels outside its rectangle before this change either, so the picture is unchanged. A test draws a wall lit by three lights (one in front of it, one 30 m off to the side, one behind the camera) both ways, and the two frames are identical to the byte. The step went from 13.8 to 11.8 ms on average over two runs each, about 2 ms of the 30 ms frame.&lt;/p&gt;
&lt;p&gt;The recording also shows the limit. When the camera is inside a light’s reach, which happens often with the player’s own engines just ahead of the chase camera, that light’s rectangle is the whole screen and every pixel checks it. The other lights are still skipped.&lt;/p&gt;
&lt;h2 id=&quot;what-is-left&quot;&gt;What is left&lt;/h2&gt;
&lt;p&gt;With the lights done, we split the surface records the same way:&lt;/p&gt;
&lt;div class=&quot;tl-table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table, scrolls sideways&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Left out&lt;/th&gt;
&lt;th&gt;Saves&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Every material’s detail (all surfaces plain gray)&lt;/td&gt;
&lt;td&gt;2.9 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Color variation between buildings&lt;/td&gt;
&lt;td&gt;0.55 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Weathering&lt;/td&gt;
&lt;td&gt;0.3 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Settled dust&lt;/td&gt;
&lt;td&gt;0.3 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Broken glass on the ground, cracked pavement&lt;/td&gt;
&lt;td&gt;about 0.1 ms each&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Part of the 2.9 ms is the noise that gives concrete its stains and mottling, worked out fresh at every pixel from several layers of random patterns. A throwaway build with the noise held constant ran about 1.3 ms faster. Reading the noise from a baked, tiling texture would save most of that, with a different pattern of stains.&lt;/p&gt;
&lt;p&gt;Another 3.5 ms goes to drawing the city’s three million triangles into the G-buffer before any pixel is shaded. A second throwaway build skipped the buildings off the screen for that stage and saved about 0.4 ms, so most of the 3.5 ms is spent on what is in view. An earlier try at the same skip shaded 75 pixels differently, and 0.4 ms is not worth that, so it stays out.&lt;/p&gt;
&lt;p&gt;The baked noise and fewer samples of how much sky each spot sees both change the picture, so each will be compared with before-and-after stills before it goes in.&lt;/p&gt;
</content:encoded><category>Engine note</category><category>rendering</category><category>performance</category><author>Alex van Rossum</author></item><item><title>0.15.5 and since: wave defense and the city at night</title><link>https://trilithon.games/devlog/since-0-15-5/</link><guid isPermaLink="true">https://trilithon.games/devlog/since-0-15-5/</guid><description>Release 0.15.5 and the work since: a wave defense mode, the city at night, a new ship death, an enemy roster, and a wide city that loads in 3.1 s.</description><pubDate>Mon, 12 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We tagged release 0.15.5 on October 10. It carries what &lt;a href=&quot;/devlog/since-0-15/&quot;&gt;Since 0.15&lt;/a&gt; described and the items in its section below. Everything after that section is in the development build as of the morning of October 12 and is not tagged yet, so some of it may change before the next release.&lt;/p&gt;
&lt;p&gt;Three posts from October 11 cover other parts of this work: &lt;a href=&quot;/devlog/enemies-werent-using-their-ships/&quot;&gt;the enemies’ new moves and attack turns&lt;/a&gt;, &lt;a href=&quot;/devlog/enemies-kept-hitting-walls/&quot;&gt;the wall bumps&lt;/a&gt;, and &lt;a href=&quot;/devlog/rounds-that-never-showed-up/&quot;&gt;the Brute’s scatter gun, the Sniper’s weapons and the shield&lt;/a&gt;. This post leaves those out.&lt;/p&gt;
&lt;h2 id=&quot;release-0155&quot;&gt;Release 0.15.5&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The player flies one of three classes: the Skyhammer, the Brute and the Sniper. A selection screen after the game mode shows each ship turning, with bars for its speed, agility, armor and firepower. The pick is kept and flown in the sandbox too.&lt;/li&gt;
&lt;li&gt;The Brute carries four missiles that reload in 6 s each and locks in 1.5 s out to 220 m. The Sniper carries two that reload in 7 s and locks in 1.8 s out to 400 m.&lt;/li&gt;
&lt;li&gt;A full-screen loading screen names where the player is going (for a run, the city’s number of seven, its kind and its enemies), shows their ship turning on the right, and moves a bar through the city’s build steps.&lt;/li&gt;
&lt;li&gt;A run’s seven levels now mix the four core cities: tight, mixed, downtown and wide. Each appears at least once and never twice running. The mix comes from the run’s seed, so a run can be flown again.&lt;/li&gt;
&lt;li&gt;The run’s boss is a larger assault craft, 1.75 times the gunship’s size and 29.5 t, as a stand-in until its model is made. With the player ahead of it between 30 and 150 m, it fires a barrage of 8 unguided rockets 0.067 s apart, then reloads for 7 s. Below half its hull it stops retreating and commits to the attack.&lt;/li&gt;
&lt;li&gt;The training ground opens from the Start menu with five lanes: Familiarization, Static, Moving, Reactive and Crossfire. In the last two, drones fire back with simulated rounds. A hit costs 2 s on the lane’s score and never harms the ship. Medals go to gold within par, silver within 1.5 times par and bronze within 2.5 times.&lt;/li&gt;
&lt;li&gt;The mixed city’s houses are six suburban types (ranch, Cape Cod, Colonial, split-level, Craftsman bungalow and modern two-storey) with front and side yards, in place of rows of half-timbered houses.&lt;/li&gt;
&lt;li&gt;Broken concrete shows rebar: one bar to every 0.2 m² of cut face, at most 8 a piece, standing out 0.08 to 0.3 m.&lt;/li&gt;
&lt;li&gt;Dust in a big collapse fades out instead of popping. In the 80-storey tower test, 368,484 of 435,387 dust particles had replaced one still on screen. Now 7,876 do.&lt;/li&gt;
&lt;li&gt;A car thrown into water sinks. One thrown in at 18 m/s had ridden 0.59 m above the surface and now settles 0.74 m under it.&lt;/li&gt;
&lt;li&gt;A ship’s collisions show and sound as hits, with blue sparks where the shield holds, sparks and a puff where it doesn’t, and a thud or a clang.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;wave-defense&quot;&gt;Wave defense&lt;/h2&gt;
&lt;p&gt;Wave defense is a new mode on the Start menu. The player picks a ship and defends a city through eight waves and then the boss’s wave. The city is tight, mixed, downtown or wide, chosen by seed.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each wave has a timer of 60 to 130 s, and when it runs out the next wave arrives whether or not the last one is cleared. Clearing a wave refills the hull and gives a short break, with an announcement of what is coming next.&lt;/li&gt;
&lt;li&gt;The player has three respawns. The defense is lost when half the city is destroyed or the ship goes down a fourth time.&lt;/li&gt;
&lt;li&gt;The HUD shows the wave number, the lives left, the share of the city still standing with a mark where it is lost, and the ships in each wave before it arrives. The results give the time, the share standing, the waves cleared and the lives left.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The early waves are swarm drones that ignore the player and go for buildings. Each drone picks a building with the most still standing for its distance, with at most two on any building. It flies there by the streets, holds 30 m off the ground floor and fires disintegrating bolts. What a bolt reaches turns to dust with no pieces left, and the floors above come down under their own load. That keeps the physics cost low. In a test, a bolt at an office’s column removed 9 structural members and left 12 fewer bodies in the scene, where a charge the same size added 325 pieces.&lt;/p&gt;
&lt;p&gt;We measured how fast the waves bring a city down with nobody shooting back, and made the bolt smaller and slower: 2.5 m every 3 s in place of 4 m every 1.6 s. With the old bolt, half of each of two tight cities came down in 150 and 160 s. With the new one, one fell at 200 s and the other was not half down after 240 s.&lt;/p&gt;
&lt;p&gt;The last wave brings the fortress, a ring 26 m across that weighs 600 t. It warps in over a building and takes out the building’s middle, then hovers 45 m up, or 10 m over the tallest building when that is higher. It holds off one building at a time and fires bolts into its side, moving to the next once half of it is down. Eight turrets on its rim aim at the player and can be shot off one at a time. With all eight it fires 145 rounds in 10 s, and with two left it fires 39. The drones and the fortress are stand-in shapes until their models are made.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;Three moments from wave defense: a red stand-in drone beside an office tower at a crossing; the tower coming down in dust behind the drone; and, seen from the street, the fortress, a dark ring with turrets on its rim, hovering high over the city&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;./wave-defense.jpg&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;enemies&quot;&gt;Enemies&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;An enemy roster is in the development build behind a menu option until we have played it. It has seven kinds of enemy, each one of the player’s classes with one thing changed. The easy ones are the Wasp, the Bruiser and the Picket, none with missiles. The medium ones are the Stinger, the Salvo and the Lancer, each with its own way of firing missiles. The boss is the Warden. A run draws its enemies from the roster by its seed, so runs differ and a run can still be flown again. Each kind flies a stand-in until its model is made.&lt;/li&gt;
&lt;li&gt;Enemies taking a turn to attack come in round the side away from the other attacker, a pincer, unless they are already behind the player. In headless fights, a fifth more attacks came from the player’s blind side, and the player took 7% less damage a minute.&lt;/li&gt;
&lt;li&gt;Cautious enemies waiting for their turn wait behind cover the player can’t see. Four cautious enemies spent 68% of their waiting out of sight, against 24% before.&lt;/li&gt;
&lt;li&gt;The minimap shows only the enemies the ship has found. The ship sees all round to 250 m, through glass but not through buildings, and a lost contact stays on the map for 1.5 s. A scanner pulse every 4 s reaches 300 m and leaves a fading outline where each enemy was, with a soft sonar ping.&lt;/li&gt;
&lt;li&gt;Every enemy in the game now flies the copper Raven, and the old Raven is gone. Enemies no longer spawn inside a structure’s footprint.&lt;/li&gt;
&lt;li&gt;Hard mode puts about one city in four at night: 368 of 1,400 cities over 200 test runs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;a-ships-death&quot;&gt;A ship’s death&lt;/h2&gt;
&lt;p&gt;A ship that is shot down now explodes as its fusion core going off. A 6 t ship at rest makes a 3.5 m blast, which scales with the cube root of the ship’s mass and grows up to 1.6 times when the ship is boosting. A gunship’s death is now larger than a ship missile’s blast (3.4 m against 3 m), and beside an office’s ground-floor column it breaks 5 structural members where the old death broke 2. It shows as a blue-white flash, a flat ring of streaks and a shockwave, with the fire 0.07 s behind.&lt;/p&gt;
&lt;p&gt;The same work found that missiles and ship deaths in a fight never touched cars or trees. They now set cars burning and snap trees, as the sandbox’s rockets always did.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;A ship dying on a street corner seen from above, four moments each: on top the old death, an orange fireball; below the new one, a blue-white flash and a flat ring of blue streaks racing out over the asphalt, then a larger fire&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;./fusion-core.jpg&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-city-at-night&quot;&gt;The city at night&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;At night the moon takes the sun’s place, and the sky, light, reflections, fog and shadows follow it. Moonlight is 16% of the sun’s and bluer, and the stars twinkle. The sandbox draws about one city in four at night.&lt;/li&gt;
&lt;li&gt;Street lamps light the street in warm pools. The light is drawn once into a map of the city, and a lamp knocked over goes dark.&lt;/li&gt;
&lt;li&gt;Offices beyond 90 m show lit rooms behind the glass, 30 to 65% of each building’s rooms by its seed.&lt;/li&gt;
&lt;li&gt;Lamp heads, lit panels and LED boards had been clipped to the brightness of their own color. At night they now glow and bloom as designed. A lit sign cabinet went from 2 bright pixels to 902.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;The same street corner at night, without the street lamps’ light on top and with it below, where the lamp on the right throws a warm pool on the sidewalk&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;./lamp-corner.jpg&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The neon city is a new sandbox city, always at night with wet streets. Every building in the tall tight city carries neon: vertical blade signs at a building’s end and words across the upper storeys, in a single-line tube lettering. Signs are drawn as trim on the facade, so they add nothing to the physics and fall with whatever they hang on. A tube knocked off bursts in glass and sparks. Seed 7 signs all 136 of its buildings with 7,958 pieces.&lt;/li&gt;
&lt;li&gt;In the tight city, six office towers carry LED video walls two storeys tall across their street faces. A wall is part of its building and comes down with its floors, and a cabinet shot away goes dark.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;An office front at night in the neon city, with “KTV” in green tube lettering across the third storey and a blade sign at the corner reading “WOK” top to bottom in blue&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;./neon-office.jpg&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;on-the-street&quot;&gt;On the street&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Bus shelters modeled on New York’s standard shelter, 4.27 by 1.52 by 2.72 m, stand on the sidewalks with backlit ad cases. A round breaks the pane nearest it, a blast breaks every pane in reach into tempered pebbles, and a blast or a ship knocks the whole shelter over. A city has 2 to 8.&lt;/li&gt;
&lt;li&gt;Two-sided ad panels stand along the curbs, 7 to 29 a city.&lt;/li&gt;
&lt;li&gt;The walls carry 43 AI-generated graffiti pieces as placeholder art: 17 tags and throw-ups, 6 pieces, 8 wheatpaste bills, 5 stencils and 7 stickers. We’re opening a call for submissions from artists to replace them. Details will follow here when they’re ready.&lt;/li&gt;
&lt;li&gt;Suburban houses have real windows and doors cut into their walls. A pane takes 4 rounds and a door 1, and the room behind is open after. A house now has 54 to 109 structural members where it had 20 to 48.&lt;/li&gt;
&lt;li&gt;A burnt-out car is charred in matte soot, so its hood and roof no longer reflect the sky.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;loading-and-stalls&quot;&gt;Loading and stalls&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The wide city loads in 3.1 s, where it took 9.7 s. Its ground took 5.8 s and now takes 0.27 s. Each sample of the terrain had been tested against every lot in the city, copying the whole lot each time. The lots are now worked out once and the ground tiles are built on every core. The streets went from 1.60 s to about 0.5 s by building each kind of car and prop once.&lt;/li&gt;
&lt;li&gt;A gunship shot down stalled the game for 290 ms while its wreck was built. Now it takes 12 ms. A respawn spent 113 ms rebuilding the ship’s meshes, and now uses parts made once. Over four minutes of wave defense, the slowest frame went from 294 ms to 85 ms.&lt;/li&gt;
&lt;li&gt;Tidying the scene’s memory in a large city stalled a frame for about 60 ms. It now takes about 25 ms.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;training-ground&quot;&gt;Training ground&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Targets on the Static and Moving lanes now hide around corners at street height, slide out for 4 s, then slide back for 4.&lt;/li&gt;
&lt;li&gt;A sixth lane, Section, is over a small downtown. Three enemy pilots fire simulated rounds, two attacking at a time by the pincer while the third waits in cover, with two sentinels. Two couriers fly slow loops and are not targets, and a burst that hits one costs 5 s. Par is two minutes.&lt;/li&gt;
&lt;li&gt;Every lane’s results end with a short debrief: what was flown, which drone landed most of its hits, the longest and quickest waits for a kill, and what was still up if the ship went down.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;controls-menus-and-sound&quot;&gt;Controls, menus and sound&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A controller uses one scheme in every mode. The right trigger fires the autocannon and the left fires the ship’s missile. In the sandbox, holding the right bumper opens a weapons wheel with an icon for each weapon.&lt;/li&gt;
&lt;li&gt;The sandbox always flies a ship, and its pause menu can send enemies in waves, by type and skill, 1 to 6 to a wave and 1 to 5 waves.&lt;/li&gt;
&lt;li&gt;The boot sequence opens on the studio’s mark and name, which turn into the engine’s as the night blue comes up.&lt;/li&gt;
&lt;li&gt;One music track plays under the title and menus and fades out as a mode starts.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>Release notes</category><category>ai</category><category>rendering</category><category>physics</category><category>performance</category><category>weapons</category><author>Alex van Rossum</author></item><item><title>Enemies kept hitting walls, so we made turns smarter</title><link>https://trilithon.games/devlog/enemies-kept-hitting-walls/</link><guid isPermaLink="true">https://trilithon.games/devlog/enemies-kept-hitting-walls/</guid><description>Enemy pilots kept bumping into buildings. Two fixes made it worse, traces found pilots cutting corners, and a smarter turn cut the bump rate.</description><pubDate>Sun, 11 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The enemy pilots in Wreckwing kept bumping into buildings, most of all in narrow streets. &lt;a href=&quot;/devlog/pilots-and-walls/&quot;&gt;An earlier post&lt;/a&gt; covered the first fixes and left a target of 1.5 bumps per ship-minute. Since then two more attempts made bumping worse, traces of single bumps found the cause, and a change to how pilots turn at corners brought the rate down.&lt;/p&gt;
&lt;p&gt;The block arena is a test arena of city blocks, run as 16 duels of 150 seconds. The tight cities are two narrow-street cities, run as 12 duels of 90 seconds each. The pilots are scripted and fly against each other. The last post’s figures, about 1 in the block arena and 3.2 in the tight city, were measured before the pilots gained new moves such as boosting and tailing on October 11. The baselines below were measured after those moves: 1.56 in the block arena and 3.08 across the two tight cities.&lt;/p&gt;
&lt;h2 id=&quot;two-tries-that-made-it-worse&quot;&gt;Two tries that made it worse&lt;/h2&gt;
&lt;p&gt;The first try was a detour. When the way to its steer point was too narrow for the hull, the pilot took the nearest open heading. With 30° steps between headings and 40 m of clear space required, the block arena rate rose from 1.56 to 2.05. In a 20 m street with the goal off to one side, no heading had 40 m free, so the pilot picked a slanted one and flew into a facade. A version with 15° steps measured 1.59, no gain.&lt;/p&gt;
&lt;p&gt;The second try was context steering. The pilot scored 16 candidate headings by room and by how well each pointed at the goal, then flew the best. The block arena rate rose from 1.56 to 3.78, and the bump rate in the maneuver test duels went from 3.0 to 4.8 a minute. Reading the code turned up no mistake.&lt;/p&gt;
&lt;p&gt;We removed both and changed method to looking at single bumps.&lt;/p&gt;
&lt;h2 id=&quot;what-the-bumps-showed&quot;&gt;What the bumps showed&lt;/h2&gt;
&lt;p&gt;Alongside the first try, a diagnosis ran 12 duels of 90 seconds in each tight city and sorted each bump by the direction the ship lost speed in.&lt;/p&gt;
&lt;div class=&quot;tl-table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table, scrolls sideways&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;City 66523&lt;/th&gt;
&lt;th&gt;City 60250&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wall bumps per ship-minute&lt;/td&gt;
&lt;td&gt;3.33&lt;/td&gt;
&lt;td&gt;2.81&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Struck front / side / bottom / other&lt;/td&gt;
&lt;td&gt;72 / 22 / 3 / 6&lt;/td&gt;
&lt;td&gt;59 / 17 / 3 / 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Last steer point was behind a wall&lt;/td&gt;
&lt;td&gt;72 of 103&lt;/td&gt;
&lt;td&gt;48 of 79&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Three of 103 and three of 79 bumps struck the bottom of the hull. The earlier post suspected that low roofs slip under the pilot’s rays and catch the hull. The numbers do not support that, so we dropped it as the main cause. Most bumps were nose first, from a pilot steering for a point with a building in front of it.&lt;/p&gt;
&lt;p&gt;For the block arena we then printed the two seconds before each of 12 bumps, one decision at a time. In 11, the pilot was not attacking and was turning for a point down a side street. It began the turn while the whisker on that side, one of its distance rays, read 8 to 12 m. Over the last half second the reading fell to 1 to 4 m as the hull swung toward the wall, and the diagonal whisker saw the corner only at the last decision. The pilot cut the corner.&lt;/p&gt;
&lt;h2 id=&quot;the-change&quot;&gt;The change&lt;/h2&gt;
&lt;p&gt;A pilot that is not attacking, is moving faster than 8 m/s and has more than 15 m free ahead now keeps its turn toward a side to at most 0.15 radians (about 9°) while a wall on that side is within 7 m. It does this only at a corner, where the other side is open past 7 m. Without that condition the rule held a pilot in the dead-end test for 6.2 seconds against a limit of 5. With it, 3.8 seconds.&lt;/p&gt;
&lt;p&gt;“Held” below is the share of flying time a ship spends within 6 m of one spot for 3 seconds or more, not counting holds while attacking, in per mille (36 per mille is 3.6%).&lt;/p&gt;
&lt;div class=&quot;tl-table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table, scrolls sideways&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Block arena, bumps per ship-minute&lt;/th&gt;
&lt;th&gt;Tight cities, bumps per ship-minute&lt;/th&gt;
&lt;th&gt;Tight cities, held per mille&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Before&lt;/td&gt;
&lt;td&gt;1.56&lt;/td&gt;
&lt;td&gt;3.08&lt;/td&gt;
&lt;td&gt;51&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wall within 10 m&lt;/td&gt;
&lt;td&gt;0.32&lt;/td&gt;
&lt;td&gt;2.47&lt;/td&gt;
&lt;td&gt;64&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wall within 7 m&lt;/td&gt;
&lt;td&gt;1.02&lt;/td&gt;
&lt;td&gt;2.67&lt;/td&gt;
&lt;td&gt;44&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7 m, corners only (built)&lt;/td&gt;
&lt;td&gt;1.02&lt;/td&gt;
&lt;td&gt;2.84&lt;/td&gt;
&lt;td&gt;36&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;The 10 m version had lower bump rates and held pilots in place more, so we built the 7 m version with the corners-only condition.&lt;/p&gt;
&lt;h2 id=&quot;where-it-stands&quot;&gt;Where it stands&lt;/h2&gt;
&lt;p&gt;The block arena is at 1.02, under the 1.5 target. The tight cities are at 2.84, well above it.&lt;/p&gt;
&lt;p&gt;Most bumps left in the tight cities are noses into facades while a pilot repositions or pursues toward a point behind a block. Later on October 11 we added the next step from the research notes. Each city builds a graph of its streets on load, and a pilot pursuing or repositioning, whose straight line to its goal runs through a building, follows the streets instead. In 12 duels per tight city, graph off and then on, wall bumps fell from 2.84 to 2.27, still above 1.5, and held time went from 36 to 37 per mille.&lt;/p&gt;
&lt;p&gt;About half of the remaining bumps come with nothing under 20 m on the two center-line rays. A look-ahead as wide as the hull, part of the research plan, is still unbuilt. We have not yet judged any of this in play.&lt;/p&gt;
</content:encoded><category>Postmortem</category><category>ai</category><author>Alex van Rossum</author></item><item><title>Enemies weren&apos;t using everything their ships can do</title><link>https://trilithon.games/devlog/enemies-werent-using-their-ships/</link><guid isPermaLink="true">https://trilithon.games/devlog/enemies-werent-using-their-ships/</guid><description>Enemy pilots rarely boosted or got on a target&apos;s tail. We found three causes, gave them four new moves, and limited how many attack at once.</description><pubDate>Sun, 11 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Every enemy ship in Wreckwing can boost, chase and get on a target’s tail, but after a full run through the game’s seven cities we saw that the enemy pilots rarely did any of it. This post covers why, and what changed.&lt;/p&gt;
&lt;h2 id=&quot;what-we-measured-first&quot;&gt;What we measured first&lt;/h2&gt;
&lt;p&gt;The game logs every fight ten times a second, with positions, speeds and what the enemy is trying to do. We read 48 recorded flights from October 10 and 11.&lt;/p&gt;
&lt;p&gt;To test each change we use headless duels (no graphics, in an arena of blocks). One computer pilot flies as an enemy does and another chases it the way a player does, straight at it and boosting to close the gap. Both hulls are 10 times tougher than normal, since at normal toughness the pilots shot each other down within seconds. A test run is 16 duels of up to a minute.&lt;/p&gt;
&lt;p&gt;In the recorded flights the nearest enemy was boosting 4% of the time, on the player’s tail 4%, had the player on its own tail 17%, and was circling 2%. In the duels, before any change, the same measures were 1%, 1%, 17% and 5%, close enough to stand in for play.&lt;/p&gt;
&lt;h2 id=&quot;three-causes&quot;&gt;Three causes&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;The pilot braked out of every boost.&lt;/strong&gt; An enemy pilot feels its way with short rays, called whiskers, cast 40 m ahead. It brakes when a wall is closer than the distance it needs to stop, plus 8 m. Boost takes a ship toward 70 m/s, and past about 52 m/s that distance is longer than the whiskers reach. In open air every boost looked like a wall ahead, so at the next decision the pilot dropped the boost and swerved 63 degrees. The pilot now also reads a 120 m ray down its nose and along its path.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Personality never reached the scripted pilot.&lt;/strong&gt; Each enemy spawns with a personality for aggression, caution and mobility. Only the experimental trained pilot read aggression and caution, and the game flies the scripted one. In the duels, aggressive and cautious pilots each boosted 1% of the time and were each chased 17%.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;There was nothing to choose.&lt;/strong&gt; The pilot picks among four goals: pursue, attack, break off and reposition. It boosted only when pursuing from far away or breaking off, and in an attack it strafed one way until a wall turned it, so two ships in the open orbited each other.&lt;/p&gt;
&lt;h2 id=&quot;four-moves-inside-the-goals&quot;&gt;Four moves inside the goals&lt;/h2&gt;
&lt;p&gt;Inside its goals, a pilot can now fly four moves, each for a few seconds:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Run-down:&lt;/strong&gt; boost toward the target until back in range.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tail:&lt;/strong&gt; get about 30 m behind the target, nose on it, matching its speed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extend:&lt;/strong&gt; boost away in a clear direction, then return.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kickstop:&lt;/strong&gt; with a chaser close behind, brake hard so it overshoots, sidestep, and turn on it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At each decision, every move that fits the moment gets a score weighted by personality. Aggression raises run-downs, tails and kickstops, and caution raises extending. The pilot picks at random, from its own seeded random numbers, among the moves scoring within half of the best, so the same build and seed replay a fight the same way. An attack’s strafe now changes side every 1.2 to 3 seconds in the open, so two ships no longer settle into an orbit.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;A bar chart titled “How an enemy flies, before and after”, showing the shares of time in the 16-minute duels before and after the change&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;./moves-chart.jpg&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Headless duels, 16 minutes before the change and 16 after. The tail result is against another enemy and the others are against a player-style chaser.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Damage is counted in full hulls at normal toughness. The enemy dealt 2.27 a minute where it dealt 1.19, and took 3.72 where it took 2.46. It bumped into walls 3.0 times a minute, up from 2.4.&lt;/p&gt;
&lt;p&gt;An aggressive pilot went from 1.6 to 4.0 damage dealt a minute and from 17% to 8% of its time chased. A cautious pilot went from 0.8 to 0.5 and from 17% to 28%.&lt;/p&gt;
&lt;h2 id=&quot;fewer-enemies-attacking-at-once&quot;&gt;Fewer enemies attacking at once&lt;/h2&gt;
&lt;p&gt;The same run showed a second problem: too many enemies attacking at once. At most two now attack at a time. The others wait about 110 m off with their nose on the player, guns and missiles quiet. A slot held for 8 seconds passes to an enemy that has waited 3. An enemy the player has locked onto or is within 30 m of still fights back.&lt;/p&gt;
&lt;p&gt;We tested this on the run’s levels 3 to 7 against a bot player that charges the nearest enemy: three seeds a level, up to 2 minutes each, the bot’s ship four times as tough so levels would last.&lt;/p&gt;
&lt;div class=&quot;tl-table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table, scrolls sideways&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;No slots&lt;/th&gt;
&lt;th&gt;Two slots&lt;/th&gt;
&lt;th&gt;One slot&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Damage the player takes a minute&lt;/td&gt;
&lt;td&gt;5.2&lt;/td&gt;
&lt;td&gt;4.3&lt;/td&gt;
&lt;td&gt;3.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time with three or more attacking&lt;/td&gt;
&lt;td&gt;19%&lt;/td&gt;
&lt;td&gt;9%&lt;/td&gt;
&lt;td&gt;0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Levels lost of 15&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;The bot is a poor shot (2 to 4 kills in 26 to 29 minutes), so the table measures pressure on the player rather than how levels end for a good one.&lt;/p&gt;
&lt;h2 id=&quot;easy-and-hard-modes&quot;&gt;Easy and hard modes&lt;/h2&gt;
&lt;p&gt;On October 11 the run gained easy and hard modes. Hard is the run as built, with two attack slots and about one city in four at night. Easy has one enemy fewer in each level of three or more (14 enemies against 19) but keeps the harder enemies that carry missiles, allows one attacker at a time (the one-slot column above), makes the player’s ship half as tough again, and is all by day.&lt;/p&gt;
&lt;h2 id=&quot;what-is-still-open&quot;&gt;What is still open&lt;/h2&gt;
&lt;p&gt;Every setting is a starting value, and retraining the experimental trained pilot on the moves is open. All of this is in the development build as of October 11.&lt;/p&gt;
</content:encoded><category>Dev journal</category><category>ai</category><category>balance</category><author>Alex van Rossum</author></item><item><title>Agents with jobs</title><link>https://trilithon.games/devlog/agents-with-jobs/</link><guid isPermaLink="true">https://trilithon.games/devlog/agents-with-jobs/</guid><description>The Claude Code agents that build the game, the asset studio and this site, the contractors they hire, and which model does which job.</description><pubDate>Sat, 10 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;four-kinds-of-agent&quot;&gt;Four kinds of agent&lt;/h2&gt;
&lt;p&gt;Almost all of our code is written by Claude Code, and the work is split between agents that each have a job, a boundary and a way to hand work on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The main agent&lt;/strong&gt; works in the game’s repository. It owns the game and its engine, and it is the only agent that commits to the game. Every commit passes the game’s checks first and names its Linear issue. It reviews the studio’s work and closes the studio’s issues, and it keeps the dev journal some of these posts started from.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The studio agent&lt;/strong&gt; works in the asset studio’s repository. It designs ships as data files, with props and vehicles to follow, renders them for review, and checks them against the engine and the flight rules. It never commits to the game. It exports a checked file, and the main agent commits it after the game’s own checks pass. The two share a build lock, so they never build at the same time, and the studio won’t build while the game is being played.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The site agent&lt;/strong&gt; runs this website. It owns the site, its Linear project and its hosting. It never writes to the game’s or the studio’s repositories. When something needs changing there, it leaves a Linear comment or a proposed change for that agent. It merges its own work once its checks pass, and it deploys only with a go-ahead. The main and studio agents will write devlog posts here by pull request, and the site agent reviews and merges them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Contractors&lt;/strong&gt; are hired by any of the three for one scoped job, and they leave when it is done.&lt;/p&gt;
&lt;h2 id=&quot;hiring-a-contractor&quot;&gt;Hiring a contractor&lt;/h2&gt;
&lt;p&gt;When something would fill the main agent’s context with material it doesn’t need to keep, we tell the main agent to go hire someone for it. Research, a sweep through dozens of files, or chasing a stray thread through old issues all count. A contractor is a subagent or a separate headless Claude Code session. It works in its own context, reads and searches as much as the job needs, and hands back a report. The main agent keeps the conclusion and the plan, and its context stays clean for the work in front of it.&lt;/p&gt;
&lt;p&gt;Contractors read and report. They don’t commit, and they don’t build or run the game while it is being played. The agent that hired them checks the work and commits it.&lt;/p&gt;
&lt;p&gt;One rule sends work to contractors on purpose. When a fix hasn’t landed after a round or two, or something feels wrong and is hard to put into words, the main agent stops iterating and hires a research pass. The pass looks at how others solved the problem in games, papers, engine docs and talks, turns the description into candidate causes and options with references, and brings them back before more attempts go in.&lt;/p&gt;
&lt;h2 id=&quot;which-model-does-which-job&quot;&gt;Which model does which job&lt;/h2&gt;
&lt;p&gt;Each job goes to the cheapest model that gets it right.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sonnet&lt;/strong&gt; is the default: research passes, sweeps through files and docs, extraction, mechanical ports, running and reading benchmarks, and drafting from a settled outline.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Opus&lt;/strong&gt; takes the jobs where a wrong answer is expensive and hard to notice: architecture and migration plans, reviews of risky changes, load-path and determinism debugging, and independent checks of finished work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The most expensive tier&lt;/strong&gt; is used rarely, and only with a stated reason.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The test we apply is whether a plausible wrong answer would survive review. If it would, the job gets a stronger model.&lt;/p&gt;
&lt;h2 id=&quot;in-numbers&quot;&gt;In numbers&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Research documents:&lt;/strong&gt; 41 of the 61 documents in the game’s docs folder were written by research passes. They cover glass, water, fire and smoke, enemy pilots, ship classes, city layout and naming, among others.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Commits:&lt;/strong&gt; 62 of the game’s 761 commits mention a contractor, a research pass or a worktree.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;One review:&lt;/strong&gt; an Opus review of a night’s gameplay code found eight problems, all fixed. The largest was that the enemy pilots’ sensing rays started inside their own hull.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;some-of-the-jobs&quot;&gt;Some of the jobs&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The name.&lt;/strong&gt; Three naming research passes checked candidates against game stores and domains. The third listed Wreckwing as clear on every check it ran, and we picked it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The studio’s plan.&lt;/strong&gt; A headless Opus session wrote the plan for the asset studio from the game’s code. The main agent spot-checked its claims about the code, and the plan was approved the same day.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A night’s review.&lt;/strong&gt; The eight problems above came from a read-only review the main agent ran after a night of gameplay work.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A look that reads as modern.&lt;/strong&gt; Four headless Sonnet research passes studied materials, lighting, geometry and atmosphere, and a synthesis turned them into a staged plan.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Parallel agents.&lt;/strong&gt; We run more than one agent at once, each in its own git worktree, like a small team merging its work. The game’s Linear record moved to the repository’s shared git directory, so parallel agents see the same list of closed issues.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ships from the studio.&lt;/strong&gt; On October 10 the studio agent exported the textured Sniper, then the Skyhammer, the Brute, the Wasp and a copper Raven. The main agent committed each one after the game’s checks passed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;These posts.&lt;/strong&gt; The back catalog on this devlog was built the same way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;three Sonnet contractors mined the game’s changelog, its docs and Linear for stories;&lt;/li&gt;
&lt;li&gt;five Sonnet contractors drafted posts;&lt;/li&gt;
&lt;li&gt;four Opus contractors checked every number and credit against the sources, independently of the drafters, and corrected what didn’t match;&lt;/li&gt;
&lt;li&gt;another contractor rendered the ads and checked the game clips frame by frame.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The site agent kept the plan, read the reports, checked the media itself, and merged the work.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-it-holds-together&quot;&gt;How it holds together&lt;/h2&gt;
&lt;p&gt;Every agent works from the same Linear issues and the same rules written in its repository, so a job can move between agents without a conversation. &lt;a href=&quot;/devlog/why-linear-runs-the-project/&quot;&gt;Why Linear runs the project&lt;/a&gt; covers that side.&lt;/p&gt;
</content:encoded><category>Studio note</category><category>claude-code</category><category>tooling</category><author>Alex van Rossum</author></item><item><title>Five textured ships in the game</title><link>https://trilithon.games/devlog/five-ships-in-the-game/</link><guid isPermaLink="true">https://trilithon.games/devlog/five-ships-in-the-game/</guid><description>The Skyhammer, Brute, Wasp and a copper Raven join the Sniper as textured ships, with exhaust cones, a visible shield, and damage drawn on the model.</description><pubDate>Sat, 10 Oct 2026 00:00:00 GMT</pubDate><category>Dev journal</category><category>rendering</category><category>weapons</category><author>Alex van Rossum</author></item><item><title>Pilots that fly into walls and sit in alleys</title><link>https://trilithon.games/devlog/pilots-and-walls/</link><guid isPermaLink="true">https://trilithon.games/devlog/pilots-and-walls/</guid><description>Scripted enemy pilots hit walls and stalled in narrow streets. A review found a flaw in our first numbers, and the wall problem is still open.</description><pubDate>Sat, 10 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;what-we-found-in-play&quot;&gt;What we found in play&lt;/h2&gt;
&lt;p&gt;The scripted pilot is the hand-written enemy behavior in the dogfight. It steers by measuring the distance to the nearest wall with rays cast ahead and to the sides, which the code calls whiskers.&lt;/p&gt;
&lt;p&gt;On October 4 the scripted pilots made poor decisions, such as flying straight into buildings, and that evening, on an M1 Mac, they often got stuck in alleys. In a run on October 9, a pilot sat in one place for 7 seconds over a low roof while its health was shot from 46% down to 25%.&lt;/p&gt;
&lt;h2 id=&quot;walls&quot;&gt;Walls&lt;/h2&gt;
&lt;p&gt;In 16 scripted duels of 150 seconds each, ships hit walls 5.6 times per ship-minute, nearly all at 10 to 20 m/s. There were two causes. When the way ahead was closed, the whiskers turned the ship but its thrust kept it creeping forward. More often, a wall beside the ship scraped it while it turned or strafed, because the whiskers pointed along the nose and did not look sideways.&lt;/p&gt;
&lt;p&gt;The pilot now looks along the direction it is moving as well as the direction it points, backs off a wall that is close ahead, and strafes away from a wall beside it. In the same 16 duels, the rate fell to 0.54 hits per ship-minute. Duels still ended with a winner in 12 of 16 cases, against 14 of 16 before.&lt;/p&gt;
&lt;h2 id=&quot;a-review-found-the-measurement-was-partly-blind&quot;&gt;A review found the measurement was partly blind&lt;/h2&gt;
&lt;p&gt;The same night, a review of that night’s code found eight problems, and all were fixed. The largest was in the senses. The pilots cast their rays from where the ship had been one physics step earlier (a physics step is the smallest slice of time the game simulates). At cruise speed that position is inside the ship’s own hull. Each ray hit the ship it came from and reported a wall at zero distance, so the Ravens braked at cruise and never boosted.&lt;/p&gt;
&lt;p&gt;The 0.54 figure had been measured with those senses. After the fix the rays start from the ship’s current position and skip its own body. Remeasured in the same 16 duels with the fixed senses, ships hit walls 3.8 times per ship-minute without the new clearance behavior and 1.0 with it.&lt;/p&gt;
&lt;h2 id=&quot;alleys&quot;&gt;Alleys&lt;/h2&gt;
&lt;p&gt;The flight logs from October 9 put a number on the stalls. The nearest pilot stayed within 6 m of one spot for 3 seconds or more 12% of the time in the tight cities, against 2 to 4% in the other cities, mostly while repositioning.&lt;/p&gt;
&lt;p&gt;Six duels of 90 seconds in each of two tight cities reproduced the stall in the same kind of spot as the October 9 run, with walls 2 to 4 m from each side and 7 to 13 m ahead. At each decision the whiskers sent the pilot the other way, and it dithered at about 1 m/s for up to 55 seconds. The pilot’s existing stuck check missed this. It required a speed under 3 m/s and thrust effort above half, and braking for the wall held the thrust near half.&lt;/p&gt;
&lt;p&gt;The pilot now keeps a trail of where it has been, one point every 0.5 seconds. When its last 2 seconds lie within 6 m of where that stretch began, it goes back along the trail to the newest point at least 12 m away, for up to 3 seconds. A first version that waited 1.5 seconds within 4 m of the last position missed a pilot swinging back and forth over 4 to 8 m.&lt;/p&gt;
&lt;div class=&quot;tl-table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table, scrolls sideways&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Duels in&lt;/th&gt;
&lt;th&gt;Held time, before&lt;/th&gt;
&lt;th&gt;Held time, after&lt;/th&gt;
&lt;th&gt;Longest hold, before&lt;/th&gt;
&lt;th&gt;Longest hold, after&lt;/th&gt;
&lt;th&gt;Wall hits, before&lt;/th&gt;
&lt;th&gt;Wall hits, after&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Our tight city&lt;/td&gt;
&lt;td&gt;23%&lt;/td&gt;
&lt;td&gt;4.4%&lt;/td&gt;
&lt;td&gt;55 s&lt;/td&gt;
&lt;td&gt;3.6 s&lt;/td&gt;
&lt;td&gt;132&lt;/td&gt;
&lt;td&gt;106&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A second tight city&lt;/td&gt;
&lt;td&gt;4.1%&lt;/td&gt;
&lt;td&gt;3.4%&lt;/td&gt;
&lt;td&gt;6.5 s&lt;/td&gt;
&lt;td&gt;3.6 s&lt;/td&gt;
&lt;td&gt;126&lt;/td&gt;
&lt;td&gt;114&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;A test pilot chasing a target into a dead end 10 m wide held in place for 28 seconds before the change and 4.3 seconds after.&lt;/p&gt;
&lt;h2 id=&quot;standing-still&quot;&gt;Standing still&lt;/h2&gt;
&lt;p&gt;On October 10 we decided that hovering motionless should be a trait, so harder enemies are more mobile. In the tight city’s duels, pilots spent 7 to 25 seconds of 15 to 17 ship-minutes held still while firing. Each enemy profile now has a mobility value from 0 to 1 that sets how long it holds still before it shifts position: 8 seconds at 0, down to 1.5 seconds at 1. Easy enemies are set at 0.2, medium at 0.5 and hard at 0.8.&lt;/p&gt;
&lt;h2 id=&quot;what-is-still-open&quot;&gt;What is still open&lt;/h2&gt;
&lt;p&gt;A reading of the avoidance code on October 10 found one more possible cause. The pilot’s whiskers all start on the center line of the nose, and the hull is a box 5 m wide and 7.6 m long. A one-story roof sits above the bottom of the hull and below every level whisker, so only the downward whisker sees it, 0.06 to 0.22 seconds before contact. That fits the pilot that sat over a low roof on October 9, but it has not been confirmed against a recorded contact.&lt;/p&gt;
&lt;p&gt;The next steps are to sort each wall hit by the direction of the contact, then to test the hull’s real shape against walls instead of single rays, then to choose among open headings. The target is 1.5 wall hits per ship-minute, half of the 3.2 measured in the tight city, against about 1 in the block arena.&lt;/p&gt;
</content:encoded><category>Postmortem</category><category>ai</category><author>Alex van Rossum</author></item><item><title>The muzzle flashed, but the rounds never showed up</title><link>https://trilithon.games/devlog/rounds-that-never-showed-up/</link><guid isPermaLink="true">https://trilithon.games/devlog/rounds-that-never-showed-up/</guid><description>Every ship&apos;s shots started on its own hull, so many rounds hit the ship that fired them. We fixed that and gave two ships new weapons.</description><pubDate>Sat, 10 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In play, a ship’s cannon would flash at the muzzle and no rounds would appear. Every ship had the problem to some degree, and the Brute, the heavy gunship with a scatter gun, had it worst. The same day, the Brute and the Sniper each got a cannon of their own, and the Sniper got a lock and a missile that work against buildings. The ships themselves are covered in &lt;a href=&quot;/devlog/five-ships-in-the-game/&quot;&gt;Five textured ships in the game&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;shots-that-started-inside-the-ship&quot;&gt;Shots that started inside the ship&lt;/h2&gt;
&lt;p&gt;The Sniper’s tracers also started a little to the right of its gun.&lt;/p&gt;
&lt;p&gt;The game finds what a shot hits by casting a ray, a line from the muzzle that reports the first thing it touches. Each ship’s muzzle sits on the face of its collision box, the simple box the physics uses for the hull. A ray that starts on or inside a convex body (a solid with no dents) hits that body at once, so a shot’s first hit could be the ship’s own nose.&lt;/p&gt;
&lt;p&gt;We fired one shot from each ship at each of 24 headings.&lt;/p&gt;
&lt;div class=&quot;tl-table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table, scrolls sideways&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ship&lt;/th&gt;
&lt;th&gt;Struck its own hull, before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Brute&lt;/td&gt;
&lt;td&gt;180 of 216 pellets, from 20 of its 24 shots&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sniper&lt;/td&gt;
&lt;td&gt;85 rounds, from 24 bursts&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Skyhammer&lt;/td&gt;
&lt;td&gt;19 rounds, from 24 shots&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;The weapons code had also added the offset used for firing on foot to the ship’s muzzle, which started every flown ship’s tracers 1.2 m from the muzzle, off to the right.&lt;/p&gt;
&lt;p&gt;A ray can now be told to ignore one body, and every weapon a flown ship fires passes through that ship. In the same test every Brute pellet now reaches the ground ahead, and tracers start at the muzzle. Nothing changes on foot or in replays.&lt;/p&gt;
&lt;h2 id=&quot;the-brutes-scatter-gun&quot;&gt;The Brute’s scatter gun&lt;/h2&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;The Brute’s textured model, white with orange stripes, twin rear engines and a cannon under its nose&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;./brute.jpg&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The Brute, as the asset studio renders it.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The Brute fires nine pellets a shot at three shots a second. The first pellet goes down the aim line and the other eight spread evenly within about 2° of it. Damage is counted in hulls, where 1 is a whole hull. Each pellet does 0.015 and is spent at 120 m, so nine landing pellets do 0.405 a second, against 0.30 for the Skyhammer’s stream.&lt;/p&gt;
&lt;p&gt;Each pellet keeps a full round’s fracture energy, the force it breaks things with, which makes 27 strikes a second against the stream’s 12. We timed two scripted cannon sweeps in a test scene, twice each, with the Mac’s Low Power Mode off. The p50 is the median time of a simulation step, and the p95 is the time that 95% of steps stay under.&lt;/p&gt;
&lt;div class=&quot;tl-table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table, scrolls sideways&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Stream, p50 / p95&lt;/th&gt;
&lt;th&gt;Scatter gun, p50 / p95&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Run 1&lt;/td&gt;
&lt;td&gt;4.35 / 4.93 ms&lt;/td&gt;
&lt;td&gt;4.69 / 6.04 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 2&lt;/td&gt;
&lt;td&gt;4.40 / 5.24 ms&lt;/td&gt;
&lt;td&gt;4.73 / 6.15 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;That is about 0.3 ms more at p50 and 1 ms at p95.&lt;/p&gt;
&lt;h2 id=&quot;the-snipers-tracking-burst&quot;&gt;The Sniper’s tracking burst&lt;/h2&gt;
&lt;p&gt;The Sniper fires bursts of five rounds, 0.06 s apart, one burst every 0.8 s. A round does 0.036 damage, which makes 0.225 a second, the lowest of the three. It travels at 400 m/s and is spent at 700 m.&lt;/p&gt;
&lt;p&gt;When a burst starts, the game picks the enemy nearest the crosshair within 15°, as long as it is in clear sight. The rounds then curve toward it, turning at most 60° a second, and fly straight on if they pass it or it is gone. The aim does not snap. A white ring on the HUD marks what a burst would track.&lt;/p&gt;
&lt;h2 id=&quot;a-lock-that-survives-a-building&quot;&gt;A lock that survives a building&lt;/h2&gt;
&lt;p&gt;A ship flying at 30 m/s passes behind a 40 m wide building in 1.3 s. A completed Sniper lock now holds a target that is out of sight for 3 s, which covers about two such buildings, as long as the target stays within the 400 m lock range and 1.5 times the lock cone. While it is held without sight, the HUD’s brackets go hollow. The other classes keep the usual 1 s grace.&lt;/p&gt;
&lt;h2 id=&quot;a-missile-through-six-pieces&quot;&gt;A missile through six pieces&lt;/h2&gt;
&lt;p&gt;The Sniper’s missile breaks through up to six pieces of standing or falling structure and goes off at the seventh. Glass does not count as a piece, and the missile flies through debris and props. The ground, a ship or a piece that will not break sets it off at once. Other classes’ missiles still go off at the first thing they hit.&lt;/p&gt;
&lt;p&gt;The plan left five or six to a count. We cast 7,360 lines through the middle of every building in four seeds (each seed generates a different city) of the run’s four kinds of city, at three heights. A line crossed a median of 5 solid pieces. Five pieces would let a missile through 59% of the lines and six through 73%, so six takes it through about three buildings in four.&lt;/p&gt;
&lt;p&gt;In a release build with Low Power Mode off, a missile through a pane and six concrete walls cost at most 2.08 ms a step in two runs, and a missile that went off at the pane cost at most 0.67 ms.&lt;/p&gt;
&lt;h2 id=&quot;a-visible-shield&quot;&gt;A visible shield&lt;/h2&gt;
&lt;p&gt;Earlier that day every ship’s shield became a shell that flares where a blow lands, 25° across for a round and 60° for a missile. It is blue-white while the shield holds, amber at a quarter left and red-orange under a tenth. Bumps count as blows, so when the Sniper meets a wall, the shell flares at its nose.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;The Sniper seen from behind over a city street, inside an amber ring of shield shell&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;./shield-shell.jpg&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The shield shell in amber, with the shield running low.&lt;/em&gt;&lt;/p&gt;
</content:encoded><category>Dev journal</category><category>weapons</category><category>balance</category><author>Alex van Rossum</author></item><item><title>Since 0.15: work in progress</title><link>https://trilithon.games/devlog/since-0-15/</link><guid isPermaLink="true">https://trilithon.games/devlog/since-0-15/</guid><description>What has gone into the development build since release 0.15 on October 7, from a freeway and new player ships to recorded sounds. None of it is tagged yet.</description><pubDate>Sat, 10 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This work is in the current development build as of October 10. It has not been tagged as a release, and some of it may change before the next one. On October 9 we picked the game’s name, Wreckwing, from a shortlist, and the title screen, boot sequence and credits now use it.&lt;/p&gt;
&lt;h2 id=&quot;game&quot;&gt;Game&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;An elevated freeway curves through a new, wider 375 m city and runs out past its edge. It has two lanes each way, lane lines, lamps, and a cloverleaf of four solid loop ramps with merge lanes. It has its own post.&lt;/li&gt;
&lt;li&gt;Every ship is twice as tough, and a shield now takes a blow whole, so fights that had stalled in draws end again. That also has its own post.&lt;/li&gt;
&lt;li&gt;An airbrake, added after a play test on October 9, turns a boosting ship onto a new line without stopping. From 70 m/s, a 90° turn takes 0.18 s where it took 0.93 s.&lt;/li&gt;
&lt;li&gt;A Hood camera shows the ship’s nose from just above the cockpit, and first-person views cut to a crash camera when the ship goes down.&lt;/li&gt;
&lt;li&gt;A damage panel lists the ship’s parts with a bar each. We chose a list over a drawing so every design gets one.&lt;/li&gt;
&lt;li&gt;The autocannon snaps its aim to a lead indicator within about 3°. A lock holds its target and takes the nearest enemy in a line, with tones for the grab and the lock. The missile pod reloads in 4.5 s and sounds a tone when one is ready.&lt;/li&gt;
&lt;li&gt;A floor that comes down on a ship crushes it. We wanted a falling building to hurt much more. A slab 4.7 times the ship’s weight finishes it in about three seconds.&lt;/li&gt;
&lt;li&gt;A ship that loses parts collides as what is left of it, and a wing tip comes off with the engine it hangs from. A wreck throws 8 to 10 bodies where it threw 109 to 118.&lt;/li&gt;
&lt;li&gt;More player ships can be chosen in Settings. One is the Sniper, built for range: very fast in a sprint and slow to turn. It glances off walls, and a head-on hit bounces it back, because crashing through a building at that speed would wreck it. Its model is drawn with textures per part. At 3840 × 2160, six at full detail add 3.56 ms of GPU time.&lt;/li&gt;
&lt;li&gt;Enemy pilots back out of dead ends. In our tight city, the time a pilot held still fell from 23% to 4.4% and the longest hold from 55 s to 3.6 s. A mobility trait makes harder enemies move more.&lt;/li&gt;
&lt;li&gt;Each enemy flies by a profile of numbers for reaction time, aim error, aggression and self-preservation. In a round robin, a pilot that scores its goals beat the state machine it replaced. The trial decision model’s goal choices added nothing over the rules in that test, so its weight is zero for now.&lt;/li&gt;
&lt;li&gt;Cars flip and tumble in a blast, and a car whose fuel goes up throws off its hood, trunk, a door and a wheel.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;engine&quot;&gt;Engine&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Rubble piles that erupt on their own, a physics mistake, now set off a secondary explosion.&lt;/li&gt;
&lt;li&gt;Trees have limbs and clumps of leaves, and sway when a blast’s wave reaches them. A trunk snaps where it was hit, and a close blast leaves a stump. Trees far from a blast stay standing.&lt;/li&gt;
&lt;li&gt;Fire and smoke follow the trial we picked on October 8 and 9, with smoke that rises with its heat, a mushroom cap and dark lumps.&lt;/li&gt;
&lt;li&gt;Recorded sounds replace synthesized ones for most blasts, cracks, crumbling, concrete landings and the beds under a collapse: 49 takes from 26 recordings, which we picked. Glass and the autocannon stay synthesized for now. A blast heard from far off is a rolling boom.&lt;/li&gt;
&lt;li&gt;New lighting includes a better ambient occlusion term, cloud shadows and daylight through broken facades. Smoke shadows are off until a fault on facades is fixed.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;tools&quot;&gt;Tools&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A development journal with images records the interesting things as they happen, and posts here draw on it.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>Release notes</category><category>ai</category><category>audio</category><category>rendering</category><category>physics</category><author>Alex van Rossum</author></item><item><title>Drawing each ship&apos;s own textured model</title><link>https://trilithon.games/devlog/textured-ship-models/</link><guid isPermaLink="true">https://trilithon.games/devlog/textured-ship-models/</guid><description>The game now draws each ship&apos;s own textured model, part by part, where it used to rebuild ships from convex blocks. What that took, and what it costs to draw.</description><pubDate>Sat, 10 Oct 2026 00:00:00 GMT</pubDate><category>Dev journal</category><category>rendering</category><category>performance</category><author>Alex van Rossum</author></item><item><title>The ships: concept art to textured models</title><link>https://trilithon.games/devlog/the-ships/</link><guid isPermaLink="true">https://trilithon.games/devlog/the-ships/</guid><description>Three ships to fly, the enemies built from them, and how each one goes from concept art to a textured model the game can break apart.</description><pubDate>Sat, 10 Oct 2026 00:00:00 GMT</pubDate><category>Dev journal</category><category>rendering</category><category>tooling</category><author>Alex van Rossum</author></item><item><title>Why Linear runs the project</title><link>https://trilithon.games/devlog/why-linear-runs-the-project/</link><guid isPermaLink="true">https://trilithon.games/devlog/why-linear-runs-the-project/</guid><description>A game built almost entirely by Claude Code sessions, and 463 Linear issues in twelve days. Why every piece of work starts and ends in Linear.</description><pubDate>Sat, 10 Oct 2026 00:00:00 GMT</pubDate><category>Studio note</category><category>claude-code</category><category>tooling</category><author>Alex van Rossum</author></item><item><title>A building that falls on the ship now hurts it</title><link>https://trilithon.games/devlog/a-building-falls-on-the-ship/</link><guid isPermaLink="true">https://trilithon.games/devlog/a-building-falls-on-the-ship/</guid><description>A floor falling on a ship took 5% of its health. Damage now follows the weight pressing on the ship, and a collapsing tower flattens it.</description><pubDate>Fri, 09 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;what-we-wanted&quot;&gt;What we wanted&lt;/h2&gt;
&lt;p&gt;A ship took almost no damage when a whole building fell on it. On October 9 we decided it should take far more, and that a floor falling on the ship should do some damage. Enemies should take the same damage, so a collapse works as a weapon against them, and ramming a car or debris should hurt the ship no more than it did.&lt;/p&gt;
&lt;h2 id=&quot;what-we-measured&quot;&gt;What we measured&lt;/h2&gt;
&lt;p&gt;In the damage code, a ship was hurt only by the speed a physics step took off it: a loss of more than 8 m/s counted, each m/s beyond that cost 0.015 of its health, one blow took at most 0.35, and blows were at least 0.4 seconds apart. Something heavy that stops a ship gradually, or pins it, did nothing.&lt;/p&gt;
&lt;p&gt;A probe dropped a 26 t slab from 14 m onto a hovering ship. The slab took 11.5 m/s off the ship in one step, which cost it 5% of its health. The slab then pinned the ship to the street and did no more damage.&lt;/p&gt;
&lt;h2 id=&quot;weight-on-the-ship&quot;&gt;Weight on the ship&lt;/h2&gt;
&lt;p&gt;The first change made weight count. Every other step, the ship takes 0.12 of its health per second for each ship’s weight of bodies pressing down on it from above, up to 0.6 a second, and the shield goes first. Only bodies at least half the ship’s own mass count. The slab weighs 4.7 times the ship and leaves it at about 0.02 of its health after 3 seconds. A 77 kg piece dropped on the ship leaves it whole. Enemies take the same damage.&lt;/p&gt;
&lt;h2 id=&quot;a-first-guess-that-was-wrong&quot;&gt;A first guess that was wrong&lt;/h2&gt;
&lt;p&gt;The first guess at what was still missing came from reading the code, before any probe of a falling tower, and it was wrong. A probe under a tower showed the cause. A tower that comes down as a mass presses on what is below with its stories, which the game moves along set paths in place of letting physics move them. The collision query still weighs them, at 2,700 to 5,100 t, which is 500 to 930 ships. At the 0.6 a second cap, a shielded ship in the lobby of a 24-story tower outlasted the fall. It ended at 0.44 of its health, pushed 5.8 m into the ground.&lt;/p&gt;
&lt;p&gt;The cap on the rate was too low for that much weight. A weight of 100 ships or more now flattens a ship at once, shield included. In the test, the ship’s crash ends 0.63 seconds after the tower’s mass starts down, where the ship was hovering.&lt;/p&gt;
&lt;h2 id=&quot;only-what-came-down-counts&quot;&gt;Only what came down counts&lt;/h2&gt;
&lt;p&gt;A body counts as crushing only once it is seen pressing on the ship while moving down faster than 0.5 m/s and touching within 2 cm, and it keeps counting as long as it keeps pressing. A slab that lands, pins the ship and comes to rest still crushes it.&lt;/p&gt;
&lt;p&gt;A review found two cases that should not count, frozen rubble that the ship rises into and other ships, and tests cover both. A ship that climbs into a standing slab or into frozen rubble for 3 seconds comes out whole, and a ship that comes down on top of another for 3 seconds leaves that one whole.&lt;/p&gt;
&lt;h2 id=&quot;a-felled-chimney&quot;&gt;A felled chimney&lt;/h2&gt;
&lt;p&gt;A later probe cut a 110 m chimney on one side, with a gunship hovering 3 m up at 30, 50 and 70 m along the fall. At all three spots the falling blocks landed on it. Each block weighs 25 to 38 t, and up to seven landed on the ship at once. The ship was bumped and then crushed at the 0.6 a second cap, and its crash ended 1 to 1.5 seconds after impact, within a few meters of where it hovered. The rules needed no change. The three numbers behind the rule are left for us to tune: 0.12 a second per ship’s weight, the 0.6 a second cap, and the 100 ships’ weight that flattens a ship at once.&lt;/p&gt;
</content:encoded><category>Dev journal</category><category>physics</category><category>balance</category><author>Alex van Rossum</author></item><item><title>A fast decision model for the enemy pilots</title><link>https://trilithon.games/devlog/decision-model-first-trial/</link><guid isPermaLink="true">https://trilithon.games/devlog/decision-model-first-trial/</guid><description>A small trained model picked goals for the enemy pilots. It beat the scripted pilot at first, then settled on one goal. The investigation continues.</description><pubDate>Fri, 09 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;what-was-tried&quot;&gt;What was tried&lt;/h2&gt;
&lt;p&gt;On October 2, we set out to trial a small, fast trained model for the enemy AI, and a way to train it. It belongs to a new class of model that its developers call “System 1” models, after the fast, intuitive System 1 in Daniel Kahneman’s account of how people think: a small model that answers a structured question in one quick pass, made for decisions in real time, where a reasoning model works through a problem step by step. It might not be the right answer for enemy AI, which is why it needed a trial.&lt;/p&gt;
&lt;p&gt;In the trial, the scripted pilot kept flying the ship. About three times a second the model was given a few sentences describing the fight (range, bearing, how hurt each ship is) and picked one of four goals: pursue, attack, break off or reposition. An answer took about 8 ms on the M5 Mac the game is built on.&lt;/p&gt;
&lt;h2 id=&quot;training-it-on-outcomes&quot;&gt;Training it on outcomes&lt;/h2&gt;
&lt;p&gt;The labels score outcomes. At a moment in a scripted duel, a labeler replays the duel once for each goal with the pilot holding that goal, scores what followed (damage dealt less damage taken, a kill or a loss), and writes the moment in words with a probability for each goal. The model was fine-tuned on those labels and flown against the scripted pilot in headless duels among city blocks, with sides swapped.&lt;/p&gt;
&lt;div class=&quot;tl-table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table, scrolls sideways&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Generation&lt;/th&gt;
&lt;th&gt;Trained on&lt;/th&gt;
&lt;th&gt;Model won–lost–drawn&lt;/th&gt;
&lt;th&gt;Goal held most&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0 (untrained)&lt;/td&gt;
&lt;td&gt;nothing&lt;/td&gt;
&lt;td&gt;4–5–11 (20 duels)&lt;/td&gt;
&lt;td&gt;pursue, 89%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;506 moments&lt;/td&gt;
&lt;td&gt;43–17–0&lt;/td&gt;
&lt;td&gt;attack, 79%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3,171 moments&lt;/td&gt;
&lt;td&gt;35–25–0&lt;/td&gt;
&lt;td&gt;attack, 97%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4,286 moments, 4 replays per goal&lt;/td&gt;
&lt;td&gt;23–33–4&lt;/td&gt;
&lt;td&gt;break off, 64%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;about 2,000 moments, a longer scoring window&lt;/td&gt;
&lt;td&gt;25–29–6&lt;/td&gt;
&lt;td&gt;pursue, 100%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Generations 1 to 4 each flew 60 duels. Generation 1 trained for about 4 minutes and won 43 to 17 by pressing the attack. A pilot that held attack for the whole duel scored 35–25 on the same duels, so generation 1 had learned something beyond that. Generation 2, with six times the data, learned only to attack and scored exactly what holding attack scores. Generations 3 and 4 used different label designs and each leaned on one goal, a different one each time.&lt;/p&gt;
&lt;h2 id=&quot;the-diagnosis&quot;&gt;The diagnosis&lt;/h2&gt;
&lt;p&gt;Three label designs in a row had produced a one-goal model, so training stopped and an analysis of the labels and checkpoints looked for the cause. It found three things.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each fine-tuned model answered with about the average label of its training set in every situation. The pilot switches goals only when another goal beats the current one by 0.1, so it held whichever goal it started on or first landed on.&lt;/li&gt;
&lt;li&gt;A goal the model chose never ended in play, while the labels had scored each goal for only 2 or 4 s.&lt;/li&gt;
&lt;li&gt;A bag-of-words regression, which only counts the words in the description, predicted the labels about as well as the fine-tuned model. It predicted the scripted pilot’s own goal 88 to 91% of the time, so the words describe the situation well and the weak part is the outcome labels.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We decided there would be no more training until we had recorded flights of our own. Checks on a later set of labels found that half the moments have a clearly best goal and that the description does not say which. With shields and missile lock-on in the game, a pilot that only attacked beat the scripted pilot 52 to 3 in 120 s duels, with 5 draws. A first model trained on our flights scored 4 wins, 7 losses and 49 draws against the scripted pilot.&lt;/p&gt;
&lt;h2 id=&quot;instinct-plus-reasoning&quot;&gt;Instinct plus reasoning&lt;/h2&gt;
&lt;p&gt;On October 9, we settled on a blend. Each enemy carries a personality matrix, a set of numbers that is its core behavior, and the model adds to it. Every enemy pursues two goals in order, survive first and win second. We built the matrix and a survival-first arbiter, added the model as an advisor with a weight, and let an experiment set that weight.&lt;/p&gt;
&lt;p&gt;The experiment was a round robin of four pilots: the scripted pilot, the model as flown, a pilot that only attacks, and a blend of the model with the scripted survival rules. Each seed was flown twice with sides swapped, and all six pairings took 11 minutes.&lt;/p&gt;
&lt;div class=&quot;tl-table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table, scrolls sideways&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;First pilot against second&lt;/th&gt;
&lt;th&gt;Rating gap (95% range)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Only attacks against scripted&lt;/td&gt;
&lt;td&gt;+215 (+118 to +358)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model against scripted&lt;/td&gt;
&lt;td&gt;+89 (+35 to +147)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blend against scripted&lt;/td&gt;
&lt;td&gt;+60 (+22 to +99)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blend against model&lt;/td&gt;
&lt;td&gt;−9 (−26 to +8)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model against only attacks&lt;/td&gt;
&lt;td&gt;−80 (−131 to −31)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blend against only attacks&lt;/td&gt;
&lt;td&gt;−84 (−136 to −35)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;The gap is an Elo rating difference, as in chess. A positive number means the first pilot wins more, and +100 is about a 64% score. The model’s goal choices rated 80 points under holding attack, and the survival rule made no difference to that. The model added nothing to goal choice, so its weight in the enemy profiles is zero for now. The arbiter now picks the dogfight enemies’ goals and rates +101 over the scripted pilot it replaced.&lt;/p&gt;
&lt;h2 id=&quot;where-it-stands&quot;&gt;Where it stands&lt;/h2&gt;
&lt;p&gt;The trial covers one model, one kind of label and one kind of opponent. The labels came from scripted duels between two equal ships. A human pilot flies differently, and fights with two or three enemies may reward survival more. Further training waits for labels that do not come from scripted duels, such as our flights.&lt;/p&gt;
&lt;p&gt;We are still investigating fast models of this kind. This one stays in the build until we have seen how the scripted pilots perform and have tried tuning it under the blended design. A setting in the game still lets it fly the enemies.&lt;/p&gt;
</content:encoded><category>Dev journal</category><category>ai</category><category>balance</category><author>Alex van Rossum</author></item><item><title>Doubling ship toughness, and duels that wouldn&apos;t end</title><link>https://trilithon.games/devlog/doubling-ship-toughness/</link><guid isPermaLink="true">https://trilithon.games/devlog/doubling-ship-toughness/</guid><description>Doubling ship toughness turned most AI duels into draws. The shield was dividing every blow too. Here is what we measured and changed.</description><pubDate>Fri, 09 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;what-we-changed&quot;&gt;What we changed&lt;/h2&gt;
&lt;p&gt;On October 9, fights ended too quickly, since two missiles took an enemy down. We doubled every ship’s toughness, the player’s included. Toughness is the number each blow is divided by before it does damage, so doubling it should make a ship last about twice as long.&lt;/p&gt;
&lt;h2 id=&quot;what-we-measured&quot;&gt;What we measured&lt;/h2&gt;
&lt;p&gt;The game has a tool that flies two computer pilots against each other in a small city of blocks, hundreds of duels at a time, much faster than real time. We use it to tune the enemy pilots: two versions fly the same duels, swapping sides, and the tool reports which one wins more. A duel that runs out of time is a draw.&lt;/p&gt;
&lt;p&gt;After the change, almost every duel was a draw, and longer duels did not fix it.&lt;/p&gt;
&lt;div class=&quot;tl-table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table, scrolls sideways&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Duel&lt;/th&gt;
&lt;th&gt;Time limit&lt;/th&gt;
&lt;th&gt;Ended with a ship shot down&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Before the change&lt;/td&gt;
&lt;td&gt;2 minutes&lt;/td&gt;
&lt;td&gt;88 of 120&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Toughness doubled&lt;/td&gt;
&lt;td&gt;2 minutes&lt;/td&gt;
&lt;td&gt;6 of 80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Toughness doubled&lt;/td&gt;
&lt;td&gt;5 minutes&lt;/td&gt;
&lt;td&gt;14 of 80&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;At the end of five minutes, 95 of the 132 ships still flying had more than 60% of their hull; the median was 76%.&lt;/p&gt;
&lt;h2 id=&quot;why&quot;&gt;Why&lt;/h2&gt;
&lt;p&gt;Every ship has a shield in front of its hull. The shield takes damage first. Once nothing has hit it for 4 seconds it starts to refill, and it goes from empty to full in 3 seconds.&lt;/p&gt;
&lt;p&gt;The toughness division happened before the shield took the blow. Doubling toughness halved every blow, and the shield then held twice as many hits. The computer pilots fight in passes: an attack run, then a break to reposition, which often takes long enough for the shield to refill. An attack run that used to get through the shield and into the hull now ended on the shield, and the hull came out of it untouched, so most passes did no hull damage at all.&lt;/p&gt;
&lt;h2 id=&quot;what-we-did&quot;&gt;What we did&lt;/h2&gt;
&lt;p&gt;We decided that toughness shouldn’t apply to shields. The shield now takes each blow whole, and toughness divides only what gets past it to the hull. The same 80 match-ups, five minutes each:&lt;/p&gt;
&lt;div class=&quot;tl-table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table, scrolls sideways&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Duel&lt;/th&gt;
&lt;th&gt;Ended with a ship shot down&lt;/th&gt;
&lt;th&gt;Median length&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Shield divided by toughness&lt;/td&gt;
&lt;td&gt;14 of 80&lt;/td&gt;
&lt;td&gt;5 minutes (the limit)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shield takes blows whole&lt;/td&gt;
&lt;td&gt;67 of 80&lt;/td&gt;
&lt;td&gt;3 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;The player’s cannon now needs 104 rounds to bring down a medium enemy. By the same arithmetic it needed 64 before the change, and 128 with the shield divided too.&lt;/p&gt;
&lt;p&gt;We also decided that a duel never ends in a draw. One still going at five minutes is now decided on points: the ship with more of its hull left wins, then the one that lost less over the duel, and if neither ship was touched, a coin toss drawn from the duel’s seed. In the 80 duels above, 13 went to points and none to the toss.&lt;/p&gt;
</content:encoded><category>Dev journal</category><category>balance</category><category>ai</category><author>Alex van Rossum</author></item><item><title>An elevated freeway for the wide city</title><link>https://trilithon.games/devlog/elevated-freeway/</link><guid isPermaLink="true">https://trilithon.games/devlog/elevated-freeway/</guid><description>An elevated freeway that curves out past the city, with cloverleaf ramps you can merge from, built to the numbers real freeways use.</description><pubDate>Fri, 09 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;what-we-wanted&quot;&gt;What we wanted&lt;/h2&gt;
&lt;p&gt;The tight city had a raised street on columns that sloped down into the city at both ends. We wanted something closer to a real highway, one that curves and runs out past the edge of the city instead of dropping into it, and looks like a freeway rather than bare concrete. The enemy pilots also flew badly underneath it, worst where it sloped down. And we wanted to be able to get onto it, with solid ramps that come off a street at an intersection.&lt;/p&gt;
&lt;h2 id=&quot;the-freeway&quot;&gt;The freeway&lt;/h2&gt;
&lt;p&gt;The freeway is built to the numbers of real elevated freeways and the standards they follow: two lanes each way on a deck 25.3 m wide, two columns per support standing 8.1 m either side of the middle, nine girders under the deck, a span every 24 m or so, and the underside 13 m up. That is above the ships’ 10 m flying ceiling, so a ship can fly under it anywhere and only has to avoid the columns.&lt;/p&gt;
&lt;p&gt;Every piece of a building in this game is a convex solid, a shape with no dents, because that is what the physics engine handles well. A curve is made the way real ones are: straight spans, each support turned to split the angle between the spans either side of it, and the deck pieces over each support cut to follow both.&lt;/p&gt;
&lt;p&gt;The first layout put a girder over each column. The game’s load model checks every contact between pieces against its strength, and those contacts carried 1.16 to 1.20 times what they could hold. Losing one support brought the whole freeway down. Moving the girders so each one frames into a beam instead brought the worst contact to 0.60, and losing a support now drops just the two spans beside it.&lt;/p&gt;
&lt;h2 id=&quot;fitting-it-into-a-city&quot;&gt;Fitting it into a city&lt;/h2&gt;
&lt;p&gt;The freeway goes into a new kind of city, wider and more spread out, 375 m across from the middle to the edge. Its line is a gentle curve through the middle, running out past the edge each way. The city builds around it: buildings under the deck are dropped, and the supports are placed over the whole line at once to keep their columns out of the streets. Placed one at a time, 10 to 15% of the columns stood in a road; over the whole line, 5 to 8% do. The rest stand where a road crosses at an angle too long for any span, as real ones do.&lt;/p&gt;
&lt;h2 id=&quot;the-cloverleaf&quot;&gt;The cloverleaf&lt;/h2&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;The same crossing before the dressing: bare concrete deck and loops, no lanes or lawns&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;./freeway-bare.jpg&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;The ramps are loops, as on a cloverleaf interchange: a ramp of solid fill around a 32 m circle in each corner of a crossing. Each loop touches the street’s edge at the ground and the deck’s edge at the top, and climbs the long way round, about 240 to 290 degrees on the cities measured, at 10 to 12%.&lt;/p&gt;
&lt;p&gt;Three things had to be found by trying. The freeway crosses most streets at about 45 degrees, and in a 45 degree corner a loop that touches both roads sits about 130 m out, across other streets, so no city found room for one. The interchange now goes where a street crosses nearly square. The freeway also curves: a loop fitted to the deck’s direction at the crossing pushed up to 2 m into the deck by the time it reached it, so each loop slides along its street until it meets the curved edge exactly. Finally, the loops near the middle of the city took the tower lots with them, and with them the city’s height, so the interchange is placed about halfway out.&lt;/p&gt;
&lt;p&gt;The first loops met the deck and the street at a single point each, which left nowhere to merge, on or off. Each loop now has a merge lane beside the deck that tapers into it, and another at street level, where the curb opens and nothing is parked.&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;A ship’s view on the deck: two lanes each way, dashed lane lines, a yellow line by the median, lamps, and a merge lane joining from the left&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;./freeway-deck.jpg&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;does-it-make-the-pilots-stupid&quot;&gt;Does it make the pilots stupid?&lt;/h2&gt;
&lt;p&gt;Twelve computer-flown duels on open ground with the freeway through the middle, against the same twelve without it: every fight crossed under the deck, 12% of the flying time was spent beneath it, and no ship stood still longer than a second. The ships bumped walls 159 times against 138 without the freeway, the difference being the columns.&lt;/p&gt;
</content:encoded><category>Dev journal</category><category>physics</category><category>collapse</category><category>ai</category><author>Alex van Rossum</author></item><item><title>Fire and smoke, picked from comparison videos</title><link>https://trilithon.games/devlog/fire-and-smoke-picks/</link><guid isPermaLink="true">https://trilithon.games/devlog/fire-and-smoke-picks/</guid><description>Over a week we steered fire and smoke from four reference stills to a pick between side-by-side clips, and each change was measured for cost.</description><pubDate>Fri, 09 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;the-first-pass-october-2&quot;&gt;The first pass, October 2&lt;/h2&gt;
&lt;p&gt;On October 1 we wanted explosions closer to Just Cause, dynamic without the full Hollywood gasoline fireball. By the morning of October 2, in Low Power Mode, the explosions had more presence and glowing embers, but they lacked the roiling, darker smoke and richness of real ones. Four Just Cause stills became the reference.&lt;/p&gt;
&lt;p&gt;The first version staged the blast. A white-hot core bursts out and is gone in about a second. Billows follow and burn down to near-black soot, and a big blast adds a dark column that rises for 5 to 10 s. Noise eats the edge of each puff. That version was still too uniform and did not look alive. The next change made the billows arrive unevenly over half a second and added small hot pops inside the cloud. The puff count stayed the same, and in the worst case, twenty 40 m charges with the camera inside the cloud, the GPU’s 95th-percentile time (the time 95% of frames beat) over the first two seconds went from 30.5 to 31.0 ms.&lt;/p&gt;
&lt;h2 id=&quot;five-problems-october-5&quot;&gt;Five problems, October 5&lt;/h2&gt;
&lt;p&gt;Three days later we listed five problems with the look of a big blast, for later polish work backed by more research. The experiments for them were kept out of the main repository at first.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Make it rise: younger, hotter smoke climbing faster, a column drifting with the wind, a mushroom cap.&lt;/li&gt;
&lt;li&gt;Break up the sphere.&lt;/li&gt;
&lt;li&gt;Put the detail on the edges.&lt;/li&gt;
&lt;li&gt;Fix or cut the ground haze.&lt;/li&gt;
&lt;li&gt;Shrink the fire to a few flickering pockets low in the cloud.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A research pass on October 7 tied each problem to the code. Fire puffs topped out at 0.36 to 1.7 m/s and dust puffs at 0.125 m/s. In a 40 m blast the puffs gained 14 to 20 m in ten seconds inside a cloud about 100 m wide, which makes a heap. Every direction of the burst drew from the same distribution, which is why the silhouette was a sphere. The ground haze had no single source, since eight different emitters left dust at street level. Every billow burned for the first 35% of its life, which is where the orange across the cloud came from.&lt;/p&gt;
&lt;h2 id=&quot;comparing-by-video-october-8&quot;&gt;Comparing by video, October 8&lt;/h2&gt;
&lt;p&gt;Each change was built behind a switch that drew the previous look, and none of them touched what the simulation does. To compare, one 40 m blast from the October 7 play session was replayed twice from the same camera, once with the old look and once with the change, and the two clips were put side by side. The trial picked on October 8 had smoke that rises with its heat, a burst of three to six lobes, edges that stay crisp while each puff erodes into islands, and fire that burns for 15% of a billow’s life instead of 35%.&lt;/p&gt;
&lt;p&gt;Small blasts rose too fast. They now rise at half speed up to a 10 m fireball, and at full speed from 24 m. Fireballs also lingered after large explosions. The fire pockets sat at the blast’s own height, rose with the cloud and each carried a small orange light. On a 40 m charge set off 24 m up, they hung over the street for up to 21 s. Pockets now burn on the ground under the blast and stay put.&lt;/p&gt;
&lt;h2 id=&quot;smoke-made-of-balls&quot;&gt;Smoke made of balls&lt;/h2&gt;
&lt;p&gt;On the morning of October 8 the smoke was still made of balls of uniform shape, and the balls faded out in place without spreading. We wanted to avoid a whole new particle system and to keep the old texture behind a switch for comparison. Two more looks followed, each behind its own switch.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Lumps,&lt;/strong&gt; from a second research pass. A coarse extra layer of noise pulls each puff’s outline in by up to 38%, so each has two to four lobes. Each puff gets its own grain, stretch and drag, and neighbors share a swirl that parts the cloud into lumps and wisps.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A mushroom cap,&lt;/strong&gt; from the first research pass. The cap answered the first problem. Middle puffs rise faster than the edges, and the edges are pushed out and then down. The research found that the flow inside a rising vortex ring has that shape. A fireball 15 m or more across now registers its cloud, and its rising puffs follow that flow.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-we-picked-october-9&quot;&gt;What we picked, October 9&lt;/h2&gt;
&lt;p&gt;On October 9 the cap and the lumps became the default. The table shows the median GPU time over a blast’s first two seconds in one test scene, two runs of each look at 2880 × 1800 in Low Power Mode. The cap was not part of this measurement.&lt;/p&gt;
&lt;div class=&quot;tl-table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table, scrolls sideways&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Look&lt;/th&gt;
&lt;th&gt;GPU time, blast’s first 2 s&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Before the October 8 pick&lt;/td&gt;
&lt;td&gt;5.3 to 5.5 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;October 8 pick&lt;/td&gt;
&lt;td&gt;6.0 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;October 8 pick with lumps&lt;/td&gt;
&lt;td&gt;5.9 to 6.2 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;We added smoke shadows so that a big dust cloud darkens the street and buildings beside it. They left odd trapezoids on some buildings. They are off for now, and the fix is filed for later.&lt;/p&gt;
</content:encoded><category>Dev journal</category><category>rendering</category><author>Alex van Rossum</author></item><item><title>Ponds, a community pool, and better splashes</title><link>https://trilithon.games/devlog/ponds-pool-and-splashes/</link><guid isPermaLink="true">https://trilithon.games/devlog/ponds-pool-and-splashes/</guid><description>Park ponds that drain into craters, a community pool, and a splash redraw after the first spray read as steam and paper.</description><pubDate>Thu, 08 Oct 2026 00:00:00 GMT</pubDate><category>Dev journal</category><category>physics</category><category>rendering</category><author>Alex van Rossum</author></item><item><title>Turning rubble eruptions into secondary explosions</title><link>https://trilithon.games/devlog/rubble-eruptions-as-secondary-explosions/</link><guid isPermaLink="true">https://trilithon.games/devlog/rubble-eruptions-as-secondary-explosions/</guid><description>Rubble piles sometimes threw pieces into the air for no reason. Instead of suppressing the glitch, we turned it into a gas-line explosion.</description><pubDate>Thu, 08 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;rubble-that-jumps-and-our-call&quot;&gt;Rubble that jumps, and our call&lt;/h2&gt;
&lt;p&gt;On October 8, after a building came down and its rubble settled, part of the pile jumped. A burst of pieces flew up out of it with nothing near to throw them, twice in one session.&lt;/p&gt;
&lt;p&gt;The usual fix would be to make it stop by breaking the debris into smaller pieces or tuning the physics until piles stay quiet. We kept the eruptions and gave them a reason. If the game could detect one as it happened and set off an explosion at its center, a physics glitch would become a secondary explosion.&lt;/p&gt;
&lt;h2 id=&quot;why-it-happens&quot;&gt;Why it happens&lt;/h2&gt;
&lt;p&gt;Wreckwing’s buildings fall as thousands of separate pieces, each a solid body in the physics engine (Jolt). In a deep pile, pieces get pressed into one another, and the engine pushes overlapping bodies apart. Most of the time it does this gently. Now and then a group of pieces comes apart at once and leaves the pile fast. We have not traced which contacts cause each eruption, and the fix does not need it, since it works from what the pieces do.&lt;/p&gt;
&lt;h2 id=&quot;what-we-built&quot;&gt;What we built&lt;/h2&gt;
&lt;p&gt;Real buildings have gas lines, and the game already sets some off as a building collapses. An eruption became one more of those.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Every other physics step (30 times a second), the engine lists every moving piece of rubble rising faster than 7 m/s.&lt;/li&gt;
&lt;li&gt;A collapse sends rubble down, and an explosion sends it outward, so most pieces flying up fast have a known cause. A piece counts as explained if an explosion in the last 4 seconds could have thrown it there: within three times the blast’s reach, plus 60 m for each second since it went off.&lt;/li&gt;
&lt;li&gt;Where 12 or more unexplained pieces are rising inside the same 8 m square, that is an eruption. A gas-line explosion goes off at their middle: a flash, a fireball, dust and the sound of a blast. The same square cannot erupt again for 3 seconds.&lt;/li&gt;
&lt;li&gt;The explosion is drawn and heard only. It pushes nothing, so the physics plays out exactly as it would have, and a replay of a session still ends in the same state.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;getting-it-to-fire-only-when-it-should&quot;&gt;Getting it to fire only when it should&lt;/h2&gt;
&lt;p&gt;The first version counted a piece as explained only within three reaches of a blast, for 2 seconds. Across the test scenes it fired 13 times. Ten of those were plain debris from real explosions: pieces thrown 3 to 4 reaches out and still rising half a second later. Letting the explained distance grow with time since the blast (60 m a second, which is faster than debris flies) left 2 of the 13, and both are real:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A deep heap of 2,000 pieces, with no explosion anywhere, throws 25 of them up at once.&lt;/li&gt;
&lt;li&gt;Inside the pile of a falling 80-story tower, 13 pieces are thrown 12 m up.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tests check that twenty pieces rising together with no charge near make one eruption at their middle, that a charge nearby explains them, that eight pieces are too few to count, and that a blast’s debris still rising 20 m out half a second later belongs to the blast.&lt;/p&gt;
&lt;p&gt;In the game’s development build, &lt;code&gt;ERUPTIONS=0&lt;/code&gt; turns eruptions off and &lt;code&gt;ERUPTIONS=log&lt;/code&gt; lists each one.&lt;/p&gt;
</content:encoded><category>Dev journal</category><category>physics</category><category>collapse</category><author>Alex van Rossum</author></item><item><title>The game&apos;s ads, and billboards that tear and glitch</title><link>https://trilithon.games/devlog/ads-and-billboards/</link><guid isPermaLink="true">https://trilithon.games/devlog/ads-and-billboards/</guid><description>Every billboard in the game&apos;s cities carries an invented ad. Here are some of the 110, still and animated, and how a board tears and glitches.</description><pubDate>Wed, 07 Oct 2026 00:00:00 GMT</pubDate><category>Dev journal</category><category>rendering</category><category>fracture</category><author>Alex van Rossum</author></item><item><title>How tall towers collapse</title><link>https://trilithon.games/devlog/how-tall-towers-collapse/</link><guid isPermaLink="true">https://trilithon.games/devlog/how-tall-towers-collapse/</guid><description>A 160-story tower fell as 41,884 loose pieces, then like it was melting. Secondary blasts and a lean fixed that, and a week later we found three faults.</description><pubDate>Wed, 07 Oct 2026 00:00:00 GMT</pubDate><category>Dev journal</category><category>collapse</category><category>physics</category><author>Alex van Rossum</author></item><item><title>Release 0.15: city depth</title><link>https://trilithon.games/devlog/release-0-15/</link><guid isPermaLink="true">https://trilithon.games/devlog/release-0-15/</guid><description>Ponds, a community pool, trees, parked cars, garages, hotels and an industrial zone fill out the city, and large cities stopped stalling after big blasts.</description><pubDate>Wed, 07 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Release 0.15 adds depth to the cities: water, cars, trees, more building types and furnished interiors. The release closed on October 7 with what had been built, and the tight city keeps both of its street-front mixes. The second half of the release is performance work on large cities, driven by freezes we hit in play.&lt;/p&gt;
&lt;h2 id=&quot;game&quot;&gt;Game&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The larger parks have ponds. A pond’s water is a simulated grid, so a crater dug beside it drains it and the water runs in. A pond holds about 2.15 m of water at the middle. A blast pushes a cavity and a ring of waves through the water, and a car or ship that goes in splashes, sends out a ring and sinks.&lt;/li&gt;
&lt;li&gt;The first splashes looked like steam and chunks of paper, so splashes are now drawn as streaks of spray and whitewater.&lt;/li&gt;
&lt;li&gt;A broken hydrant jets water about 5 m up for 20 s, tapers, and stops at 32 s, and the water wets the ground around it. We wanted the hydrants to stop on their own.&lt;/li&gt;
&lt;li&gt;A community pool, a 25-yard tank of six lanes with a bathhouse, stands in every city. We made it a building type so a map holds at most two bodies of water.&lt;/li&gt;
&lt;li&gt;Trees stand in parks, along streets and in yards, 181 in seed 7’s mixed city.&lt;/li&gt;
&lt;li&gt;About 250 cars are left in each city’s streets, parked at curbs and in knots at crossings as if everyone had fled. About 14 rounds set one burning, and 3 to 7 s later its fuel goes up and sets the cars beside it burning. A car is crushed when something ten or more times heavier lands on it.&lt;/li&gt;
&lt;li&gt;Cities have two parking garages where there is room. Cars fill 12% of the stalls, down from 30%, after chain reactions overwhelmed the game in large maps.&lt;/li&gt;
&lt;li&gt;A hotel with a balcony for every room stands in each city. Cities of 250 m and up give part of their outskirts to warehouses and sheds.&lt;/li&gt;
&lt;li&gt;Heavy industry joined the stock in this order: a cooling tower 114 m tall, a tank farm that burns down tank by tank, and a chimney.&lt;/li&gt;
&lt;li&gt;Every furnishable building and every house has a door at ground level on its street face. Shops are furnished by trade, in eight kinds. A fifth of the offices stand vacant with leasing posters.&lt;/li&gt;
&lt;li&gt;Six suburban house types were built as scenes: ranch, Cape Cod, Colonial, split-level, Craftsman bungalow and a modern two-story.&lt;/li&gt;
&lt;li&gt;Close, Far and Nose camera positions join the chase view, with a field-of-view slider. We made tight handling the default after flying both.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;engine&quot;&gt;Engine&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Stalls of 0.2 to 1.4 s after blasts in large cities came from several causes found by replaying our logged sessions. The worst simulation frame in one replay fell from 202 ms to 31.5 ms.&lt;/li&gt;
&lt;li&gt;The renderer now skips buildings outside the view for the depth and shadow passes. In our large cities the live shadow pass had taken 5.8 to 7.9 ms against 2.8 ms in a still benchmark.&lt;/li&gt;
&lt;li&gt;The Large sandbox city is 250 m, and the physics body cap doubled to 262,144 to hold it.&lt;/li&gt;
&lt;li&gt;Buildings differ by member and by age, with darker, grayer walls, grime and rain streaks. Dust settles on surfaces after a collapse. Walls take bounce light from the street in front of them, and the grade warms as the sun lowers.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;tools&quot;&gt;Tools&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A gameplay recorder writes a movie with the HUD and an events file beside it. A tool cuts the newest recording into a reel of about 75 s, since a plain screen capture ran long and dull. The first reel was short on dogfights, so a kill now counts as much as a big blast.&lt;/li&gt;
&lt;li&gt;Pressing a marker key in play writes the 20 s of frames before it into the session log, to point to the moments of a freeze.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>Release notes</category><category>performance</category><category>rendering</category><category>physics</category><category>tooling</category><author>Alex van Rossum</author></item><item><title>Finding 1.4-second stalls by replaying play</title><link>https://trilithon.games/devlog/stalls-found-by-replay/</link><guid isPermaLink="true">https://trilithon.games/devlog/stalls-found-by-replay/</guid><description>Large cities froze for up to 1.4 s after big blasts. Replaying a logged play session headless reproduced an 836 ms step and led to fixes, one at a time.</description><pubDate>Wed, 07 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;what-we-found-in-play&quot;&gt;What we found in play&lt;/h2&gt;
&lt;p&gt;On October 6, play in the large cities hit many slowdowns, once down to a complete crawl. The game’s telemetry put them in the simulation, 0.7 to 1.4 s after charges that broke 140 to 500 pieces of structure. The stalls reported in play ran from 0.2 to 1.4 s.&lt;/p&gt;
&lt;h2 id=&quot;the-marker-key&quot;&gt;The marker key&lt;/h2&gt;
&lt;p&gt;That morning we wanted a way to point at exact moments, to get better information about performance problems. The game now keeps the last 4,096 frames of live play in memory, about 34 s at 120 frames a second, with the time each part of the frame and each phase of the simulation took. Pressing X on the keyboard or Y on a gamepad shows a “Marked” count and saves that moment with the camera position, the 20 s before it and the 5 s after. Any frame over 40 ms is saved with its breakdown even when nothing was marked. Recording a frame took the main thread under 5 µs at the median, and the game ended in the same state with recording on and off.&lt;/p&gt;
&lt;h2 id=&quot;replaying-the-session&quot;&gt;Replaying the session&lt;/h2&gt;
&lt;p&gt;The game logs each play session. A replay tool sets off the logged blasts and cannon rounds again at their original game times, without a window, in the city that session last loaded. A replay of our mixed city up to one of the stalls reached 836 ms in a single physics step. A physics step is one slice of simulated time, 1/60 s, that the engine computes at once. That made the stall repeatable, so each cause could be measured, fixed and measured again. Each fix below was checked by replaying a logged session and comparing a state digest, a fingerprint of the whole world at the end of a run. In each case the digest matched, so the fix changed timing and nothing else.&lt;/p&gt;
&lt;h2 id=&quot;causes-one-at-a-time&quot;&gt;Causes, one at a time&lt;/h2&gt;
&lt;div class=&quot;tl-table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table, scrolls sideways&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cause&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;The renderer’s removal hook walked every drawn part in the scene (furniture, trim, cars, ships) for each piece that broke&lt;/td&gt;
&lt;td&gt;124 and 134 ms applying finished fractures, two missiles in one city&lt;/td&gt;
&lt;td&gt;7.9 and 8.1 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A hash that removes duplicate terrain vertices read only the low bits of a product, which are zero for a grid of whole and quarter meters, so the vertices piled into a few slots&lt;/td&gt;
&lt;td&gt;192 ms for one missile’s craters; the replay’s worst simulation frame was 202 ms&lt;/td&gt;
&lt;td&gt;31.5 ms, after mixing the bits with Murmur3’s finalizer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Presenting one 40 m blast: a search over its whole 100 m reach, and a lookup in every structure for each body&lt;/td&gt;
&lt;td&gt;52 and 83 ms in the replay; 86 ms of a 164 ms frame in our live play&lt;/td&gt;
&lt;td&gt;5.1 and 6.1 ms in the replay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compacting the drawn city’s meshes in one frame&lt;/td&gt;
&lt;td&gt;221 ms in the replay; 233 and 311 ms in our play&lt;/td&gt;
&lt;td&gt;87 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Each crater checking every support in the city&lt;/td&gt;
&lt;td&gt;32 ms for a 64 m crater, 30 of it in the check&lt;/td&gt;
&lt;td&gt;12.6 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;On a replay of our October 7 session, a second round of work on the compaction took it from 125 and 134 ms to 60 and 65 ms. The bookkeeping after rubble comes down also got cheaper. In a replay of our large-city session at the low tier, steps over 33 ms fell from 40 to 79 a run to 4 to 48.&lt;/p&gt;
&lt;h2 id=&quot;suspects-that-saved-nothing&quot;&gt;Suspects that saved nothing&lt;/h2&gt;
&lt;p&gt;In the heaviest window of a replay of our first city, run in Low Power Mode (the throttled macOS setting used as the baseline), a rocket barrage left up to 22,000 bodies awake, 99% of them fracture debris. About half were nearly still, held awake by one group of 7,000 to 9,800 touching bodies that the physics engine can only put to sleep together. Steps there reached 84 to 114 ms. Five changes were tried, and each saved nothing: fewer job threads, fewer solver iterations, a looser contact cache, a looser sleep threshold and a blast floor. Freezing rubble in place after 1 s instead of 2 did help, cutting the slowest step to 62 to 70 ms.&lt;/p&gt;
&lt;h2 id=&quot;what-we-decided&quot;&gt;What we decided&lt;/h2&gt;
&lt;p&gt;There were two ways to cut the load, freezing rubble sooner or making fewer fragments. We kept the fracturing as it is and chose the freeze, at 1.4 s rather than the 1 s the test used. The change at the high tier, from 2 s to 1.4 s, waits on a second replay to confirm it.&lt;/p&gt;
&lt;h2 id=&quot;the-next-morning&quot;&gt;The next morning&lt;/h2&gt;
&lt;p&gt;On the morning of October 7, ten minutes of play in the large downtown city became the new replay baseline. The one-second freezes were almost gone. A huge missile dropped in the middle of the city, and several other large drops, caused no freeze at all. We marked one moment with floating debris.&lt;/p&gt;
</content:encoded><category>Postmortem</category><category>performance</category><category>physics</category><author>Alex van Rossum</author></item><item><title>Release 0.14: ships and the city dogfight</title><link>https://trilithon.games/devlog/release-0-14/</link><guid isPermaLink="true">https://trilithon.games/devlog/release-0-14/</guid><description>The first playable dogfight, with a gunship, a computer-flown Raven, shields, locks and damage by part, plus a tight city to fly through and a new look.</description><pubDate>Sun, 04 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Release 0.14 is the first playable dogfight. The player flies a gunship through a city against computer-flown ships called Ravens, under a ceiling of about 2.5 stories that came out of flying low in play. The shields, missile locks and juke below came out of a play session on October 3 and were built that night.&lt;/p&gt;
&lt;h2 id=&quot;game&quot;&gt;Game&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The gunship cruises at 30 m/s and boosts at 70 m/s for 2.5 s, then refills over 6 s. The ceiling is 10 m above the ground under it. The chase camera sits 12 m behind and 3 m above where the player aims.&lt;/li&gt;
&lt;li&gt;A Raven decides ten times a second from what it senses. It sees only what it could see, along rays that pass through glass, so it can shoot through a window. Out of sight it heads for where the target would be by now, then searches, then patrols. In tests a patrolling Raven found a ship hiding behind blocks on 6 of 6 seeds, in 5 to 14.5 s.&lt;/li&gt;
&lt;li&gt;One homing missile had taken a ship out about 90% of the time. Every ship now has a shield that holds 0.6 of its health, starts recharging 4 s after the last blow and refills from empty in 3 s. A missile that had taken up to 1.07 of a ship now leaves a whole ship at 0.53 or more.&lt;/li&gt;
&lt;li&gt;Missiles home only after a 1.5 s lock, announced by a tone. The player can juke, a 40 m/s throw sideways that covers 7.7 m in 0.15 s, to shake one.&lt;/li&gt;
&lt;li&gt;Ships take damage by part. A lost engine pulls the ship to one side and costs a fifth of its top speed, a lost cannon means no gun, and a lost canopy fills the HUD with static. A ship at zero health crashes, trailing smoke and fire, and leaves its parts on the street.&lt;/li&gt;
&lt;li&gt;Ships jittered in the first duels, so decisions and turns are now eased, and how much a ship’s heading jerks fell from 76 rad/s² to 9 rad/s².&lt;/li&gt;
&lt;li&gt;The tight city is Manhattan-like, with a regular grid, 1 and 2 story storefront strips, buildings of 5 to 10 stories behind them, and 3 or 4 towers.&lt;/li&gt;
&lt;li&gt;Buildings gained precast facade panels that break away, dark-glass mid-rises, L-shaped and wedge-shaped footprints, twisting towers, and four ornamented towers inspired by real buildings. A raised street gives the ships 13 m of clearance, above the 10 m ceiling.&lt;/li&gt;
&lt;li&gt;A teal forcefield wall follows the edge of the city, and each city draws its own sun between 20° and 55° up.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;engine&quot;&gt;Engine&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A boost smashes through buildings. A member breaks when the ship’s energy exceeds the energy needed to punch through it. Boosting, the final gunship goes through brick at 42 m/s and a concrete column at 46 m/s, and never through steel or a reinforced core wall.&lt;/li&gt;
&lt;li&gt;Glass comes in three kinds, annealed, tempered and laminated, that crack and break differently. We set 4 or 5 autocannon rounds to break a pane, and rounds fly on through it.&lt;/li&gt;
&lt;li&gt;Paved ground is a layer, so a crater shows asphalt, then the base under it, then soil. Cracks run out from craters and slabs of pavement are thrown.&lt;/li&gt;
&lt;li&gt;The graphics page has Auto, Low, Medium, High and Custom presets, and a first antialiasing option.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;tools&quot;&gt;Tools&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Ships are files the game reads. We set up an asset studio, a separate tool that designs, checks and exports them, and picked the final gunship and Raven from its drafts.&lt;/li&gt;
&lt;li&gt;A small trained decision model picks the Raven’s goals three times a second as a trial. We are trying it as the fast decision layer for the enemies. The first version beat the scripted pilot 43 to 17 in 60 headless duels, at 8.4 ms an answer. Three later versions each ended up favoring one goal, so the work moved to how the training examples are labeled.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>Release notes</category><category>weapons</category><category>ai</category><category>rendering</category><category>collapse</category><author>Alex van Rossum</author></item><item><title>Cracked pavement as a layered material</title><link>https://trilithon.games/devlog/cracked-pavement-as-material/</link><guid isPermaLink="true">https://trilithon.games/devlog/cracked-pavement-as-material/</guid><description>Craters used to turn paved ground into dirt cell by cell. Pavement is now a layer that cuts, throws slabs and curbs, and cracks around the crater.</description><pubDate>Sat, 03 Oct 2026 00:00:00 GMT</pubDate><category>Dev journal</category><category>rendering</category><category>fracture</category><author>Alex van Rossum</author></item><item><title>The first dogfight playtest</title><link>https://trilithon.games/devlog/the-first-dogfight-playtest/</link><guid isPermaLink="true">https://trilithon.games/devlog/the-first-dogfight-playtest/</guid><description>Our first night flying the city dogfight produced a list of faults, each with a proposed fix. Shields, a missile lock-on and a juke were built overnight.</description><pubDate>Sat, 03 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;On the night of October 2 to 3, 2026, we flew the city dogfight and the sandbox with ships for the first time. Each fault went on a list with a proposed fix, and the fixes were built overnight.&lt;/p&gt;
&lt;h2 id=&quot;how-it-played&quot;&gt;How it played&lt;/h2&gt;
&lt;p&gt;The scripted enemy was annoying but not difficult. A trial decision model for the enemies was relentless, in one-on-one fights and more so with three enemies at once. The sandbox was more fun with three ships flying around us. By then the project had gone from demolishing a building or two to enemy ships with guns in about five days.&lt;/p&gt;
&lt;h2 id=&quot;the-list&quot;&gt;The list&lt;/h2&gt;
&lt;div class=&quot;tl-table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table, scrolls sideways&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fault&lt;/th&gt;
&lt;th&gt;Proposed fix&lt;/th&gt;
&lt;th&gt;What was built&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;One homing missile took us out about 90% of the time.&lt;/td&gt;
&lt;td&gt;Add a shield.&lt;/td&gt;
&lt;td&gt;A shield holds 0.6 of a ship’s health. It starts refilling 4 s after the last hit and fills from empty in 3 s.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Missiles homed without any lock, and the enemy’s missiles killed us outright.&lt;/td&gt;
&lt;td&gt;A lock-on before missiles home.&lt;/td&gt;
&lt;td&gt;A shooter has to hold its target in its lock cone for 1.5 s. A missile fired before then flies straight.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;There was no way to dodge a missile in open air.&lt;/td&gt;
&lt;td&gt;A dash in the style of a shooter’s omni-movement, where the first move is a juke in the held direction.&lt;/td&gt;
&lt;td&gt;Boost with a direction held throws the ship in that direction at 40 m/s, fading over 0.4 s, with a 2 s cooldown.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spawns were close together, and some enemies spawned inside buildings.&lt;/td&gt;
&lt;td&gt;Check the radius before spawning.&lt;/td&gt;
&lt;td&gt;Ships spawn at least 90 m from the player and 30 m from each other, with no structure within 4 m of the hull.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After dying in the sandbox, we could keep moving and shooting until respawn.&lt;/td&gt;
&lt;td&gt;Lock the controls on death and restore them on spawn.&lt;/td&gt;
&lt;td&gt;Neither movement nor fire works while the player is down. Looking around does.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;In the dogfight we had two weapons, but the HUD showed the whole weapon strip.&lt;/td&gt;
&lt;td&gt;Missiles on one trigger, cannon on the other, and a count of missiles left.&lt;/td&gt;
&lt;td&gt;The dogfight HUD shows the missiles left as pips, with the next reload. The sandbox keeps every weapon and gains a homing missile.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;We also wanted health bars over the enemy ships, arcs on screen showing where hits come from, a minimap, a choice of three ships for the player with different stats, and more variety in the enemies. The bars and arcs were built that night. The minimap and the ship choice were queued for later.&lt;/p&gt;
&lt;h2 id=&quot;numbers-from-the-build&quot;&gt;Numbers from the build&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Without a shield, a missile could take up to 1.07 of a whole ship’s health, so hits on two of a ship’s six faces destroyed it. With the shield, a whole ship survives any face’s hit with 0.53 or more left.&lt;/li&gt;
&lt;li&gt;The juke moves a ship 7.7 m sideways in 0.15 s and 18 m in 0.4 s. A missile that passes its target now loses its lock, rather than circling back.&lt;/li&gt;
&lt;li&gt;Shields and locks made the scripted test duels longer. At 90 s, 11 of 16 duels ended in a draw, so the duel test now allows 150 s.&lt;/li&gt;
&lt;li&gt;The scripted pilot now takes shields into account. With its own shield under 30%, it stays off until the shield is back, for at most 10 s, unless its target is nearly down.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A shield also gives the enemy AI more to decide and train on: retreat to recharge, or press an attack on an opponent that is almost beaten.&lt;/p&gt;
&lt;h2 id=&quot;one-problem-left-open&quot;&gt;One problem left open&lt;/h2&gt;
&lt;p&gt;A dogfight could drift out of the city into open ground. We ruled out a dome and considered a wall that could not be destroyed but could lose its facade. On October 4, we settled on a forcefield wall along the city’s edge.&lt;/p&gt;
&lt;h2 id=&quot;scope-limits&quot;&gt;Scope limits&lt;/h2&gt;
&lt;p&gt;The dogfight is a progression through seeded cities with harder and more numerous enemies. The player is healed after each city, and being shot down ends the run. Later that night, we ruled out currency, a metagame and upgrades that depend on each other. The mode is a fixed ladder of short fights that moves from one easy enemy to groups of easy and medium ones.&lt;/p&gt;
</content:encoded><category>Dev journal</category><category>balance</category><category>weapons</category><author>Alex van Rossum</author></item><item><title>Releases 0.10 and 0.11: an engine and a game shell</title><link>https://trilithon.games/devlog/releases-0-10-and-0-11/</link><guid isPermaLink="true">https://trilithon.games/devlog/releases-0-10-and-0-11/</guid><description>The simulation moved out of the viewer into engine modules, then a game bundle got a boot sequence, a title screen, menus, settings and gamepad support.</description><pubDate>Fri, 02 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The work after 0.9 had three steps: split the engine out of the viewer, build a game shell, then build a sandbox on a bigger city. Release 0.10 was the split. Release 0.11 was the shell, a game application that boots, shows a title and menus, and plays.&lt;/p&gt;
&lt;h2 id=&quot;engine&quot;&gt;Engine&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The simulation, fracture and step pipeline moved out of the viewer into engine modules in numbered steps, S1 to S10. After each step a check compared a state digest, a fingerprint of every body’s position, for frame benches, captures and two of our recorded sessions against a saved baseline. Every step left the digests identical except one.&lt;/li&gt;
&lt;li&gt;That one change is deliberate. Fracture now reads where a body is in the physics world, not where it was last drawn. Of 40 runs, 12 were unchanged. Scene 8’s demo now ends with 1,690 bodies against 2,068.&lt;/li&gt;
&lt;li&gt;A scene can run headless, with no window, renderer or sound, and ends in the same state as the app. All 13 frame-bench scenes match. When it covered ten scenes, a headless pass took about a minute where the rendered runs took about ten.&lt;/li&gt;
&lt;li&gt;The engine was named and given a splash screen of about three seconds.&lt;/li&gt;
&lt;li&gt;Replays, benchmarks and recordings no longer take the keyboard. They had activated their windows on launch, so a stray T pressed in another app switched quality tiers mid-run, and one run in five ended in a different state.&lt;/li&gt;
&lt;li&gt;Floors that a gutted story let go no longer land back on the columns it kept. In a replay of one of our sessions, members left within half a meter of where they were built, more than 10 m up, fell from 385 to 21.&lt;/li&gt;
&lt;li&gt;We wanted the finish on concrete and plaster to spall before the core breaks, as stucco does when a car hits it. The first version read as a decal laid on the wall, so the broken edge was reshaped to read as cut into the member. A blast spalls the finish on up to 40 members and costs at most 1 ms.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;game&quot;&gt;Game&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A game bundle opens on a photosensitivity warning, the engine splash and a title screen. The warning holds about six seconds and can be skipped after one and a half. Any key, click or gamepad button skips a screen.&lt;/li&gt;
&lt;li&gt;Behind the title, three cities come down in turn, 40 s each, while the camera circles them.&lt;/li&gt;
&lt;li&gt;Menus work from the keyboard, the mouse or a gamepad, and the menus and settings were tested with an Xbox controller. Settings are kept between launches and cover quality, shadows, bloom, sound, volume, slow motion, look speed and more. A new setting appears in the menu by itself.&lt;/li&gt;
&lt;li&gt;On the low tier in Low Power Mode, the title holds a p95 frame time under 16.7 ms.&lt;/li&gt;
&lt;li&gt;The sandbox takes a seed and plays the city it grows.&lt;/li&gt;
&lt;li&gt;Set charges go off in sequence, a quarter second apart, when the right button is pressed. We wanted sequential firing or a way to switch modes, and Shift fires them all at once as before.&lt;/li&gt;
&lt;li&gt;Two faults found in play are fixed. Starting a game with a gamepad now captures the mouse, and set charges, which had gone invisible, show again.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;tools&quot;&gt;Tools&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;digests.sh&lt;/code&gt; runs the baseline check, with a weapons range scene that fires every weapon on a script so the digests cover them.&lt;/li&gt;
&lt;li&gt;One of our recorded game sessions, 1,591 shots in one city, replays twice to the same state digest.&lt;/li&gt;
&lt;li&gt;The game bundle links none of the development code. A build check fails if any development symbol appears in its binary.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>Release notes</category><category>tooling</category><category>determinism</category><category>rendering</category><author>Alex van Rossum</author></item><item><title>Releases 0.12 and 0.13: sandbox city and explosions</title><link>https://trilithon.games/devlog/releases-0-12-and-0-13/</link><guid isPermaLink="true">https://trilithon.games/devlog/releases-0-12-and-0-13/</guid><description>The game&apos;s sandbox became a seeded 200 m city with billboards and gas stations, and explosions got a staged fireball, a shockwave and debris thrown outward.</description><pubDate>Fri, 02 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Releases 0.12 and 0.13 both shipped on October 2. Release 0.12 turned the game’s sandbox into a seeded city of about 200 m with roads running out past its edge, and release 0.13 reworked explosions. We cut both down to keep the long tail manageable, so height, highways, the 300 m city, paved roads and respawn moved to a later release.&lt;/p&gt;
&lt;h2 id=&quot;engine&quot;&gt;Engine&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The camera now collides with the world. It is a 0.4 m sphere swept through the physics world each frame, so it slides along walls and cannot pass through terrain at any speed, including a 400 m drop in one frame. A sweep costs 6.5 µs at p50. We built this early because it would be harder to add later.&lt;/li&gt;
&lt;li&gt;Blasts throw what they reach outward, where craters and broken structures had lacked outward momentum. The push now travels as a shock front at 500 m/s, so a 40 m charge ripples out over a quarter of a second. For a 12 m charge at energy 8, pieces one to two reaches out move at 5.9 m/s on average, up from 2.3 m/s.&lt;/li&gt;
&lt;li&gt;Charges set off together no longer fling debris into the sky. A replay of twenty 40 m charges had left about 11,000 pieces climbing past 1.6 km at 370 m/s. A blast now raises a body’s speed only up to its own.&lt;/li&gt;
&lt;li&gt;A big blast sends a warping shockwave outward at 300 m/s, a little under the speed of sound, and the picture bends along its outline. Blasts with a reach under 4 m make none. The effect scales with the size of the explosion. The tone-mapping pass that draws it takes 0.47 ms with no shell, about 0.7 ms with one on screen and 1.07 ms with four.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;game&quot;&gt;Game&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The sandbox city has towers at the core, offices falling to low ones outward, a ring of Romanesque blocks, half-timbered houses on the outer streets, two parks and 14 billboards. Its edge wanders. It holds 78,000 to 87,000 members on three seeds, under the body cap of 131,072. On the low tier in Low Power Mode (a macOS setting that lowers clock speeds), seed 7 measured 5.9 ms p50 and 8.3 ms p95 per frame.&lt;/li&gt;
&lt;li&gt;The Sandbox menu takes a size, 150 m or 200 m, a district mix such as mixed, downtown or old town, and a seed typed as a number or a word.&lt;/li&gt;
&lt;li&gt;Billboards break into panels. The printed ad tears like paper along the seams, and a printed face is twelve panels of about 1.2 m.&lt;/li&gt;
&lt;li&gt;LED boards are 4 rows of 14 square cabinets. A cabinet that falls away goes dark, and the ones still up glitch more as more are gone. The app carries 44 ads, and one board in three is LED.&lt;/li&gt;
&lt;li&gt;Fireballs are staged. A white-hot core is gone in about a second, billows swell and darken to near-black soot over half a second, small pops burst inside the cloud, and a big blast adds a dark column that rises for 5 to 10 s. The first version was too uniform. With the same number of puffs, the worst case (twenty 40 m charges, camera inside the cloud) costs 31.0 ms GPU p95 against 30.5 ms.&lt;/li&gt;
&lt;li&gt;The ground stays hot after a blast, with glowing cracks that cool over 10 to 30 s. Craters throw clods of what they were cut from, and blasts shake the view.&lt;/li&gt;
&lt;li&gt;Gas pumps explode when broken, as large dark fuel fireballs with low pressure. We wanted heat and little concussive force from them. One broken pump sets off its neighbor and brings down 12 of the canopy’s 14 pieces.&lt;/li&gt;
&lt;li&gt;A gas station has a canopy on six columns, two pump islands, a brick shop and a price sign, and the sandbox places two. Four roads run out past the city’s edge.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;tools&quot;&gt;Tools&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;LED boards had been stuck on their first frame, and water ripples had stood still since the dam. Both read a time value the renderer never set. Both move now.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>Release notes</category><category>rendering</category><category>physics</category><category>performance</category><author>Alex van Rossum</author></item><item><title>Fly low: why the ceiling is 2.5 stories</title><link>https://trilithon.games/devlog/fly-low-the-2-5-story-ceiling/</link><guid isPermaLink="true">https://trilithon.games/devlog/fly-low-the-2-5-story-ceiling/</guid><description>We flew the sandbox at no more than about 2.5 stories, found it far more dynamic, and made that the dogfight&apos;s ceiling. The city changed to fit it.</description><pubDate>Thu, 01 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We made the decision on October 1, 2026. This post also follows what the limit meant for the game’s cities and cameras through October 7.&lt;/p&gt;
&lt;h2 id=&quot;what-we-found&quot;&gt;What we found&lt;/h2&gt;
&lt;p&gt;In the sandbox, flight stayed low, at a self-imposed maximum height of about 2.5 stories, and play at that height was more dynamic than higher up. We marked it as the high point for the dogfight, and possibly for the whole game, because blowing up a building from high up does not come close to looking up and seeing it come down.&lt;/p&gt;
&lt;h2 id=&quot;the-ceiling&quot;&gt;The ceiling&lt;/h2&gt;
&lt;p&gt;We made about 2.5 stories the dogfight’s ceiling, and the chase camera was set at street level to match. At roughly 4 m a story, that is a ceiling of 10 m over the ground under the ship. Climbing fades out over the last 2 m, so the ship eases into the limit. The chase camera sits 12 m behind the ship and stays among the buildings under the ceiling.&lt;/p&gt;
&lt;h2 id=&quot;what-the-ceiling-does-to-a-city&quot;&gt;What the ceiling does to a city&lt;/h2&gt;
&lt;p&gt;The city has to work at that height. Storefront strips along the streets are one or two stories. A one-story strip stands 5.1 to 6.2 m, and a two-story strip 8.6 to 10.1 m. The enemy pilots cruise at 6 m, so they see and shoot over a one-story strip and meet a two-story one as a wall. A ship at the ceiling sees over almost every strip to the 5 to 10 story buildings behind.&lt;/p&gt;
&lt;p&gt;As of October 4, too many buildings sat below the ship in flight. The tight city, with short buildings in front of taller ones, was built to fix that. We wanted to keep the open streets, since seeing something at the end of a street and going after it is part of the appeal, and the cities might need to be larger. Smaller ships and a lower ceiling were another option, but they would conflict with the “overstreet” designs.&lt;/p&gt;
&lt;p&gt;We built a second mix of the tight city, with strip buildings of 2 and 3 stories and roofs 9 to 13 m up, to fly against the first on the same seed. On three test seeds, the share of street frontage walled off at a ship’s height went from between 36 and 41% to between 69 and 70%. The old wooden houses already fit the ceiling and stayed as they were. On October 7 we decided to keep both street-front mixes.&lt;/p&gt;
&lt;h2 id=&quot;flying-under-and-around&quot;&gt;Flying under and around&lt;/h2&gt;
&lt;p&gt;We wanted ships to be able to fly under and around the elevated roads, except where there is an off-ramp. The raised street’s underside is 13 m up, above the ceiling, so ships can fly beneath it.&lt;/p&gt;
&lt;p&gt;The ceiling is measured from the ground under the ship. After a ship flew into a ramp, we made the solid mass under the ship, meaning roads and low roofs, the ground that sets the maximum height. The 10 m is now measured from a raised street’s deck or ramp, or from a roof at most 10 m over the terrain. Taller roofs still wall the fight in.&lt;/p&gt;
&lt;h2 id=&quot;cameras&quot;&gt;Cameras&lt;/h2&gt;
&lt;p&gt;On October 4 we also added several camera positions for play. The game now has Chase, Close, Far and Nose, and a field-of-view setting from 55 to 85 degrees, 70 by default. Far sits 17 m back and 7 m up and looks down the street ahead. On October 7, we moved the camera to Far, widened the field of view, and kept both.&lt;/p&gt;
</content:encoded><category>Dev journal</category><author>Alex van Rossum</author></item><item><title>Release 0.9: pressure testing</title><link>https://trilithon.games/devlog/release-0-9/</link><guid isPermaLink="true">https://trilithon.games/devlog/release-0-9/</guid><description>Progressively heavier scenes found where the engine gives out. Release 0.9 fixed what they found and added new weapons, water, and buildings from photos.</description><pubDate>Thu, 01 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Release 0.9 asked how much the engine could take. We ran progressively denser and heavier scenes to find where the hardware gives out, and most of the release is fixes for what we found. Seven of eight performance gates pass in Low Power Mode, a macOS setting that lowers clock speeds. The eighth, fracture on one worker, reads 3.62 ms p95 (the time 95% of steps beat) against a 3 ms limit.&lt;/p&gt;
&lt;h2 id=&quot;engine&quot;&gt;Engine&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A density ladder of five scenes puts 2, 4, 9, 16 and 25 offices on one site and collapses them together. The first rung over budget in Low Power Mode is nine offices at once, at 10.93 ms of physics p95.&lt;/li&gt;
&lt;li&gt;A blast now spends a budget of fracture cells instead of shattering everything in reach. At 20 m and energy 12 it makes 2,758 pieces where it made 16,398, and physics p50 fell from 21.05 ms to 2.75 ms. A 40 m charge had made 43,111 pieces and overflowed the physics engine’s contact buffers.&lt;/li&gt;
&lt;li&gt;Detail by distance leaves anything within 90 m alone and breaks far debris into fewer cells. With 25 offices, physics p95 fell from about 19 ms to about 8 ms.&lt;/li&gt;
&lt;li&gt;When a physics step costs more than 12 ms the game clock slows to fit. The first threshold, 8 ms, kept the game in slow motion too long after a collapse.&lt;/li&gt;
&lt;li&gt;Gas lines go off in collapses and vaporize fallen chunks instead of adding more pieces to track. In scene 8 it halved collapse physics p50, from 1.06 ms to 0.50 ms.&lt;/li&gt;
&lt;li&gt;A tall part that detaches whole now comes down story by story as a driven mass. In the 160-story demolition, steps over 8 ms fell from 2,199 to 49, and the run’s wall time from 495 s to 25 s.&lt;/li&gt;
&lt;li&gt;The first tower collapses looked like melting, so four to six gas lines now go off as a tower starts down, and it leans up to about 8°.&lt;/li&gt;
&lt;li&gt;Rubble held up in the sky by other rubble is now found and woken so it falls. A replay of our 80-story session left 478 pieces more than 3 m up before the fix and 8 after.&lt;/li&gt;
&lt;li&gt;Dust drawn from inside a cloud of twenty 40 m charges cost up to 1.2 s of GPU time a frame. Lighting each puff once brought GPU p95 down from 1,101 ms to 446 ms, and drawing dust at half resolution then cut p95 wall time from 206 ms to 60 ms.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;game&quot;&gt;Game&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Weapons are chosen with keys 1 to 5: an autocannon, a rocket, a charge, an impulse cannon and a gravity cannon. We also set a maximum range on rockets, now 400 m, and a reload time for them.&lt;/li&gt;
&lt;li&gt;Autocannon rounds wear a member before it breaks. A 20 cm panel goes at about the fourth round and a 60 cm column at the tenth.&lt;/li&gt;
&lt;li&gt;A dam and flood scene has 66 concrete blocks holding back a reservoir, and a 45 m ravine has a 64 m girder bridge across it.&lt;/li&gt;
&lt;li&gt;Four buildings built from photographs joined the stock. A half-timbered town house and a Romanesque block come from our own photos. A Doric temple and a domed mosque came from a research pass.&lt;/li&gt;
&lt;li&gt;Cities follow research on real ones, with streets by class, curbs, lane paint and lamp posts. A downtown scene with towers of 30 to 44 stories tests towers falling onto neighbors.&lt;/li&gt;
&lt;li&gt;Offices have stairs, which had been missing, and furnished floors with partitions and ceiling lights that flicker and fail panel by panel as the building takes damage.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;tools&quot;&gt;Tools&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Scenes load on a worker behind a loading panel, which cut one scene’s worst frame from 4,470 ms to 46 ms.&lt;/li&gt;
&lt;li&gt;Every live session writes a log of frame times, charges and loads, and a script turns it into a report.&lt;/li&gt;
&lt;li&gt;The black blocks seen when flying fast and the dark flash where a missile hits most likely came from queued frames drawing meshes that had just been replaced. That is fixed.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>Release notes</category><category>performance</category><category>collapse</category><category>weapons</category><category>physics</category><author>Alex van Rossum</author></item><item><title>Cheats we measured, and the ones that didn&apos;t help</title><link>https://trilithon.games/devlog/cheats-we-measured/</link><guid isPermaLink="true">https://trilithon.games/devlog/cheats-we-measured/</guid><description>When 25 collapsing offices overran the physics budget, we looked for perceptual cheats. Four helped, four did not, and one is still on trial.</description><pubDate>Wed, 30 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;where-the-budget-ran-out&quot;&gt;Where the budget ran out&lt;/h2&gt;
&lt;p&gt;On September 29 the engine got a density ladder: scenes that put 2, 4, 9, 16 and 25 copies of the same office on one site and blow the front columns of every building at once. This first run was at full power with the machine in use, so it places the limit roughly.&lt;/p&gt;
&lt;div class=&quot;tl-table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table, scrolls sideways&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Offices&lt;/th&gt;
&lt;th&gt;Bodies at the end&lt;/th&gt;
&lt;th&gt;Physics p95&lt;/th&gt;
&lt;th&gt;Frame p95&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2,611&lt;/td&gt;
&lt;td&gt;2.13 ms&lt;/td&gt;
&lt;td&gt;5.03 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;16,559&lt;/td&gt;
&lt;td&gt;8.86 ms&lt;/td&gt;
&lt;td&gt;13.01 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;43,238&lt;/td&gt;
&lt;td&gt;25.04 ms&lt;/td&gt;
&lt;td&gt;39.75 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;p95 means 95% of steps or frames were faster than the figure. The gates are 6 ms for physics and 12.5 ms for the frame, so nine offices at once is the first rung over. The graphics chip stays at 5 to 7 ms on every rung, so the frame follows the physics step. A profile of the nine-office collapse put the cost in the number of moving bodies and their contacts. Finding collisions took 55% of the physics library’s work.&lt;/p&gt;
&lt;p&gt;We wanted to know whether physics cheats, rendering cheats or low-resolution tricks could keep the dense rungs smooth. Research covering physics, fracture and debris, rendering, and design and platform produced one ranked list of ideas.&lt;/p&gt;
&lt;h2 id=&quot;four-ideas-that-did-not-help&quot;&gt;Four ideas that did not help&lt;/h2&gt;
&lt;p&gt;The research ranked the first three of these at the top, and we measured each before keeping it. The first two used a spree of 60 charges across the 25-office rung. The third used the rung’s demo.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A separate layer for light fragments.&lt;/strong&gt; Pieces under 25 kg would skip collisions with each other. At the low tier, physics p50 went from 20.5 to 25.2 ms and steps over 8 ms from about 1,200 to 1,356. More of the buildings came down (29,179 members against 27,638) and more bodies stayed awake at the end (841 against 285), because light pieces sank through each other onto standing floors and loaded them more directly. We canceled it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Frozen rubble in the static part of the collision tree.&lt;/strong&gt; About 30,000 frozen pieces were sitting in a part of the tree that the physics library updates every step. Moving them to the part for immovable bodies gave physics p50 of 13.0 ms before and 12.0 to 14.0 ms after. There was no repeatable difference, so we canceled it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;One release budget per step.&lt;/strong&gt; Limit how many pieces can break loose in one step across all buildings and hold the rest back. Physics p95 was 18.6 and 19.2 ms with no budget, 19.1 ms twice with 400 a step, and 18.9 ms with 1,000. The slow steps come from thousands of bodies that stay awake for seconds, and a budget on releases does not change that. We canceled it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Frozen rubble merged into static piles.&lt;/strong&gt; This one was measured before it was built. With 600 moving pieces over 6,000 frozen ones, merging would save about 0.03 ms a step at p50, so we did not build it.&lt;/p&gt;
&lt;h2 id=&quot;what-helped&quot;&gt;What helped&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Detail by distance.&lt;/strong&gt; Detail gets coarser far from the camera. Past 90 m, a charge breaks members into a share of their cells that falls with distance, to 0.35 at 257 m and beyond, and a far collapse comes down in bigger chunks. On the 25-office rung, physics p95 went from 18.6 and 19.2 ms to 7.7 and 8.2 ms, and steps over 8 ms from 514 and 520 to 72 and 96. The same buildings came down.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Gas lines.&lt;/strong&gt; Secondary explosions, like gas lines going off, become part of a building collapse. They vaporize falling chunks without adding force, so there are fewer parts to track. In scene 8, 11 blasts vaporized 120 members and collapse physics went from 1.06 to 0.50 ms p50, with 1,136 bodies left against 1,596. The blasts also changed the collapse, with 1,308 members falling against 920.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A rocket hopper.&lt;/strong&gt; We wanted a reload time on large weapons. The hopper holds six rockets and loads one a second. In a spree of 8.4 m charges across the 25-office rung, physics p95 was 12.56 ms with the hopper and 43.37 ms firing one every 0.2 s, and 23,229 pieces were made against 42,133.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Slow motion.&lt;/strong&gt; When a physics step costs more than 12 ms, the game clock slows so the steps fit in real time and the frame rate holds. A 24 ms step runs at half speed and 48 ms at a quarter, the floor. The rules follow EVE Online’s time dilation, which slows the game clock when a server overloads. We wanted explosions to stay solid in slow motion so it does not look cheap. The first threshold was 8 ms, and the game stayed in slow motion long after a collapse, since settling rubble keeps steps over 8 ms for a long time (998 of 1,500 steps in one tower test). The threshold became 12 ms.&lt;/p&gt;
&lt;h2 id=&quot;not-yet-helping&quot;&gt;Not yet helping&lt;/h2&gt;
&lt;p&gt;Physics smoothing was among the first cheats proposed. A setting turns it on or off and caps how many ticks it may skip. Under load, physics steps every 2 to 4 ticks and the game draws bodies between their last two poses. A step covering two ticks cost about what two steps do (8.30 ms a tick at a gap of 1, 8.06 at 2, 8.95 at 3 in the 25-office demo), and the longer steps pressed more pieces into the ground. It stays in as an option while it is tried in play.&lt;/p&gt;
</content:encoded><category>Engine note</category><category>performance</category><category>physics</category><category>collapse</category><author>Alex van Rossum</author></item><item><title>Rubble with weight, and rubble that stays</title><link>https://trilithon.games/devlog/rubble-with-weight/</link><guid isPermaLink="true">https://trilithon.games/devlog/rubble-with-weight/</guid><description>We set two rules on September 29. Fallen rubble loads the floors it lands on, and resting rubble stays where it lies. This is how the engine pays for both.</description><pubDate>Tue, 29 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;the-lesson-from-the-first-attempt&quot;&gt;The lesson from the first attempt&lt;/h2&gt;
&lt;p&gt;In the earlier version of this game, rubble did not interact properly with the structures around it. It did not load them by weight, hit them with impact force, or crush what was under it. A lot of time had gone into chasing that. When Stage 1 of the engine closed on September 29, we put rubble weight into the next milestone, ahead of the work on running well on a throttled Mac. The plan was to write the tests that define weight first.&lt;/p&gt;
&lt;p&gt;The engine at that point had a gap. A building’s load model took no outside loads. Standing pieces were fixed bodies that never felt debris landing on them. A cap added to stop fragments falling through the ground also made light rubble stronger than it is.&lt;/p&gt;
&lt;h2 id=&quot;what-weight-means-as-tests&quot;&gt;What weight means, as tests&lt;/h2&gt;
&lt;p&gt;The tests use a two-bay office whose floor slabs are designed to run at 35% of their capacity when the building is intact. Utilization is the share of capacity a piece is using.&lt;/p&gt;
&lt;div class=&quot;tl-table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table, scrolls sideways&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A 2 t pile on a slab, or a 65 kg drop&lt;/td&gt;
&lt;td&gt;The slab stands&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A 150 t pile on a slab&lt;/td&gt;
&lt;td&gt;The slab breaks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11 t of resting rubble&lt;/td&gt;
&lt;td&gt;Raises the slab’s utilization by about half&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A 30 t drop from 10 m&lt;/td&gt;
&lt;td&gt;Breaks the floor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A roof bay falling onto the floor below&lt;/td&gt;
&lt;td&gt;Takes that floor with it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;They were written before the weight code and all pass.&lt;/p&gt;
&lt;h2 id=&quot;how-weight-reaches-the-structure&quot;&gt;How weight reaches the structure&lt;/h2&gt;
&lt;p&gt;Resting bodies pass their weight down through the pile into the members under it, as outside loads in the structure’s load graph. An impact becomes a force on the member it hits, applied for one load wave (1/60 s). Crushing covers small pieces. A body ten or more times heavier than a fragment it lands on crushes that fragment to chips and dust once the force over 1/60 s exceeds the fragment’s compressive strength. In the tests, 20 t landing from 2 m crushes a 65 kg concrete fragment, and 1 t set down on it does not.&lt;/p&gt;
&lt;p&gt;With weight on, scene 8’s office collapse brought down 834 members by 30 s, against 678 without it.&lt;/p&gt;
&lt;h2 id=&quot;rubble-that-stays&quot;&gt;Rubble that stays&lt;/h2&gt;
&lt;p&gt;Debris disappeared from the viewer when the body count got high, so rubble looked temporary instead of part of the scene. The engine had been retiring small pieces after 3 s at rest, and the oldest resting pieces past a cap of 2,000 bodies at the high tier, 1,200 at medium and 600 at low. Small pieces are no longer retired at rest, and the caps became safety limits of 30,000, 20,000 and 12,000.&lt;/p&gt;
&lt;p&gt;Keeping that many pieces is affordable only if resting pieces are nearly free, and they are not free by default. The physics library puts a pile to sleep only as a whole, and one rocking piece kept 450 bodies awake 25 s after the last detach in scene 8. The settler now freezes a body that has stayed within 1 cm and 0.005 rad for 2 s at the high tier, 1.5 s at medium and 1 s at low. A frozen body becomes immovable where it lies. A blast or a charge in its reach thaws it.&lt;/p&gt;
&lt;p&gt;A frozen pile still presses on what it rests on, so it keeps its weight on the floors. The high tier’s collapse ran 36 s from first charge to last detach against 16.5 s, and brought down 1,234 members against 760.&lt;/p&gt;
&lt;h2 id=&quot;what-keeping-it-costs&quot;&gt;What keeping it costs&lt;/h2&gt;
&lt;div class=&quot;tl-table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table, scrolls sideways&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Measure&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Low tier, scene 10, every piece kept (323) against 10 live and 313 retired&lt;/td&gt;
&lt;td&gt;0.3 ms more graphics time at rest (3.99 against 3.70 ms p50), no physics cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High tier, collapse window graphics, kept against retired&lt;/td&gt;
&lt;td&gt;5.32 against 5.39 ms p50, 5.94 against 6.78 ms p95&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6,000 frozen pieces under 600 moving ones&lt;/td&gt;
&lt;td&gt;About 0.13 ms more physics per step (0.344 against 0.215 ms p50)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;All three comparisons were run at full power. p50 is the typical step and p95 is the slow end, where 95% of steps are faster.&lt;/p&gt;
&lt;p&gt;Pieces can still turn to dust. Fragments too small to matter are made as dust, and crushed fragments become chips. Every fragment the engine creates is tracked in a ledger that has to balance: created pieces equal live, chipped, consumed, retired and lost. Retirement now happens only past the safety limits.&lt;/p&gt;
</content:encoded><category>Engine note</category><category>physics</category><category>collapse</category><category>performance</category><author>Alex van Rossum</author></item><item><title>Stage 1 in one day: releases 0.1 to 0.8</title><link>https://trilithon.games/devlog/stage-1-in-one-day/</link><guid isPermaLink="true">https://trilithon.games/devlog/stage-1-in-one-day/</guid><description>Eight releases on September 29 took the project from an empty repository to a building that comes down under its own weight, with every gate measured.</description><pubDate>Tue, 29 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Releases 0.1 to 0.8 closed Stage 1 of the project on September 29: choose a physics engine, break things apart, draw them, give them ground to stand on, and bring a building down. Each release had numbered performance gates, and the gate runs were measured in macOS Low Power Mode, which lowers clock speeds on the M5 Mac the game is built on. Times below are p50 (the median) and p95 (the time 95% of frames or steps beat).&lt;/p&gt;
&lt;h2 id=&quot;engine&quot;&gt;Engine&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;0.1 compiles Jolt Physics, an open-source physics engine, through the Swift package manager and wraps it in a small C interface. A convex hull dropped on the ground lands and goes to sleep within 3 s.&lt;/li&gt;
&lt;li&gt;0.2 adds a benchmark harness and scenarios from 500 to 10,000 loose pieces. The package build runs at 93 to 100% of the throughput of Jolt’s own reference build, and every 0.2 gate passes in Low Power Mode.&lt;/li&gt;
&lt;li&gt;0.3 adds fracture. A member breaks into cells around seed points that cluster near the hit, and the cells keep a graph of which ones were bonded. A 64-cell fracture costs 2.44 ms p95 on one worker at full power.&lt;/li&gt;
&lt;li&gt;0.4 adds the renderer. In a probe, one pass with a G-buffer that lives in tile memory took 2.2 ms against 8.7 ms for two passes at 2880 × 1800. It also has a sky, three shadow cascades, materials generated in code with no texture files, ambient occlusion, glass, bloom and MetalFX upscaling.&lt;/li&gt;
&lt;li&gt;0.5 puts tiled terrain into the physics world and the renderer, with ground materials that blend at their edges, and a dig key. A 16 m crater rebuilds its terrain mesh in 0.28 ms p50.&lt;/li&gt;
&lt;li&gt;0.6 puts a fixed 60 Hz simulation step behind the renderer. Physics time in the app measured 12 to 24% above the benchmark for the same scenes.&lt;/li&gt;
&lt;li&gt;0.7 adds structures and a generator for reinforced-concrete offices of 4 to 10 floors. A load graph passes every piece’s weight down to the ground, so a building that loses a column either holds or comes down in waves.&lt;/li&gt;
&lt;li&gt;0.8 gives rubble weight and closes the first collapse. A 2 t pile leaves a floor slab standing and a 150 t pile breaks it. Scene 8’s collapse brings down 834 members by 30 s where it had brought down 678.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;game&quot;&gt;Game&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A click sets off a charge where the pointer points, with a reach and an energy set from the keyboard.&lt;/li&gt;
&lt;li&gt;Mouse-look arrived in 0.8. We chose it over steering toward a free pointer.&lt;/li&gt;
&lt;li&gt;A rocket launcher fires at 90 m/s with a smoke trail and sets off a charge where it first hits.&lt;/li&gt;
&lt;li&gt;Rubble at rest stays where it lies. Small pieces are no longer retired past the body cap, so debris no longer pops out of the scene. Keeping them brought down 1,234 members against 760, because the rubble keeps loading the floors.&lt;/li&gt;
&lt;li&gt;Pieces freeze in place after 1 to 2 s at rest. Before, one rocking piece kept a pile awake, and 450 bodies were still awake 25 s after the last detach.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;tools&quot;&gt;Tools&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The live viewer (0.3) runs every benchmark scene with a free camera. It has a heads-up display of frame, physics and GPU times.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--record&lt;/code&gt; writes a scene’s demo as a video without a display attached, 1600 × 1000 at 30 frames a second.&lt;/li&gt;
&lt;li&gt;The viewer follows Low Power Mode and drops to a lighter quality tier while it is on.&lt;/li&gt;
&lt;li&gt;The physics step while charges fire dropped from about 20.5 ms to 0.90 to 1.24 ms p50 in Low Power Mode. A profile had put the cost in collision against terrain, and which change removed it was not isolated.&lt;/li&gt;
&lt;li&gt;Two recordings of the same collapse had dropped 696 and 1,123 members. After queries and event lists were put in a fixed order, scene 8 gives 1,266 members fallen and the same state digest in all three run modes, run after run. A digest is a fingerprint of every body’s position.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>Release notes</category><category>physics</category><category>fracture</category><category>rendering</category><category>performance</category><author>Alex van Rossum</author></item><item><title>Why we started over</title><link>https://trilithon.games/devlog/why-we-started-over/</link><guid isPermaLink="true">https://trilithon.games/devlog/why-we-started-over/</guid><description>The first attempt at a building-destruction game failed at about six buildings. This is what we scoped for the restart and what carried over.</description><pubDate>Mon, 28 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;the-first-attempt&quot;&gt;The first attempt&lt;/h2&gt;
&lt;p&gt;Before the current project there was an earlier one with the same idea: a toy for wrecking buildings, in the spirit of an older destruction game we like. It built its buildings from voxels (small cubes), drew them with Three.js in a browser engine wrapped in a desktop shell, and ran its physics single-threaded, in WebAssembly, on the main thread.&lt;/p&gt;
&lt;p&gt;It was an experiment that kept growing until it was unusable, and with about six buildings at once it started to fail. Part of the trouble was the voxels. They made the math easy, but the voxel compute and the physics could not keep up. The project had also tried to build too much at once, including a grand story, when it should first have been a place to take a building down.&lt;/p&gt;
&lt;h2 id=&quot;what-we-scoped-for-the-restart&quot;&gt;What we scoped for the restart&lt;/h2&gt;
&lt;p&gt;On September 28, we decided to try again with a narrower goal and set these terms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The first stage is an engine that holds up. Custom or existing both count, as long as it can carry what we want to do with it.&lt;/li&gt;
&lt;li&gt;No Unity, which was too much for this project, and no Java, which is not efficient enough.&lt;/li&gt;
&lt;li&gt;The game is Mac only, and it is for us to play. If anyone else gets to play it, that is a bonus.&lt;/li&gt;
&lt;li&gt;Prefer structural destruction to voxels: buildings that break along their structure.&lt;/li&gt;
&lt;li&gt;The first playable is one procedurally generated building at a time that can be respawned. A tiny house, a set of silos, a very tall skyscraper or a stadium would all do, each with real structure and debris that looks plausible when it comes down.&lt;/li&gt;
&lt;li&gt;Later, the player flies, with rifles, bombs and remote charges, and the world grows toward a larger one with more buildings.&lt;/li&gt;
&lt;li&gt;Not wanted: people in the world, automatic defenses, a grand story.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The planning that followed, recorded in the brief, added two decisions. Buildings should fracture and render the way real buildings would, and the architecture should leave room for terrain, trees and water later.&lt;/p&gt;
&lt;h2 id=&quot;what-carried-over&quot;&gt;What carried over&lt;/h2&gt;
&lt;p&gt;The restart kept the earlier project’s measurements. The architecture notes list them as rules.&lt;/p&gt;
&lt;div class=&quot;tl-table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table, scrolls sideways&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Earlier measurement&lt;/th&gt;
&lt;th&gt;Rule in the restart&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A voxel collider on a moving body cost 98 times a plain box&lt;/td&gt;
&lt;td&gt;No mesh or voxel colliders on moving bodies. Debris is a convex hull (a solid with no dents) or a group of them&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;800 resting pieces cost 0.06 ms a step, against 11.87 ms awake&lt;/td&gt;
&lt;td&gt;Resting debris is nearly free and awake debris is not, so settling is a feature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;About 3,300 draw calls, roughly 70% of them in the shadow pass&lt;/td&gt;
&lt;td&gt;All opaque geometry is drawn in one call per pass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contacts with the static world cost about 30 µs each in the full scene, against 1.4 µs alone, and the cause was never found&lt;/td&gt;
&lt;td&gt;Measure those contacts instead of assuming them&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;A step here is one tick of the physics simulation, 1/60 s. A draw call is one batch of geometry sent to the graphics chip.&lt;/p&gt;
&lt;p&gt;We added a lesson about how the earlier project was measured. It was tuned on clean scenes without rubble, and slowed to a crawl once debris built up. The restart’s rendering gate draws six intact buildings with 20,000 fragments lying around them, so a result includes rubble.&lt;/p&gt;
&lt;h2 id=&quot;what-the-restart-measured-differently&quot;&gt;What the restart measured differently&lt;/h2&gt;
&lt;p&gt;The same kind of test, rubble in a building compared with rubble on flat ground, cost about 20 times as much in the earlier project. On the physics library the restart uses, it costs 1.3 times as much. The earlier project never found why its number was so high, so the comparison shows the new stack does not have the problem. It does not explain the old one.&lt;/p&gt;
&lt;p&gt;The decision record also lists what was considered and set aside, with the reasons as of September 29:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Unity, which we ruled out.&lt;/li&gt;
&lt;li&gt;Godot, which is built around its editor and had open reports of freezes in its Mac renderer.&lt;/li&gt;
&lt;li&gt;Bevy, kept as the fallback if the flat-ground numbers failed.&lt;/li&gt;
&lt;li&gt;NVIDIA’s Blast and PhysX 5, which do not run on macOS.&lt;/li&gt;
&lt;li&gt;Apple’s SceneKit and RealityKit, which have no fracture support.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The stack that stayed is Swift and Metal with the Jolt physics library, compiled from source.&lt;/p&gt;
</content:encoded><category>Postmortem</category><category>physics</category><category>performance</category><category>tooling</category><author>Alex van Rossum</author></item></channel></rss>