openaudio-suite 2.4.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.
- package/CHANGELOG.md +196 -0
- package/CONTRIBUTING.md +349 -0
- package/LICENSE +674 -0
- package/OpenAudio.js +386 -0
- package/OpenAudio_r.js +863 -0
- package/OpenAudio_s.js +445 -0
- package/README.md +433 -0
- package/docs/COMPARISON.md +449 -0
- package/docs/OPENAUDIO-v1.1.0.md +641 -0
- package/docs/OPENAUDIO_R.md +571 -0
- package/docs/OPENAUDIO_S.md +760 -0
- package/package.json +79 -0
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
# OpenAudio Suite — Feature Comparison
|
|
2
|
+
|
|
3
|
+
**Quick Answer:** Use **OpenAudio_r.js** for ambient/randomized audio; **OpenAudio_s.js** for sequential; **OpenAudio.js** for UI sounds and one-shots.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Side-by-Side Comparison
|
|
8
|
+
|
|
9
|
+
| Feature | OpenAudio_r.js | OpenAudio_s.js | OpenAudio.js | Notes |
|
|
10
|
+
|---------|---|---|---|---|
|
|
11
|
+
| **Clips** | Multiple | Multiple | Single | r: 3–1000+; s: ordered list; single: one file |
|
|
12
|
+
| **Scheduling** | Random with delays | Sequential | On-demand | r: 3–5 sec between; s: manual or auto-advance; single: plays when called |
|
|
13
|
+
| **Shuffle Bag** | ✅ Yes | ❌ No | ❌ N/A | r: each clip plays once per cycle |
|
|
14
|
+
| **Inter-Clip Delays** | ✅ Configurable | ❌ No | ❌ N/A | r: minTime/maxTime (milliseconds) |
|
|
15
|
+
| **Volume Control** | ✅ `setVolume()` | ❌ No | ✅ Constructor | r: runtime; single: constructor only |
|
|
16
|
+
| **Navigation** | ❌ No | ✅ goto/gotoLabel | ❌ No | s: jump to any clip in sequence |
|
|
17
|
+
| **Callbacks** | `onPlay`, `onEnd`, `onCycleReset` | `onPlay`, `onEnd`, `onComplete` | `onPlay`, `onEnd`, `onHidden`, `onVisible` | r: cycle events; s: completion; single: tab focus |
|
|
18
|
+
| **Background Tab Detection** | ✅ Yes (recalculates) | ❌ No | ✅ Yes (pause/resume) | r: timing resilient; single: smart pause |
|
|
19
|
+
| **Add Clips at Runtime** | ✅ `addClip()` | ❌ No | ❌ No | r only |
|
|
20
|
+
| **Stop/Pause/Resume** | ✅ Yes/No/No | ✅ Yes/Yes/Yes | ✅ Yes/No/No (pauseOnHidden) | All can stop; s & single have pause |
|
|
21
|
+
| **Format Support Check** | ✅ `canPlay()` | ✅ `canPlay()` | ✅ `canPlay()` | Static method in all |
|
|
22
|
+
| **SPA Cleanup** | ✅ `destroy()` | ✅ `destroy()` | ✅ `destroy()` | Essential for React/Vue/Svelte |
|
|
23
|
+
| **File Size (Minified)** | ~9 KB | ~5 KB | ~5 KB | Uncompressed |
|
|
24
|
+
| **Gzipped** | ~3 KB | ~2 KB | ~2 KB | Network transfer size |
|
|
25
|
+
| **Dependencies** | None | None | None | Pure HTML5 Audio API |
|
|
26
|
+
| **Browser Support** | Chrome 70+, FF 65+, Safari 12+, Edge 79+, iOS 12+ | Same | Same | All modern browsers |
|
|
27
|
+
| **Mobile** | ✅ iOS & Android | ✅ iOS & Android | ✅ iOS & Android | All mobile-friendly |
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Use Case Decision Tree
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
START
|
|
35
|
+
|
|
36
|
+
Q1: Do you need to play multiple audio clips?
|
|
37
|
+
├─ YES: Continue to Q2
|
|
38
|
+
└─ NO: Go to "Use OpenAudio.js" (below)
|
|
39
|
+
|
|
40
|
+
Q2: Do you want clips to play in a randomized schedule?
|
|
41
|
+
├─ YES (random): Go to "Use OpenAudio_r.js" (below)
|
|
42
|
+
├─ NO (ordered): Continue to Q3
|
|
43
|
+
|
|
44
|
+
Q3: Do you need manual or auto-advance control?
|
|
45
|
+
├─ YES: Go to "Use OpenAudio_s.js" (below)
|
|
46
|
+
└─ NO: Go to "Use OpenAudio_r.js" (below)
|
|
47
|
+
|
|
48
|
+
================
|
|
49
|
+
|
|
50
|
+
USE OpenAudio_r.js IF:
|
|
51
|
+
✓ Ambient soundscapes (forest, rain, wind)
|
|
52
|
+
✓ Game background music cycling
|
|
53
|
+
✓ Random sound effects (footsteps, birds, etc.)
|
|
54
|
+
✓ Binaural beats or meditation audio
|
|
55
|
+
✓ Randomized event sounds (explosions, impacts)
|
|
56
|
+
✓ Audio with long inter-clip delays (5–30 seconds)
|
|
57
|
+
|
|
58
|
+
DO NOT use OpenAudio_r.js IF:
|
|
59
|
+
✗ You only have one audio file
|
|
60
|
+
✗ You want precise timing (< 1 second differences)
|
|
61
|
+
✗ You need crossfading or audio effects (DSP)
|
|
62
|
+
✗ You need clips in a specific order
|
|
63
|
+
|
|
64
|
+
================
|
|
65
|
+
|
|
66
|
+
USE OpenAudio_s.js IF:
|
|
67
|
+
✓ Tutorials or guided tours (narration chapters)
|
|
68
|
+
✓ Interactive stories (click to advance)
|
|
69
|
+
✓ Audiobook or podcast player
|
|
70
|
+
✓ Narrated presentations (slide audio)
|
|
71
|
+
✓ Language lessons (sequential sentences)
|
|
72
|
+
✓ You need clips in a fixed order
|
|
73
|
+
✓ You want manual or auto-advance control
|
|
74
|
+
|
|
75
|
+
DO NOT use OpenAudio_s.js IF:
|
|
76
|
+
✗ You only have one audio file
|
|
77
|
+
✗ You need randomized playback
|
|
78
|
+
✗ You need complex inter-clip scheduling
|
|
79
|
+
|
|
80
|
+
================
|
|
81
|
+
|
|
82
|
+
USE OpenAudio.js IF:
|
|
83
|
+
✓ UI sounds (button clicks, hovers)
|
|
84
|
+
✓ Notifications and alerts
|
|
85
|
+
✓ Single chimes or bells
|
|
86
|
+
✓ Victory/failure fanfares
|
|
87
|
+
✓ Narration or dialogue tracks
|
|
88
|
+
✓ Sound effects on specific actions
|
|
89
|
+
✓ Game audio that pauses when tab loses focus
|
|
90
|
+
✓ One sound at a time
|
|
91
|
+
|
|
92
|
+
DO NOT use OpenAudio.js IF:
|
|
93
|
+
✗ You need multiple clips playing in rotation
|
|
94
|
+
✗ You need inter-clip scheduling
|
|
95
|
+
✗ You need long ambient soundscapes
|
|
96
|
+
|
|
97
|
+
================
|
|
98
|
+
|
|
99
|
+
GRADUATE TO WEB AUDIO API IF:
|
|
100
|
+
• You need crossfading between clips
|
|
101
|
+
• Scheduling must be frame-perfect (hardware clock)
|
|
102
|
+
• You need real-time DSP (reverb, EQ, compression)
|
|
103
|
+
• You need frequency analysis or visualization
|
|
104
|
+
• You're building a full DAW or music player
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Detailed Comparison By Category
|
|
110
|
+
|
|
111
|
+
### Playback Control
|
|
112
|
+
|
|
113
|
+
**OpenAudio_r.js:**
|
|
114
|
+
```javascript
|
|
115
|
+
engine.start() // Begin randomized playback
|
|
116
|
+
engine.stop() // Pause mid-cycle
|
|
117
|
+
engine.reset() // Stop & clear played flags
|
|
118
|
+
engine.start() // Resume/restart
|
|
119
|
+
|
|
120
|
+
// Volume mid-playback
|
|
121
|
+
engine.setVolume(0.5)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**OpenAudio.js:**
|
|
125
|
+
```javascript
|
|
126
|
+
player.play() // Play the clip (or replay if ended)
|
|
127
|
+
player.stop() // Pause & rewind to start
|
|
128
|
+
player.play() // Replay from start (or resume if paused)
|
|
129
|
+
|
|
130
|
+
// Volume in constructor, not changeable at runtime
|
|
131
|
+
const player = new SingleAudio('sound.mp3', { volume: 0.8 });
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Verdict:** OpenAudio_r.js offers more runtime control; OpenAudio.js is simpler.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
### Scheduling & Timing
|
|
139
|
+
|
|
140
|
+
**OpenAudio_r.js:**
|
|
141
|
+
```javascript
|
|
142
|
+
const engine = new AudioEngine(clips, {
|
|
143
|
+
lowTime: 3, // Min 3 seconds
|
|
144
|
+
maxTime: 8 // Max 8 seconds between clips
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Random clip every 3–8 seconds, shuffled
|
|
148
|
+
// Background tab throttling mitigation included
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**OpenAudio.js:**
|
|
152
|
+
```javascript
|
|
153
|
+
const player = new SingleAudio('sound.mp3');
|
|
154
|
+
|
|
155
|
+
// Play on demand, no inter-clip delays
|
|
156
|
+
// Intended for immediate playback
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Verdict:** OpenAudio_r.js for ambient; OpenAudio.js for reactive.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
### Memory & Performance
|
|
164
|
+
|
|
165
|
+
**Both:**
|
|
166
|
+
- Negligible CPU impact
|
|
167
|
+
- No external dependencies
|
|
168
|
+
- Lightweight
|
|
169
|
+
|
|
170
|
+
| Scenario | OpenAudio_r.js | OpenAudio.js |
|
|
171
|
+
|----------|---|---|
|
|
172
|
+
| 1 clip looped | Overkill | Perfect |
|
|
173
|
+
| 3 clips ambient | Perfect | Suboptimal |
|
|
174
|
+
| 10 UI sounds | Overkill | Perfect |
|
|
175
|
+
| 50-clip scheduler | Perfect | Impossible |
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
### Flexibility
|
|
180
|
+
|
|
181
|
+
**OpenAudio_r.js:**
|
|
182
|
+
- ✅ Add clips at runtime: `engine.addClip()`
|
|
183
|
+
- ✅ Change volume mid-playback: `engine.setVolume()`
|
|
184
|
+
- ✅ Inspect state: `engine.isStarted`, `engine.isPlaying`
|
|
185
|
+
- ✅ Get cycle completion notice: `onCycleReset` callback
|
|
186
|
+
|
|
187
|
+
**OpenAudio.js:**
|
|
188
|
+
- ✅ Basic control: play/stop
|
|
189
|
+
- ✅ Inspect: `player.isPlaying`
|
|
190
|
+
- ✅ Callbacks: `onPlay`, `onEnd`
|
|
191
|
+
- ❌ Can't add clips (single file only)
|
|
192
|
+
- ❌ Can't change volume at runtime
|
|
193
|
+
|
|
194
|
+
**Verdict:** OpenAudio_r.js is more flexible; OpenAudio.js is more predictable.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Code Size Comparison
|
|
199
|
+
|
|
200
|
+
### Simple Example
|
|
201
|
+
|
|
202
|
+
**OpenAudio_r.js** (with 3 clips):
|
|
203
|
+
```javascript
|
|
204
|
+
const engine = new AudioEngine([
|
|
205
|
+
{ src: 'sound1.mp3', label: 'S1' },
|
|
206
|
+
{ src: 'sound2.mp3', label: 'S2' },
|
|
207
|
+
{ src: 'sound3.mp3', label: 'S3' }
|
|
208
|
+
], {
|
|
209
|
+
lowTime: 3,
|
|
210
|
+
maxTime: 5,
|
|
211
|
+
onPlay: (clip) => console.log(clip.label)
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
document.addEventListener('click', () => engine.start(), { once: true });
|
|
215
|
+
// 7 lines of code
|
|
216
|
+
// 9 KB library
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**OpenAudio.js** (single clip):
|
|
220
|
+
```javascript
|
|
221
|
+
const player = new SingleAudio('sound.mp3', {
|
|
222
|
+
label: 'Click',
|
|
223
|
+
onPlay: () => console.log('Playing')
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
document.addEventListener('click', () => player.play());
|
|
227
|
+
// 5 lines of code
|
|
228
|
+
// 4 KB library
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Total project size:**
|
|
232
|
+
- OpenAudio_r.js: ~9 KB
|
|
233
|
+
- OpenAudio.js: ~4 KB
|
|
234
|
+
- Both: ~13 KB
|
|
235
|
+
- Both gzipped: ~4.5 KB
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Real-World Scenarios
|
|
240
|
+
|
|
241
|
+
### Scenario 1: Game with Ambient Audio + UI Sounds
|
|
242
|
+
|
|
243
|
+
**Problem:** Game needs forest ambience (random bird calls, wind, rustling) AND button click feedback.
|
|
244
|
+
|
|
245
|
+
**Solution:** Use both libraries.
|
|
246
|
+
|
|
247
|
+
```javascript
|
|
248
|
+
// Ambient
|
|
249
|
+
const ambience = new AudioEngine([
|
|
250
|
+
{ src: 'birds.mp3' },
|
|
251
|
+
{ src: 'wind.mp3' },
|
|
252
|
+
{ src: 'rain.mp3' }
|
|
253
|
+
], {
|
|
254
|
+
lowTime: 5,
|
|
255
|
+
maxTime: 15,
|
|
256
|
+
volume: 0.6
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// UI
|
|
260
|
+
const clickSound = new SingleAudio('ui/click.mp3', { volume: 0.8 });
|
|
261
|
+
|
|
262
|
+
// Start ambience, add UI sounds
|
|
263
|
+
document.addEventListener('click', () => ambience.start(), { once: true });
|
|
264
|
+
document.querySelectorAll('button').forEach(btn => {
|
|
265
|
+
btn.addEventListener('click', () => clickSound.play());
|
|
266
|
+
});
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
### Scenario 2: Music Player App
|
|
272
|
+
|
|
273
|
+
**Problem:** Need to play background music, handle play/pause, show current track.
|
|
274
|
+
|
|
275
|
+
**Not ideal for either library.** OpenAudio.js plays single clips but can't swap tracks; OpenAudio_r.js is for ambient/random.
|
|
276
|
+
|
|
277
|
+
**Better solution:** Use Web Audio API or a music library.
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
### Scenario 3: Notification System
|
|
282
|
+
|
|
283
|
+
**Problem:** Different sound for each notification type (email, message, alert).
|
|
284
|
+
|
|
285
|
+
**Solution:** Use OpenAudio.js with multiple instances.
|
|
286
|
+
|
|
287
|
+
```javascript
|
|
288
|
+
const sounds = {
|
|
289
|
+
email: new SingleAudio('notif/email.mp3'),
|
|
290
|
+
message: new SingleAudio('notif/message.mp3'),
|
|
291
|
+
alert: new SingleAudio('notif/alert.mp3')
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
function notify(type, message) {
|
|
295
|
+
sounds[type].play();
|
|
296
|
+
showNotification(message);
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
### Scenario 4: Meditation App
|
|
303
|
+
|
|
304
|
+
**Problem:** Ambient binaural beats with occasional bell chimes.
|
|
305
|
+
|
|
306
|
+
**Solution:** Use both libraries.
|
|
307
|
+
|
|
308
|
+
```javascript
|
|
309
|
+
// Binaural beats (looped ambience)
|
|
310
|
+
const beats = new AudioEngine([
|
|
311
|
+
{ src: 'binaural/isochronic-40hz.mp3' }
|
|
312
|
+
], {
|
|
313
|
+
lowTime: 30,
|
|
314
|
+
maxTime: 60,
|
|
315
|
+
volume: 0.7
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// Session timer bell (every 5 minutes)
|
|
319
|
+
const bell = new SingleAudio('bell.mp3', { volume: 0.9 });
|
|
320
|
+
|
|
321
|
+
beats.start();
|
|
322
|
+
|
|
323
|
+
setInterval(() => {
|
|
324
|
+
bell.play(); // Ring bell every 5 min
|
|
325
|
+
}, 5 * 60 * 1000);
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
### Scenario 5: Narrator-Driven Story Game
|
|
331
|
+
|
|
332
|
+
**Problem:** Narration tracks play sequentially, each followed by a specific delay.
|
|
333
|
+
|
|
334
|
+
**OpenAudio.js solution:**
|
|
335
|
+
```javascript
|
|
336
|
+
const narration = new SingleAudio('story/narration-1.mp3', {
|
|
337
|
+
onEnd: () => {
|
|
338
|
+
console.log('Narration 1 done, waiting...');
|
|
339
|
+
setTimeout(() => {
|
|
340
|
+
narration2.play();
|
|
341
|
+
}, 2000); // 2 sec pause
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
narration.play();
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
This works but is a bit manual. OpenAudio_r.js is not ideal either (it randomizes).
|
|
349
|
+
|
|
350
|
+
**Better:** Sequential playlist logic, or Web Audio API.
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## Migration & Mixing
|
|
355
|
+
|
|
356
|
+
### Can You Use Both?
|
|
357
|
+
|
|
358
|
+
**Yes!** They don't conflict.
|
|
359
|
+
|
|
360
|
+
```javascript
|
|
361
|
+
const ambience = new AudioEngine([...]); // Multiple clips
|
|
362
|
+
const uiClick = new SingleAudio('click'); // Single clip
|
|
363
|
+
|
|
364
|
+
// Both can run simultaneously
|
|
365
|
+
ambience.start();
|
|
366
|
+
document.addEventListener('click', () => {
|
|
367
|
+
uiClick.play();
|
|
368
|
+
});
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
### Upgrading from OpenAudio.js to OpenAudio_r.js
|
|
374
|
+
|
|
375
|
+
If you outgrow single-clip playback:
|
|
376
|
+
|
|
377
|
+
```javascript
|
|
378
|
+
// Old (OpenAudio.js)
|
|
379
|
+
const sound = new SingleAudio('sound.mp3');
|
|
380
|
+
document.addEventListener('click', () => sound.play());
|
|
381
|
+
|
|
382
|
+
// New (OpenAudio_r.js)
|
|
383
|
+
const engine = new AudioEngine([
|
|
384
|
+
{ src: 'sound1.mp3', label: 'S1' },
|
|
385
|
+
{ src: 'sound2.mp3', label: 'S2' },
|
|
386
|
+
{ src: 'sound3.mp3', label: 'S3' }
|
|
387
|
+
]);
|
|
388
|
+
document.addEventListener('click', () => engine.start(), { once: true });
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
API is intentionally similar (both have `play()`, callbacks, `destroy()`, etc.), so migration is straightforward.
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
## Summary Decision Table
|
|
396
|
+
|
|
397
|
+
| Your Situation | Recommendation | Why |
|
|
398
|
+
|---|---|---|
|
|
399
|
+
| Button click sound | OpenAudio.js | Simple, fits perfectly |
|
|
400
|
+
| Multiple UI sounds | OpenAudio.js (multiple instances) | Lightweight, reactive |
|
|
401
|
+
| Ambient forest sounds | OpenAudio_r.js | Designed for this exact use case |
|
|
402
|
+
| Random background music | OpenAudio_r.js | Shuffle bag + scheduling |
|
|
403
|
+
| Both ambient + UI | Both | Use each for its purpose |
|
|
404
|
+
| Multiple music tracks, playlists | Web Audio API | Neither library designed for this |
|
|
405
|
+
| Real-time audio effects | Web Audio API | HTML5 Audio can't do DSP |
|
|
406
|
+
| Precise sub-second timing | Web Audio API | Need hardware clock |
|
|
407
|
+
| Mobile notification sound | OpenAudio.js | Simple, autoplay-safe |
|
|
408
|
+
| Game sound effects loop | OpenAudio_r.js | Perfect for randomization |
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## When to Graduate to Web Audio API
|
|
413
|
+
|
|
414
|
+
Use **Web Audio API** (not OpenAudio) when you need:
|
|
415
|
+
|
|
416
|
+
1. **Crossfading**
|
|
417
|
+
- Fade out one clip, fade in the next
|
|
418
|
+
- OpenAudio can't do this (abrupt transitions only)
|
|
419
|
+
|
|
420
|
+
2. **Sub-second precision**
|
|
421
|
+
- Timing must be frame-perfect (60 FPS)
|
|
422
|
+
- OpenAudio uses `setTimeout`, which is throttled
|
|
423
|
+
|
|
424
|
+
3. **Real-time DSP**
|
|
425
|
+
- Reverb, EQ, compression, distortion
|
|
426
|
+
- OpenAudio is audio routing only
|
|
427
|
+
|
|
428
|
+
4. **Advanced scheduling**
|
|
429
|
+
- Sequenced playback with complex logic
|
|
430
|
+
- Multiple simultaneous playback with timing synchronization
|
|
431
|
+
|
|
432
|
+
5. **Analysis & visualization**
|
|
433
|
+
- Frequency spectrum, waveform display
|
|
434
|
+
- Loudness detection
|
|
435
|
+
|
|
436
|
+
**Cost:** Web Audio API requires more setup, especially on mobile (AudioContext unlock boilerplate).
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
## Summary
|
|
441
|
+
|
|
442
|
+
- **OpenAudio_r.js:** For ambient, randomized, or scheduled audio (games, soundscapes)
|
|
443
|
+
- **OpenAudio.js:** For UI sounds and single-clip playback (buttons, notifications)
|
|
444
|
+
- **Both:** Can be used together in the same project
|
|
445
|
+
- **Web Audio API:** For advanced features (effects, visualization, precise timing)
|
|
446
|
+
|
|
447
|
+
---
|
|
448
|
+
|
|
449
|
+
*Last updated: March 2025*
|