AI Portfolio Lab Tools Games Blog Feedback
All Posts

The Astronaut Wasn't Slow. The Phone Was Hot.

Four back-to-back bench passes on my Samsung Galaxy, and the last one came back wrong. 94.3 fps, 18.6% frame drop at peak load. I was about to trim the drawing. The phone was just hot.

2 min read
mobileandroidperformance

The four runs were back to back. That was the mistake.

I’m benchmarking each new crew type for Salvage Swarm before it ships. The game is an HTML canvas wrapped in Capacitor on a Samsung SM-S942U (Android 16, 120 Hz panel). bench.mjs plays a stored solution with the same taps every time and logs frame times. Four crew types to get through, so I ran them in sequence: satellite rest-paced, satellite eager, astronaut rest-paced, astronaut eager.

First three runs looked fine. Satellite clocked 117.2 fps rest-paced, 113.8 fps eager. Astronaut rest-paced: 114.9 fps, barely anything over 16.7 ms. Then the fourth run. Astronaut eager: 94.3 fps, 4.2% of frames over 16.7 ms, and 18.6% when 20 or more robots were in the air. That reads as a real problem.

My first thought was the drawing cost. The astronaut is the most complex skin, roughly 1.7 times the saucer’s estimated draw cost. The saucer had run clean at 115.7 fps eager. I started thinking about what to cut.

Then I checked the battery temperature. 44.8°C. Thirty minutes before the first run it had been 33°C. Four bench sessions back to back raised the phone’s core temperature by nearly 12 degrees. The phone throttles to protect itself. It slows the game to stay cool, and that slowdown looks exactly like a drawing bug.

I let the phone cool and ran the control. 31.8°C at the start. Astronaut eager first: 119.2 fps, zero percent of frames over 16.7 ms, 0.1% with 20 or more robots out. Then saucer eager as a check: 117.7 fps, 0.1%. The astronaut costs about the same as the saucer. I was about to redesign a skin I didn’t need to touch.

Bar chart of frames per second on each benchmark run. Back to back, with the phone going from 33°C to 44.8°C: satellite rest-paced 117.2, satellite eager 113.8, astronaut rest-paced 114.9, astronaut eager 94.3. After cooling to 31.8°C: astronaut eager 119.2, saucer eager 117.7.

There was a second thing I found the same day. bench.mjs calls unlockAll for testing and restores localStorage at the end, but the game persists its in-memory save on page unload, which fires after the restore. Every bench run was leaving the phone with every order unlocked. A different kind of test corruption. The tool quietly doing something it wasn’t supposed to, visible only when you go check.

Two rules now. No more than two five-minute runs back to back, and log adb shell dumpsys battery temperature before and after each one.

If I were starting this performance pass over, I’d log the phone temperature before the first run. The measurement had assumptions. I hadn’t written any of them down.