@maccesar/titools 5.1.0 → 5.2.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 (85) hide show
  1. package/README.md +44 -12
  2. package/agents/ti-pro.md +4 -0
  3. package/commands/ti-audit.md +4 -0
  4. package/lib/config.js +2 -0
  5. package/package.json +1 -1
  6. package/skills/purgetss/SKILL.md +4 -4
  7. package/skills/purgetss/references/EXAMPLES.md +3 -3
  8. package/skills/purgetss/references/adopting-purgetss.md +2 -2
  9. package/skills/purgetss/references/animation-advanced.md +2 -2
  10. package/skills/purgetss/references/arbitrary-values.md +8 -5
  11. package/skills/purgetss/references/class-categories.md +5 -5
  12. package/skills/purgetss/references/class-index-properties.md +6 -6
  13. package/skills/purgetss/references/class-index.md +2 -2
  14. package/skills/purgetss/references/cli-commands.md +2 -2
  15. package/skills/purgetss/references/dynamic-component-creation.md +2 -2
  16. package/skills/purgetss/references/grid-layout.md +1 -1
  17. package/skills/purgetss/references/migration-guide.md +34 -4
  18. package/skills/purgetss/references/multi-density-images.md +6 -6
  19. package/skills/purgetss/references/platform-modifiers.md +21 -0
  20. package/skills/purgetss/references/ui-ux-design.md +1 -1
  21. package/skills/purgetss/references/version-history.md +9 -4
  22. package/skills/ti-admob-unified/SKILL.md +44 -0
  23. package/skills/ti-admob-unified/agents/openai.yaml +4 -0
  24. package/skills/ti-admob-unified/assets/example/app.js +1003 -0
  25. package/skills/ti-admob-unified/assets/example/lib/ads.js +618 -0
  26. package/skills/ti-admob-unified/assets/example/library-usage.js +104 -0
  27. package/skills/ti-admob-unified/references/api-reference.md +592 -0
  28. package/skills/ti-admob-unified/references/banners.md +435 -0
  29. package/skills/ti-admob-unified/references/contributing.md +228 -0
  30. package/skills/ti-admob-unified/references/dependencies.md +85 -0
  31. package/skills/ti-admob-unified/references/fullscreen.md +223 -0
  32. package/skills/ti-admob-unified/references/game-monetization.md +17 -0
  33. package/skills/ti-admob-unified/references/getting-started.md +275 -0
  34. package/skills/ti-admob-unified/references/index.md +30 -0
  35. package/skills/ti-admob-unified/references/integration.md +85 -0
  36. package/skills/ti-admob-unified/references/library.md +189 -0
  37. package/skills/ti-admob-unified/references/mediation.md +132 -0
  38. package/skills/ti-admob-unified/references/migration.md +255 -0
  39. package/skills/ti-admob-unified/references/module-changelog.md +54 -0
  40. package/skills/ti-admob-unified/references/module-license.txt +21 -0
  41. package/skills/ti-admob-unified/references/privacy.md +254 -0
  42. package/skills/ti-admob-unified/references/roadmap.md +146 -0
  43. package/skills/ti-admob-unified/references/source-notes.md +33 -0
  44. package/skills/ti-admob-unified/references/source-snapshot.json +440 -0
  45. package/skills/ti-admob-unified/references/third-party-notices.md +22 -0
  46. package/skills/ti-game/SKILL.md +10 -1
  47. package/skills/ti-game/references/api.md +10 -2
  48. package/skills/ti-game/references/debugging-performance.md +6 -0
  49. package/skills/ti-game/references/examples.md +11 -1
  50. package/skills/ti-game/references/patterns.md +24 -2
  51. package/skills/ti-game/references/physics.md +502 -0
  52. package/skills/ti-game/references/project-setup.md +23 -1
  53. package/skills/ti-game/references/recipes-action.md +46 -14
  54. package/skills/ti-game/references/recipes-adventure-effects.md +12 -1
  55. package/skills/ti-game/references/roadmap.md +2 -2
  56. package/skills/ti-game/references/tilemaps.md +2 -1
  57. package/skills/ti-game-assets/SKILL.md +53 -0
  58. package/skills/ti-game-assets/agents/openai.yaml +4 -0
  59. package/skills/ti-game-assets/references/alpha.md +26 -0
  60. package/skills/ti-game-assets/references/art-direction.md +37 -0
  61. package/skills/ti-game-assets/references/integration.md +49 -0
  62. package/skills/ti-game-assets/references/manifest.example.json +429 -0
  63. package/skills/ti-game-assets/references/manifest.md +39 -0
  64. package/skills/ti-game-assets/references/offline-audio.md +33 -0
  65. package/skills/ti-game-assets/references/prompts.md +33 -0
  66. package/skills/ti-game-assets/references/repeating-textures.md +17 -0
  67. package/skills/ti-game-assets/references/responsible-ads.md +31 -0
  68. package/skills/ti-game-assets/references/sound-assets.md +42 -0
  69. package/skills/ti-game-assets/references/vector-sources.md +21 -0
  70. package/skills/ti-game-assets/scripts/alpha_tools.py +160 -0
  71. package/skills/ti-game-assets/scripts/requirements.txt +1 -0
  72. package/skills/ti-game-assets/scripts/validate_manifest.py +267 -0
  73. package/skills/ti-synthengine/SKILL.md +37 -13
  74. package/skills/ti-synthengine/references/api.md +397 -31
  75. package/skills/ti-synthengine/references/audio-engine.md +235 -14
  76. package/skills/ti-synthengine/references/examples.md +396 -17
  77. package/skills/ti-synthengine/references/guide.md +331 -12
  78. package/skills/ti-synthengine/references/recipes.md +212 -22
  79. package/skills/ti-synthengine/references/recommendations.md +53 -16
  80. package/skills/ti-synthengine/references/sound-design.md +359 -15
  81. package/skills/ti-synthengine/references/source-notes.md +121 -0
  82. package/skills/ti-synthengine/references/source-snapshot.json +105 -0
  83. package/skills/ti-synthengine/references/stats-performance.json +2992 -0
  84. package/skills/ti-synthengine/references/stats-performance.md +38 -0
  85. package/skills/ti-synthengine/references/troubleshooting.md +61 -4
@@ -1,13 +1,34 @@
1
- > Source snapshot: official TiSynthEngine 1.0.0 documentation at commit `6685b81` (2026-08-29).
1
+ > Source snapshot: official TiSynthEngine 1.0.0 beta documentation from the reviewed working tree based on `95901c98af5353b86d9dc7c60b29ab9186b038cd` (2026-10-14), including uncommitted statistics changes. Upstream `documentation/guide.md` SHA-256: `c5fb4ff34ec425487221b80d6793bda24aa328281ab9ccb02e65b0065e54842e`.
2
2
 
3
3
  # Guide
4
4
 
5
5
  This guide covers the normal path from installing TiSynthEngine to releasing
6
6
  the native audio resources when the owning screen closes.
7
7
 
8
+ <!-- TOC-START -->
9
+ ## Contents
10
+
11
+ - [Install the module](#install-the-module)
12
+ - [Load and verify the contract](#load-and-verify-the-contract)
13
+ - [Lifecycle](#lifecycle)
14
+ - [Play the first sound](#play-the-first-sound)
15
+ - [Choose a pitch](#choose-a-pitch)
16
+ - [Shape a tone](#shape-a-tone)
17
+ - [Settle, filter and modulate](#settle-filter-and-modulate)
18
+ - [Shape noise and waveforms](#shape-noise-and-waveforms)
19
+ - [Play simultaneous notes](#play-simultaneous-notes)
20
+ - [Play a pattern](#play-a-pattern)
21
+ - [Know when a pattern ends](#know-when-a-pattern-ends)
22
+ - [Master level and stopping](#master-level-and-stopping)
23
+ - [Output route changes](#output-route-changes)
24
+ - [Integrate binaural in a 2D app](#integrate-binaural-in-a-2d-app)
25
+ - [Next steps](#next-steps)
26
+
27
+ <!-- TOC-END -->
28
+
8
29
  ## Install the module
9
30
 
10
- Use the archive for each target platform:
31
+ For the unreleased beta, use the packages built from the current module source in `android/dist` and `ios/dist`. The [latest release](https://github.com/macCesar/TiSynthEngine/releases/latest) is a separate package snapshot; it does not update when source files change. The archive names are:
11
32
 
12
33
  - `ti.synthengine-android-1.0.0.zip`
13
34
  - `ti.synthengine-iphone-1.0.0.zip`
@@ -22,8 +43,8 @@ Titanium CLI. Pin the same version for both platforms:
22
43
  </modules>
23
44
  ```
24
45
 
25
- TiSynthEngine requires Titanium SDK 13.3.1.GA or newer. The iOS renderer
26
- requires iOS 13 or newer.
46
+ TiSynthEngine requires Titanium SDK 13.2.0.GA or newer. Android projects must
47
+ compile against API 35 or newer. The iOS module requires iOS 15 or newer.
27
48
 
28
49
  ## Load and verify the contract
29
50
 
@@ -161,6 +182,126 @@ synth.playTone({
161
182
  channels. `lfoFreq` is the vibrato rate in hertz and `lfoDepth` is its depth in
162
183
  hertz.
163
184
 
185
+ ## Settle, filter and modulate
186
+
187
+ A flat envelope suits beeps. For plucks, keys and bells, add `decay` and
188
+ `sustain`: the level falls from full scale to `sustain` over `decay`
189
+ milliseconds and holds there until the release.
190
+
191
+ ```javascript
192
+ synth.playTone({
193
+ note: 'E4',
194
+ duration: 900,
195
+ attack: 2,
196
+ decay: 260,
197
+ sustain: 0.18,
198
+ release: 380,
199
+ waveType: contract.waveTypes.TRIANGLE
200
+ })
201
+ ```
202
+
203
+ `sustain` below 1 needs a nonzero `decay`, and `attack` plus `decay` must fit in
204
+ `duration`. The 50 ms default attack counts, so set a short `attack` when the
205
+ decay is long.
206
+
207
+ `filterType` adds a resonant filter: `lowpass`, `highpass` or `bandpass`.
208
+ `filterHz` is the cutoff, `filterQ` the resonance, and `filterHzEnd` sweeps the
209
+ cutoff over the note.
210
+
211
+ ```javascript
212
+ synth.playTone({
213
+ note: 'A2',
214
+ duration: 1000,
215
+ release: 200,
216
+ waveType: contract.waveTypes.SAWTOOTH,
217
+ filterType: 'bandpass',
218
+ filterHz: 350,
219
+ filterHzEnd: 2400,
220
+ filterQ: 5
221
+ })
222
+ ```
223
+
224
+ Any `filterHz`, `filterHzEnd` or `filterQ` without a `filterType` other than
225
+ `none` is rejected with `filter_type_required`.
226
+
227
+ `fmDepth` makes a `SINE` voice an FM voice. `fmRatio` is the modulator
228
+ frequency as a multiple of the carrier, and `fmDepthEnd` moves the modulation
229
+ index over the note. Omit `fmDepthEnd` for no sweep; 0 is a real target.
230
+
231
+ ```javascript
232
+ synth.playTone({
233
+ note: 'C5',
234
+ duration: 2600,
235
+ attack: 1,
236
+ decay: 2000,
237
+ sustain: 0.02,
238
+ fmRatio: 3.5,
239
+ fmDepth: 4,
240
+ fmDepthEnd: 0
241
+ })
242
+ ```
243
+
244
+ `detune` shifts a pitch in cents. In a chord, `detunes` gives one offset per
245
+ pitch, so repeating a note with small offsets thickens it:
246
+
247
+ ```javascript
248
+ synth.playChord({
249
+ notes: ['A3', 'A3', 'E4', 'E4'],
250
+ detunes: [-8, 8, -8, 8],
251
+ duration: 2000,
252
+ waveType: contract.waveTypes.SAWTOOTH
253
+ })
254
+ ```
255
+
256
+ ## Shape noise and waveforms
257
+
258
+ Use `noiseRate` to hold each pseudo-random noise value instead of generating a
259
+ new one every sample. A low rate sounds sparse and grainy; a high rate moves
260
+ toward white noise. `noiseRateEnd` sweeps the update rate.
261
+
262
+ ```javascript
263
+ synth.playTone({
264
+ frequency: 120,
265
+ duration: 700,
266
+ attack: 1,
267
+ release: 520,
268
+ waveType: contract.waveTypes.NOISE,
269
+ noiseRate: 11000,
270
+ noiseRateEnd: 650,
271
+ lowPassHz: 2600,
272
+ bitDepth: 8
273
+ })
274
+ ```
275
+
276
+ `lowPassHz` is a per-voice one-pole filter. `bitDepth` applies amplitude
277
+ quantization from 2 to 16 bits. Zero disables either control.
278
+
279
+ Square waves accept a duty cycle and a duty-cycle sweep:
280
+
281
+ ```javascript
282
+ synth.playTone({
283
+ note: 'A3',
284
+ duration: 650,
285
+ release: 180,
286
+ waveType: contract.waveTypes.SQUARE,
287
+ pulseWidth: 0.12,
288
+ pulseWidthEnd: 0.82
289
+ })
290
+ ```
291
+
292
+ For a caller-defined repeating shape, select `WAVETABLE` and provide 2 to 64
293
+ samples from -1 to 1. The table is stepped rather than interpolated.
294
+
295
+ ```javascript
296
+ synth.playTone({
297
+ note: 'D4',
298
+ duration: 500,
299
+ waveType: contract.waveTypes.WAVETABLE,
300
+ waveTable: [-1, -0.8, -0.1, 0.9, 0.4, 0.1, -0.2, -0.6],
301
+ bitDepth: 8
302
+ })
303
+ ```
304
+
164
305
  ## Play simultaneous notes
165
306
 
166
307
  Use `playChord` when notes belong to one musical event:
@@ -180,8 +321,7 @@ The chord is placed on the audio queue atomically. Its voices also share a
180
321
  constant-power event gain. Several separate `playTone` calls are independent,
181
322
  can begin in different callbacks, and are hotter when summed.
182
323
 
183
- Use `pans` when every note needs a deliberate position. Missing entries remain
184
- centered, and an array longer than the note array is rejected.
324
+ Use `pans` when notes need deliberate positions. Supplied entries override `panSpread`; missing entries use the spread position, or remain centered when spread is 0. An array longer than the note array is rejected.
185
325
 
186
326
  ```javascript
187
327
  synth.playChord({
@@ -213,13 +353,43 @@ synth.playPattern({
213
353
 
214
354
  Step timing is chosen in this order: step `duration`, step `noteValue`, pattern
215
355
  `noteValue`, pattern `stepDuration`, then the 100 ms default. A step also
216
- inherits the pattern's envelope, volume, waveform, pan, sweep and vibrato
217
- options. Values set on the step override the inherited value.
356
+ inherits the pattern's envelope, decay, sustain, volume, waveform, pan, sweep,
357
+ vibrato, noise, filter, resonant filter, FM, detune, pulse-width, wavetable and
358
+ quantization options. Values set on the step override the inherited value.
218
359
 
219
360
  Only one pattern scheduler is active. Starting another pattern cancels the
220
361
  pending steps of the previous pattern. It does not stop voices that have
221
362
  already started.
222
363
 
364
+ ## Know when a pattern ends
365
+
366
+ Each accepted pattern produces one `patternend` event. `completed` is `true`
367
+ when the pattern reaches its natural end and `false` when a newer pattern
368
+ replaces it. Use it to chain jingles or to release state that follows a
369
+ pattern.
370
+
371
+ ```javascript
372
+ function onPatternEnd (event) {
373
+ if (event.completed) playNextJingle()
374
+ }
375
+
376
+ synth.addEventListener('patternend', onPatternEnd)
377
+ ```
378
+
379
+ The listener belongs to the module, not to the pattern, so it outlives the
380
+ screen unless you remove it. Add it to the same cleanup that calls
381
+ `shutdown()`:
382
+
383
+ ```javascript
384
+ function closeAudio () {
385
+ synth.removeEventListener('patternend', onPatternEnd)
386
+ if (running) synth.shutdown()
387
+ }
388
+ ```
389
+
390
+ `shutdown()` discards a pending pattern without an event, so a closing owner
391
+ never receives a callback after cleanup.
392
+
223
393
  ## Master level and stopping
224
394
 
225
395
  ```javascript
@@ -238,15 +408,164 @@ other output routes change. The note crossing the transition may end early;
238
408
  new notes can be played normally after the system settles. Do not restart the
239
409
  whole app for a route change.
240
410
 
241
- On iOS, `startEngine()` activates a `Playback` audio session. That ignores the
242
- silent switch and normally interrupts other audio. If the app needs different
243
- session behavior, read [coexisting with other audio](recommendations.md#coexisting-with-other-audio)
411
+ On iOS, `startEngine()` activates a `Playback` audio session by default. That
412
+ ignores the silent switch and normally interrupts other audio. Pass
413
+ `audioSession: 'ambient'` to respect the silent switch and mix with other apps;
414
+ read [coexisting with other audio](recommendations.md#coexisting-with-other-audio)
244
415
  and test the final app on a device.
245
416
 
417
+ ## Integrate binaural in a 2D app
418
+
419
+ The JavaScript app chooses a position for each sound; the native module generates and filters that voice. Installing the module or enabling an app switch does not convert existing `pan` calls into binaural calls. Update every playback path that should use positioning, including explosions, impacts, shots, lasers and repeated motor/thrust events. A central audio service can do this once for all effects that supply their current source position. Calls bypassing that service keep their existing behavior.
420
+
421
+ Keep the synthesis recipe (pitch, waveform, duration, envelope, filters, FM and volume). Replace its placement fields when submitting the event. This is per-event processing inside the native ports using the shared KEMAR FIR, rather than a global platform spatial-audio switch.
422
+
423
+ ### Detect the installed feature
424
+
425
+ During the unreleased 1.0.0 beta, the version string alone does not prove that an installed binary has binaural support. Check both coordinate limits before enabling the controls:
426
+
427
+ ```javascript
428
+ var synth = require('ti.synthengine')
429
+ var contract = synth.getDefaults()
430
+ var supportsBinaural = !!(contract.limits &&
431
+ contract.limits.spatialX && contract.limits.spatialY)
432
+ ```
433
+
434
+ If the limits are absent, disable the binaural switch and keep stereo playback. Replace the installed module package and rebuild and reinstall the app to use a new native binary. Updating `tiapp.xml` or copying module files does not change an app already installed on a device. Follow the [lifecycle](#lifecycle) above to start the engine before playing.
435
+
436
+ ### Direction and amplitude are different controls
437
+
438
+ `spatialX`/`spatialY` are signed directions from -1 to 1. A 0 to 100% amplitude slider scales the position derived from the object; it does not select a direction. At height 100%, an upper object remains above and a lower object remains below. At height 0%, both are at the front-center height, while binaural remains enabled. Direct direction sliders would instead need -100 to +100%, divided by 100.
439
+
440
+ | App control | Effect |
441
+ | --- | --- |
442
+ | Binaural switch | Send spatial coordinates when on; omit both when off. |
443
+ | Horizontal amplitude, 0 to 100% | Scale each source's left/right coordinate; also controls stereo `pan` when binaural is off. |
444
+ | Height amplitude, 0 to 100% | Scale each source's below/above coordinate; inactive in stereo mode. |
445
+ | Master volume | Use `setVolume`; independent of direction and amplitude. |
446
+
447
+ These are app preferences, not options for `startEngine` or additional native methods. Keep the app's chosen defaults and saved preferences; the module does not impose a 100% slider default. Persist them in the app if they should survive restart. A switch change applies to newly submitted events, without restarting the engine or repositioning voices already playing.
448
+
449
+ With both amplitude controls at 100%, screen positions map as follows:
450
+
451
+ | Position | `spatialX` | `spatialY` |
452
+ | --- | ---: | ---: |
453
+ | Top left | -1 | 1 |
454
+ | Top center | 0 | 1 |
455
+ | Top right | 1 | 1 |
456
+ | Middle left | -1 | 0 |
457
+ | Center | 0 | 0 |
458
+ | Middle right | 1 | 0 |
459
+ | Bottom left | -1 | -1 |
460
+ | Bottom center | 0 | -1 |
461
+ | Bottom right | 1 | -1 |
462
+
463
+ ### Convert screen positions when playing a tone
464
+
465
+ This example preserves a recipe, removes conflicting placement fields, and computes new coordinates for each call. Coordinates use the same local arena space: X grows right, Y grows down, and width/height are the arena dimensions. Convert world or camera coordinates into that space first. The example percentages are app choices; retain your own defaults.
466
+
467
+ ```javascript
468
+ var audioSettings = {
469
+ binaural: supportsBinaural,
470
+ horizontalPercent: 50,
471
+ heightPercent: 100
472
+ }
473
+
474
+ function clamp (value, min, max) {
475
+ return Math.max(min, Math.min(max, value))
476
+ }
477
+
478
+ function playPositionedTone (recipe, x, y, width, height) {
479
+ var values = [x, y, width, height]
480
+ if (!values.every(function (value) {
481
+ return typeof value === 'number' && isFinite(value)
482
+ }) || width <= 0 || height <= 0) {
483
+ throw new Error('Sound position needs finite coordinates and positive dimensions')
484
+ }
485
+
486
+ var options = Object.assign({}, recipe)
487
+ delete options.pan
488
+ delete options.panEnd
489
+ delete options.pans
490
+ delete options.panSpread
491
+ delete options.spatialX
492
+ delete options.spatialY
493
+
494
+ var horizontal = clamp(audioSettings.horizontalPercent, 0, 100) / 100
495
+ var vertical = clamp(audioSettings.heightPercent, 0, 100) / 100
496
+ var nx = clamp(2 * x / width - 1, -1, 1)
497
+ var ny = clamp(1 - 2 * y / height, -1, 1)
498
+
499
+ if (audioSettings.binaural && supportsBinaural) {
500
+ options.spatialX = nx * horizontal
501
+ options.spatialY = ny * vertical
502
+ } else {
503
+ options.pan = nx * horizontal
504
+ options.panEnd = options.pan
505
+ }
506
+ return synth.playTone(options)
507
+ }
508
+
509
+ var explosion = {
510
+ frequency: 90,
511
+ waveType: contract.waveTypes.NOISE,
512
+ duration: 500,
513
+ attack: 2,
514
+ release: 200,
515
+ volume: 0.3
516
+ }
517
+ if (!playPositionedTone(explosion, 900, 100, 1000, 800)) {
518
+ Ti.API.warn('Positioned explosion rejected')
519
+ }
520
+ ```
521
+
522
+ Connect a Titanium switch's `change` event to `audioSettings.binaural = event.value`; connect the two sliders' `change` events to `horizontalPercent` and `heightPercent`. Configure slider minimum 0 and maximum 100, and disable the height slider while the switch is off. Validate restored preference values as finite numbers within those ranges before using them. Do not pass these preference names to `playTone`: that would return `false` with `unsupported_option`.
523
+
524
+ Supply the current position whenever an effect fires. A projectile launch cue can use the launch position; an impact uses the collision position. Repeated short motor events can use successive positions. A single active tone cannot move after submission because this API has no per-voice handle or position-update method. Stereo `panEnd` movement cannot be combined with binaural; do not continually retrigger long sounds every frame to imitate movement.
525
+
526
+ ### Chords and patterns
527
+
528
+ A chord shares one binaural position across all its voices. Remove `pans` and `panSpread` when sending coordinates. For different simultaneous source positions, use independent tone events and account for their different gain/overlap behavior:
529
+
530
+ ```javascript
531
+ if (!synth.playChord({
532
+ notes: ['C4', 'E4', 'G4'],
533
+ duration: 500,
534
+ volume: 0.3,
535
+ spatialX: -0.5,
536
+ spatialY: 0.7
537
+ })) Ti.API.warn('Spatial chord rejected')
538
+ ```
539
+
540
+ Pattern steps inherit coordinates from the root and can override either one. Remove conflicting stereo fields from both the root and every step; even `pan: 0` conflicts with a spatial coordinate. A step cannot opt out of inherited binaural by passing zero coordinates; use a pattern without spatial coordinates on its root for a mix of spatial and stereo steps.
541
+
542
+ ```javascript
543
+ if (!synth.playPattern({
544
+ stepDuration: 300,
545
+ attack: 5,
546
+ release: 80,
547
+ volume: 0.3,
548
+ spatialX: 0,
549
+ spatialY: 1,
550
+ steps: [
551
+ { note: 'C4' },
552
+ { note: 'E4', spatialY: -1 },
553
+ { rest: true },
554
+ { note: 'G4', spatialX: 1, spatialY: 0 }
555
+ ]
556
+ })) Ti.API.warn('Spatial pattern rejected')
557
+ ```
558
+
559
+ ### Listen and measure
560
+
561
+ Use stereo headphones and compare isolated broadband noise above/below before testing many collisions. Perceived elevation varies with the listener, headphones and spectrum; low sine tones are weak tests. The output is still stereo, with a front plane and no rear sources, distance attenuation, room reflections or head tracking. A 100% slider uses the modeled directional range; it does not guarantee strong localization for every person.
562
+
563
+ CPU cost depends on concurrent spatial voices and their short tails, not the total number of effects over a game. Measure the intended device and realistic event overlap. Host replay is a comparison tool, not device CPU, battery, heat or underrun evidence. See [the renderer](audio-engine.md#binaural-front-plane-renderer) and [diagnostic checks](troubleshooting.md#binaural-does-not-change-the-position).
564
+
246
565
  ## Next steps
247
566
 
248
567
  - [API reference](api.md) for every accepted key and limit.
249
- - [Sound design](sound-design.md) for waveform and envelope recipes.
568
+ - [Sound design](sound-design.md) for waveform, envelope, filter and FM recipes.
250
569
  - [Examples](examples.md) for copy-ready sounds and patterns.
251
570
  - [Troubleshooting](troubleshooting.md) when a call returns `false` or audio is
252
571
  silent.