• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

excaliburjs / Excalibur / 17003581536

16 Aug 2025 03:07AM UTC coverage: 87.367% (-0.4%) from 87.801%
17003581536

push

github

web-flow
feat: Add SoundManager for managing music/fx/audio in a friendly way (#3472)

Closes #590
Related #1161 

## Changes:

- Added a new `ex.Sound({...})` option back constructor to set all the props you could set on sound at constructor time
- Added new `ex.SoundManager({...}` api to manage volume and muting on large groups of sounds at once

```typescript
var soundManager = new ex.SoundManger({
  channels: ['fx', 'music', 'background'],
  sounds: {
    jumpSnd: { sound: jumpSnd, volume: 0.4, channels: ['fx'] },
    forestSnd: { sound: forestSnd, volume: 0.2, channels: ['music', 'background'] },
    challengeMusic: { sound: challengeMusic, volume: 0.2, channels: ['music'] },
    guitarLoop: { sound: guitarLoop, volume: 0.2, channels: ['music'] }
  }
});

toggleMusic.on('pointerdown', () => {
  soundManager.channel.toggle('music');
});
game.add(toggleMusic);

game.input.keyboard.on('press', (evt) => {
  if (evt.key === ex.Keys.J) {
    soundManager.play('jumpSnd');
  }

  if (evt.key === ex.Keys.M) {
    soundManager.channel.mute('music');
  }

  if (evt.key === ex.Keys.A) {
    soundManager.mute();
  }

  if (evt.key === ex.Keys.S) {
    soundManager.unmute();
  }

  if (evt.key === ex.Keys.U) {
    soundManager.channel.unmute('music');
  }

  if (evt.key === ex.Keys.P) {
    soundManager.channel.play('music');
  }

  if (evt.key === ex.Keys.V) {
    soundManager.channel.setVolume('music', 0.9);
  }
});


```

5172 of 7237 branches covered (71.47%)

89 of 189 new or added lines in 3 files covered. (47.09%)

14018 of 16045 relevant lines covered (87.37%)

24498.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.75
/src/engine/Util/IFrame.ts
1
import { Logger } from './Log';
2

3
/**
4
 * Checks if excalibur is in a x-origin iframe
5
 */
6
export function isCrossOriginIframe() {
7
  try {
2,145✔
8
    // Try and listen to events on top window frame if within an iframe.
9
    //
10
    // See https://github.com/excaliburjs/Excalibur/issues/1294
11
    //
12
    // Attempt to add an event listener, which triggers a DOMException on
13
    // cross-origin iframes
14
    const noop = () => {
2,145✔
15
      return;
×
16
    };
17
    window.top.addEventListener('blur', noop);
2,145✔
18
    window.top.removeEventListener('blur', noop);
2,145✔
19
  } catch {
20
    return true;
×
21
  }
22
  return false;
2,145✔
23
}
24

25
export function isIframe() {
26
  return window !== window.top;
2,145✔
27
}
28

29
/**
30
 * Grabs the default global object for Excalibur
31
 */
32
export function getDefaultGlobal(): GlobalEventHandlers {
33
  let global: GlobalEventHandlers;
34
  if (isCrossOriginIframe()) {
730!
35
    global = window;
×
NEW
36
    Logger.getInstance().warnOnce('Excalibur might be in a cross-origin iframe, in order to receive keyboard events it must be in focus');
×
37
  } else if (isIframe()) {
730!
38
    global = window;
730✔
39
    Logger.getInstance().warnOnce('Excalibur might be in a iframe, in order to receive keyboard events it must be in focus');
730✔
40
  } else {
41
    global = window.top;
×
42
  }
43

44
  return global;
730✔
45
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc