Aug 11 2009
[AS3] Fixing The Lag That Arises When Playing A Short Sound Effect
There is a Flash Player issue that arises when you play a short sound effect in the absence of any other sound playing. If your application plays a sound effect when there is not already another sound playing, then your application will experience some lag (length of lag depends on the performance of the client machine).
Things will seem to freeze for that short moment, and then the sound effect plays, and then execution of code resumes.
To demonstrate this issue, we start with a simple SWF that plays no sound and has no lag issue:
- We are not playing any sound in this SWF.
- A circle Sprite is drawn.
- The Sprite moves to another random spot when you move mouse cursor over it.
- Notice the text displayed on the top-left corner showing the amount of lag – there is no lag.
The Problem
The next SWF is primarily the same as that above, but it will play a sound when the mouse is over the circle sprite. The lag problem is now observable:
- Move the mouse cursor over the circle Sprite.
- Unlike in the first SWF, the sprite does not move to another spot immediately.
- A short sound effect is played after a noticeable lag.
- The sprite then moves to another spot.
- Notice the text displayed on the top-left corner showing the amount of lag.
- If you repeat the mouse over action quickly (ie no waiting in between), there will be no lag.
- If you wait a little before repeating the mouse over action, the lag problem persists.
In the SWF above, the sprite does not move to another spot immediately when you mouse over it – this is lag (compared to the first SWF above where the change is immediate).
Sometimes the lag will not be that noticeable if sound effects are played in quick sequence, ie, one sound after another with no pause or little pause in between. But once the SWF has gone silent, then the next sound played will cause lag. This is obviously a problem if you are attaching short sound effects to user interface elements or implementing various sound effects in a game – whenever the end-user clicks a button, presses a key, etc. that triggers a sound effect when no sound is already playing, your application/game will seem to lag.
The problem seems to arise due to the initialization of the Flash Player’s sound player – this presumably takes some CPU time, causing lag. But I am not qualified to say for sure why it happens.
The Fix
The problem is apparently fixed by playing another sound indefinitely in the background, such as having a background music playing endlessly. The idea is to not let the sound player “go to sleep”. In the absence of any sound playing, the sound player apparently “goes to sleep” after playing the short sound effect, and then re-initializes again (consuming CPU time and causing lag) when it needs to play a sound effect again.
If it is not appropriate for your application to play any music in the background, you can also fix the problem by playing one of the application’s sound effects endlessly, silently:
[Embed(source="sfx.mp3")] private static const snd_sfx:Class; private static const sfx:Sound = new snd_sfx(); private static const SilentSoundTransform:SoundTransform = new SoundTransform(0); private static function playSoundSilentlyEndlessly(evt:Event = null):void { sfx.play(0, 1000, SilentSoundTransform).addEventListener(Event.SOUND_COMPLETE, playSoundSilentlyEndlessly, false, 0, true); // plays the sound with volume 0 endlessly }
Here is the SWF’s Main class with the “endless looping silent background sound” workaround fix:
package { import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.media.Sound; import flash.media.SoundChannel; import flash.media.SoundTransform; import flash.system.Capabilities; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.utils.getTimer; public class Main extends Sprite { [Embed(source="sfx.mp3")] private static const snd_sfx:Class; private static const sfx:Sound = new snd_sfx(); private static const SilentSoundTransform:SoundTransform = new SoundTransform(0); private static function playSoundSilentlyEndlessly(evt:Event = null):void { sfx.play(0, 1000, SilentSoundTransform).addEventListener(Event.SOUND_COMPLETE, playSoundSilentlyEndlessly, false, 0, true); // plays the sound with volume 0 endlessly } public function Main():void { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private var txt:TextField; private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); // entry point txt = new TextField(); txt.autoSize = TextFieldAutoSize.LEFT; txt.selectable = false; addChild(txt); var ball:Sprite = new Sprite(); ball.graphics.beginFill(0xFF0000); ball.graphics.drawCircle(0, 0, 10); ball.graphics.endFill(); ball.x = 100; ball.y = 100; addChild(ball); ball.addEventListener(MouseEvent.MOUSE_OVER, on_ballOver, false, 0, true); playSoundSilentlyEndlessly(); // ** call this once to initialize looping silent sound ** } private function on_ballOver(evt:MouseEvent):void { var t:int = getTimer(); var ball:Sprite = evt.target as Sprite; if (ball) { ball.x = 10 + Math.random() * 180; ball.y = 10 + Math.random() * 180; } sfx.play(); txt.text = "Delay: "+ (getTimer() - t) + "ms\n" + Capabilities.version; } } }
Note I:
The fixed SWF is not embedded here on this page because the “sound lag” problem is also fixed by having any SWF, with sound playing, in the same browser (it doesn’t even have to be on the same page, as long as the browser’s Flash plugin is playing sound, which can mean another SWF on another page playing sound, the problem is fixed). You can try this – open a new browser tab, go to another page that has a SWF playing a music (eg. a YouTube video), and view this page again – you will no longer experience the sound lag problem in the second SWF above.
Note II:
In the above example, we have embedded the sound asset in the SWF, but this has no bearing on the issue – it happens whether you embed the asset or load it during run-time.
Note III:
The problem exists for both Flash Player versions 9 and 10 (even the latest version 10,0,32,18 at the time of this post). Even if the problem is fixed in a future player version, as long as your application may be used in earlier versions, you probably want to continue employing the “keep sound playing” workaround suggested above.
Please feel free to leave a comment if you know of a better way to fix the above problem – thanks!









very helpful, thank you for sharing this. I have this issue with one of my games
Thank you SO much for this. I had been up many blind alleys searching in vain for a solution, but this works beautifully – not the most elegant solution ever, but admirably sneaky.
Thanks. This works. But i found that in Linux the latency is very much less.
Hi. Are you measuring the latency of the actual sound (check position against startTime) or only the latency of the code executing after the plsy() method?
Sorry.. “play() method” of course.
@Nook
The latency is measured in the function on_ballOver() (see code above). Would you happen to find that method debatable? Are you actually not experiencing any latency at all? Otherwise, there should be no question about the latency at all…
Interesting stuff. I’ve tested some variants of this and it appears the sound engine sleeps the instant there are no channels playing; There is no delay whatsoever.
Try playing a sound repeatedly that stops itself before replaying, it’s absolutely ghastly.
I’m solving this currently by generating constant silence in Flash 10.
silence = new ByteArray();
for (var i:int = 4096; i–; )
{
silence.writeFloat(0);
}
private function onSampleDataEvent(e:SampleDataEvent):void
{
e.data.writeBytes(silence);
}
Performance impact is absolutely nonexistent, and solves this problem very nicely. Of course only in FP10 :/
Thanks Andreas, but would you care to share why that would be a better solution? I am not sure how your solution works – do you get every Sound object to listen to that event?
Just as a side note, if you just want an empty ByteArray, I am sure the following is the same:
var silence:ByteArray = new ByteArray();
silence.length = 4096;
Cheers!
This was incredibly helpful. You’re a life saver. You’ll be getting an acknowledgment once my game is finished.
A great solution, thank you. With this I was able to overcome the slight delay/lag after each sound loop. But how would this affect the performance? I am just curious. Thank you!
That’s brillant, indeed. I was wondering why I was experiencing the problem sometimes and not all the time (and not never…). It was really annoying.
It seems fixed, now. Thank you.
Do you want to be notified when the game is released ?
Hello sunny,
many thanks for having posted this solution and especially the explanation to the problem – due to I was about to solve one of my sound problems by playing an endless sound and only turning the volume on and off, I probably would have solved the other problems, too, but never have known how
I guess it’s still effective today. I use this solution to fix the song latency in a musical game. Thank you for sharing ! I waste a lot of time on this …
Great solution, just the workaround I was looking for!
I looked into what Andreas R was talking about, and is currently what i’m using in my application. Have not seen any performance loss using this method, and you don’t have to worry about setting a play repeat count long enough to last; nor do you need to load/embed some random sound file.
private static var silence_data:ByteArray = new ByteArray();
private static function fill_silence(event:SampleDataEvent):void {
event.data.writeBytes(silence_data);
}
public static function init():void {
for (var i:int=0; i<4096; i++)
silence_data.writeFloat(0);
var snd_silence:Sound = new Sound();
snd_silence.addEventListener(SampleDataEvent.SAMPLE_DATA, fill_silence);
snd_silence.play();
}
It basically creates a segment of silence (stored in a ByteArray), and any time the sound system needs to sample data it just dumps more silence into it.
I am still struggling with this, what i find if the audio engine is “alive” i hear latency calling audio, however if its been silent for 15 seconds (audio engine “asleep”) i get the first call of the audio instantly, just that 1 though. By implementing this method or the byte array method mentioned, i do indeed keep it “alive” but now even that first call is delayed. This is very upsetting, but this information has at least given me a way to keep it consistent! However since my experience differs, i assume performance differs per computer
hi chunk, are you perhaps looking at a different problem here? is the delay in playing a sound for the first time due to loading the sound?
hi sunny,
yes looks like i was indeed looking at another issue, my issue is audio latency after calling play(). I see now this article is focusing on lag of the swf itself, my mistake. (still helpful to me!)
The sound is loaded, and actually the sound does play on time the first call, its any call AFTER that which can be delayed up to a few hundred milliseconds. It seems only to happen on flash player 10.1, on 10.0 its fine.
“It seems only to happen on flash player 10.1, on 10.0 its fine.”
Gotta love Flash! Let Adobe know… ; )
But then again, check if the problem exists on debug or release versions of the player, standalone or plugin, etc.
Hi, I get 3ms in the sound demo (0ms in just the visual) and I didn’t hear any lag. Is this problem fixed?
best
helped a lot. thnx. one question though. What exactly is the effect of calling play() twice on the same sound object ? Does each play independently as it seems ?