@maccesar/titools 5.2.0 → 5.2.1

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 (78) hide show
  1. package/CONTRIBUTING.md +69 -0
  2. package/README.md +80 -72
  3. package/agents/ti-pro.md +1 -1
  4. package/commands/ti-audit.md +2 -2
  5. package/docs/examples.md +91 -0
  6. package/docs/knowledge-index.md +57 -0
  7. package/lib/downloader.js +1 -1
  8. package/package.json +4 -2
  9. package/skills/purgetss/references/multi-density-images.md +1 -1
  10. package/skills/ti-admob-unified/SKILL.md +21 -7
  11. package/skills/ti-admob-unified/assets/example/app.js +229 -25
  12. package/skills/ti-admob-unified/assets/example/lib/ads.js +30 -2
  13. package/skills/ti-admob-unified/assets/example/library-usage.js +33 -1
  14. package/skills/ti-admob-unified/references/api-reference.md +39 -16
  15. package/skills/ti-admob-unified/references/banners.md +9 -9
  16. package/skills/ti-admob-unified/references/contributing.md +14 -9
  17. package/skills/ti-admob-unified/references/dependencies.md +31 -25
  18. package/skills/ti-admob-unified/references/fullscreen.md +43 -28
  19. package/skills/ti-admob-unified/references/game-monetization.md +138 -7
  20. package/skills/ti-admob-unified/references/getting-started.md +75 -31
  21. package/skills/ti-admob-unified/references/index.md +5 -2
  22. package/skills/ti-admob-unified/references/integration.md +8 -6
  23. package/skills/ti-admob-unified/references/library.md +41 -10
  24. package/skills/ti-admob-unified/references/mediation-parity.md +17 -0
  25. package/skills/ti-admob-unified/references/mediation.md +22 -9
  26. package/skills/ti-admob-unified/references/migration.md +11 -7
  27. package/skills/ti-admob-unified/references/module-changelog.md +41 -11
  28. package/skills/ti-admob-unified/references/privacy.md +35 -8
  29. package/skills/ti-admob-unified/references/production.md +67 -0
  30. package/skills/ti-admob-unified/references/rewarded-interstitial.md +101 -0
  31. package/skills/ti-admob-unified/references/roadmap.md +28 -31
  32. package/skills/ti-admob-unified/references/source-notes.md +51 -4
  33. package/skills/ti-admob-unified/references/source-snapshot.json +249 -45
  34. package/skills/ti-admob-unified/references/third-party-notices.md +4 -4
  35. package/skills/ti-game/SKILL.md +23 -6
  36. package/skills/ti-game/references/api.md +34 -30
  37. package/skills/ti-game/references/debugging-performance.md +18 -9
  38. package/skills/ti-game/references/examples.md +103 -20
  39. package/skills/ti-game/references/input.md +101 -0
  40. package/skills/ti-game/references/patterns.md +39 -15
  41. package/skills/ti-game/references/physics.md +80 -52
  42. package/skills/ti-game/references/project-setup.md +118 -56
  43. package/skills/ti-game/references/recipes-action.md +107 -8
  44. package/skills/ti-game/references/recipes-adventure-effects.md +41 -9
  45. package/skills/ti-game/references/roadmap.md +10 -3
  46. package/skills/ti-game/references/source-notes.md +81 -0
  47. package/skills/ti-game/references/tilemaps.md +12 -4
  48. package/skills/ti-synthengine/SKILL.md +81 -95
  49. package/skills/ti-synthengine/assets/example/README.md +84 -0
  50. package/skills/ti-synthengine/assets/example/app.js +1178 -0
  51. package/skills/ti-synthengine/assets/example/lib/sequenceOwner.js +83 -0
  52. package/skills/ti-synthengine/assets/example/lib/sequenceScore.js +25 -0
  53. package/skills/ti-synthengine/references/advanced-examples.md +174 -0
  54. package/skills/ti-synthengine/references/android-sim80-correction-evidence.zip +0 -0
  55. package/skills/ti-synthengine/references/android-sim80-correction.json +162 -0
  56. package/skills/ti-synthengine/references/api.md +434 -380
  57. package/skills/ti-synthengine/references/audio-engine.md +157 -230
  58. package/skills/ti-synthengine/references/bypass-physical-recheck-evidence.zip +0 -0
  59. package/skills/ti-synthengine/references/examples.md +123 -253
  60. package/skills/ti-synthengine/references/frontier-android-evidence.zip +0 -0
  61. package/skills/ti-synthengine/references/frontier-ipad-evidence.zip +0 -0
  62. package/skills/ti-synthengine/references/frontier-physical-validation.json +1918 -0
  63. package/skills/ti-synthengine/references/guide.md +188 -156
  64. package/skills/ti-synthengine/references/performance-review-evidence.zip +0 -0
  65. package/skills/ti-synthengine/references/performance-review.json +1146 -0
  66. package/skills/ti-synthengine/references/physical-validation.json +1457 -0
  67. package/skills/ti-synthengine/references/recipes-interaction.md +549 -0
  68. package/skills/ti-synthengine/references/recipes.md +423 -599
  69. package/skills/ti-synthengine/references/recommendations.md +63 -113
  70. package/skills/ti-synthengine/references/sound-design.md +134 -258
  71. package/skills/ti-synthengine/references/source-notes.md +93 -105
  72. package/skills/ti-synthengine/references/source-snapshot.json +379 -32
  73. package/skills/ti-synthengine/references/stats-performance.md +64 -20
  74. package/skills/ti-synthengine/references/studio-validation.json +6202 -0
  75. package/skills/ti-synthengine/references/troubleshooting.md +109 -121
  76. package/skills/ti-synthengine/references/unified-bypass-performance.json +22425 -0
  77. package/skills/ti-synthengine/references/unified-validation.json +314 -0
  78. package/AGENTS-VERCEL-RESEARCH.md +0 -175
@@ -1,11 +1,10 @@
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/sound-design.md` SHA-256: `4ccbb55b92879c59d20116aff942e16d431c619341b3d4714da4c50c3c691cf9`.
1
+ > Source snapshot: official TiSynthEngine 1.0.0 documentation at release source commit `e7cbbe8a6f9ab38088fd8029c7b43cef6501101f` (2026-10-24). Upstream `documentation/sound-design.md` SHA-256: `dd543e07c825aaf67a0a5f85619088a8d02721a9d33dbf4579edf760dad3e262`.
2
2
 
3
3
  # Sound design with TiSynthEngine
4
4
 
5
- TiSynthEngine builds sounds from oscillators. There is no recorded sample
6
- underneath a tone: pitch, waveform, envelope, movement and level describe the
7
- whole sound. That small set of controls is enough for interface feedback,
8
- musical cues, alarms, retro effects and simple percussion.
5
+ TiSynthEngine generates sound with oscillators, without recorded samples. Pitch, waveform, envelope, movement and level define each sound, from interface feedback and musical cues to alarms, retro effects and simple percussion.
6
+
7
+ Additional [curated recipes](recipes.md) provide application sound and interaction variants.
9
8
 
10
9
  The examples assume the engine is running and use these constants:
11
10
 
@@ -31,95 +30,68 @@ var WAVE = synth.getDefaults().waveTypes
31
30
  - [Mixing profiles and limiter](#mixing-profiles-and-limiter)
32
31
  - [Speakers, headphones and devices](#speakers-headphones-and-devices)
33
32
  - [Recipes](#recipes)
33
+ - [Holding a sound and adding movement](#holding-a-sound-and-adding-movement)
34
+ - [Native space and creative drive](#native-space-and-creative-drive)
34
35
 
35
36
  <!-- TOC-END -->
36
37
 
37
38
  ## Notes, frequencies and octaves
38
39
 
39
- Frequency is the number of waveform cycles per second, measured in hertz. A
40
- 440 Hz oscillator repeats 440 times each second. TiSynthEngine accepts a
41
- frequency directly or converts a note name for you:
40
+ Frequency is the number of waveform cycles per second, measured in hertz. A 440 Hz oscillator repeats 440 times each second. TiSynthEngine accepts a frequency directly or converts a note name for you:
42
41
 
43
42
  ```javascript
44
43
  synth.playTone({ frequency: 440 })
45
44
  synth.playTone({ note: 'A4' })
46
45
  ```
47
46
 
48
- Both calls have the same pitch. Note names use an octave number: `C4` is middle
49
- C, `C5` is one octave higher, and `C3` is one octave lower. Moving up one
50
- octave doubles frequency; moving down halves it.
47
+ Both calls have the same pitch. Note names use an octave number: `C4` is middle C, `C5` is one octave higher, and `C3` is one octave lower. Moving up one octave doubles frequency; moving down halves it.
51
48
 
52
- The note parser accepts English letters and solfege. Sharps use `#`; flats use
53
- a lowercase `b`.
49
+ The note parser accepts English letters and solfege. Sharps use `#`; flats use a lowercase `b`.
54
50
 
55
51
  ```text
56
52
  C4 C#4 Db4 D4
57
53
  DO4 DO#4 REb4 RE4
58
54
  ```
59
55
 
60
- When no octave is present, octave 4 is used. The supported octave digit is 0
61
- through 9, subject to the current frequency ceiling.
56
+ When no octave is present, octave 4 is used. The supported octave digit is 0 through 9, subject to the current frequency ceiling.
62
57
 
63
- The tuning system is twelve-tone equal temperament with A4 at 440 Hz. Each
64
- semitone multiplies frequency by the twelfth root of two:
58
+ The tuning system is twelve-tone equal temperament with A4 at 440 Hz. Each semitone multiplies frequency by the twelfth root of two:
65
59
 
66
60
  ```text
67
61
  frequency = 440 * 2 ^ ((midiNote - 69) / 12)
68
62
  ```
69
63
 
70
- Equal temperament keeps intervals consistent in every key. It also means a
71
- major third is the practical keyboard interval, not a pure 5:4 frequency
72
- ratio. That distinction rarely matters for game or interface sounds.
64
+ Equal temperament keeps intervals consistent in every key. It also means a major third is the practical keyboard interval, not a pure 5:4 frequency ratio. That distinction rarely matters for game or interface sounds.
73
65
 
74
66
  ## Waveform and timbre
75
67
 
76
- Pitch says how fast the oscillator repeats. Waveform says how it moves within
77
- each cycle, which determines the harmonic content and perceived timbre.
68
+ Pitch says how fast the oscillator repeats. Waveform says how it moves within each cycle, which determines the harmonic content and perceived timbre.
78
69
 
79
70
  ### Sine
80
71
 
81
- `SINE` contains the fundamental and no intentional overtones. It works for
82
- clean beeps, soft bass notes and sounds that should stay out of the way. On a
83
- small speaker, very low sines may be quiet or may excite the speaker's own
84
- distortion because there are no upper harmonics to carry the pitch.
72
+ `SINE` contains the fundamental and no intentional overtones. Use it for clean beeps, soft bass notes and unobtrusive sounds. On a small speaker, very low sines may be quiet or may excite the speaker's own distortion because there are no upper harmonics to carry the pitch.
85
73
 
86
74
  ### Square
87
75
 
88
- `SQUARE` contains strong odd harmonics. It sounds hollow, bright and electronic.
89
- It is a good starting point for retro game cues and clear melodic prompts. The
90
- extra high-frequency energy makes it more assertive than a sine at the same
91
- pitch.
76
+ `SQUARE` contains strong odd harmonics. It sounds hollow, bright and electronic. Try it for retro game cues and clear melodic prompts. The extra high-frequency energy makes it more assertive than a sine at the same pitch.
92
77
 
93
78
  ### Sawtooth
94
79
 
95
- `SAWTOOTH` contains both even and odd harmonics. It is the densest pitched
96
- waveform in the module and reads as buzzy or brassy. Use it for alarms, arcade
97
- leads and short sweeps. Square and sawtooth are band-limited with PolyBLEP to
98
- reduce aliasing at discontinuities.
80
+ `SAWTOOTH` contains both even and odd harmonics. It is the densest pitched waveform in the module and reads as buzzy or brassy. Use it for alarms, arcade leads and short sweeps. Square and sawtooth are band-limited with PolyBLEP to reduce aliasing at discontinuities.
99
81
 
100
82
  ### Triangle
101
83
 
102
- `TRIANGLE` contains odd harmonics that fall off faster than a square wave. It
103
- has more body than a sine without the hard edge of a square. It suits gentle
104
- melodies, pads and xylophone-like sketches.
84
+ `TRIANGLE` contains odd harmonics that fall off faster than a square wave. It has more body than a sine without the hard edge of a square. It suits gentle melodies, pads and xylophone-like sketches.
105
85
 
106
86
  ### Noise
107
87
 
108
- `NOISE` produces white noise rather than a repeating pitched cycle. Its
109
- `frequency` is accepted because every sound event uses the same contract, but
110
- changing that value does not tune the noise. A short envelope turns it into a
111
- click, hat or impact layer; a long envelope creates wind or static.
88
+ `NOISE` produces white noise rather than a repeating pitched cycle. Its `frequency` is accepted because every sound event uses the same contract, but changing that value does not tune the noise. A short envelope turns it into a click, hat or impact layer; a long envelope creates wind or static.
112
89
 
113
- `noiseRate` changes the source into variable-rate sample-and-hold noise. Zero
114
- keeps the white-noise default. Lower rates hold each pseudo-random value longer
115
- and sound grainier. `noiseRateEnd` moves the rate over the event.
90
+ `noiseRate` changes the source into variable-rate sample-and-hold noise. Zero keeps the white-noise default. Lower rates hold each pseudo-random value longer and sound grainier. `noiseRateEnd` moves the rate over the event.
116
91
 
117
92
  ### Wavetable
118
93
 
119
- `WAVETABLE` repeats 2 to 64 values supplied in `waveTable`. Values range from
120
- -1 to 1 and are read as steps, without interpolation. Short asymmetric tables
121
- are useful for coarse DAC-like voices, buzzy radio tones and original retro
122
- effects that do not fit the four built-in oscillator shapes.
94
+ `WAVETABLE` repeats 2 to 64 values supplied in `waveTable`. Values range from -1 to 1 and are read as steps, without interpolation. Short asymmetric tables are useful for coarse DAC-like voices, buzzy radio tones and original retro effects that do not fit the four built-in oscillator shapes.
123
95
 
124
96
  ```javascript
125
97
  synth.playTone({
@@ -134,31 +106,20 @@ synth.playTone({
134
106
  Try the built-in sources at a moderate level before choosing one:
135
107
 
136
108
  ```javascript
137
- ;[WAVE.SINE, WAVE.SQUARE, WAVE.SAWTOOTH, WAVE.TRIANGLE, WAVE.NOISE]
138
- .forEach(function (waveType, index) {
139
- setTimeout(function () {
140
- synth.playTone({
141
- note: 'A4',
142
- waveType: waveType,
143
- duration: 450,
144
- attack: 8,
145
- release: 120,
146
- volume: 0.65
147
- })
148
- }, index * 550)
149
- })
109
+ synth.playSequence({ timeUnit: 'ms', stepDuration: 550,
110
+ voice: { attack: 8, release: 120, volume: 0.65 },
111
+ steps: [WAVE.SINE, WAVE.SQUARE, WAVE.SAWTOOTH, WAVE.TRIANGLE, WAVE.NOISE]
112
+ .map(function (waveType) {
113
+ return { notes: [69], length: 330, overrides: { waveType: waveType } }
114
+ })
115
+ })
150
116
  ```
151
117
 
152
118
  ## Filter, pulse width and quantization
153
119
 
154
- `lowPassHz` enables a per-voice one-pole low-pass filter. It is most obvious on
155
- noise, square and sawtooth sources. Zero bypasses the filter. For resonance,
156
- high-pass and band-pass shapes or a moving cutoff, use the
157
- [resonant filter](#resonant-filter) below.
120
+ `lowPassHz` enables a per-voice one-pole low-pass filter. It is most obvious on noise, square and sawtooth sources. Zero bypasses the filter. For resonance, high-pass and band-pass shapes or a moving cutoff, use the [resonant filter](#resonant-filter) below.
158
121
 
159
- `pulseWidth` controls how much of each square-wave cycle stays high. The 0.5
160
- default is a normal square. Values closer to 0.05 or 0.95 become thinner and
161
- brighter. `pulseWidthEnd` creates a linear duty-cycle sweep.
122
+ `pulseWidth` controls how much of each square-wave cycle stays high. The 0.5 default is a normal square. Values closer to 0.05 or 0.95 become thinner and brighter. `pulseWidthEnd` creates a linear duty-cycle sweep.
162
123
 
163
124
  ```javascript
164
125
  synth.playTone({
@@ -172,10 +133,7 @@ synth.playTone({
172
133
  })
173
134
  ```
174
135
 
175
- `bitDepth` rounds the oscillator or noise amplitude to a fixed number of
176
- levels before filtering. Values from 2 to 16 are accepted. Zero preserves the
177
- floating-point signal. Quantization adds harmonic grit; it does not change the
178
- audio device's output format.
136
+ `bitDepth` rounds the oscillator or noise amplitude to a fixed number of levels before filtering. Values from 2 to 16 are accepted. Zero preserves the floating-point signal. Quantization adds harmonic grit; it does not change the audio device's output format.
179
137
 
180
138
  ## Envelope and total duration
181
139
 
@@ -186,16 +144,11 @@ silence -> attack -> full level -> release -> silence
186
144
  |<---------- duration ---------->|
187
145
  ```
188
146
 
189
- `duration` is the total time, not the sustain time. If duration is 500 ms,
190
- attack is 50 ms and release is 100 ms, the full-level middle lasts 350 ms.
147
+ `duration` is the total time, not the sustain time. If duration is 500 ms, attack is 50 ms and release is 100 ms, the full-level middle lasts 350 ms.
191
148
 
192
- Short attacks feel immediate. A 0 or 1 ms attack can be useful for percussion,
193
- but an abrupt start at an unlucky phase can sound like a click. Values between
194
- 5 and 15 ms keep most game feedback crisp without a hard edge.
149
+ Short attacks feel immediate. A 0 or 1 ms attack can be useful for percussion, but an abrupt start at an unlucky phase can sound like a click. Values between 5 and 15 ms keep most game feedback crisp without a hard edge.
195
150
 
196
- Long attacks soften the onset. Use 100 to 500 ms for pads and slow warnings.
197
- Release controls how the sound leaves: 20 to 80 ms feels tight, while several
198
- hundred milliseconds leaves a tail.
151
+ Long attacks soften the onset. Use 100 to 500 ms for pads and slow warnings. Release controls how the sound leaves: 20 to 80 ms feels tight, while several hundred milliseconds leaves a tail.
199
152
 
200
153
  Explicit attack and release values must fit. These relationships are required:
201
154
 
@@ -204,15 +157,11 @@ attack < duration
204
157
  release <= duration - attack
205
158
  ```
206
159
 
207
- Defaults are adjusted internally if a very short duration leaves no room, but
208
- values supplied by the caller are never reshaped silently.
160
+ Defaults are adjusted internally if a very short duration leaves no room, but values supplied by the caller are never reshaped silently.
209
161
 
210
162
  ## Decay and sustain
211
163
 
212
- Plucked strings, struck keys and mallets start loud and settle. A flat
213
- envelope cannot do that, so `decay` and `sustain` add a stage after the attack:
214
- the level falls from full scale to `sustain` over `decay` milliseconds, then
215
- holds until the release.
164
+ Plucked strings, struck keys and mallets start loud and settle. A flat envelope cannot do that, so `decay` and `sustain` add a stage after the attack: the level falls from full scale to `sustain` over `decay` milliseconds, then holds until the release.
216
165
 
217
166
  ```text
218
167
  decay: 0 flat tone, the default
@@ -223,22 +172,16 @@ decay: 2000, sustain: 0.02 bell or mallet: fades by itself
223
172
  Practical starting regions:
224
173
 
225
174
  - plucks and keys: attack 2 to 10 ms, decay 100 to 400 ms, sustain 0.1 to 0.4;
226
- - struck percussion with a pitch drop: decay about half the duration, sustain
227
- near 0.3;
175
+ - struck percussion with a pitch drop: decay about half the duration, sustain near 0.3;
228
176
  - bells and mallets: decay over most of the duration, sustain near 0.
229
177
 
230
- `sustain` below 1 needs a nonzero `decay`. Without one the level would jump
231
- instantly, which clicks, so the call is rejected with `sustain_without_decay`.
178
+ `sustain` below 1 needs a nonzero `decay`. Without one the level would jump instantly, which clicks, so the call is rejected with `sustain_without_decay`.
232
179
 
233
- The release scales the level the note has reached. If the release begins while
234
- the decay is still running, the note leaves smoothly from that point. The
235
- decay still has to fit: `attack` plus `decay` cannot exceed `duration`, and the
236
- 50 ms default attack counts. Set a short `attack` explicitly for a tight pluck.
180
+ The release scales the level the note has reached. If the release begins while the decay is still running, the note leaves smoothly from that point. The decay still has to fit: `attack` plus `decay` cannot exceed `duration`, and the 50 ms default attack counts. Set a short `attack` explicitly for a tight pluck.
237
181
 
238
182
  ## Pan and movement
239
183
 
240
- `pan` ranges from -1 on the left to 1 on the right. Zero is centered. Stereo
241
- movement is a straight interpolation from `pan` to `panEnd` over the event.
184
+ `pan` ranges from -1 on the left to 1 on the right. Zero is centered. Stereo movement is a straight interpolation from `pan` to `panEnd` over the event.
242
185
 
243
186
  ```javascript
244
187
  synth.playTone({
@@ -252,11 +195,9 @@ synth.playTone({
252
195
  })
253
196
  ```
254
197
 
255
- Phone speakers are often mono or close together, so pan may be subtle. Treat it
256
- as extra detail rather than the only cue that distinguishes two sounds.
198
+ Phone speakers are often mono or close together, so pan may be subtle. Treat it as extra detail rather than the only cue that distinguishes two sounds.
257
199
 
258
- `frequencyEnd` makes a linear pitch sweep. Use an upward sweep for activation
259
- or reward and a downward sweep for dismissal, power-down or impact.
200
+ `frequencyEnd` makes a linear pitch sweep. Use an upward sweep for activation or reward and a downward sweep for dismissal, power-down or impact.
260
201
 
261
202
  ```javascript
262
203
  synth.playTone({
@@ -272,8 +213,7 @@ synth.playTone({
272
213
 
273
214
  ## Vibrato and the LFO
274
215
 
275
- The low-frequency oscillator modulates pitch. `lfoFreq` is the modulation rate
276
- in cycles per second, and `lfoDepth` is the maximum pitch movement in hertz.
216
+ The low-frequency oscillator modulates pitch. `lfoFreq` is the modulation rate in cycles per second, and `lfoDepth` is the maximum pitch movement in hertz.
277
217
 
278
218
  ```javascript
279
219
  synth.playTone({
@@ -287,37 +227,21 @@ synth.playTone({
287
227
  })
288
228
  ```
289
229
 
290
- A rate around 4 to 7 Hz with a small depth sounds like musical vibrato. A fast
291
- rate or large depth becomes an effect. Because depth is measured in hertz, 8 Hz
292
- is a larger musical interval on a low note than on a high note.
230
+ A rate around 4 to 7 Hz with a small depth sounds like musical vibrato. A fast rate or large depth becomes an effect. Because depth is measured in hertz, 8 Hz is a larger musical interval on a low note than on a high note.
293
231
 
294
232
  ## Resonant filter
295
233
 
296
- `filterType` adds a filter that removes or emphasizes part of the spectrum. It
297
- has 12 dB per octave of slope, twice as steep as the one-pole `lowPassHz`
298
- filter, and can be combined with it (`lowPassHz` runs first).
234
+ `filterType` adds a filter that removes or emphasizes part of the spectrum. It has 12 dB per octave of slope, twice as steep as the one-pole `lowPassHz` filter, and can be combined with it (`lowPassHz` runs first).
299
235
 
300
- - `lowpass` darkens the sound. A sawtooth through a closing low-pass reads as a
301
- zap or a sound moving away; an opening one reads as a swell.
302
- - `highpass` thins the sound by removing body. It makes hats, hiss and tinny
303
- effects out of noise.
304
- - `bandpass` keeps one band around `filterHz`, which gives vocal, nasal and
305
- wah-like colors.
236
+ - `lowpass` darkens the sound. A sawtooth through a closing low-pass reads as a zap or a sound moving away; an opening one reads as a swell.
237
+ - `highpass` thins the sound by removing body. It makes hats, hiss and tinny effects out of noise.
238
+ - `bandpass` keeps one band around `filterHz`, which gives vocal, nasal and wah-like colors.
306
239
 
307
- Filters shape harmonics, so give them a source that has some: `SAWTOOTH`,
308
- `SQUARE` and `NOISE` respond strongly. A sine has only its fundamental, so a
309
- filter mostly changes its level.
240
+ Filters shape harmonics, so give them a source that has some: `SAWTOOTH`, `SQUARE` and `NOISE` respond strongly. A sine has only its fundamental, so a filter mostly changes its level.
310
241
 
311
- `filterQ` is the resonance. The default 0.7071 is flat. Values from 2 to 4 add
312
- character, and above about 6 the filter whistles around the cutoff. A low-pass
313
- or high-pass boosts the cutoff region by about `filterQ`, so Q 10 is roughly
314
- 20 dB. Lower the event `volume` as Q goes up. The limiter catches peaks, but a
315
- resonance that keeps hitting it makes the whole mix pump. The band-pass keeps
316
- unity gain at its center for every Q and only narrows.
242
+ `filterQ` is the resonance. The default 0.7071 is flat. Values from 2 to 4 add character, and above about 6 the filter whistles around the cutoff. A low-pass or high-pass boosts the cutoff region by about `filterQ`, so Q 10 is roughly 20 dB. Lower the event `volume` as Q goes up. The limiter catches peaks, but a resonance that keeps hitting it makes the whole mix pump. The band-pass keeps unity gain at its center for every Q and only narrows.
317
243
 
318
- `filterHzEnd` moves the cutoff over the whole event. The motion is exponential,
319
- so each second covers the same musical distance. Set it above `filterHz` to
320
- open the filter and below to close it. Zero disables the sweep.
244
+ `filterHzEnd` moves the cutoff over the whole event. The motion is exponential, so each second covers the same musical distance. Set it above `filterHz` to open the filter and below to close it. Zero disables the sweep.
321
245
 
322
246
  ```javascript
323
247
  synth.playTone({
@@ -336,39 +260,19 @@ synth.playTone({
336
260
 
337
261
  ## Frequency modulation
338
262
 
339
- FM makes rich, bright and metallic sounds from a plain sine. A second sine, the
340
- modulator, bends the pitch of the first one, the carrier. Turn it on with
341
- `fmDepth` or `fmDepthEnd`, on a `SINE` voice.
342
-
343
- - `fmRatio` is the modulator frequency as a multiple of the carrier's. Whole
344
- numbers (1, 2, 3) keep the added overtones on the harmonic series, so the
345
- pitch stays clear and the sound reads as organ- or piano-like. Fractional
346
- ratios such as 1.4142 or 3.5 put the overtones between harmonics, which reads
347
- as bells, glass and metal.
348
- - `fmDepth` is the modulation index and works like a brightness control. Around
349
- 0.5 to 1.5 it warms a sine, 2 to 5 makes it bright and rich, and values near
350
- 10 get harsh.
351
- - `fmDepthEnd` moves the index over the event. Starting high and ending low
352
- gives a bright attack that mellows, which is the signature of electric pianos,
353
- bells and mallets. Pair it with `decay` so the level falls with the brightness.
354
-
355
- FM does not change the sine's amplitude, so gain staging is the same as for a
356
- plain sine. There is no band-limiting: a high carrier with a large index and
357
- ratio puts sidebands above the Nyquist frequency, where they alias into
358
- inharmonic noise. Keep bell carriers modest, around 500 Hz, or lower the index.
263
+ FM makes rich, bright and metallic sounds from a plain sine. A second sine, the modulator, bends the pitch of the first one, the carrier. Turn it on with `fmDepth` or `fmDepthEnd`, on a `SINE` voice.
264
+
265
+ - `fmRatio` is the modulator frequency as a multiple of the carrier's. Whole numbers (1, 2, 3) keep the added overtones on the harmonic series, so the pitch stays clear and the sound reads as organ- or piano-like. Fractional ratios such as 1.4142 or 3.5 put the overtones between harmonics, which reads as bells, glass and metal.
266
+ - `fmDepth` is the modulation index and works like a brightness control. Around 0.5 to 1.5 it warms a sine, 2 to 5 makes it bright and rich, and values near 10 get harsh.
267
+ - `fmDepthEnd` moves the index over the event. Starting high and ending low gives a bright attack that mellows, which is the signature of electric pianos, bells and mallets. Pair it with `decay` so the level falls with the brightness.
268
+
269
+ FM does not change the sine's amplitude, so gain staging is the same as for a plain sine. There is no band-limiting: a high carrier with a large index and ratio puts sidebands above the Nyquist frequency, where they alias into inharmonic noise. Keep bell carriers modest, around 500 Hz, or lower the index.
359
270
 
360
271
  ## Detune
361
272
 
362
- `detune` shifts a pitch by cents, hundredths of a semitone. Two tones a few
363
- cents apart drift in and out of phase and beat at a rate close to
364
- `frequency * (2 ^ (cents / 1200) - 1)`. A 12-cent gap at 440 Hz beats about
365
- 3 times a second. Slow beating (0.5 to 4 Hz) sounds warm and wide, like a
366
- chorus; past about 20 cents it starts to sound out of tune.
273
+ `detune` shifts a pitch by cents, hundredths of a semitone. Two tones a few cents apart drift in and out of phase and beat at a rate close to `frequency * (2 ^ (cents / 1200) - 1)`. A 12-cent gap at 440 Hz beats about 3 times a second. Slow beating (0.5 to 4 Hz) sounds warm and wide, like a chorus; past about 20 cents it starts to sound out of tune.
367
274
 
368
- On a single tone, `detune` just retunes it, which is handy for small variations
369
- so a repeated effect does not sound identical. The effect comes from stacking
370
- detuned copies. In a chord, give `detunes` one entry per pitch and repeat a
371
- pitch to thicken it:
275
+ Use `detune` on a single tone to vary the pitch slightly between repetitions. The effect comes from stacking detuned copies. In a chord, give `detunes` one entry per pitch and repeat a pitch to thicken it:
372
276
 
373
277
  ```javascript
374
278
  synth.playChord({
@@ -382,20 +286,11 @@ synth.playChord({
382
286
  })
383
287
  ```
384
288
 
385
- Each repeated pitch is a voice, so size `maxVoices` for the full stack. Protected
386
- profiles divide per-voice source gain by the square root of the event voice
387
- count. This reduces buildup; it does not guarantee equal loudness. Correlated
388
- voices can still reinforce each other, and detuned voices can produce peaks
389
- as their phases align. `raw` does not apply this event normalization.
289
+ Each repeated pitch is a voice, so size `maxVoices` for the full stack. Protected profiles divide per-voice source gain by the square root of the event voice count. This reduces buildup; it does not guarantee equal loudness. Correlated voices can still reinforce each other, and detuned voices can produce peaks as their phases align. `raw` does not apply this event normalization.
390
290
 
391
291
  ## Chords, polyphony and gain
392
292
 
393
- `playChord` is for notes that should start together. The call is placed in the
394
- native queue as one event, so the audio callback sees the whole chord or none
395
- of it. In protected profiles, per-voice source gain is divided by the square
396
- root of the chord's voice count. This approximately stabilizes power for
397
- uncorrelated voices, not coherent copies; it does not guarantee constant peaks
398
- or perceived loudness. `raw` bypasses this normalization.
293
+ `playChord` is for notes that should start together. The call is placed in the native queue as one event, so the audio callback sees the whole chord or none of it. In protected profiles, per-voice source gain is divided by the square root of the chord's voice count. This approximately stabilizes power for uncorrelated voices, not coherent copies; it does not guarantee constant peaks or perceived loudness. `raw` bypasses this normalization.
399
294
 
400
295
  ```javascript
401
296
  synth.playChord({
@@ -409,42 +304,28 @@ synth.playChord({
409
304
  })
410
305
  ```
411
306
 
412
- Four separate `playTone` calls are four independent full-gain events. They are
413
- not atomic and may start in different audio callbacks. Their sum is also hotter
414
- and can push the limiter. Separate calls are correct for unrelated taps or
415
- effects; use `playChord` when the app already knows the pitches are one chord.
307
+ Four separate `playTone` calls are four independent full-gain events. They are not atomic and may start in different audio callbacks. Their sum is also hotter and can push the limiter. Separate calls are correct for unrelated taps or effects; use `playChord` when the app already knows the pitches are one chord.
416
308
 
417
- `maxVoices` is capacity, not a loudness control. When the pool is full, the
418
- voice with the least time remaining is faded and reused. Leave a small margin for release tails, but do
419
- not set 32 by habit when the app can only make four sounds at once.
309
+ `maxVoices` sets voice capacity without changing loudness. Timed-only pools replace the least remaining duration; held/transport pools prefer a releasing voice, then the oldest attack. Leave a small margin for release tails, but do not set 32 by habit when the app can only make four sounds at once.
420
310
 
421
311
  ## Mixing profiles and limiter
422
312
 
423
- Choose the profile once in `startEngine()`:
313
+ Choose the profile once in `start()`:
424
314
 
425
- | Profile | Practical use |
426
- | --- | --- |
427
- | `conservative` | Default for mixed effects, chords and unpredictable overlap. |
428
- | `balanced` | More level for mostly monophonic sounds. |
429
- | `speakerSafe` | Lower source gain and a 0.70 ceiling for small speakers. |
430
- | `raw` | Full source gain; useful only when the app owns all gain staging. |
315
+ | Profile | Practical use |
316
+ | -------------- | ----------------------------------------------------------------- |
317
+ | `conservative` | Default for mixed effects, chords and unpredictable overlap. |
318
+ | `balanced` | More level for mostly monophonic sounds. |
319
+ | `speakerSafe` | Lower source gain and a 0.70 ceiling for small speakers. |
320
+ | `raw` | Full source gain; useful only when the app owns all gain staging. |
431
321
 
432
- Protected profiles calibrate source gain by waveform and event voice count.
433
- The final stereo-linked limiter catches exceptional peaks with 10 ms of
434
- lookahead and a 100 ms release. Its ceiling is 0.85, or 0.70 for `speakerSafe`.
322
+ Protected profiles calibrate source gain by waveform and event voice count. The final stereo-linked limiter catches exceptional peaks with 10 ms of lookahead and a 100 ms release. Its ceiling is 0.85, or 0.70 for `speakerSafe`.
435
323
 
436
- The limiter is protection, not a loudness effect. If it works on every note,
437
- lower event volume or choose a more conservative profile. Repeatedly starting
438
- the same long sound before its previous release ends raises and lowers the sum,
439
- which can produce audible pumping. That overlap is useful as a stress test, but
440
- it is a poor reference for the tone's normal quality.
324
+ The limiter protects against peaks. If it engages on every note, lower event volume or choose a more conservative profile instead of using it to raise loudness. Repeatedly starting the same long sound before its previous release ends raises and lowers the sum, which can produce audible pumping. That overlap is useful as a stress test, but it is a poor reference for the tone's normal quality.
441
325
 
442
326
  ## Speakers, headphones and devices
443
327
 
444
- Headphones usually reproduce bass and stereo movement more cleanly than a phone
445
- speaker. A sound that is smooth in headphones can buzz on a small driver even
446
- when the digital signal never clips. Low pitches and square or sawtooth waves
447
- are the most demanding.
328
+ Headphones usually reproduce bass and stereo movement more cleanly than a phone speaker. A sound that is smooth in headphones can buzz on a small driver even when the digital signal never clips. Low pitches and square or sawtooth waves are the most demanding.
448
329
 
449
330
  For speaker-facing sounds:
450
331
 
@@ -454,15 +335,11 @@ For speaker-facing sounds:
454
335
  - test at several system volume settings on the target device;
455
336
  - keep critical meaning in rhythm and pitch, not pan alone.
456
337
 
457
- Different phones have different sample rates, speaker protection and acoustic
458
- resonances. Test the real device family. A desktop browser or simulator cannot
459
- answer whether a small speaker will rattle.
338
+ Different phones have different sample rates, speaker protection and acoustic resonances. Test the real device family. A desktop browser or simulator cannot answer whether a small speaker will rattle.
460
339
 
461
340
  ## Recipes
462
341
 
463
- These recipes use only the 1.0.0 contract and keep explicit envelopes valid.
464
- Tune pitch and volume for the app's other audio rather than treating the values
465
- as mastering targets.
342
+ These recipes use only the 1.0.0 contract and keep explicit envelopes valid. Adjust pitch and volume to fit the app's other audio; the recipe values are starting points, not mastering targets.
466
343
 
467
344
  ### Interface click
468
345
 
@@ -477,8 +354,7 @@ synth.playTone({
477
354
  })
478
355
  ```
479
356
 
480
- The short sine is cleaner than a full-band noise click. Raise the frequency for
481
- a lighter control or lower it for a heavier one.
357
+ The short sine is cleaner than a full-band noise click. Raise the frequency for a lighter control or lower it for a heavier one.
482
358
 
483
359
  ### Confirmation hit
484
360
 
@@ -511,18 +387,16 @@ synth.playChord({
511
387
  ### Two-tone alarm
512
388
 
513
389
  ```javascript
514
- synth.playPattern({
515
- bpm: 150,
516
- noteValue: '8n',
517
- attack: 8,
518
- release: 70,
519
- volume: 0.55,
520
- waveType: WAVE.SAWTOOTH,
390
+ synth.setTempo({ bpm: 150 })
391
+ synth.playSequence({
392
+ timeUnit: 'beats',
393
+ stepDuration: 0.5,
394
+ voice: { attack: 8, release: 70, volume: 0.55, waveType: WAVE.SAWTOOTH },
521
395
  steps: [
522
- { note: 'A4' },
523
- { note: 'E5' },
524
- { note: 'A4' },
525
- { note: 'E5' }
396
+ { notes: [69] },
397
+ { notes: [76] },
398
+ { notes: [69] },
399
+ { notes: [76] }
526
400
  ]
527
401
  })
528
402
  ```
@@ -530,50 +404,43 @@ synth.playPattern({
530
404
  ### Retro pickup
531
405
 
532
406
  ```javascript
533
- synth.playPattern({
534
- bpm: 180,
407
+ synth.setTempo({ bpm: 180 })
408
+ synth.playSequence({
409
+ timeUnit: 'beats',
410
+ stepDuration: 0.25,
535
411
  swing: 0.06,
536
- noteValue: '16n',
537
- attack: 2,
538
- release: 45,
539
- volume: 0.5,
540
- waveType: WAVE.SQUARE,
412
+ voice: { attack: 2, release: 45, volume: 0.5, waveType: WAVE.SQUARE },
541
413
  steps: [
542
- { note: 'C5' },
543
- { note: 'E5' },
544
- { note: 'G5' },
545
- { note: 'C6', noteValue: '8n' }
414
+ { notes: [72] },
415
+ { notes: [76] },
416
+ { notes: [79] },
417
+ { notes: [84], duration: 0.5 }
546
418
  ]
547
419
  })
548
420
  ```
549
421
 
550
- The first three steps use sixteenth notes and the last uses an eighth note.
551
- `release` fades each voice within that rhythmic length. The pattern root does
552
- not accept `duration`; a step `duration` replaces its rhythmic length as well
553
- as its voice length. There is no separate gate parameter.
422
+ The first three intervals are sixteenth notes and the last is an eighth note. Gate sets when release begins; `length` overrides that note duration independently of the next attack.
554
423
 
555
424
  ### Noise percussion
556
425
 
557
426
  ```javascript
558
- synth.playPattern({
559
- bpm: 112,
560
- noteValue: '8n',
561
- waveType: WAVE.NOISE,
562
- volume: 0.34,
427
+ synth.setTempo({ bpm: 112 })
428
+ synth.playSequence({
429
+ timeUnit: 'beats',
430
+ stepDuration: 0.5,
431
+ voice: { waveType: WAVE.NOISE, volume: 0.34 },
563
432
  steps: [
564
- { frequency: 220, attack: 1, decay: 65, sustain: 0, release: 55 },
433
+ { frequencies: [220], overrides: { attack: 1, decay: 65, sustain: 0, release: 55 } },
565
434
  { rest: true },
566
- { frequency: 220, attack: 1, decay: 35, sustain: 0, release: 28 },
567
- { frequency: 220, attack: 1, decay: 35, sustain: 0, release: 28 }
435
+ { frequencies: [220], overrides: { attack: 1, decay: 35, sustain: 0, release: 28 } },
436
+ { frequencies: [220], overrides: { attack: 1, decay: 35, sustain: 0, release: 28 } }
568
437
  ]
569
438
  })
570
439
  ```
571
440
 
572
- The steps keep the eighth-note grid while short decay and `sustain: 0` let
573
- each hit settle to silence inside its step.
441
+ The steps keep the eighth-note grid while short decay and `sustain: 0` let each hit settle to silence inside its step.
574
442
 
575
- Noise steps still need `frequency` or `note` because every sounding pattern
576
- step follows the same shape. The pitch value is not used by the noise source.
443
+ Noise steps still need `frequencies` or `notes` because every sounding pattern step follows the same shape. The pitch value is not used by the noise source.
577
444
 
578
445
  ### Filtered noise burst
579
446
 
@@ -592,9 +459,7 @@ synth.playTone({
592
459
  })
593
460
  ```
594
461
 
595
- The decreasing update rate makes the texture break into slower chunks while
596
- the envelope removes energy. Reverse the two rates for an effect that becomes
597
- more frantic.
462
+ The decreasing update rate makes the texture break into slower chunks while the envelope removes energy. Reverse the two rates for an effect that becomes more frantic.
598
463
 
599
464
  ### Pluck
600
465
 
@@ -629,8 +494,7 @@ synth.playTone({
629
494
  })
630
495
  ```
631
496
 
632
- The index falls from 2.2 to 0.25 while the level settles, so the attack is
633
- bright and the sustain is mellow.
497
+ The index falls from 2.2 to 0.25 while the level settles, so the attack is bright and the sustain is mellow.
634
498
 
635
499
  ### Bell
636
500
 
@@ -650,13 +514,11 @@ synth.playTone({
650
514
  })
651
515
  ```
652
516
 
653
- The fractional ratio makes the overtones inharmonic. Try 1.4142 or 2.76 for a
654
- different metal.
517
+ The fractional ratio makes the overtones inharmonic. Try 1.4142 or 2.76 for a different metal.
655
518
 
656
519
  ### Wah
657
520
 
658
- The wah in [Resonant filter](#resonant-filter) is also a recipe. Reverse the
659
- two cutoffs for a sound that closes instead of opening.
521
+ The wah in [Resonant filter](#resonant-filter) is also a recipe. Reverse the two cutoffs for a sound that closes instead of opening.
660
522
 
661
523
  ### Filtered laser
662
524
 
@@ -711,9 +573,7 @@ synth.playChord({
711
573
  })
712
574
  ```
713
575
 
714
- Six voices play two pitches, each in three slightly different tunings. The
715
- low-pass takes the harshness off the sawtooth stack. Start the engine with
716
- `maxVoices` of at least 6.
576
+ Six voices play two pitches, each in three slightly different tunings. The low-pass takes the harshness off the sawtooth stack. Start the engine with `maxVoices` of at least 6.
717
577
 
718
578
  ### Kick
719
579
 
@@ -731,13 +591,11 @@ synth.playTone({
731
591
  })
732
592
  ```
733
593
 
734
- The pitch drop is the punch and the decay is the thump. A phone speaker cannot
735
- reproduce 42 Hz, so raise `frequencyEnd` toward 70 for speaker playback.
594
+ The pitch drop is the punch and the decay is the thump. A phone speaker cannot reproduce 42 Hz, so raise `frequencyEnd` toward 70 for speaker playback.
736
595
 
737
596
  ### Simon-style cues
738
597
 
739
- Use short, distinct square-wave pitches. Play one tone for the sequence and
740
- one atomic chord only when demonstrating all pads together.
598
+ Use short, distinct square-wave pitches. Play one tone for the sequence and one atomic chord only when demonstrating all pads together.
741
599
 
742
600
  ```javascript
743
601
  var SIMON = [329.63, 440, 554.37, 659.25]
@@ -766,7 +624,25 @@ function playAllSimonPads () {
766
624
  }
767
625
  ```
768
626
 
769
- For more complete code, continue with [Examples](examples.md) and
770
- [Curated sound and interaction recipes](recipes.md). The latter includes retro
771
- effects, a playable xylophone, memory-pad timbres, layered chords and timer
772
- ownership. For gain and device-testing advice, see [Recommendations](recommendations.md).
627
+ See [Examples](examples.md) for complete code and [Recommendations](recommendations.md) for gain and device-testing advice.
628
+
629
+ ## Holding a sound and adding movement
630
+
631
+ Choose a held voice when the ending follows a player action instead of a fixed duration: motor, charge beam, pad or the last chord of a ceremony. `noteOn` starts a positive app-owned ID; `noteOff` begins release from the current level, and compatible `updateVoice` edits ramp over 10 ms. Distinct IDs remain independent events with fixed gain. Reserve enough capacity for their releases and unrelated effects, and release on cancellation as well as ordinary key up. See the [held motor recipe](advanced-examples.md#sustained-game-motor) for retry/cleanup handling.
632
+
633
+ A fanfare can use beat or millisecond sequences with native chords, rests, gate, independent length, swing and pitch-ordered arpeggios. A long release can ring over later steps. Use held voices when release follows presentation or interaction. See [transport usage](guide.md#choose-the-musical-transport).
634
+
635
+ The two additional voice LFOs move filter cutoff, square PWM or sine FM index using sine/triangle shapes. Cutoff movement creates slow breathing or rhythmic filtering; PWM adds changing harmonic color; a small FM-index modulation varies brightness. Free rates reach 20 Hz, and synced rates follow native transport tempo. These slots are separate from pitch vibrato. Avoid deep/high-rate modulation with strong resonance or high-frequency FM; safe parameter clamping does not guarantee a pleasant or alias-free sound. The [two-LFO example](advanced-examples.md#two-voice-lfos) shows a filtered PWM pad.
636
+
637
+ ## Native space and creative drive
638
+
639
+ | Processor | What changes | Useful starting region |
640
+ | --- | --- | --- |
641
+ | Stereo chorus | A short interpolated delay with offset LFO phases adds width and movement. | Rate 0.3 to 0.8 Hz, depth 0.15 to 0.35, mix 0.08 to 0.2. |
642
+ | Ping-pong delay | Alternating echoes create rhythmic or spatial repeats. | 180 to 350 ms or synced eighths, feedback 0.15 to 0.35, mix 0.08 to 0.18. |
643
+ | Stereo reverb | Damped comb/allpass reflections create a diffuse decay. | Decay 0.8 to 1.8 seconds, damping 0.5 to 0.7, mix 0.08 to 0.18. |
644
+ | Optional saturation | Soft waveshaping adds harmonics and changes attack/timbre. | Drive 1.2 to 1.6 with a small blend; compare at matched levels. |
645
+
646
+ All processors are off initially, with input gain 1. Enable one shared chain through `setMasterProcessing`; select `applyTo` categories such as `music` and `ambience` to keep explosions dry. Per-voice `effectSend` overrides that category selection. [Configuration](advanced-examples.md#chorus-delay-reverb-and-creative-saturation) and [exact ranges](api.md#setmasterprocessingoptions) explain chorus, delay, reverb and optional saturation.
647
+
648
+ Disabling an effect closes its input gradually and drains the tail before exact bypass; it does not immediately erase its history. Panic invalidates histories. Saturation is a creative choice rather than automatic gain protection; the final limiter stays in place. Match dry/wet comparison levels and watch accumulated limiter reduction so a louder version does not win an A/B comparison merely by level. None of these effects makes a small speaker reproduce missing bass.