27 days
On HEATSINK
The whole game can be summarized in one sentence, and we had the sentence before we had any code. Hold to fire, heat kills you and it multiplies you.
A gauge wraps around your turret and fills while you hold the trigger. Max it out and overheat, and you are dead. Your score multiplier rides that same gauge, so the safest way to play is the lowest scoring way to play.
We committed Sector 1 on August 2nd. 50 commits and 27 days later we built a full working prototype of Heatsink. This is what happened in between.
On day 2 we deleted the aiming
The first playable build aimed itself. The turret picked a target and you held the trigger. It felt fine, and that was the problem: nothing your thumb did mattered except deciding when to stop.
Enemies would die before you ever saw them, so we stopped letting the turret choose. One finger holds the trigger and points the barrel, and a shot that lands is a shot you aimed. The beam became a ray instead of a lock. It cost a day and broke the weapon swap, a horizontal swipe: the same motion as aiming. Swapping moved onto the holstered weapon in the HUD.
The game got harder that afternoon. It also became yours.
The grade was punishing people for playing correctly
Runs get a letter grade, and accuracy was one of the 2 heaviest terms in it. That looks reasonable written down. In practice it was incoherent: holding the trigger is the point of the game, and the early stages have wide gaps between waves in order to get the players used to the mechanics. All that holding happened over an empty field and scored as a miss.
We ran the numbers on stage 1-1. Clearing it means about 48 HP of drones, roughly 30 shots that connect. Holding the redline for the stage means the Repeater fires about 295 times. That is a 10 percent accuracy ceiling on a flawless run, at any skill level. Riding hot made it worse: 2.44x damage at 90 percent heat more than halves the shots that can possibly connect. A perfect run was capped at a B.
Accuracy stopped being a term and our replay of stage 1-1 went from a B to an A.
Then the replacement grew the same bug in a new shape. You can play well 2 ways: ride the gauge hot, or take kills deep in the pocket where they are worth more. We graded both and added them together. A bot that killed on sight scored 0.00 on depth. One that waited for depth scored 0.01 on redline. Both landed at B, and the only S the formula would issue went to the one play satisfying both at once: holding the trigger on empty sky. Adding 2 virtues together had quietly demanded both.
The grade reads one number now
So the sum is gone too. There is one reading behind the letter now: what the run scored, against what the same bodies would have paid to a player who took every one of them at the top of the gauge. We call it yield, and 100 percent is that player.
Overdrive pays no yield at all. A letter you can buy with a button is not a letter.
Which leaves 100 percent, and it's worth saying what it's not. It is not a ceiling. It is a statement: you were paid everything the gauge could pay. Depth multiplies past it, so the meter on the results card fills to 100 and stops there while the figure beside it keeps climbing. A run that dies is graded nothing at all, so what sits above that line is limited by what a player can survive rather than by what they can reach.
S starts at 100 percent. It is not the last letter on the table. That is all we are going to say about that.
2.45 seconds of nothing
Launch the app and you got a black window for 2.45 seconds. Not a slow load behind a spinner. Nothing.
We assumed the new splash sequence, since both arrived the same week. Measuring put the splash at 132ms. The real cost was an autoload synthesising 63 sound effects on the main thread before the first frame, 993 milliseconds of it, with another 148ms of enemy scenes behind that.
Then we made the fix that does not work, and it is worth more than the fix that did. We moved the sound bake to call_deferred, ran it, and the app froze for exactly as long as before. call_deferred flushes at the top of the next frame, still ahead of that frame's draw. All it changed was a frozen black window into a frozen splash card. It looked like progress. It was zero progress. Nothing about the measurement had been wrong, only our idea of where the frame boundary was.
The bake runs on a real thread now, joined the first time anything needs a sound. The enemy scenes load threaded and get collected after the first frame is on screen. Time to first frame is 1.29 seconds. About 1.09 of that is engine init before any of our code runs, and we are leaving it alone.
Where it stands
6 sectors, 60 authored stages, 6 weapons, a campaign, an endless mode and a daily board. The frame above is stage 3-9. The one further up is Sector 1, 27 days earlier.
Next it leaves our machines: export templates, a real device, and the first person who is not us holding the trigger a second too long. More on Heatsink here.