@montionugera/joy-brand 0.2.0 → 0.3.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.
Files changed (57) hide show
  1. package/PROVENANCE.md +72 -0
  2. package/assets/sfx/bombBlast.mp3 +0 -0
  3. package/assets/sfx/buyPower.mp3 +0 -0
  4. package/assets/sfx/damageHit.mp3 +0 -0
  5. package/assets/sfx/dawnBreak.mp3 +0 -0
  6. package/assets/sfx/diceRoll.mp3 +0 -0
  7. package/assets/sfx/draw.mp3 +0 -0
  8. package/assets/sfx/emotePop.mp3 +0 -0
  9. package/assets/sfx/emote_anger.mp3 +0 -0
  10. package/assets/sfx/emote_bulb.mp3 +0 -0
  11. package/assets/sfx/emote_clap.mp3 +0 -0
  12. package/assets/sfx/emote_cockroach.mp3 +0 -0
  13. package/assets/sfx/emote_cry.mp3 +0 -0
  14. package/assets/sfx/emote_dots.mp3 +0 -0
  15. package/assets/sfx/emote_exclamation.mp3 +0 -0
  16. package/assets/sfx/emote_fire.mp3 +0 -0
  17. package/assets/sfx/emote_gg.mp3 +0 -0
  18. package/assets/sfx/emote_heart.mp3 +0 -0
  19. package/assets/sfx/emote_laugh.mp3 +0 -0
  20. package/assets/sfx/emote_music.mp3 +0 -0
  21. package/assets/sfx/emote_paper.mp3 +0 -0
  22. package/assets/sfx/emote_question.mp3 +0 -0
  23. package/assets/sfx/emote_rock.mp3 +0 -0
  24. package/assets/sfx/emote_scissors.mp3 +0 -0
  25. package/assets/sfx/emote_shock.mp3 +0 -0
  26. package/assets/sfx/emote_sleepy.mp3 +0 -0
  27. package/assets/sfx/emote_sweat.mp3 +0 -0
  28. package/assets/sfx/emote_thanks.mp3 +0 -0
  29. package/assets/sfx/gameEnd.mp3 +0 -0
  30. package/assets/sfx/join.mp3 +0 -0
  31. package/assets/sfx/kaijuDiceRoll3D.mp3 +0 -0
  32. package/assets/sfx/kaijuLaserBlast.mp3 +0 -0
  33. package/assets/sfx/kaijuRoar.mp3 +0 -0
  34. package/assets/sfx/kaijuShopBuy.mp3 +0 -0
  35. package/assets/sfx/laserHit.mp3 +0 -0
  36. package/assets/sfx/loss.mp3 +0 -0
  37. package/assets/sfx/nightFall.mp3 +0 -0
  38. package/assets/sfx/powerBurst.mp3 +0 -0
  39. package/assets/sfx/reveal.mp3 +0 -0
  40. package/assets/sfx/roar.mp3 +0 -0
  41. package/assets/sfx/shieldBlock.mp3 +0 -0
  42. package/assets/sfx/unlockChime.mp3 +0 -0
  43. package/assets/sfx/voteLock.mp3 +0 -0
  44. package/assets/sfx/warn.mp3 +0 -0
  45. package/assets/sfx/win.mp3 +0 -0
  46. package/assets/sfx/wireCut.mp3 +0 -0
  47. package/assets/sfx/wwDawnChime.mp3 +0 -0
  48. package/assets/sfx/wwExecutionDrum.mp3 +0 -0
  49. package/assets/sfx/wwNightWind.mp3 +0 -0
  50. package/assets/sfx/wwWolfHowl.mp3 +0 -0
  51. package/assets/sfx/yieldAlert.mp3 +0 -0
  52. package/assets/sfx/yourTurn.mp3 +0 -0
  53. package/package.json +7 -4
  54. package/src/sfx/index.d.ts +98 -0
  55. package/src/sfx/index.js +230 -0
  56. package/src/sfx/manifest.d.ts +13 -0
  57. package/src/sfx/manifest.json +410 -0
package/PROVENANCE.md CHANGED
@@ -134,3 +134,75 @@ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
134
134
  OTHER DEALINGS IN THE FONT SOFTWARE.
135
135
 
136
136
  ````
137
+
138
+ ## Sound effects
139
+
140
+ Shared sound effects library containing 51 short game audio clips as mono 96 kbps MP3 files (peak normalized to -12 dBFS) powering the Joymify ecosystem, with TypeScript manifest and Web Audio player (`createSfxPlayer`) exported via `@montionugera/joy-brand/sfx`.
141
+
142
+ ### Source of the audio clips
143
+ The clips originate from three sources:
144
+ 1. **Pixabay** (13 clips): Free library sound effects under the Pixabay Content License, trimmed to the first pulse (≤2 s; `wwDawnChime` keeps bell tail), mono 96 kbps.
145
+ 2. **Mixkit** (11 clips): Free library sound effects under the Mixkit Sound Effects Free License, trimmed and normalized.
146
+ 3. **ElevenLabs** (27 clips: 7 game cues + 20 reaction emote cues): Generated with ElevenLabs Sound Effects (model v3, mono 96 kbps via `lame -m m -b 96`).
147
+
148
+ ### Full Inventory (51 Clips)
149
+
150
+ | Cue | Source | Title / Prompt | Licence |
151
+ |---|---|---|---|
152
+ | `bombBlast` | Pixabay | Animated Cartoon Explosion Impact | Pixabay Content License |
153
+ | `buyPower` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
154
+ | `damageHit` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
155
+ | `dawnBreak` | Pixabay | Chime sound | Pixabay Content License |
156
+ | `diceRoll` | Pixabay | rolling dice 2 | Pixabay Content License |
157
+ | `draw` | Pixabay | flipCard | Pixabay Content License |
158
+ | `emotePop` | Pixabay | Bubble Pop 06 | Pixabay Content License |
159
+ | `emote_anger` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
160
+ | `emote_bulb` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
161
+ | `emote_clap` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
162
+ | `emote_cockroach` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
163
+ | `emote_cry` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
164
+ | `emote_dots` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
165
+ | `emote_exclamation` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
166
+ | `emote_fire` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
167
+ | `emote_gg` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
168
+ | `emote_heart` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
169
+ | `emote_laugh` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
170
+ | `emote_music` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
171
+ | `emote_paper` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
172
+ | `emote_question` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
173
+ | `emote_rock` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
174
+ | `emote_scissors` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
175
+ | `emote_shock` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
176
+ | `emote_sleepy` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
177
+ | `emote_sweat` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
178
+ | `emote_thanks` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
179
+ | `gameEnd` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
180
+ | `join` | Mixkit | Kids cartoon close bells | Mixkit Sound Effects Free License |
181
+ | `kaijuDiceRoll3D` | Pixabay | rolling dice 2 | Pixabay Content License |
182
+ | `kaijuLaserBlast` | Mixkit | Sci-fi battle laser shots | Mixkit Sound Effects Free License |
183
+ | `kaijuRoar` | Mixkit | Giant monster roar | Mixkit Sound Effects Free License |
184
+ | `kaijuShopBuy` | Pixabay | Power Up 01A | Pixabay Content License |
185
+ | `laserHit` | Mixkit | Laser weapon shot | Mixkit Sound Effects Free License |
186
+ | `loss` | Mixkit | Losing drums | Mixkit Sound Effects Free License |
187
+ | `nightFall` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
188
+ | `powerBurst` | Mixkit | Sci fi plasma gun power up | Mixkit Sound Effects Free License |
189
+ | `reveal` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
190
+ | `roar` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
191
+ | `shieldBlock` | Pixabay | Shield Block Shortsword | Pixabay Content License |
192
+ | `unlockChime` | Pixabay | Achievement Unlocked | Pixabay Content License |
193
+ | `voteLock` | Mixkit | Gaming lock | Mixkit Sound Effects Free License |
194
+ | `warn` | Pixabay | Warning Notification | Pixabay Content License |
195
+ | `win` | Mixkit | Video game win | Mixkit Sound Effects Free License |
196
+ | `wireCut` | Pixabay | Scissors Snip 1 | Pixabay Content License |
197
+ | `wwDawnChime` | Pixabay | Single Church Bell 2 | Pixabay Content License |
198
+ | `wwExecutionDrum` | Mixkit | Cinematic mystery trailer drum hit | Mixkit Sound Effects Free License |
199
+ | `wwNightWind` | ElevenLabs | ElevenLabs Sound Effects take #1 | ElevenLabs (commercial terms follow-up) |
200
+ | `wwWolfHowl` | Mixkit | Lone wolf howling | Mixkit Sound Effects Free License |
201
+ | `yieldAlert` | Mixkit | Digital quick tone | Mixkit Sound Effects Free License |
202
+ | `yourTurn` | Pixabay | New Notification - 3 | Pixabay Content License |
203
+
204
+ ### SFX Licences and Terms
205
+ - **Pixabay Content License**: Permitted for commercial and non-commercial use with no attribution required. See https://pixabay.com/service/license-summary/
206
+ - **Mixkit Sound Effects Free License**: Permitted for commercial and non-commercial projects with no attribution required. See https://mixkit.co/license/#sfxFree
207
+ - **ElevenLabs**: Generated with the owner's ElevenLabs account. Owner follow-up: confirmed owner decision (2026-11-09) to include these 27 clips with an open action to review plan commercial terms before external distribution.
208
+ - **Code & Metadata**: The manifest, TypeScript definitions, and player code are owned by Joymify. All rights reserved.
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@montionugera/joy-brand",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "license": "SEE LICENSE IN PROVENANCE.md",
6
6
  "repository": {
@@ -9,8 +9,7 @@
9
9
  "directory": "packages/brand"
10
10
  },
11
11
  "publishConfig": {
12
- "registry": "https://registry.npmjs.org",
13
- "access": "public"
12
+ "registry": "https://registry.npmjs.org"
14
13
  },
15
14
  "files": [
16
15
  "src",
@@ -23,13 +22,17 @@
23
22
  "types": "./src/index.d.ts",
24
23
  "default": "./src/index.js"
25
24
  },
25
+ "./sfx": {
26
+ "types": "./src/sfx/index.d.ts",
27
+ "default": "./src/sfx/index.js"
28
+ },
26
29
  "./assets/*": "./assets/*"
27
30
  },
28
31
  "devDependencies": {
29
32
  "opentype.js": "^2.0.0",
30
33
  "@fontsource/cormorant-garamond": "^5.3.0",
31
34
  "@resvg/resvg-js": "^2.6.2",
32
- "@montionugera/joy-tokens": "0.2.1"
35
+ "@montionugera/joy-tokens": "0.2.2"
33
36
  },
34
37
  "scripts": {
35
38
  "build": "node build.js",
@@ -0,0 +1,98 @@
1
+ export interface SfxManifestEntry {
2
+ name: string;
3
+ file: string;
4
+ durationMs: number;
5
+ source: 'Pixabay' | 'Mixkit' | 'ElevenLabs';
6
+ licence: string;
7
+ title: string;
8
+ }
9
+
10
+ export declare const MANIFEST: readonly SfxManifestEntry[];
11
+
12
+ export declare const SFX_NAMES: readonly [
13
+ 'bombBlast',
14
+ 'buyPower',
15
+ 'damageHit',
16
+ 'dawnBreak',
17
+ 'diceRoll',
18
+ 'draw',
19
+ 'emotePop',
20
+ 'emote_anger',
21
+ 'emote_bulb',
22
+ 'emote_clap',
23
+ 'emote_cockroach',
24
+ 'emote_cry',
25
+ 'emote_dots',
26
+ 'emote_exclamation',
27
+ 'emote_fire',
28
+ 'emote_gg',
29
+ 'emote_heart',
30
+ 'emote_laugh',
31
+ 'emote_music',
32
+ 'emote_paper',
33
+ 'emote_question',
34
+ 'emote_rock',
35
+ 'emote_scissors',
36
+ 'emote_shock',
37
+ 'emote_sleepy',
38
+ 'emote_sweat',
39
+ 'emote_thanks',
40
+ 'gameEnd',
41
+ 'join',
42
+ 'kaijuDiceRoll3D',
43
+ 'kaijuLaserBlast',
44
+ 'kaijuRoar',
45
+ 'kaijuShopBuy',
46
+ 'laserHit',
47
+ 'loss',
48
+ 'nightFall',
49
+ 'powerBurst',
50
+ 'reveal',
51
+ 'roar',
52
+ 'shieldBlock',
53
+ 'unlockChime',
54
+ 'voteLock',
55
+ 'warn',
56
+ 'win',
57
+ 'wireCut',
58
+ 'wwDawnChime',
59
+ 'wwExecutionDrum',
60
+ 'wwNightWind',
61
+ 'wwWolfHowl',
62
+ 'yieldAlert',
63
+ 'yourTurn'
64
+ ];
65
+
66
+ export type SfxName = (typeof SFX_NAMES)[number];
67
+
68
+ export interface SfxUrlOptions {
69
+ baseUrl?: string;
70
+ }
71
+
72
+ export declare const DEFAULT_SFX_BASE_URL = "/audio/sfx/";
73
+
74
+ export declare function sfxUrl(name: SfxName, baseUrlOrOptions?: string | SfxUrlOptions): string;
75
+
76
+ export interface SfxPlayerOptions {
77
+ baseUrl?: string;
78
+ enabled?: boolean;
79
+ reducedMotion?: boolean;
80
+ ctxFactory?: () => AudioContext | null;
81
+ }
82
+
83
+ export interface PlayOptions {
84
+ gain?: number;
85
+ }
86
+
87
+ export interface SfxPlayer {
88
+ play(name: SfxName, options?: PlayOptions): void;
89
+ setEnabled(enabled: boolean): void;
90
+ unlock(): void;
91
+ readonly enabled: boolean;
92
+ readonly reducedMotion: boolean;
93
+ }
94
+
95
+ export declare function createSfxPlayer(
96
+ options?: SfxPlayerOptions,
97
+ ctxFactoryParam?: () => AudioContext | null
98
+ ): SfxPlayer;
@@ -0,0 +1,230 @@
1
+ import manifestData from './manifest.json' with { type: 'json' };
2
+
3
+ export const MANIFEST = Object.freeze(manifestData);
4
+
5
+ export const SFX_NAMES = Object.freeze([
6
+ 'bombBlast',
7
+ 'buyPower',
8
+ 'damageHit',
9
+ 'dawnBreak',
10
+ 'diceRoll',
11
+ 'draw',
12
+ 'emotePop',
13
+ 'emote_anger',
14
+ 'emote_bulb',
15
+ 'emote_clap',
16
+ 'emote_cockroach',
17
+ 'emote_cry',
18
+ 'emote_dots',
19
+ 'emote_exclamation',
20
+ 'emote_fire',
21
+ 'emote_gg',
22
+ 'emote_heart',
23
+ 'emote_laugh',
24
+ 'emote_music',
25
+ 'emote_paper',
26
+ 'emote_question',
27
+ 'emote_rock',
28
+ 'emote_scissors',
29
+ 'emote_shock',
30
+ 'emote_sleepy',
31
+ 'emote_sweat',
32
+ 'emote_thanks',
33
+ 'gameEnd',
34
+ 'join',
35
+ 'kaijuDiceRoll3D',
36
+ 'kaijuLaserBlast',
37
+ 'kaijuRoar',
38
+ 'kaijuShopBuy',
39
+ 'laserHit',
40
+ 'loss',
41
+ 'nightFall',
42
+ 'powerBurst',
43
+ 'reveal',
44
+ 'roar',
45
+ 'shieldBlock',
46
+ 'unlockChime',
47
+ 'voteLock',
48
+ 'warn',
49
+ 'win',
50
+ 'wireCut',
51
+ 'wwDawnChime',
52
+ 'wwExecutionDrum',
53
+ 'wwNightWind',
54
+ 'wwWolfHowl',
55
+ 'yieldAlert',
56
+ 'yourTurn',
57
+ ]);
58
+
59
+ export const DEFAULT_SFX_BASE_URL = '/audio/sfx/';
60
+
61
+ /**
62
+ * Resolves the public audio asset URL for a given sound effect name.
63
+ * Default resolves the packaged asset via new URL('../../assets/sfx/<name>.mp3', import.meta.url).
64
+ * Throws RangeError for any cue name not in SFX_NAMES.
65
+ */
66
+ export function sfxUrl(name, baseUrlOrOptions) {
67
+ if (!SFX_NAMES.includes(name)) {
68
+ throw new RangeError(`Unknown SFX cue: ${String(name)}`);
69
+ }
70
+ let base;
71
+ if (typeof baseUrlOrOptions === 'string') {
72
+ base = baseUrlOrOptions;
73
+ } else if (baseUrlOrOptions && typeof baseUrlOrOptions.baseUrl === 'string') {
74
+ base = baseUrlOrOptions.baseUrl;
75
+ }
76
+ if (base !== undefined) {
77
+ const cleanBase = base.endsWith('/') ? base : `${base}/`;
78
+ return `${cleanBase}${name}.mp3`;
79
+ }
80
+ return new URL(`../../assets/sfx/${name}.mp3`, import.meta.url).href;
81
+ }
82
+
83
+ function defaultAudioContextFactory() {
84
+ try {
85
+ if (typeof window === 'undefined') return null;
86
+ const w = window;
87
+ const Ctor = w.AudioContext ?? w.webkitAudioContext;
88
+ if (!Ctor) return null;
89
+ return new Ctor();
90
+ } catch {
91
+ return null;
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Creates a Web Audio player for Joymify sound effects with a -12 dB hard limiter.
97
+ * Never throws when Web Audio is unavailable (e.g. in Node, SSR, or unsupported browsers).
98
+ * Honours enabled and reducedMotion flags.
99
+ */
100
+ export function createSfxPlayer(options, ctxFactoryParam) {
101
+ let isEnabled = options?.enabled ?? true;
102
+ const isReducedMotion = options?.reducedMotion ?? false;
103
+ const baseUrl = options?.baseUrl;
104
+ const factory = ctxFactoryParam ?? options?.ctxFactory ?? defaultAudioContextFactory;
105
+
106
+ let audioCtx = null;
107
+ let limiterNode = null;
108
+ const audioBuffers = new Map();
109
+ const loadingPromises = new Map();
110
+
111
+ function setupGraph(ctx) {
112
+ try {
113
+ // Hard limiter at -12 dB ceiling
114
+ const limiter = ctx.createDynamicsCompressor();
115
+ limiter.threshold.value = -12;
116
+ limiter.knee.value = 0;
117
+ limiter.ratio.value = 20;
118
+ limiter.attack.value = 0.002;
119
+ limiter.release.value = 0.15;
120
+ limiter.connect(ctx.destination);
121
+ limiterNode = limiter;
122
+ } catch {
123
+ limiterNode = null;
124
+ }
125
+ }
126
+
127
+ function playBuffer(ctx, dest, buf, gainVal) {
128
+ try {
129
+ const src = ctx.createBufferSource();
130
+ src.buffer = buf;
131
+ const gainNode = ctx.createGain();
132
+ gainNode.gain.value = gainVal;
133
+ src.connect(gainNode);
134
+ gainNode.connect(dest);
135
+ src.onended = () => {
136
+ try {
137
+ src.disconnect();
138
+ gainNode.disconnect();
139
+ } catch {}
140
+ };
141
+ src.start(0);
142
+ } catch {
143
+ // Audio playback failure must never throw or break calling app
144
+ }
145
+ }
146
+
147
+ async function loadBuffer(ctx, name, url) {
148
+ const existing = loadingPromises.get(name);
149
+ if (existing) return existing;
150
+
151
+ const promise = (async () => {
152
+ try {
153
+ if (typeof fetch !== 'function' || typeof ctx.decodeAudioData !== 'function') {
154
+ return null;
155
+ }
156
+ const res = await fetch(url);
157
+ if (!res.ok) return null;
158
+ const arrayBuf = await res.arrayBuffer();
159
+ const decoded = await ctx.decodeAudioData(arrayBuf);
160
+ audioBuffers.set(name, decoded);
161
+ return decoded;
162
+ } catch {
163
+ return null;
164
+ } finally {
165
+ loadingPromises.delete(name);
166
+ }
167
+ })();
168
+
169
+ loadingPromises.set(name, promise);
170
+ return promise;
171
+ }
172
+
173
+ const player = {
174
+ get enabled() {
175
+ return isEnabled;
176
+ },
177
+ get reducedMotion() {
178
+ return isReducedMotion;
179
+ },
180
+ setEnabled(enabled) {
181
+ isEnabled = Boolean(enabled);
182
+ },
183
+ unlock() {
184
+ try {
185
+ if (!audioCtx) {
186
+ audioCtx = factory();
187
+ if (audioCtx) {
188
+ setupGraph(audioCtx);
189
+ }
190
+ }
191
+ if (audioCtx && audioCtx.state === 'suspended' && typeof audioCtx.resume === 'function') {
192
+ audioCtx.resume().catch(() => {});
193
+ }
194
+ } catch {
195
+ // never throws
196
+ }
197
+ },
198
+ play(name, playOptions) {
199
+ if (!isEnabled || isReducedMotion) {
200
+ return;
201
+ }
202
+ try {
203
+ if (!audioCtx) {
204
+ player.unlock();
205
+ }
206
+ if (!audioCtx || !limiterNode) {
207
+ return;
208
+ }
209
+ const gainValue = playOptions?.gain ?? 1;
210
+ const url = sfxUrl(name, baseUrl);
211
+
212
+ const cached = audioBuffers.get(name);
213
+ if (cached) {
214
+ playBuffer(audioCtx, limiterNode, cached, gainValue);
215
+ return;
216
+ }
217
+
218
+ void loadBuffer(audioCtx, name, url).then((buf) => {
219
+ if (buf && isEnabled && !isReducedMotion && audioCtx && limiterNode) {
220
+ playBuffer(audioCtx, limiterNode, buf, gainValue);
221
+ }
222
+ }).catch(() => {});
223
+ } catch {
224
+ // never throws
225
+ }
226
+ },
227
+ };
228
+
229
+ return player;
230
+ }
@@ -0,0 +1,13 @@
1
+ // Generated by scripts/build-manifest.mjs. Do not edit by hand.
2
+
3
+ export interface SfxManifestEntry {
4
+ name: string;
5
+ file: string;
6
+ durationMs: number;
7
+ source: 'Pixabay' | 'Mixkit' | 'ElevenLabs';
8
+ licence: string;
9
+ title: string;
10
+ }
11
+
12
+ declare const manifest: SfxManifestEntry[];
13
+ export default manifest;
@@ -0,0 +1,410 @@
1
+ [
2
+ {
3
+ "name": "bombBlast",
4
+ "file": "bombBlast.mp3",
5
+ "durationMs": 1500,
6
+ "source": "Pixabay",
7
+ "licence": "Pixabay Content License",
8
+ "title": "Animated Cartoon Explosion Impact"
9
+ },
10
+ {
11
+ "name": "buyPower",
12
+ "file": "buyPower.mp3",
13
+ "durationMs": 963,
14
+ "source": "ElevenLabs",
15
+ "licence": "ElevenLabs (commercial terms follow-up)",
16
+ "title": "ElevenLabs Sound Effects take #1"
17
+ },
18
+ {
19
+ "name": "damageHit",
20
+ "file": "damageHit.mp3",
21
+ "durationMs": 546,
22
+ "source": "ElevenLabs",
23
+ "licence": "ElevenLabs (commercial terms follow-up)",
24
+ "title": "ElevenLabs Sound Effects take #1"
25
+ },
26
+ {
27
+ "name": "dawnBreak",
28
+ "file": "dawnBreak.mp3",
29
+ "durationMs": 1632,
30
+ "source": "Pixabay",
31
+ "licence": "Pixabay Content License",
32
+ "title": "Chime sound"
33
+ },
34
+ {
35
+ "name": "diceRoll",
36
+ "file": "diceRoll.mp3",
37
+ "durationMs": 1030,
38
+ "source": "Pixabay",
39
+ "licence": "Pixabay Content License",
40
+ "title": "rolling dice 2"
41
+ },
42
+ {
43
+ "name": "draw",
44
+ "file": "draw.mp3",
45
+ "durationMs": 540,
46
+ "source": "Pixabay",
47
+ "licence": "Pixabay Content License",
48
+ "title": "flipCard"
49
+ },
50
+ {
51
+ "name": "emote_anger",
52
+ "file": "emote_anger.mp3",
53
+ "durationMs": 1200,
54
+ "source": "ElevenLabs",
55
+ "licence": "ElevenLabs (commercial terms follow-up)",
56
+ "title": "ElevenLabs Sound Effects take #1"
57
+ },
58
+ {
59
+ "name": "emote_bulb",
60
+ "file": "emote_bulb.mp3",
61
+ "durationMs": 1030,
62
+ "source": "ElevenLabs",
63
+ "licence": "ElevenLabs (commercial terms follow-up)",
64
+ "title": "ElevenLabs Sound Effects take #1"
65
+ },
66
+ {
67
+ "name": "emote_clap",
68
+ "file": "emote_clap.mp3",
69
+ "durationMs": 1200,
70
+ "source": "ElevenLabs",
71
+ "licence": "ElevenLabs (commercial terms follow-up)",
72
+ "title": "ElevenLabs Sound Effects take #1"
73
+ },
74
+ {
75
+ "name": "emote_cockroach",
76
+ "file": "emote_cockroach.mp3",
77
+ "durationMs": 571,
78
+ "source": "ElevenLabs",
79
+ "licence": "ElevenLabs (commercial terms follow-up)",
80
+ "title": "ElevenLabs Sound Effects take #1"
81
+ },
82
+ {
83
+ "name": "emote_cry",
84
+ "file": "emote_cry.mp3",
85
+ "durationMs": 1200,
86
+ "source": "ElevenLabs",
87
+ "licence": "ElevenLabs (commercial terms follow-up)",
88
+ "title": "ElevenLabs Sound Effects take #1"
89
+ },
90
+ {
91
+ "name": "emote_dots",
92
+ "file": "emote_dots.mp3",
93
+ "durationMs": 1200,
94
+ "source": "ElevenLabs",
95
+ "licence": "ElevenLabs (commercial terms follow-up)",
96
+ "title": "ElevenLabs Sound Effects take #1"
97
+ },
98
+ {
99
+ "name": "emote_exclamation",
100
+ "file": "emote_exclamation.mp3",
101
+ "durationMs": 460,
102
+ "source": "ElevenLabs",
103
+ "licence": "ElevenLabs (commercial terms follow-up)",
104
+ "title": "ElevenLabs Sound Effects take #1"
105
+ },
106
+ {
107
+ "name": "emote_fire",
108
+ "file": "emote_fire.mp3",
109
+ "durationMs": 1200,
110
+ "source": "ElevenLabs",
111
+ "licence": "ElevenLabs (commercial terms follow-up)",
112
+ "title": "ElevenLabs Sound Effects take #1"
113
+ },
114
+ {
115
+ "name": "emote_gg",
116
+ "file": "emote_gg.mp3",
117
+ "durationMs": 750,
118
+ "source": "ElevenLabs",
119
+ "licence": "ElevenLabs (commercial terms follow-up)",
120
+ "title": "ElevenLabs Sound Effects take #1"
121
+ },
122
+ {
123
+ "name": "emote_heart",
124
+ "file": "emote_heart.mp3",
125
+ "durationMs": 300,
126
+ "source": "ElevenLabs",
127
+ "licence": "ElevenLabs (commercial terms follow-up)",
128
+ "title": "ElevenLabs Sound Effects take #1"
129
+ },
130
+ {
131
+ "name": "emote_laugh",
132
+ "file": "emote_laugh.mp3",
133
+ "durationMs": 320,
134
+ "source": "ElevenLabs",
135
+ "licence": "ElevenLabs (commercial terms follow-up)",
136
+ "title": "ElevenLabs Sound Effects take #1"
137
+ },
138
+ {
139
+ "name": "emote_music",
140
+ "file": "emote_music.mp3",
141
+ "durationMs": 1200,
142
+ "source": "ElevenLabs",
143
+ "licence": "ElevenLabs (commercial terms follow-up)",
144
+ "title": "ElevenLabs Sound Effects take #1"
145
+ },
146
+ {
147
+ "name": "emote_paper",
148
+ "file": "emote_paper.mp3",
149
+ "durationMs": 300,
150
+ "source": "ElevenLabs",
151
+ "licence": "ElevenLabs (commercial terms follow-up)",
152
+ "title": "ElevenLabs Sound Effects take #1"
153
+ },
154
+ {
155
+ "name": "emote_question",
156
+ "file": "emote_question.mp3",
157
+ "durationMs": 490,
158
+ "source": "ElevenLabs",
159
+ "licence": "ElevenLabs (commercial terms follow-up)",
160
+ "title": "ElevenLabs Sound Effects take #1"
161
+ },
162
+ {
163
+ "name": "emote_rock",
164
+ "file": "emote_rock.mp3",
165
+ "durationMs": 450,
166
+ "source": "ElevenLabs",
167
+ "licence": "ElevenLabs (commercial terms follow-up)",
168
+ "title": "ElevenLabs Sound Effects take #1"
169
+ },
170
+ {
171
+ "name": "emote_scissors",
172
+ "file": "emote_scissors.mp3",
173
+ "durationMs": 340,
174
+ "source": "ElevenLabs",
175
+ "licence": "ElevenLabs (commercial terms follow-up)",
176
+ "title": "ElevenLabs Sound Effects take #1"
177
+ },
178
+ {
179
+ "name": "emote_shock",
180
+ "file": "emote_shock.mp3",
181
+ "durationMs": 1072,
182
+ "source": "ElevenLabs",
183
+ "licence": "ElevenLabs (commercial terms follow-up)",
184
+ "title": "ElevenLabs Sound Effects take #1"
185
+ },
186
+ {
187
+ "name": "emote_sleepy",
188
+ "file": "emote_sleepy.mp3",
189
+ "durationMs": 360,
190
+ "source": "ElevenLabs",
191
+ "licence": "ElevenLabs (commercial terms follow-up)",
192
+ "title": "ElevenLabs Sound Effects take #1"
193
+ },
194
+ {
195
+ "name": "emote_sweat",
196
+ "file": "emote_sweat.mp3",
197
+ "durationMs": 430,
198
+ "source": "ElevenLabs",
199
+ "licence": "ElevenLabs (commercial terms follow-up)",
200
+ "title": "ElevenLabs Sound Effects take #1"
201
+ },
202
+ {
203
+ "name": "emote_thanks",
204
+ "file": "emote_thanks.mp3",
205
+ "durationMs": 1140,
206
+ "source": "ElevenLabs",
207
+ "licence": "ElevenLabs (commercial terms follow-up)",
208
+ "title": "ElevenLabs Sound Effects take #1"
209
+ },
210
+ {
211
+ "name": "emotePop",
212
+ "file": "emotePop.mp3",
213
+ "durationMs": 300,
214
+ "source": "Pixabay",
215
+ "licence": "Pixabay Content License",
216
+ "title": "Bubble Pop 06"
217
+ },
218
+ {
219
+ "name": "gameEnd",
220
+ "file": "gameEnd.mp3",
221
+ "durationMs": 1046,
222
+ "source": "ElevenLabs",
223
+ "licence": "ElevenLabs (commercial terms follow-up)",
224
+ "title": "ElevenLabs Sound Effects take #1"
225
+ },
226
+ {
227
+ "name": "join",
228
+ "file": "join.mp3",
229
+ "durationMs": 811,
230
+ "source": "Mixkit",
231
+ "licence": "Mixkit Sound Effects Free License",
232
+ "title": "Kids cartoon close bells"
233
+ },
234
+ {
235
+ "name": "kaijuDiceRoll3D",
236
+ "file": "kaijuDiceRoll3D.mp3",
237
+ "durationMs": 1030,
238
+ "source": "Pixabay",
239
+ "licence": "Pixabay Content License",
240
+ "title": "rolling dice 2"
241
+ },
242
+ {
243
+ "name": "kaijuLaserBlast",
244
+ "file": "kaijuLaserBlast.mp3",
245
+ "durationMs": 1800,
246
+ "source": "Mixkit",
247
+ "licence": "Mixkit Sound Effects Free License",
248
+ "title": "Sci-fi battle laser shots"
249
+ },
250
+ {
251
+ "name": "kaijuRoar",
252
+ "file": "kaijuRoar.mp3",
253
+ "durationMs": 1000,
254
+ "source": "Mixkit",
255
+ "licence": "Mixkit Sound Effects Free License",
256
+ "title": "Giant monster roar"
257
+ },
258
+ {
259
+ "name": "kaijuShopBuy",
260
+ "file": "kaijuShopBuy.mp3",
261
+ "durationMs": 490,
262
+ "source": "Pixabay",
263
+ "licence": "Pixabay Content License",
264
+ "title": "Power Up 01A"
265
+ },
266
+ {
267
+ "name": "laserHit",
268
+ "file": "laserHit.mp3",
269
+ "durationMs": 1690,
270
+ "source": "Mixkit",
271
+ "licence": "Mixkit Sound Effects Free License",
272
+ "title": "Laser weapon shot"
273
+ },
274
+ {
275
+ "name": "loss",
276
+ "file": "loss.mp3",
277
+ "durationMs": 1200,
278
+ "source": "Mixkit",
279
+ "licence": "Mixkit Sound Effects Free License",
280
+ "title": "Losing drums"
281
+ },
282
+ {
283
+ "name": "nightFall",
284
+ "file": "nightFall.mp3",
285
+ "durationMs": 1922,
286
+ "source": "ElevenLabs",
287
+ "licence": "ElevenLabs (commercial terms follow-up)",
288
+ "title": "ElevenLabs Sound Effects take #1"
289
+ },
290
+ {
291
+ "name": "powerBurst",
292
+ "file": "powerBurst.mp3",
293
+ "durationMs": 1780,
294
+ "source": "Mixkit",
295
+ "licence": "Mixkit Sound Effects Free License",
296
+ "title": "Sci fi plasma gun power up"
297
+ },
298
+ {
299
+ "name": "reveal",
300
+ "file": "reveal.mp3",
301
+ "durationMs": 765,
302
+ "source": "ElevenLabs",
303
+ "licence": "ElevenLabs (commercial terms follow-up)",
304
+ "title": "ElevenLabs Sound Effects take #1"
305
+ },
306
+ {
307
+ "name": "roar",
308
+ "file": "roar.mp3",
309
+ "durationMs": 1950,
310
+ "source": "ElevenLabs",
311
+ "licence": "ElevenLabs (commercial terms follow-up)",
312
+ "title": "ElevenLabs Sound Effects take #1"
313
+ },
314
+ {
315
+ "name": "shieldBlock",
316
+ "file": "shieldBlock.mp3",
317
+ "durationMs": 400,
318
+ "source": "Pixabay",
319
+ "licence": "Pixabay Content License",
320
+ "title": "Shield Block Shortsword"
321
+ },
322
+ {
323
+ "name": "unlockChime",
324
+ "file": "unlockChime.mp3",
325
+ "durationMs": 430,
326
+ "source": "Pixabay",
327
+ "licence": "Pixabay Content License",
328
+ "title": "Achievement Unlocked"
329
+ },
330
+ {
331
+ "name": "voteLock",
332
+ "file": "voteLock.mp3",
333
+ "durationMs": 300,
334
+ "source": "Mixkit",
335
+ "licence": "Mixkit Sound Effects Free License",
336
+ "title": "Gaming lock"
337
+ },
338
+ {
339
+ "name": "warn",
340
+ "file": "warn.mp3",
341
+ "durationMs": 530,
342
+ "source": "Pixabay",
343
+ "licence": "Pixabay Content License",
344
+ "title": "Warning Notification"
345
+ },
346
+ {
347
+ "name": "win",
348
+ "file": "win.mp3",
349
+ "durationMs": 1850,
350
+ "source": "Mixkit",
351
+ "licence": "Mixkit Sound Effects Free License",
352
+ "title": "Video game win"
353
+ },
354
+ {
355
+ "name": "wireCut",
356
+ "file": "wireCut.mp3",
357
+ "durationMs": 341,
358
+ "source": "Pixabay",
359
+ "licence": "Pixabay Content License",
360
+ "title": "Scissors Snip 1"
361
+ },
362
+ {
363
+ "name": "wwDawnChime",
364
+ "file": "wwDawnChime.mp3",
365
+ "durationMs": 2948,
366
+ "source": "Pixabay",
367
+ "licence": "Pixabay Content License",
368
+ "title": "Single Church Bell 2"
369
+ },
370
+ {
371
+ "name": "wwExecutionDrum",
372
+ "file": "wwExecutionDrum.mp3",
373
+ "durationMs": 2000,
374
+ "source": "Mixkit",
375
+ "licence": "Mixkit Sound Effects Free License",
376
+ "title": "Cinematic mystery trailer drum hit"
377
+ },
378
+ {
379
+ "name": "wwNightWind",
380
+ "file": "wwNightWind.mp3",
381
+ "durationMs": 1950,
382
+ "source": "ElevenLabs",
383
+ "licence": "ElevenLabs (commercial terms follow-up)",
384
+ "title": "ElevenLabs Sound Effects take #1"
385
+ },
386
+ {
387
+ "name": "wwWolfHowl",
388
+ "file": "wwWolfHowl.mp3",
389
+ "durationMs": 2000,
390
+ "source": "Mixkit",
391
+ "licence": "Mixkit Sound Effects Free License",
392
+ "title": "Lone wolf howling"
393
+ },
394
+ {
395
+ "name": "yieldAlert",
396
+ "file": "yieldAlert.mp3",
397
+ "durationMs": 420,
398
+ "source": "Mixkit",
399
+ "licence": "Mixkit Sound Effects Free License",
400
+ "title": "Digital quick tone"
401
+ },
402
+ {
403
+ "name": "yourTurn",
404
+ "file": "yourTurn.mp3",
405
+ "durationMs": 460,
406
+ "source": "Pixabay",
407
+ "licence": "Pixabay Content License",
408
+ "title": "New Notification - 3"
409
+ }
410
+ ]