Audio Loop Delay
Demonstrates how to stagger multiple looping audio tracks using the delay parameter in the sound playback configuration. It shows how to layer stems by calculating delays based on track duration and syncing visual elements to the audio loop events.
800 × 600
class Example extends Phaser.Scene
{
bass;
gui;
bottomSpeaker;
middleSpeaker;
topRightSpeaker;
topLeftSpeaker;
preload ()
{
this.load.setBaseURL('https://cdn.phaserfiles.com/v410');
this.load.bitmapFont('atari-classic', 'assets/fonts/bitmap/atari-classic.png', 'assets/fonts/bitmap/atari-classic.xml');
this.load.image('streets', 'assets/sprites/cyberpunk-street.png');
this.load.atlas('speakers', 'assets/sprites/speakers/speakers.png', 'assets/sprites/speakers/speakers.json');
this.load.audio('bass', [ 'assets/audio/tech/bass.ogg', 'assets/audio/tech/bass.mp3' ]);
this.load.audio('drums', [ 'assets/audio/tech/drums.ogg', 'assets/audio/tech/drums.mp3' ]);
this.load.audio('percussion', [ 'assets/audio/tech/percussion.ogg', 'assets/audio/tech/percussion.mp3' ]);
this.load.audio('synth1', [ 'assets/audio/tech/synth1.ogg', 'assets/audio/tech/synth1.mp3' ]);
this.load.audio('synth2', [ 'assets/audio/tech/synth2.ogg', 'assets/audio/tech/synth2.mp3' ]);
this.load.audio('top1', [ 'assets/audio/tech/top1.ogg', 'assets/audio/tech/top1.mp3' ]);
this.load.audio('top2', [ 'assets/audio/tech/top2.ogg', 'assets/audio/tech/top2.mp3' ]);
}
create ()
{
const streets = this.add.image(0, 0, 'streets');
streets.setScale(600 / 192);
streets.setOrigin(0);
this.topLeftSpeaker = this.add.image(445, 332, 'speakers', 'top-left');
this.topLeftSpeaker.setOrigin(1, 0.46);
this.topRightSpeaker = this.add.image(445, 332, 'speakers', 'top-right');
this.topRightSpeaker.setOrigin(0, 0.975);
this.middleSpeaker = this.add.image(443, 417, 'speakers', 'middle');
this.middleSpeaker.setOrigin(0.5, 1);
this.bottomSpeaker = this.add.image(443, 504, 'speakers', 'bottom');
this.bottomSpeaker.setOrigin(0.5, 1);
this.bass = this.sound.add('bass');
const drums = this.sound.add('drums');
const percussion = this.sound.add('percussion');
const synth1 = this.sound.add('synth1');
const synth2 = this.sound.add('synth2');
const top1 = this.sound.add('top1');
const top2 = this.sound.add('top2');
this.gui = new dat.GUI();
const sm = this.gui.addFolder('Sound Manager');
sm.add(this.sound, 'rate', 0.5, 2).listen();
sm.add(this.sound, 'detune', -1200, 1200).step(50).listen();
const loopMarker = {
name: 'loop',
start: 0,
duration: 7.68,
config: {
loop: true
}
};
if (this.sound.locked)
{
const text = this.add.bitmapText(400, 70, 'atari-classic', 'Tap to start', 40);
text.x -= Math.round(text.width / 2);
text.y -= Math.round(text.height / 2);
this.sound.once('unlocked', function (soundManager)
{
text.visible = false;
this.startStem.call(this, this.bass, 'Bass', this.bottomSpeaker);
}, this);
}
else
{
this.startStem.call(this, this.bass, 'Bass', this.bottomSpeaker);
}
this.bass.addMarker(loopMarker);
// Delay option can only be passed in config
this.bass.play('loop', {
delay: 0
});
// Below won't work
// sound.delay = delay;
// sound.play('loop');
this.bass.once('looped', function (sound)
{
this.startStem.call(this, drums, 'Drums', this.middleSpeaker);
}, this);
drums.addMarker(loopMarker);
drums.play('loop', {
delay: loopMarker.duration
});
drums.once('looped', function (sound)
{
this.startStem.call(this, percussion, 'Percussion', this.middleSpeaker);
}, this);
percussion.addMarker(loopMarker);
percussion.play('loop', {
delay: loopMarker.duration * 2
});
percussion.once('looped', function (sound)
{
this.startStem.call(this, synth1, 'Synth 1', this.topRightSpeaker);
}, this);
synth1.addMarker(loopMarker);
synth1.play('loop', {
delay: loopMarker.duration * 3
});
synth1.once('looped', function (sound)
{
this.startStem.call(this, synth2, 'Synth 2', this.topRightSpeaker);
}, this);
synth2.addMarker(loopMarker);
synth2.play('loop', {
delay: loopMarker.duration * 4
});
synth2.once('looped', function (sound)
{
this.startStem.call(this, top1, 'Top 1', this.topLeftSpeaker);
}, this);
top1.addMarker(loopMarker);
top1.play('loop', {
delay: loopMarker.duration * 5
});
top1.once('looped', function (sound)
{
this.startStem.call(this, top2, 'Top 2', this.topLeftSpeaker);
sm.open();
}, this);
top2.addMarker(loopMarker);
top2.play('loop', {
delay: loopMarker.duration * 6
});
}
update ()
{
this.middleSpeaker.y = this.bottomSpeaker.y - this.bottomSpeaker.height * this.bottomSpeaker.scaleY;
this.topLeftSpeaker.y =
this.topRightSpeaker.y =
this.middleSpeaker.y - this.middleSpeaker.height * this.middleSpeaker.scaleY;
this.tweens.setGlobalTimeScale(this.bass.totalRate);
}
startStem (stem, text, speaker)
{
const s = this.gui.addFolder(text);
s.add(stem, 'seek', 0, stem.duration).step(0.01).listen();
s.add(stem, 'mute').listen();
s.open();
this.tweens.add({
targets: speaker,
scaleX: 1.3,
scaleY: 1.1,
duration: 241,
ease: 'Sine.easeInOut',
repeat: -1,
yoyo: true
});
}
}
/**
* @author Pavle Goloskokovic <[email protected]> (http://prunegames.com)
*
* Cyberpunk Street Environment by Luis Zuno (https://www.patreon.com/posts/8303915) / CC-BY-3.0
*/
const config = {
scale: { mode: Phaser.Scale.FIT },
type: Phaser.AUTO,
parent: 'game-container',
width: 800,
height: 600,
backgroundColor: '#838282',
scene: Example,
pixelArt: true,
audio: {
noAudio: true
}
};
const game = new Phaser.Game(config);