Advertisement
Game Audio

Heal Latency Matters More Than Heal Quality on Mobile—Here is How to Test It

On mobile, a beautiful heal cue that arrives a fifth of a second late reads as a broken button. Where the delay comes from, how to measure it honestly, and how to remove it.

A tester taps the health pickup while walking backwards. The character heals, the bar jumps, and the sound lands late enough that she taps the pickup again. Nothing in the mix is wrong. The heal is late, and on a phone that reads as a broken button.

Heal quality is what you spend an afternoon on. Heal latency decides whether the cue works at all. On desktop a sloppy trigger path hides inside the frame budget. On a phone the same path can cost a fifth of a second, and no amount of shimmer in the tail buys it back.

The Cue Is Usually Fine; the Path to the Speaker Is Not

When a heal feels unresponsive, the first instinct is to fix the sound. That treats the wrong end of the chain. The gap a player perceives runs from the frame the health value changes to the sample that reaches the speaker, and almost all of it is spent before anyone touches the waveform.

Two numbers get confused here. Audio path latency is buffer, decode, voice start. Trigger latency is the delay between the game deciding the player healed and the engine being asked to play. A build can have a clean 20 ms audio path and still feel laggy, because the trigger waited on a load.

Where the Milliseconds Go, in Order

The order below is roughly how much delay each source tends to add on mobile. It is a heuristic, not a ranking.

1. DSP buffer size

The output callback runs on a fixed buffer, and double buffering puts it at least two buffers behind the game thread. In Unity, AudioSettings.GetConfiguration().dspBufferSize is 256, 512 or 1024 frames depending on the latency preset; at 48 kHz that is about 5.3, 10.7 and 21.3 ms per buffer. At 1024 frames with double buffering you are near 43 ms before anything else runs. Android's low latency path uses 192 frames, about 4 ms at 48 kHz, when the device supports it. An iOS session asking for a 5 ms preferred IO buffer duration behaves differently from one left at the default near 23 ms.

2. First-trigger asset load

A clip that was never loaded has to come off storage the first time it plays. On mobile that is flash, and a cold read plus decompression can take tens to hundreds of milliseconds, which is why the first heal of a session often feels worse than the rest. The usual bug is a pickup that lives in its own bundle and is only requested when the player touches the item.

Preload removes it. Load the heal clip with the level, hold a reference, and never call a load API from the pickup handler. In Unity that means a LoadType of Decompress On Load rather than Streaming, or an AudioClip.LoadAudioData call at scene start. The rule holds: the heal handler may not touch synchronous resource loading.

3. Codec decode

Compressed formats buy size with decode work and, sometimes, fixed algorithmic delay. MP3 carries encoder and decoder padding worth roughly 25 ms at 44.1 kHz before the first sample you wanted. An AAC frame is 1024 samples, about 23 ms at that rate. PCM has neither.

For a cue measured in tens of kilobytes, the trade often goes the other way: keep the file small with a cheap decode format instead of chasing the last few kilobytes with one the device decodes expensively. The targets in heal sounds under 30 KB are reachable without compression. A 300 ms mono 22.05 kHz 16-bit WAV is about 13 KB and starts on the first frame.

4. Garbage collection and per-trigger allocation

A collection pause of 10 to 20 ms is enough to push a trigger past the next frame, and on mobile those pauses land mid-combat more often than the profiler average suggests. The allocations are usually not in the audio code. They sit in the pickup handler, or in AudioSource.PlayClipAtPoint, which creates a temporary GameObject and AudioSource and destroys them when the clip ends. Pool a fixed set of sources instead.

5. Device variance

The same build can measure 30 ms on one phone and 120 ms on another. Thermal throttling, slow flash, a vendor audio HAL and background apps all move the number, which is why one test device proves little. On a handheld the platform audio stack is one more variable; the same reasoning in designing for Switch and handheld play covers it.

How to Measure the Gap Without Fooling Yourself

You do not need a lab. You need a recording where the tap and the sound are both visible.

Put a visible tap indicator on screen, a debug button that flashes a solid rectangle on touch-down. Record the screen at 60 fps or higher with audio from the same device, open the clip in an editor that shows frames, then count from the frame where the indicator changes to the sample where the waveform leaves the noise floor. 16.7 ms per frame at 60 fps, 8.3 ms at 120.

The profiler will not show you this gap. It reports frame cost, not the distance between an input event and a sample reaching the speaker. A frame averaging 14 ms can hide a 40 ms spike on the frame the player touched the pickup, and the average will look healthy while the heal feels late.

Two corrections. A screen recorder can add latency or drop frames, so calibrate against something you trust, or confirm once with an external camera at 240 fps. And a tap is not frame aligned: the input lands somewhere inside a frame, which smears one result by up to 16.7 ms. Report a median and a range.

Measure three cases, because they behave differently. First heal after launch tests asset loading. A heal mid-combat tests the steady state. A heal after the app resumes tests whether the preload survived, which on Android it often does not.

What the Numbers Tend to Feel Like

These thresholds are observations from playing builds, not a standard.

  • Under about 50 ms. The cue feels attached to the tap. At 60 fps that is inside three frames, roughly the window where players stop thinking about the input.
  • About 50 to 100 ms. Tolerated. Players still read the heal as their own action, but a fast player may already be looking at the next threat when it lands.
  • Past about 100 ms. It reads as input lag. At 60 fps, 100 ms is six frames, enough for the player to tap again or assume the pickup failed.

How much of a cue has to land early is a separate clock, covered in the first fifty milliseconds. Both fail in the same place: the confirmation arrived after the decision closed.

Fixes in the Order They Pay Off

Trim the head of the clip. A cue with 20 ms of silence before the transient adds 20 ms no engine setting can remove.

Preload everything the heal needs, clip and snapshot, before gameplay starts. A load on the touch frame means the latency is storage and decode cost.

Pool voices. A fixed set of AudioSources, claimed and released, with a retrigger rule set in advance.

Shorten the buffer, then watch frame time. Dropping to 256 frames buys real milliseconds and costs CPU.

Choose formats the platform decodes cheaply. For a 200 to 300 ms one-shot, uncompressed audio is often smaller than people expect and costs nothing to decode.

Instrument the trigger, not only the mixer. A timestamp when the health value changes and one when the voice starts tells you which half of the gap you are fighting. The rest of the budget is covered in mobile audio performance.

Test on the Slowest Device You Support

Numbers from the newest phone in the office are close to meaningless. Run the test on the oldest supported device, full storage and three background apps, then again after twenty minutes of play.

Under 50 ms there, the cue has room to be pretty. Past 100 ms, quality is not the problem and more of it will not help.

Advertisement