@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,4 +1,4 @@
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/examples.md` SHA-256: `53a8bd95215cc24a35776edb8686fa0d60c5399be0b4ff2685eee1563738f6cb`.
2
2
 
3
3
  # Examples
4
4
 
@@ -19,7 +19,12 @@ if (contract.apiVersion !== '1.0.0') {
19
19
  ## Contents
20
20
 
21
21
  - [Start once after layout](#start-once-after-layout)
22
- - [Five waveforms](#five-waveforms)
22
+ - [Built-in waveforms](#built-in-waveforms)
23
+ - [Variable-rate filtered noise](#variable-rate-filtered-noise)
24
+ - [Pulse-width sweep](#pulse-width-sweep)
25
+ - [Stepped wavetable](#stepped-wavetable)
26
+ - [Fixed controls on a chord](#fixed-controls-on-a-chord)
27
+ - [Wave-shaping pattern overrides](#wave-shaping-pattern-overrides)
23
28
  - [Pitch sweep and pan movement](#pitch-sweep-and-pan-movement)
24
29
  - [Vibrato](#vibrato)
25
30
  - [Chord with an even spread](#chord-with-an-even-spread)
@@ -30,9 +35,17 @@ if (contract.apiVersion !== '1.0.0') {
30
35
  - [Noise percussion](#noise-percussion)
31
36
  - [Xylophone sketch](#xylophone-sketch)
32
37
  - [Simon-style pads](#simon-style-pads)
38
+ - [Pluck and electric piano](#pluck-and-electric-piano)
39
+ - [Bell](#bell)
40
+ - [Wah and filtered laser](#wah-and-filtered-laser)
41
+ - [Kick and hi-hat in one pattern](#kick-and-hi-hat-in-one-pattern)
42
+ - [Detuned chords](#detuned-chords)
43
+ - [Chain patterns with `patternend`](#chain-patterns-with-patternend)
44
+ - [Rules across keys](#rules-across-keys)
33
45
  - [Speaker-safe startup](#speaker-safe-startup)
34
46
  - [Validation at an input boundary](#validation-at-an-input-boundary)
35
47
  - [Stop and clean up](#stop-and-clean-up)
48
+ - [Positioned headphone effect](#positioned-headphone-effect)
36
49
 
37
50
  <!-- TOC-END -->
38
51
 
@@ -65,7 +78,7 @@ win.addEventListener('close', cleanup)
65
78
  win.open()
66
79
  ```
67
80
 
68
- ## Five waveforms
81
+ ## Built-in waveforms
69
82
 
70
83
  ```javascript
71
84
  var names = ['SINE', 'SQUARE', 'SAWTOOTH', 'TRIANGLE', 'NOISE']
@@ -86,6 +99,121 @@ names.forEach(function (name, index) {
86
99
 
87
100
  Noise ignores pitch, but `note` keeps the event shape valid.
88
101
 
102
+ ## Variable-rate filtered noise
103
+
104
+ ```javascript
105
+ synth.playTone({
106
+ frequency: 100,
107
+ duration: 760,
108
+ attack: 1,
109
+ release: 580,
110
+ volume: 0.65,
111
+ waveType: WAVE.NOISE,
112
+ noiseRate: 12000,
113
+ noiseRateEnd: 600,
114
+ lowPassHz: 2400,
115
+ bitDepth: 8
116
+ })
117
+ ```
118
+
119
+ `noiseRate` starts at 12 kHz and falls to 600 Hz. `lowPassHz` removes high
120
+ frequency energy after the 8-bit source quantizer.
121
+
122
+ ## Pulse-width sweep
123
+
124
+ ```javascript
125
+ synth.playTone({
126
+ note: 'A3',
127
+ duration: 700,
128
+ attack: 3,
129
+ release: 180,
130
+ volume: 0.55,
131
+ waveType: WAVE.SQUARE,
132
+ pulseWidth: 0.1,
133
+ pulseWidthEnd: 0.86
134
+ })
135
+ ```
136
+
137
+ The square duty cycle moves from 10 percent to 86 percent over the event.
138
+
139
+ ## Stepped wavetable
140
+
141
+ ```javascript
142
+ synth.playTone({
143
+ note: 'D4',
144
+ duration: 650,
145
+ attack: 4,
146
+ release: 220,
147
+ volume: 0.58,
148
+ waveType: WAVE.WAVETABLE,
149
+ waveTable: [-1, -0.7, -0.1, 0.8, 1, 0.35, -0.25, -0.55],
150
+ bitDepth: 8
151
+ })
152
+ ```
153
+
154
+ The table repeats as a stepped cycle. `bitDepth: 8` quantizes each table value
155
+ before filtering and the amplitude envelope.
156
+
157
+ ## Fixed controls on a chord
158
+
159
+ ```javascript
160
+ synth.playChord({
161
+ notes: ['A3', 'E4', 'A4'],
162
+ duration: 900,
163
+ attack: 8,
164
+ release: 220,
165
+ volume: 0.5,
166
+ waveType: WAVE.SQUARE,
167
+ pulseWidth: 0.22,
168
+ lowPassHz: 3600,
169
+ bitDepth: 8,
170
+ pans: [-0.65, 0, 0.65]
171
+ })
172
+ ```
173
+
174
+ Chord voices share fixed shaping controls. Sweep fields such as
175
+ `pulseWidthEnd` and `noiseRateEnd` are not accepted by `playChord()`.
176
+
177
+ ## Wave-shaping pattern overrides
178
+
179
+ ```javascript
180
+ synth.playPattern({
181
+ stepDuration: 210,
182
+ attack: 2,
183
+ release: 80,
184
+ volume: 0.48,
185
+ waveType: WAVE.NOISE,
186
+ noiseRate: 12000,
187
+ lowPassHz: 2600,
188
+ bitDepth: 8,
189
+ steps: [
190
+ { frequency: 120, duration: 180, release: 150, noiseRateEnd: 800 },
191
+ { rest: true, duration: 100 },
192
+ {
193
+ note: 'A3',
194
+ duration: 300,
195
+ release: 120,
196
+ waveType: WAVE.SQUARE,
197
+ pulseWidth: 0.1,
198
+ pulseWidthEnd: 0.82,
199
+ lowPassHz: 0,
200
+ bitDepth: 0
201
+ },
202
+ {
203
+ note: 'D4',
204
+ duration: 360,
205
+ release: 160,
206
+ waveType: WAVE.WAVETABLE,
207
+ waveTable: [-1, -0.7, -0.1, 0.8, 1, 0.35, -0.25, -0.55]
208
+ }
209
+ ]
210
+ })
211
+ ```
212
+
213
+ Pattern roots provide inherited values. Each sounding step can override the
214
+ source and any shaping control; a `WAVETABLE` step needs a local or inherited
215
+ `waveTable`.
216
+
89
217
  ## Pitch sweep and pan movement
90
218
 
91
219
  ```javascript
@@ -166,37 +294,41 @@ synth.playPattern({
166
294
  volume: 0.55,
167
295
  waveType: WAVE.SQUARE,
168
296
  steps: [
169
- { note: 'C4', duration: 150 },
170
- { note: 'E4', duration: 150 },
297
+ { note: 'C4' },
298
+ { note: 'E4' },
171
299
  { rest: true },
172
- { note: 'G4', duration: 150 },
173
- { note: 'C5', duration: 280, noteValue: '4n' }
300
+ { note: 'G4' },
301
+ { note: 'C5', noteValue: '4n' }
174
302
  ]
175
303
  })
176
304
  ```
177
305
 
178
- Positive swing lengthens one step and shortens the next. Negative swing flips
179
- that relationship. Keep it subtle unless the lurch is the intended effect.
306
+ Swing offsets scheduled starts without changing voice durations: positive swing delays even steps and advances odd steps (zero-based); negative swing reverses those offsets. Each offset is the swing value times that step's duration, and a start before the call time is clamped to zero. This can overlap voices. Keep it subtle unless the lurch is the intended effect.
180
307
 
181
308
  ## Millisecond pattern
182
309
 
183
310
  ```javascript
184
311
  synth.playPattern({
185
- stepDuration: 110,
186
312
  pan: -0.4,
187
313
  panEnd: 0.4,
188
314
  waveType: WAVE.TRIANGLE,
189
315
  steps: [
190
316
  { note: 'C5', duration: 90 },
317
+ { rest: true, duration: 20 },
191
318
  { note: 'D5', duration: 90 },
319
+ { rest: true, duration: 20 },
192
320
  { note: 'E5', duration: 90 },
321
+ { rest: true, duration: 20 },
193
322
  { note: 'G5', duration: 180 }
194
323
  ]
195
324
  })
196
325
  ```
197
326
 
198
- `stepDuration` controls spacing. Per-step `duration` controls how long the
199
- sound itself lasts. The pattern root does not accept `duration`.
327
+ Each `duration` sets both the voice length and the advance to the next step.
328
+ The 90 ms tones followed by 20 ms rests put the first four notes 110 ms apart;
329
+ the last tone lasts 180 ms. Rests provide actual silence. `release` only fades
330
+ within a sounding step, not a separate gate. The pattern root does not accept
331
+ `duration`, and `stepDuration` is only a fallback when a step has no timing.
200
332
 
201
333
  ## Step overrides
202
334
 
@@ -233,16 +365,18 @@ synth.playPattern({
233
365
  volume: 0.32,
234
366
  waveType: WAVE.NOISE,
235
367
  steps: [
236
- { frequency: 200, duration: 70, attack: 1, release: 60 },
368
+ { frequency: 200, attack: 1, decay: 70, sustain: 0, release: 60 },
237
369
  { rest: true },
238
- { frequency: 200, duration: 32, attack: 1, release: 25 },
239
- { frequency: 200, duration: 32, attack: 1, release: 25 },
370
+ { frequency: 200, attack: 1, decay: 32, sustain: 0, release: 25 },
371
+ { frequency: 200, attack: 1, decay: 32, sustain: 0, release: 25 },
240
372
  { rest: true },
241
- { frequency: 200, duration: 52, attack: 1, release: 44 }
373
+ { frequency: 200, attack: 1, decay: 52, sustain: 0, release: 44 }
242
374
  ]
243
375
  })
244
376
  ```
245
377
 
378
+ The steps retain the sixteenth-note grid. Short decay with `sustain: 0` makes
379
+ each hit settle to silence before its step ends, without changing that grid.
246
380
  Every sounding step needs `frequency` or `note`, including a noise step. Do not
247
381
  write a primitive step such as `200`; primitive steps return
248
382
  `invalid_step_type`.
@@ -300,6 +434,229 @@ function playAllPads () {
300
434
  }
301
435
  ```
302
436
 
437
+ ## Pluck and electric piano
438
+
439
+ `decay` and `sustain` turn a flat tone into an instrument. The electric piano
440
+ adds an FM index that falls with the level, so the attack is bright and the
441
+ tail is mellow.
442
+
443
+ ```javascript
444
+ function pluck (note) {
445
+ return synth.playTone({
446
+ note: note,
447
+ duration: 900,
448
+ attack: 2,
449
+ decay: 260,
450
+ sustain: 0.18,
451
+ release: 380,
452
+ volume: 0.6,
453
+ waveType: WAVE.TRIANGLE
454
+ })
455
+ }
456
+
457
+ function electricPiano (note) {
458
+ return synth.playTone({
459
+ note: note,
460
+ duration: 1800,
461
+ attack: 3,
462
+ decay: 700,
463
+ sustain: 0.3,
464
+ release: 600,
465
+ volume: 0.55,
466
+ waveType: WAVE.SINE,
467
+ fmRatio: 1,
468
+ fmDepth: 2.2,
469
+ fmDepthEnd: 0.25
470
+ })
471
+ }
472
+ ```
473
+
474
+ ## Bell
475
+
476
+ ```javascript
477
+ synth.playTone({
478
+ note: 'C5',
479
+ duration: 2600,
480
+ attack: 1,
481
+ decay: 2000,
482
+ sustain: 0.02,
483
+ release: 500,
484
+ volume: 0.5,
485
+ waveType: WAVE.SINE,
486
+ fmRatio: 3.5,
487
+ fmDepth: 4,
488
+ fmDepthEnd: 0
489
+ })
490
+ ```
491
+
492
+ A fractional `fmRatio` puts the overtones between harmonics, which reads as
493
+ metal. `fmDepthEnd: 0` lets the brightness die away completely.
494
+
495
+ ## Wah and filtered laser
496
+
497
+ ```javascript
498
+ synth.playTone({
499
+ note: 'A2',
500
+ duration: 1000,
501
+ attack: 10,
502
+ release: 200,
503
+ volume: 0.5,
504
+ waveType: WAVE.SAWTOOTH,
505
+ filterType: 'bandpass',
506
+ filterHz: 350,
507
+ filterHzEnd: 2400,
508
+ filterQ: 5
509
+ })
510
+
511
+ setTimeout(function () {
512
+ synth.playTone({
513
+ frequency: 1600,
514
+ frequencyEnd: 160,
515
+ duration: 340,
516
+ attack: 2,
517
+ release: 90,
518
+ volume: 0.4,
519
+ waveType: WAVE.SAWTOOTH,
520
+ filterType: 'lowpass',
521
+ filterHz: 7000,
522
+ filterHzEnd: 500,
523
+ filterQ: 4
524
+ })
525
+ }, 1300)
526
+ ```
527
+
528
+ The cutoff sweep spans the whole event, so a longer note sweeps more slowly.
529
+
530
+ ## Kick and hi-hat in one pattern
531
+
532
+ Each step can carry its own envelope and filter.
533
+
534
+ ```javascript
535
+ synth.playPattern({
536
+ bpm: 110,
537
+ noteValue: '8n',
538
+ steps: [
539
+ {
540
+ frequency: 150,
541
+ frequencyEnd: 42,
542
+ noteValue: '4n',
543
+ attack: 1,
544
+ decay: 140,
545
+ sustain: 0.3,
546
+ release: 120,
547
+ volume: 0.85
548
+ },
549
+ {
550
+ frequency: 440,
551
+ waveType: WAVE.NOISE,
552
+ attack: 1,
553
+ decay: 30,
554
+ sustain: 0.25,
555
+ release: 45,
556
+ volume: 0.45,
557
+ filterType: 'highpass',
558
+ filterHz: 7000
559
+ },
560
+ { rest: true },
561
+ {
562
+ frequency: 440,
563
+ waveType: WAVE.NOISE,
564
+ attack: 1,
565
+ decay: 30,
566
+ sustain: 0.25,
567
+ release: 45,
568
+ volume: 0.45,
569
+ filterType: 'highpass',
570
+ filterHz: 7000
571
+ }
572
+ ]
573
+ })
574
+ ```
575
+
576
+ ## Detuned chords
577
+
578
+ Repeat a pitch with small offsets in `detunes` to make it beat and widen. Two
579
+ copies 12 cents apart at 440 Hz beat about three times a second.
580
+
581
+ ```javascript
582
+ synth.playChord({
583
+ frequencies: [440, 440],
584
+ detunes: [-6, 6],
585
+ duration: 2000,
586
+ attack: 200,
587
+ release: 600,
588
+ volume: 0.55,
589
+ waveType: WAVE.SINE
590
+ })
591
+
592
+ setTimeout(function () {
593
+ synth.playChord({
594
+ notes: ['A3', 'E4', 'A3', 'E4', 'A3', 'E4'],
595
+ detunes: [-9, -9, 0, 0, 9, 9],
596
+ duration: 3200,
597
+ attack: 700,
598
+ release: 1300,
599
+ volume: 0.5,
600
+ waveType: WAVE.SAWTOOTH,
601
+ filterType: 'lowpass',
602
+ filterHz: 1500,
603
+ panSpread: 0.8
604
+ })
605
+ }, 2400)
606
+ ```
607
+
608
+ Each repeated pitch is a voice. The second chord needs `maxVoices` of at least
609
+ 6.
610
+
611
+ ## Chain patterns with `patternend`
612
+
613
+ ```javascript
614
+ var jingles = [
615
+ [{ note: 'C5' }, { note: 'E5' }, { note: 'G5' }],
616
+ [{ note: 'G5' }, { note: 'E5' }, { note: 'C5' }]
617
+ ]
618
+ var nextJingle = 0
619
+
620
+ function playNextJingle () {
621
+ if (nextJingle >= jingles.length) return false
622
+ return synth.playPattern({
623
+ noteValue: '8n',
624
+ attack: 5,
625
+ release: 60,
626
+ volume: 0.5,
627
+ waveType: WAVE.SQUARE,
628
+ steps: jingles[nextJingle++]
629
+ })
630
+ }
631
+
632
+ function onPatternEnd (event) {
633
+ if (event.completed) playNextJingle()
634
+ }
635
+
636
+ synth.addEventListener('patternend', onPatternEnd)
637
+ playNextJingle()
638
+
639
+ // When the audio owner closes:
640
+ synth.removeEventListener('patternend', onPatternEnd)
641
+ ```
642
+
643
+ The event reports `completed: false` when another pattern replaces the running
644
+ one, so a listener that only chains on `completed: true` never double-starts.
645
+
646
+ ## Rules across keys
647
+
648
+ Some combinations are rejected even when every value is in range. This call
649
+ returns `false` with `sustain_without_decay`, because a sustain below 1 with no
650
+ decay would jump in level and click:
651
+
652
+ ```javascript
653
+ // Rejected on purpose: sustain needs a decay.
654
+ var accepted = synth.playTone({ note: 'A4', sustain: 0.5 })
655
+
656
+ // Accepted: the same note with a decay.
657
+ synth.playTone({ note: 'A4', decay: 200, sustain: 0.5, duration: 800 })
658
+ ```
659
+
303
660
  ## Speaker-safe startup
304
661
 
305
662
  ```javascript
@@ -345,4 +702,26 @@ synth.shutdown()
345
702
  `stopAll()` fades current voices but leaves pending pattern scheduling alone.
346
703
  `shutdown()` cancels the scheduler and releases the native stream.
347
704
 
348
- The complete interactive demo is `example/app.js` in the upstream TiSynthEngine repository. For additional retro effects, playable xylophone interaction, memory pads, layered polyphony and cancellable repetition, continue with [Curated sound and interaction recipes](recipes.md).
705
+ The last committed interactive demo is in the upstream [`example/app.js`](https://github.com/macCesar/TiSynthEngine/blob/95901c98af5353b86d9dc7c60b29ab9186b038cd/example/app.js); that commit does not contain the pending statistics example. For additional retro effects, playable xylophone interaction, memory pads, layered polyphony and cancellable repetition, continue with [Curated sound and interaction recipes](recipes.md).
706
+
707
+ ## Positioned headphone effect
708
+
709
+ Check the installed coordinate limits and keep the engine running. This JavaScript places one generated noise effect above and to the right; it does not change other sound calls:
710
+
711
+ ```javascript
712
+ if (!contract.limits.spatialX || !contract.limits.spatialY) {
713
+ throw new Error('This installed module does not report binaural support')
714
+ }
715
+ if (!synth.playTone({
716
+ frequency: 90,
717
+ waveType: WAVE.NOISE,
718
+ duration: 500,
719
+ attack: 2,
720
+ release: 200,
721
+ volume: 0.3,
722
+ spatialX: 0.8,
723
+ spatialY: 0.75
724
+ })) Ti.API.warn('Positioned effect rejected')
725
+ ```
726
+
727
+ For app switches, amplitude sliders, all nine screen positions and routing multiple effects through one service, use the [binaural integration guide](guide.md#integrate-binaural-in-a-2d-app). Omitting both coordinates disables binaural for that event; setting both to zero keeps it enabled at the front center.