@indiegems/gem-web-sdk 14.0.3 → 15.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -585,12 +585,15 @@ credit for it until you say so.
585
585
  Five messages arrive that a game cannot ask for — the parent pushes them on its own schedule, and
586
586
  there is no route by which the frame could request one. `createGem` forwards all five:
587
587
 
588
- - **`onPrefs`** — `reducedMotion`, `muted`, `locale`, `showFrameStats` and `recommended`, sent
589
- once after the handshake and again on every change. Also **held**, as `gem.prefs`, because a component mounted
588
+ - **`onPrefs`** — `reducedMotion`, `muted`, `music`, `sounds`, `locale`, `showFrameStats` and
589
+ `recommended`, sent once after the handshake and again on every change. Also **held**, as `gem.prefs`, because a component mounted
590
590
  after the push would otherwise have missed it and cannot ask again. Read it where you need it
591
591
  rather than caching it — the locale changes mid-session. `muted` is the Arcade's mute: the SDK
592
592
  silences your page itself, and your own mute control follows it — see
593
- [sound](#sound-your-own-mute-control-and-the-arcades) below. `showFrameStats` you need not act
593
+ [sound](#sound-your-own-mute-control-and-the-arcades) below. `music` and `sounds` are the
594
+ Arcade's Music and Sound switches, which your game honours itself — see
595
+ [the Arcade's Music and Sound switches](#the-arcades-music-and-sound-switches-onaudiochannels).
596
+ `showFrameStats` you need not act
594
597
  on: the SDK counts your frames itself — see [the Arcade's FPS overlay](#the-arcades-fps-overlay). See the language section for how to combine
595
598
  `locale` with your own detection. `recommended` is the player's Arcade-wide defaults, which your
596
599
  own settings start from — see [game settings](#game-settings).
@@ -1017,11 +1020,15 @@ This is the whole model, and every other page that mentions settings links here:
1017
1020
  - **The Arcade's settings are the player's Arcade-wide defaults for this device.** Your game reads
1018
1021
  them from `gem.prefs.recommended` (and `muted` and `reducedMotion` from `gem.prefs`) and applies
1019
1022
  them again on `onPrefs`. The Arcade never asks your game anything about them.
1023
+ - **The quick row's Music and Sound switches are the Arcade's, for every game**: they reach your
1024
+ game as `gem.prefs.music` and `gem.prefs.sounds`, and `onAudioChannels` is how your game honours
1025
+ them ([the Arcade's Music and Sound switches](#the-arcades-music-and-sound-switches-onaudiochannels)).
1020
1026
  - **Every game draws its own settings button, top-right ([the top of the
1021
1027
  screen](#the-top-of-the-screen)), and its own settings screen.** The Arcade's menu never opens it.
1022
1028
  - **A game should also declare its settings — all of them, up to eight — to the Arcade's menu**,
1023
- with `gem.settings.declare`: the Arcade's Settings screen lists every one, and the first music
1024
- and colour-help toggles fill the Music and Color slots of its fixed quick row. It is a second way
1029
+ with `gem.settings.declare`: the Arcade's Settings screen lists every one, and the first
1030
+ colour-help toggle fills the Color slot of its fixed quick row (the first music toggle fills Music
1031
+ for a game that does not pass `onAudioChannels`). It is a second way
1025
1032
  to reach them, never the only one, and never a second mute or vibration switch ([mirror your
1026
1033
  settings into the Arcade's menu](#mirror-your-settings-into-the-arcades-menu-gemsettings)).
1027
1034
  - **A setting starts from `recommended` until the player changes it in your game, and then the
@@ -1149,12 +1156,15 @@ player's Arcade-wide ones, in two places:
1149
1156
  fit the three kinds below, not a chosen few: a player who opens the menu mid-game finds every one
1150
1157
  of them without hunting for your settings screen.
1151
1158
  - **Its quick row is fixed: Music, Sound, Color and Vibration, in that order**, with the Arcade's
1152
- own labels; Vibration is there on phones and tablets only. Sound (your game's mute) and Vibration
1153
- (the player's Arcade setting) are the Arcade's. Your game fills the other two: **Music** with the
1154
- first `toggle` you declare with `icon: 'music'`, and **Color** with the first `toggle` you
1155
- declare with `icon: 'eye'` — a colour-help, colour-blind or high-contrast switch. A slot your
1156
- game does not fill stays in its place, greyed and inert. Nothing else of yours reaches the quick
1157
- row.
1159
+ own labels; Vibration is there on phones and tablets only. **Music and Sound are the Arcade's
1160
+ switches for your music and your sound effects**, which reach your game as `gem.prefs.music` and
1161
+ `gem.prefs.sounds` — pass `onAudioChannels` so both work for your game
1162
+ ([the Arcade's Music and Sound switches](#the-arcades-music-and-sound-switches-onaudiochannels)).
1163
+ For a game that does not, the Arcade's Music falls back to the first `toggle` you declare with
1164
+ `icon: 'music'`. Vibration is the player's Arcade setting. **Color** is your game's: the first
1165
+ `toggle` you declare with `icon: 'eye'` — a colour-help, colour-blind or high-contrast switch. A
1166
+ slot that nothing fills stays in its place, greyed and inert. Nothing else of yours reaches the
1167
+ quick row.
1158
1168
 
1159
1169
  **It is a mirror, never a replacement: keep your own settings button and screen**, with every
1160
1170
  setting you declare on it too. An embedder that draws no game settings shows none of this, and the
@@ -1220,10 +1230,10 @@ musicToggle.onchange = () => {
1220
1230
  - **Labels are already in the player's language** (`gem.prefs.locale`): the Arcade does not
1221
1231
  translate them, and cuts them at 40 characters. Declare again when the language changes. The
1222
1232
  quick row shows the Arcade's own labels; yours are what its Settings list shows.
1223
- - **Never declare a second mute or vibration switch.** The quick row already has the Arcade's Sound
1224
- (your game's mute) and Vibration (the player's Arcade setting); a "Mute", "Sound off" or
1225
- "Vibration" of yours beside them is a second switch for the same thing, and the player cannot
1226
- tell which one wins. **Your own levels are yours to declare**: a game volume, music volume or
1233
+ - **Never declare a second mute or vibration switch.** The quick row already has the Arcade's Music
1234
+ and Sound (`gem.prefs.music` and `gem.prefs.sounds`, on top of the Arcade's mute) and Vibration
1235
+ (the player's Arcade setting); a "Mute", "All sound off" or "Vibration" of yours beside them is a
1236
+ second switch for the same thing, and the player cannot tell which one wins. **Your own levels are yours to declare**: a game volume, music volume or
1227
1237
  sound-effects volume is your game's level, which starts from the Arcade's `recommended` volume
1228
1238
  like any [game setting](#game-settings) and is set over it — and so are Music and Sound effects
1229
1239
  on/off toggles. Graphics quality and the controls defaults (look sensitivity, invert, touch stick,
@@ -1473,6 +1483,45 @@ calls above, the Arcade's entry and yours stay in step whichever the player uses
1473
1483
  starts, and nothing earlier. The boot order on this page already does that; an `AudioContext` made
1474
1484
  in a module that runs before `createGem` is out of reach.
1475
1485
 
1486
+ ### The Arcade's Music and Sound switches: `onAudioChannels`
1487
+
1488
+ The quick row of the Arcade's Gem menu has a **Music** switch and a **Sound** switch, for every
1489
+ game. They tell your game whether to play its **music** and whether to play its **sound effects**
1490
+ (everything that is not music: effects, ambience, interface sounds, voices). Only your game knows
1491
+ which of its sounds is which, so **your game does the switching**: pass `onAudioChannels` to
1492
+ `createGem`, turn your music off when `music` is false, and turn your effects off when `sounds` is
1493
+ false.
1494
+
1495
+ ```js
1496
+ const gem = createGem({
1497
+ // ...
1498
+ onAudioChannels: ({ music, sounds }) => {
1499
+ musicBus.gain.value = music ? 1 : 0; // false: stop the music, keep the effects
1500
+ effectsBus.gain.value = sounds ? 1 : 0; // false: silence the effects, keep the music
1501
+ },
1502
+ });
1503
+ ```
1504
+
1505
+ - **Passing it is how the Arcade knows.** The SDK tells the Arcade your game honours the two
1506
+ switches only when you pass `onAudioChannels`. Without it the Arcade cannot split your audio: it
1507
+ mutes your whole game (`muted`) when the player turns both off, and its Music switch flips your own
1508
+ music toggle if you [declared one](#mirror-your-settings-into-the-arcades-menu-gemsettings) with
1509
+ `icon: 'music'`. Turning only Sound off then does nothing to your game.
1510
+ - **Called when the Arcade's preferences first arrive, and again whenever either value changes** —
1511
+ not on every `gem.prefs` push. The same two values are always readable as `gem.prefs.music` and
1512
+ `gem.prefs.sounds`.
1513
+ - **Both are `true` until the Arcade says otherwise**, and stay `true` under an Arcade or an
1514
+ embedder that has no such switches, so the default is the audio your game plays today.
1515
+ - **They sit on top of your own settings, and are never saved as them.** Your music plays while
1516
+ `music` is true **and** your own music setting is on; your effects play while `sounds` is true
1517
+ **and** your own sound setting is on. Do not write the Arcade's switch into your saved settings or
1518
+ change your own toggle because it moved: when the player turns it back on, your game's own choice
1519
+ is still there. A music or effects **volume** of yours stays yours too.
1520
+ - **Apply them at once, even while paused.** The Gem menu holds your game paused while it is open,
1521
+ and a player who turns Music off expects the music to stop while the menu is still open.
1522
+ - **`muted` is unchanged.** It is still the master mute the SDK applies for you, above both
1523
+ switches.
1524
+
1476
1525
  ### Start sound right away; a lift releases it where the browser holds it
1477
1526
 
1478
1527
  **Start your music and sound as soon as the game is ready.** Don't wait for a touch you may not
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indiegems/gem-web-sdk",
3
- "version": "14.0.3",
3
+ "version": "15.1.0",
4
4
  "description": "postMessage bridge contract between gem-web and browser-playable games",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -891,6 +891,51 @@
891
891
  "the frame would be counting for nobody."
892
892
  ]
893
893
  },
894
+ "music": {
895
+ "kind": "boolean",
896
+ "optional": true,
897
+ "$comment": [
898
+ "The player wants this game's MUSIC on (true) or off (false) — the",
899
+ "Arcade's Music switch (owner decision 2026-10-22). Absent means true.",
900
+ "",
901
+ "A WISH A COOPERATING GAME HONOURS, NOT A MUTE THE SDK ENFORCES. `muted`",
902
+ "above stays the master mute: the SDK silences the whole frame on it,",
903
+ "whatever the game does. `music` and `sounds` split the game's audio",
904
+ "into its two channels, and only the game knows which of its sounds is",
905
+ "which, so nothing below the game can act on them: a game that opted in",
906
+ "(capability `audioChannels`) stops its music while this is false and",
907
+ "starts it again on true, and leaves its sound effects alone. Both",
908
+ "apply on top of `muted` and on top of the game's own settings — music",
909
+ "plays only while `muted` is false, this is true, and the game's own",
910
+ "music setting is on — and a game never writes this into its own saved",
911
+ "setting.",
912
+ "",
913
+ "OPTIONAL because it is additive: a parent that predates it sends none,",
914
+ "and the frame reads the absence as true — the music the game would",
915
+ "have played anyway. A parent may send it to any frame (an older one",
916
+ "ignores the unknown field), but only a frame that advertised",
917
+ "`audioChannels` is promising to act on it; see that row for what a",
918
+ "parent does for one that did not.",
919
+ "",
920
+ "Sent in every push, like `muted`, and again whenever it changes."
921
+ ]
922
+ },
923
+ "sounds": {
924
+ "kind": "boolean",
925
+ "optional": true,
926
+ "$comment": [
927
+ "The player wants this game's SOUND EFFECTS on (true) or off (false) —",
928
+ "the Arcade's Sound switch (owner decision 2026-10-22): every sound the",
929
+ "game makes that is not its music (effects, ambience, interface clicks,",
930
+ "voices). Absent means true.",
931
+ "",
932
+ "The same contract as `music` above, for the other channel: a wish a",
933
+ "game that advertised `audioChannels` honours by silencing its effects",
934
+ "while this is false and leaving its music alone, on top of `muted`",
935
+ "(still the master mute the SDK enforces) and of the game's own",
936
+ "settings. OPTIONAL because it is additive; absence is true."
937
+ ]
938
+ },
894
939
  "locale": {
895
940
  "kind": "string",
896
941
  "maxLength": 35,
@@ -161,6 +161,14 @@
161
161
  }
162
162
  ]
163
163
  },
164
+ "audioChannels": {
165
+ "implemented": true,
166
+ "gates": [
167
+ {
168
+ "namespace": "gem.prefs"
169
+ }
170
+ ]
171
+ },
164
172
  "walletDisplay": {
165
173
  "implemented": true,
166
174
  "gates": [