<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Trilithon Engine devlog</title><description>The studio’s own Swift and Metal engine for Mac, on Jolt Physics, built around fracture, collapse and a deterministic simulation.</description><link>https://trilithon.games/</link><language>en-us</language><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>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>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>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>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 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>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>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>Making a collapse repeat exactly</title><link>https://trilithon.games/devlog/making-a-collapse-repeat-exactly/</link><guid isPermaLink="true">https://trilithon.games/devlog/making-a-collapse-repeat-exactly/</guid><description>Two recordings of one demolition scene fell differently. Sorting the engine&apos;s queries fixed it, and a state-digest check now guards every engine change.</description><pubDate>Thu, 01 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This covers September 29 to October 1, 2026. The engine’s results are deterministic on one machine: the same build, seed and inputs reach the same state. This is how we got there and how we keep it.&lt;/p&gt;
&lt;h2 id=&quot;two-recordings-that-disagreed&quot;&gt;Two recordings that disagreed&lt;/h2&gt;
&lt;p&gt;On September 29, two recordings of the same test scene gave different results. The scene is an office with charges set off at its front columns. In one recording 696 members, the structural pieces of the building, fell. In the other, 1,123 did.&lt;/p&gt;
&lt;p&gt;We followed the rules the physics engine’s own documentation lists for repeatable results:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Queries that find bodies in a box recheck each hit against the body’s real bounds and act in body ID order.&lt;/li&gt;
&lt;li&gt;The list of active bodies, impact events and contacts all come back sorted.&lt;/li&gt;
&lt;li&gt;Recordings and frame benches wait at each step for the fractures requested before it, and apply them in the order requested. A frame bench is a timed headless run of a scene. The live game still applies whatever is finished.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After that, the scene gave 1,266 members fallen and the same digest in a capture, a recording and a frame bench, run after run. A digest is a short fingerprint of where every body is, to the millimeter. If one body ends up a millimeter away, the digest changes.&lt;/p&gt;
&lt;h2 id=&quot;a-check-for-every-change&quot;&gt;A check for every change&lt;/h2&gt;
&lt;p&gt;On October 1, we decided to separate the engine from the game so it could be reused. The simulation, fracture and step code moved into engine modules in a series of steps. Each step had to leave the world exactly as it was.&lt;/p&gt;
&lt;p&gt;We wrote &lt;code&gt;tools/digests.sh&lt;/code&gt; for that. It runs 13 test scenes, replays two of our recorded play sessions, runs one scene stepping every other tick and another with sound on, and runs every scene as a screen capture. It prints a digest for each run and compares them with a stored baseline, using a release build with every environment switch cleared. A full run takes about 10 minutes.&lt;/p&gt;
&lt;p&gt;A weapons range scene was added so the digests cover every weapon: the autocannon, rockets, the impulse cannon, charges and the gravity well, fired on a fixed script.&lt;/p&gt;
&lt;p&gt;The check also catches changes that are meant. When fracture was changed to read where a body is, rather than where it was last drawn, 28 of the 40 runs changed and 12 did not. That change went in as a deliberate change with a new baseline. Scene 8’s demo ended with 1,690 bodies, against 2,068 before.&lt;/p&gt;
&lt;p&gt;Headless mode runs a scene with no window, renderer or sound. Ten test scenes run that way ended with the same digests as the rendered app, in about a minute against about ten. That showed that nothing drawn on screen feeds back into what happens in the simulation.&lt;/p&gt;
&lt;h2 id=&quot;one-replay-in-seven&quot;&gt;One replay in seven&lt;/h2&gt;
&lt;p&gt;Replays of our recorded play sessions were part of the check, and two of them did not always repeat. A replay of a downtown session ended differently in about one run in seven, and a replay of a district-city session in one run in five.&lt;/p&gt;
&lt;p&gt;The cause was the test runs themselves. Replays, frame benches, recordings and captures each brought their window to the front on launch, so whatever was typed in another app reached the simulation. One press of T changed a replay from the high quality tier to medium partway through. The run that ended differently once in seven had been running beside the live game.&lt;/p&gt;
&lt;p&gt;Offline runs now show their window without taking focus, drop keys and clicks, and keep a quality tier given on the command line whatever the power state does. The test sends a power-state notification and a T press 600 steps into a frame bench. It still ends in the same state as a run without them, and three runs of the downtown replay end in the same state.&lt;/p&gt;
&lt;h2 id=&quot;what-it-allows&quot;&gt;What it allows&lt;/h2&gt;
&lt;p&gt;Determinism is what makes exact replays, refactor checks and balance comparisons possible.&lt;/p&gt;
</content:encoded><category>Engine note</category><category>determinism</category><category>tooling</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>Choosing the engine&apos;s stack, in Low Power Mode</title><link>https://trilithon.games/devlog/choosing-the-engine-stack/</link><guid isPermaLink="true">https://trilithon.games/devlog/choosing-the-engine-stack/</guid><description>Numbered gates decided the engine stack, run with the Mac throttled. Four failed, three were fixed, and our first diagnosis of the slow collapse was wrong.</description><pubDate>Tue, 29 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;the-question&quot;&gt;The question&lt;/h2&gt;
&lt;p&gt;The first stage of the project was to find out whether a Swift, Metal and Jolt stack would hold up under building destruction. We set the constraints: Mac only, no Unity, and structural destruction in place of voxels. The session on Stage 1 compiled the Jolt physics library from source through Swift’s package manager, wrote a benchmark harness, and set numbered gates before measuring anything. A gate is a limit a scenario has to stay under. If a gate in the first group failed, work would stop until we decided what to do with the numbers.&lt;/p&gt;
&lt;p&gt;The budget came from an M4 Mac: 8 ms for physics and 11 ms for graphics inside a 16.7 ms frame. Gates run on the M5 the game is built on use 0.75 of that, an assumed margin until an M4 is measured.&lt;/p&gt;
&lt;p&gt;Two checks came first. The package-manager build of Jolt ran at 92.5 to 99.5% of the throughput of Jolt’s own optimized build, depending on thread count, so later numbers could be trusted. And a single-pass graphics layout that keeps its intermediate buffers in on-chip memory took 2.2 ms against 8.7 ms for a two-pass layout at 2880 × 1800.&lt;/p&gt;
&lt;h2 id=&quot;throttled-by-accident-kept-on-purpose&quot;&gt;Throttled by accident, kept on purpose&lt;/h2&gt;
&lt;p&gt;The benchmark run recorded for the first verdict turned out to have run in macOS Low Power Mode, on battery. That mode slows the processor and graphics chip to save power. We decided it was a better baseline because it leaves room, released on those numbers and kept Low Power Mode as the reference for every gate. A gate that passes throttled has headroom at full power and on a slower machine. Every result records the mode, and full-power runs count as information only.&lt;/p&gt;
&lt;h2 id=&quot;the-first-verdict&quot;&gt;The first verdict&lt;/h2&gt;
&lt;p&gt;The recorded run, in Low Power Mode, passed every physics gate in the first group.&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;Gate&lt;/th&gt;
&lt;th&gt;What it measures&lt;/th&gt;
&lt;th&gt;Limit&lt;/th&gt;
&lt;th&gt;Measured&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;G1&lt;/td&gt;
&lt;td&gt;2,000 awake fragments, 95th percentile (p95)&lt;/td&gt;
&lt;td&gt;6 ms&lt;/td&gt;
&lt;td&gt;1.67 ms on a building, 1.75 ms on terrain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;G2&lt;/td&gt;
&lt;td&gt;10,000 sleeping fragments, per step&lt;/td&gt;
&lt;td&gt;0.75 ms&lt;/td&gt;
&lt;td&gt;0.001 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;G4&lt;/td&gt;
&lt;td&gt;Six buildings, one collapsing, no renderer, p95&lt;/td&gt;
&lt;td&gt;6 ms&lt;/td&gt;
&lt;td&gt;1.99 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;G5&lt;/td&gt;
&lt;td&gt;One piece detaching, p95&lt;/td&gt;
&lt;td&gt;1.5 ms&lt;/td&gt;
&lt;td&gt;1.21 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;G8&lt;/td&gt;
&lt;td&gt;One 16 × 16 m crater edit&lt;/td&gt;
&lt;td&gt;1 ms&lt;/td&gt;
&lt;td&gt;0.185 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;p95 means 95% of steps were faster than that figure. The record’s conclusion was that the engine decision was final. The changelog dates all eight releases, 0.1.0 through 0.8.0, to September 29.&lt;/p&gt;
&lt;h2 id=&quot;the-stage-1-close&quot;&gt;The Stage 1 close&lt;/h2&gt;
&lt;p&gt;Closing Stage 1 meant running the remaining gates in Low Power Mode. Four failed.&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;Gate&lt;/th&gt;
&lt;th&gt;Limit&lt;/th&gt;
&lt;th&gt;First Low Power Mode run&lt;/th&gt;
&lt;th&gt;Later the same day&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;G3, graphics for six buildings and 20,000 fragments, p95&lt;/td&gt;
&lt;td&gt;8 ms&lt;/td&gt;
&lt;td&gt;9.97 ms&lt;/td&gt;
&lt;td&gt;7.01 to 7.63 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;G4, full frame through a collapse, p95&lt;/td&gt;
&lt;td&gt;12.5 ms&lt;/td&gt;
&lt;td&gt;24.7 ms&lt;/td&gt;
&lt;td&gt;8.86 and 10.39 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;G6, load calculation for a 20,000-piece structure, p95&lt;/td&gt;
&lt;td&gt;1 ms&lt;/td&gt;
&lt;td&gt;1.68 ms&lt;/td&gt;
&lt;td&gt;0.808 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;G7, one 64-cell fracture on one worker, p95&lt;/td&gt;
&lt;td&gt;3 ms&lt;/td&gt;
&lt;td&gt;4.58 ms&lt;/td&gt;
&lt;td&gt;4.19 ms, still over&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;G3 came down after two rendering changes: the distant shadow layers now alternate frames, and terrain detail follows how large the error would look on screen. G6 came down after a term in the load solver was simplified. G7 stayed over. We decided to keep 64 fracture cells at the high tier, and had the game detect Low Power Mode and adjust the way it would on a slower machine, while the high tier keeps being optimized. The game now switches to its low tier in Low Power Mode. The low tier fractures at 24 cells, which takes 1.32 ms p95.&lt;/p&gt;
&lt;h2 id=&quot;the-wrong-diagnosis&quot;&gt;The wrong diagnosis&lt;/h2&gt;
&lt;p&gt;The slow collapse frame was the largest failure. Physics steps took about 20.5 ms while the charges fired, and 2 to 4 ms once they stopped.&lt;/p&gt;
&lt;p&gt;The first diagnosis, made with the Low Power Mode run, blamed the fracture work, on the theory that the background workers that break pieces apart were taking cores from the physics step. It was corrected about 13 minutes later. The 18 fractures took 2 to 5 ms each on a worker, and the steps between charges were just as slow. A profiler trace of the window put the time in two places. One was collision detection against the terrain, including the slow path Jolt takes when two shapes are deep inside each other, which happens when fragments are pressed into the ground. The other was solving contacts.&lt;/p&gt;
&lt;p&gt;Later that day, after the rubble-weight, crushing and settling work, the same demo cost 0.90 to 1.24 ms p50 per physics step over its first two seconds, and the collapse frame was 8.86 and 10.39 ms p95. Which change removed the 20.5 ms steps was not isolated.&lt;/p&gt;
&lt;p&gt;Some slow steps remain, 16 to 40 over 8 ms in a run of about 1,900, in short bursts. Two runs of the same simulation put them at different steps, and changing the number of job threads made no difference beyond the noise. The record reads them as scheduling, outside the simulation’s work. With the display asleep, two runs had 3 and 6.&lt;/p&gt;
</content:encoded><category>Engine note</category><category>performance</category><category>physics</category><category>rendering</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>