The symptom

Testing the game with the boss active, one detail caught my eye. Once a second, the scroll hiccuped very slightly. Not a slowdown, not a flash, just a jump. As if the image froze for a fraction of a second before resuming its run. Standing still, you see nothing. But as soon as the camera moves, it shows, because a smooth scroll makes the tiniest bug visible.

On the Atari ST there is no ready-made measuring tool. A machine from 1989, an emulator, and my eyes. Here is one evening's investigation, and quite the evening it was ;)

First lead: the plague computing too much

The obvious suspect was the plague. It spreads once a second, and at that moment it inspects a great many cells at once to decide which ones will contaminate their neighbours. I first hunted down an expensive calculation it kept redoing, and replaced it with a simple value prepared once and for all. On paper, a decent gain.

Result: the jump is still there. Same frequency, same amplitude. Well done, but no.

Second lead: too many cells redrawn at once

Second reflex. When the plague spreads, every cell that changes state has to be redrawn on the next frame. If a lot of cells flip at the same time, that suddenly makes a big pile of drawing to catch up on. So I limited the number of flips allowed per second, with the surplus waiting its turn.

Result: the jump is still there.

At that point in the evening I started to doubt my hypotheses. I needed to see what was happening, not guess.

Seeing time, at last

There is an old Atari ST trick, used by demo makers, for visualising where the processing time goes. You change the screen's background colour at every stage of a frame's work. The result: coloured bands appear, and the thickness of each band tells you at a glance how much time that stage cost. No figures, no table, just an image you read instantly.

So I coloured each major phase of the game loop and ran it again. Most frames looked alike, nicely balanced, with a wide margin of free time at the bottom, a sign that all is well.

And then, exactly once a second, a radically different frame. The plague phase was devouring the top half of the screen. Everything else was crushed against the bottom, and the margin had all but vanished. The diagnosis was finally clear and unambiguous: doing all the plague's propagation work in one go exceeded the time available for a frame, and the emulator then skipped that frame. Neither the expensive calculation nor the pile of drawing was the real culprit. It was simply the total volume, done all at once.

The good idea: spread the work out

Since doing everything at once is a problem, I may as well spread that work across the fifty frames of a second. On each frame, the plague only handles a small slice. After a second, all the slices together cover a complete cycle. The overall rhythm is preserved, but the spike disappears.

I implement it, I test. Perfectly smooth scroll, no more jumps. Victory. I start to savour it.

Then I walk into the boss room, and everything turns red in three seconds. The plague had swept across the whole map.

The hidden trap

I saw the mistake immediately. I had decided to handle a fixed number of cells per frame. When the plague is very widespread, that fixed number only covers a small share of the population on each frame, and all is well. But at the start of a game, when there is only a handful of active cells, that same fixed number takes in all of them, on every frame. So each cell was trying to contaminate a neighbour fifty times faster than intended. The plague exploded.

The real solution is to think in proportions rather than in fixed quantities. Instead of promising a number of cells per frame, I guarantee that every cell will be handled once a second, whatever the size of the population. When there are few cells, we do few per frame. When there are many, we do more. The rhythm stays right in every case, from the peaceful opening right through to a saturated boss room.

New test, boss room, smooth scroll and plague spreading at the right rate. End of story.

What I take away

Two lessons I'm keeping.

The first: optimising without a diagnosis is a waste of time. My first two fixes were honest improvements, but neither attacked the real cost. Visualising with coloured bands should have been my first step, not my third. It's a gift from Atari ST history: no modern tool gives you a reading that immediate, because on this machine the boundary between computation and image is very thin.

The second: spreading periodic work with a fixed quantity per frame is a trap as soon as the population varies. Thinking in proportions guarantees the right rhythm in every case. Obvious afterwards, much less so at the time.

The visualisation rig stays in the code, switched off. At the next hitch, I turn it back on and we do this again.

The current .prg is available for download. Arrows to move, fire for the lantern, P to pause, Esc to quit. Test the scroll in the boss room, that is the area where the optimisation shows best.