@maccesar/titools 5.1.1 → 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.
- package/CONTRIBUTING.md +69 -0
- package/README.md +112 -72
- package/agents/ti-pro.md +5 -1
- package/commands/ti-audit.md +5 -1
- package/docs/examples.md +91 -0
- package/docs/knowledge-index.md +57 -0
- package/lib/config.js +2 -0
- package/lib/downloader.js +1 -1
- package/package.json +4 -2
- package/skills/purgetss/references/multi-density-images.md +1 -1
- package/skills/ti-admob-unified/SKILL.md +58 -0
- package/skills/ti-admob-unified/agents/openai.yaml +4 -0
- package/skills/ti-admob-unified/assets/example/app.js +1207 -0
- package/skills/ti-admob-unified/assets/example/lib/ads.js +646 -0
- package/skills/ti-admob-unified/assets/example/library-usage.js +136 -0
- package/skills/ti-admob-unified/references/api-reference.md +615 -0
- package/skills/ti-admob-unified/references/banners.md +435 -0
- package/skills/ti-admob-unified/references/contributing.md +233 -0
- package/skills/ti-admob-unified/references/dependencies.md +91 -0
- package/skills/ti-admob-unified/references/fullscreen.md +238 -0
- package/skills/ti-admob-unified/references/game-monetization.md +148 -0
- package/skills/ti-admob-unified/references/getting-started.md +319 -0
- package/skills/ti-admob-unified/references/index.md +33 -0
- package/skills/ti-admob-unified/references/integration.md +87 -0
- package/skills/ti-admob-unified/references/library.md +220 -0
- package/skills/ti-admob-unified/references/mediation-parity.md +17 -0
- package/skills/ti-admob-unified/references/mediation.md +145 -0
- package/skills/ti-admob-unified/references/migration.md +259 -0
- package/skills/ti-admob-unified/references/module-changelog.md +84 -0
- package/skills/ti-admob-unified/references/module-license.txt +21 -0
- package/skills/ti-admob-unified/references/privacy.md +281 -0
- package/skills/ti-admob-unified/references/production.md +67 -0
- package/skills/ti-admob-unified/references/rewarded-interstitial.md +101 -0
- package/skills/ti-admob-unified/references/roadmap.md +143 -0
- package/skills/ti-admob-unified/references/source-notes.md +80 -0
- package/skills/ti-admob-unified/references/source-snapshot.json +644 -0
- package/skills/ti-admob-unified/references/third-party-notices.md +22 -0
- package/skills/ti-game/SKILL.md +31 -5
- package/skills/ti-game/references/api.md +36 -24
- package/skills/ti-game/references/debugging-performance.md +22 -7
- package/skills/ti-game/references/examples.md +113 -20
- package/skills/ti-game/references/input.md +101 -0
- package/skills/ti-game/references/patterns.md +56 -10
- package/skills/ti-game/references/physics.md +530 -0
- package/skills/ti-game/references/project-setup.md +131 -47
- package/skills/ti-game/references/recipes-action.md +149 -18
- package/skills/ti-game/references/recipes-adventure-effects.md +47 -4
- package/skills/ti-game/references/roadmap.md +11 -4
- package/skills/ti-game/references/source-notes.md +81 -0
- package/skills/ti-game/references/tilemaps.md +13 -4
- package/skills/ti-game-assets/SKILL.md +53 -0
- package/skills/ti-game-assets/agents/openai.yaml +4 -0
- package/skills/ti-game-assets/references/alpha.md +26 -0
- package/skills/ti-game-assets/references/art-direction.md +37 -0
- package/skills/ti-game-assets/references/integration.md +49 -0
- package/skills/ti-game-assets/references/manifest.example.json +429 -0
- package/skills/ti-game-assets/references/manifest.md +39 -0
- package/skills/ti-game-assets/references/offline-audio.md +33 -0
- package/skills/ti-game-assets/references/prompts.md +33 -0
- package/skills/ti-game-assets/references/repeating-textures.md +17 -0
- package/skills/ti-game-assets/references/responsible-ads.md +31 -0
- package/skills/ti-game-assets/references/sound-assets.md +42 -0
- package/skills/ti-game-assets/references/vector-sources.md +21 -0
- package/skills/ti-game-assets/scripts/alpha_tools.py +160 -0
- package/skills/ti-game-assets/scripts/requirements.txt +1 -0
- package/skills/ti-game-assets/scripts/validate_manifest.py +267 -0
- package/skills/ti-synthengine/SKILL.md +77 -67
- package/skills/ti-synthengine/assets/example/README.md +84 -0
- package/skills/ti-synthengine/assets/example/app.js +1178 -0
- package/skills/ti-synthengine/assets/example/lib/sequenceOwner.js +83 -0
- package/skills/ti-synthengine/assets/example/lib/sequenceScore.js +25 -0
- package/skills/ti-synthengine/references/advanced-examples.md +174 -0
- package/skills/ti-synthengine/references/android-sim80-correction-evidence.zip +0 -0
- package/skills/ti-synthengine/references/android-sim80-correction.json +162 -0
- package/skills/ti-synthengine/references/api.md +573 -153
- package/skills/ti-synthengine/references/audio-engine.md +272 -124
- package/skills/ti-synthengine/references/bypass-physical-recheck-evidence.zip +0 -0
- package/skills/ti-synthengine/references/examples.md +355 -106
- package/skills/ti-synthengine/references/frontier-android-evidence.zip +0 -0
- package/skills/ti-synthengine/references/frontier-ipad-evidence.zip +0 -0
- package/skills/ti-synthengine/references/frontier-physical-validation.json +1918 -0
- package/skills/ti-synthengine/references/guide.md +426 -75
- package/skills/ti-synthengine/references/performance-review-evidence.zip +0 -0
- package/skills/ti-synthengine/references/performance-review.json +1146 -0
- package/skills/ti-synthengine/references/physical-validation.json +1457 -0
- package/skills/ti-synthengine/references/recipes-interaction.md +549 -0
- package/skills/ti-synthengine/references/recipes.md +434 -420
- package/skills/ti-synthengine/references/recommendations.md +72 -85
- package/skills/ti-synthengine/references/sound-design.md +375 -155
- package/skills/ti-synthengine/references/source-notes.md +109 -0
- package/skills/ti-synthengine/references/source-snapshot.json +452 -0
- package/skills/ti-synthengine/references/stats-performance.json +2992 -0
- package/skills/ti-synthengine/references/stats-performance.md +82 -0
- package/skills/ti-synthengine/references/studio-validation.json +6202 -0
- package/skills/ti-synthengine/references/troubleshooting.md +123 -78
- package/skills/ti-synthengine/references/unified-bypass-performance.json +22425 -0
- package/skills/ti-synthengine/references/unified-validation.json +314 -0
- package/AGENTS-VERCEL-RESEARCH.md +0 -175
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
> Source snapshot: official TiSynthEngine 1.0.0 documentation at commit `
|
|
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
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
|
@@ -19,110 +18,123 @@ var WAVE = synth.getDefaults().waveTypes
|
|
|
19
18
|
|
|
20
19
|
- [Notes, frequencies and octaves](#notes-frequencies-and-octaves)
|
|
21
20
|
- [Waveform and timbre](#waveform-and-timbre)
|
|
21
|
+
- [Filter, pulse width and quantization](#filter-pulse-width-and-quantization)
|
|
22
22
|
- [Envelope and total duration](#envelope-and-total-duration)
|
|
23
|
+
- [Decay and sustain](#decay-and-sustain)
|
|
23
24
|
- [Pan and movement](#pan-and-movement)
|
|
24
25
|
- [Vibrato and the LFO](#vibrato-and-the-lfo)
|
|
26
|
+
- [Resonant filter](#resonant-filter)
|
|
27
|
+
- [Frequency modulation](#frequency-modulation)
|
|
28
|
+
- [Detune](#detune)
|
|
25
29
|
- [Chords, polyphony and gain](#chords-polyphony-and-gain)
|
|
26
30
|
- [Mixing profiles and limiter](#mixing-profiles-and-limiter)
|
|
27
31
|
- [Speakers, headphones and devices](#speakers-headphones-and-devices)
|
|
28
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)
|
|
29
35
|
|
|
30
36
|
<!-- TOC-END -->
|
|
31
37
|
|
|
32
38
|
## Notes, frequencies and octaves
|
|
33
39
|
|
|
34
|
-
Frequency is the number of waveform cycles per second, measured in hertz. A
|
|
35
|
-
440 Hz oscillator repeats 440 times each second. TiSynthEngine accepts a
|
|
36
|
-
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:
|
|
37
41
|
|
|
38
42
|
```javascript
|
|
39
43
|
synth.playTone({ frequency: 440 })
|
|
40
44
|
synth.playTone({ note: 'A4' })
|
|
41
45
|
```
|
|
42
46
|
|
|
43
|
-
Both calls have the same pitch. Note names use an octave number: `C4` is middle
|
|
44
|
-
C, `C5` is one octave higher, and `C3` is one octave lower. Moving up one
|
|
45
|
-
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.
|
|
46
48
|
|
|
47
|
-
The note parser accepts English letters and solfege. Sharps use `#`; flats use
|
|
48
|
-
a lowercase `b`.
|
|
49
|
+
The note parser accepts English letters and solfege. Sharps use `#`; flats use a lowercase `b`.
|
|
49
50
|
|
|
50
51
|
```text
|
|
51
52
|
C4 C#4 Db4 D4
|
|
52
53
|
DO4 DO#4 REb4 RE4
|
|
53
54
|
```
|
|
54
55
|
|
|
55
|
-
When no octave is present, octave 4 is used. The supported octave digit is 0
|
|
56
|
-
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.
|
|
57
57
|
|
|
58
|
-
The tuning system is twelve-tone equal temperament with A4 at 440 Hz. Each
|
|
59
|
-
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:
|
|
60
59
|
|
|
61
60
|
```text
|
|
62
61
|
frequency = 440 * 2 ^ ((midiNote - 69) / 12)
|
|
63
62
|
```
|
|
64
63
|
|
|
65
|
-
Equal temperament keeps intervals consistent in every key. It also means a
|
|
66
|
-
major third is the practical keyboard interval, not a pure 5:4 frequency
|
|
67
|
-
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.
|
|
68
65
|
|
|
69
66
|
## Waveform and timbre
|
|
70
67
|
|
|
71
|
-
Pitch says how fast the oscillator repeats. Waveform says how it moves within
|
|
72
|
-
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.
|
|
73
69
|
|
|
74
70
|
### Sine
|
|
75
71
|
|
|
76
|
-
`SINE` contains the fundamental and no intentional overtones.
|
|
77
|
-
clean beeps, soft bass notes and sounds that should stay out of the way. On a
|
|
78
|
-
small speaker, very low sines may be quiet or may excite the speaker's own
|
|
79
|
-
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.
|
|
80
73
|
|
|
81
74
|
### Square
|
|
82
75
|
|
|
83
|
-
`SQUARE` contains strong odd harmonics. It sounds hollow, bright and electronic.
|
|
84
|
-
It is a good starting point for retro game cues and clear melodic prompts. The
|
|
85
|
-
extra high-frequency energy makes it more assertive than a sine at the same
|
|
86
|
-
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.
|
|
87
77
|
|
|
88
78
|
### Sawtooth
|
|
89
79
|
|
|
90
|
-
`SAWTOOTH` contains both even and odd harmonics. It is the densest pitched
|
|
91
|
-
waveform in the module and reads as buzzy or brassy. Use it for alarms, arcade
|
|
92
|
-
leads and short sweeps. Square and sawtooth are band-limited with PolyBLEP to
|
|
93
|
-
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.
|
|
94
81
|
|
|
95
82
|
### Triangle
|
|
96
83
|
|
|
97
|
-
`TRIANGLE` contains odd harmonics that fall off faster than a square wave. It
|
|
98
|
-
has more body than a sine without the hard edge of a square. It suits gentle
|
|
99
|
-
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.
|
|
100
85
|
|
|
101
86
|
### Noise
|
|
102
87
|
|
|
103
|
-
`NOISE` produces white noise rather than a repeating pitched cycle. Its
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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.
|
|
89
|
+
|
|
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.
|
|
91
|
+
|
|
92
|
+
### Wavetable
|
|
107
93
|
|
|
108
|
-
|
|
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.
|
|
109
95
|
|
|
110
96
|
```javascript
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
97
|
+
synth.playTone({
|
|
98
|
+
note: 'D4',
|
|
99
|
+
duration: 600,
|
|
100
|
+
waveType: WAVE.WAVETABLE,
|
|
101
|
+
waveTable: [-1, -0.8, -0.1, 0.9, 0.45, 0.1, -0.25, -0.6],
|
|
102
|
+
bitDepth: 8
|
|
103
|
+
})
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Try the built-in sources at a moderate level before choosing one:
|
|
107
|
+
|
|
108
|
+
```javascript
|
|
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
|
+
})
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Filter, pulse width and quantization
|
|
119
|
+
|
|
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.
|
|
121
|
+
|
|
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.
|
|
123
|
+
|
|
124
|
+
```javascript
|
|
125
|
+
synth.playTone({
|
|
126
|
+
note: 'A3',
|
|
127
|
+
duration: 700,
|
|
128
|
+
release: 180,
|
|
129
|
+
waveType: WAVE.SQUARE,
|
|
130
|
+
pulseWidth: 0.1,
|
|
131
|
+
pulseWidthEnd: 0.86,
|
|
132
|
+
lowPassHz: 4200
|
|
133
|
+
})
|
|
124
134
|
```
|
|
125
135
|
|
|
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.
|
|
137
|
+
|
|
126
138
|
## Envelope and total duration
|
|
127
139
|
|
|
128
140
|
Every event uses an attack, an implicit sustain section and a release:
|
|
@@ -132,16 +144,11 @@ silence -> attack -> full level -> release -> silence
|
|
|
132
144
|
|<---------- duration ---------->|
|
|
133
145
|
```
|
|
134
146
|
|
|
135
|
-
`duration` is the total time, not the sustain time. If duration is 500 ms,
|
|
136
|
-
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.
|
|
137
148
|
|
|
138
|
-
Short attacks feel immediate. A 0 or 1 ms attack can be useful for percussion,
|
|
139
|
-
but an abrupt start at an unlucky phase can sound like a click. Values between
|
|
140
|
-
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.
|
|
141
150
|
|
|
142
|
-
Long attacks soften the onset. Use 100 to 500 ms for pads and slow warnings.
|
|
143
|
-
Release controls how the sound leaves: 20 to 80 ms feels tight, while several
|
|
144
|
-
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.
|
|
145
152
|
|
|
146
153
|
Explicit attack and release values must fit. These relationships are required:
|
|
147
154
|
|
|
@@ -150,13 +157,31 @@ attack < duration
|
|
|
150
157
|
release <= duration - attack
|
|
151
158
|
```
|
|
152
159
|
|
|
153
|
-
Defaults are adjusted internally if a very short duration leaves no room, but
|
|
154
|
-
|
|
160
|
+
Defaults are adjusted internally if a very short duration leaves no room, but values supplied by the caller are never reshaped silently.
|
|
161
|
+
|
|
162
|
+
## Decay and sustain
|
|
163
|
+
|
|
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.
|
|
165
|
+
|
|
166
|
+
```text
|
|
167
|
+
decay: 0 flat tone, the default
|
|
168
|
+
decay: 260, sustain: 0.18 pluck: loud start, settles low
|
|
169
|
+
decay: 2000, sustain: 0.02 bell or mallet: fades by itself
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Practical starting regions:
|
|
173
|
+
|
|
174
|
+
- plucks and keys: attack 2 to 10 ms, decay 100 to 400 ms, sustain 0.1 to 0.4;
|
|
175
|
+
- struck percussion with a pitch drop: decay about half the duration, sustain near 0.3;
|
|
176
|
+
- bells and mallets: decay over most of the duration, sustain near 0.
|
|
177
|
+
|
|
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`.
|
|
179
|
+
|
|
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.
|
|
155
181
|
|
|
156
182
|
## Pan and movement
|
|
157
183
|
|
|
158
|
-
`pan` ranges from -1 on the left to 1 on the right. Zero is centered. Stereo
|
|
159
|
-
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.
|
|
160
185
|
|
|
161
186
|
```javascript
|
|
162
187
|
synth.playTone({
|
|
@@ -170,11 +195,9 @@ synth.playTone({
|
|
|
170
195
|
})
|
|
171
196
|
```
|
|
172
197
|
|
|
173
|
-
Phone speakers are often mono or close together, so pan may be subtle. Treat it
|
|
174
|
-
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.
|
|
175
199
|
|
|
176
|
-
`frequencyEnd` makes a linear pitch sweep. Use an upward sweep for activation
|
|
177
|
-
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.
|
|
178
201
|
|
|
179
202
|
```javascript
|
|
180
203
|
synth.playTone({
|
|
@@ -190,8 +213,7 @@ synth.playTone({
|
|
|
190
213
|
|
|
191
214
|
## Vibrato and the LFO
|
|
192
215
|
|
|
193
|
-
The low-frequency oscillator modulates pitch. `lfoFreq` is the modulation rate
|
|
194
|
-
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.
|
|
195
217
|
|
|
196
218
|
```javascript
|
|
197
219
|
synth.playTone({
|
|
@@ -205,16 +227,70 @@ synth.playTone({
|
|
|
205
227
|
})
|
|
206
228
|
```
|
|
207
229
|
|
|
208
|
-
A rate around 4 to 7 Hz with a small depth sounds like musical vibrato. A fast
|
|
209
|
-
|
|
210
|
-
|
|
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.
|
|
231
|
+
|
|
232
|
+
## Resonant filter
|
|
233
|
+
|
|
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).
|
|
235
|
+
|
|
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.
|
|
239
|
+
|
|
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.
|
|
241
|
+
|
|
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.
|
|
243
|
+
|
|
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.
|
|
245
|
+
|
|
246
|
+
```javascript
|
|
247
|
+
synth.playTone({
|
|
248
|
+
note: 'A2',
|
|
249
|
+
duration: 1000,
|
|
250
|
+
attack: 10,
|
|
251
|
+
release: 200,
|
|
252
|
+
volume: 0.5,
|
|
253
|
+
waveType: WAVE.SAWTOOTH,
|
|
254
|
+
filterType: 'bandpass',
|
|
255
|
+
filterHz: 350,
|
|
256
|
+
filterHzEnd: 2400,
|
|
257
|
+
filterQ: 5
|
|
258
|
+
})
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Frequency modulation
|
|
262
|
+
|
|
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.
|
|
270
|
+
|
|
271
|
+
## Detune
|
|
272
|
+
|
|
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.
|
|
274
|
+
|
|
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:
|
|
276
|
+
|
|
277
|
+
```javascript
|
|
278
|
+
synth.playChord({
|
|
279
|
+
frequencies: [440, 440],
|
|
280
|
+
detunes: [-6, 6],
|
|
281
|
+
duration: 2000,
|
|
282
|
+
attack: 200,
|
|
283
|
+
release: 600,
|
|
284
|
+
volume: 0.55,
|
|
285
|
+
waveType: WAVE.SINE
|
|
286
|
+
})
|
|
287
|
+
```
|
|
288
|
+
|
|
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.
|
|
211
290
|
|
|
212
291
|
## Chords, polyphony and gain
|
|
213
292
|
|
|
214
|
-
`playChord` is for notes that should start together. The call is placed in the
|
|
215
|
-
native queue as one event, so the audio callback sees the whole chord or none
|
|
216
|
-
of it. Its source gain is divided by the square root of the chord's voice count
|
|
217
|
-
to keep power stable.
|
|
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.
|
|
218
294
|
|
|
219
295
|
```javascript
|
|
220
296
|
synth.playChord({
|
|
@@ -228,42 +304,28 @@ synth.playChord({
|
|
|
228
304
|
})
|
|
229
305
|
```
|
|
230
306
|
|
|
231
|
-
Four separate `playTone` calls are four independent full-gain events. They are
|
|
232
|
-
not atomic and may start in different audio callbacks. Their sum is also hotter
|
|
233
|
-
and can push the limiter. Separate calls are correct for unrelated taps or
|
|
234
|
-
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.
|
|
235
308
|
|
|
236
|
-
`maxVoices`
|
|
237
|
-
voice with the least time remaining is faded and reused. Leave a small margin for release tails, but do
|
|
238
|
-
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.
|
|
239
310
|
|
|
240
311
|
## Mixing profiles and limiter
|
|
241
312
|
|
|
242
|
-
Choose the profile once in `
|
|
313
|
+
Choose the profile once in `start()`:
|
|
243
314
|
|
|
244
|
-
| Profile
|
|
245
|
-
|
|
|
246
|
-
| `conservative` | Default for mixed effects, chords and unpredictable overlap.
|
|
247
|
-
| `balanced`
|
|
248
|
-
| `speakerSafe`
|
|
249
|
-
| `raw`
|
|
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. |
|
|
250
321
|
|
|
251
|
-
Protected profiles calibrate source gain by waveform and event voice count.
|
|
252
|
-
The final stereo-linked limiter catches exceptional peaks with 10 ms of
|
|
253
|
-
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`.
|
|
254
323
|
|
|
255
|
-
The limiter
|
|
256
|
-
lower event volume or choose a more conservative profile. Repeatedly starting
|
|
257
|
-
the same long sound before its previous release ends raises and lowers the sum,
|
|
258
|
-
which can produce audible pumping. That overlap is useful as a stress test, but
|
|
259
|
-
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.
|
|
260
325
|
|
|
261
326
|
## Speakers, headphones and devices
|
|
262
327
|
|
|
263
|
-
Headphones usually reproduce bass and stereo movement more cleanly than a phone
|
|
264
|
-
speaker. A sound that is smooth in headphones can buzz on a small driver even
|
|
265
|
-
when the digital signal never clips. Low pitches and square or sawtooth waves
|
|
266
|
-
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.
|
|
267
329
|
|
|
268
330
|
For speaker-facing sounds:
|
|
269
331
|
|
|
@@ -273,15 +335,11 @@ For speaker-facing sounds:
|
|
|
273
335
|
- test at several system volume settings on the target device;
|
|
274
336
|
- keep critical meaning in rhythm and pitch, not pan alone.
|
|
275
337
|
|
|
276
|
-
Different phones have different sample rates, speaker protection and acoustic
|
|
277
|
-
resonances. Test the real device family. A desktop browser or simulator cannot
|
|
278
|
-
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.
|
|
279
339
|
|
|
280
340
|
## Recipes
|
|
281
341
|
|
|
282
|
-
These recipes use only the 1.0.0 contract and keep explicit envelopes valid.
|
|
283
|
-
Tune pitch and volume for the app's other audio rather than treating the values
|
|
284
|
-
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.
|
|
285
343
|
|
|
286
344
|
### Interface click
|
|
287
345
|
|
|
@@ -296,8 +354,7 @@ synth.playTone({
|
|
|
296
354
|
})
|
|
297
355
|
```
|
|
298
356
|
|
|
299
|
-
The short sine is cleaner than a full-band noise click. Raise the frequency for
|
|
300
|
-
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.
|
|
301
358
|
|
|
302
359
|
### Confirmation hit
|
|
303
360
|
|
|
@@ -330,18 +387,16 @@ synth.playChord({
|
|
|
330
387
|
### Two-tone alarm
|
|
331
388
|
|
|
332
389
|
```javascript
|
|
333
|
-
synth.
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
release: 70,
|
|
338
|
-
volume: 0.55,
|
|
339
|
-
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 },
|
|
340
395
|
steps: [
|
|
341
|
-
{
|
|
342
|
-
{
|
|
343
|
-
{
|
|
344
|
-
{
|
|
396
|
+
{ notes: [69] },
|
|
397
|
+
{ notes: [76] },
|
|
398
|
+
{ notes: [69] },
|
|
399
|
+
{ notes: [76] }
|
|
345
400
|
]
|
|
346
401
|
})
|
|
347
402
|
```
|
|
@@ -349,50 +404,198 @@ synth.playPattern({
|
|
|
349
404
|
### Retro pickup
|
|
350
405
|
|
|
351
406
|
```javascript
|
|
352
|
-
synth.
|
|
353
|
-
|
|
407
|
+
synth.setTempo({ bpm: 180 })
|
|
408
|
+
synth.playSequence({
|
|
409
|
+
timeUnit: 'beats',
|
|
410
|
+
stepDuration: 0.25,
|
|
354
411
|
swing: 0.06,
|
|
355
|
-
|
|
356
|
-
attack: 2,
|
|
357
|
-
release: 45,
|
|
358
|
-
volume: 0.5,
|
|
359
|
-
waveType: WAVE.SQUARE,
|
|
412
|
+
voice: { attack: 2, release: 45, volume: 0.5, waveType: WAVE.SQUARE },
|
|
360
413
|
steps: [
|
|
361
|
-
{
|
|
362
|
-
{
|
|
363
|
-
{
|
|
364
|
-
{
|
|
414
|
+
{ notes: [72] },
|
|
415
|
+
{ notes: [76] },
|
|
416
|
+
{ notes: [79] },
|
|
417
|
+
{ notes: [84], duration: 0.5 }
|
|
365
418
|
]
|
|
366
419
|
})
|
|
367
420
|
```
|
|
368
421
|
|
|
369
|
-
The
|
|
370
|
-
you want a gate shorter than its rhythmic step.
|
|
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.
|
|
371
423
|
|
|
372
424
|
### Noise percussion
|
|
373
425
|
|
|
374
426
|
```javascript
|
|
375
|
-
synth.
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
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 },
|
|
380
432
|
steps: [
|
|
381
|
-
{
|
|
433
|
+
{ frequencies: [220], overrides: { attack: 1, decay: 65, sustain: 0, release: 55 } },
|
|
382
434
|
{ rest: true },
|
|
383
|
-
{
|
|
384
|
-
{
|
|
435
|
+
{ frequencies: [220], overrides: { attack: 1, decay: 35, sustain: 0, release: 28 } },
|
|
436
|
+
{ frequencies: [220], overrides: { attack: 1, decay: 35, sustain: 0, release: 28 } }
|
|
385
437
|
]
|
|
386
438
|
})
|
|
387
439
|
```
|
|
388
440
|
|
|
389
|
-
|
|
390
|
-
|
|
441
|
+
The steps keep the eighth-note grid while short decay and `sustain: 0` let each hit settle to silence inside its step.
|
|
442
|
+
|
|
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.
|
|
444
|
+
|
|
445
|
+
### Filtered noise burst
|
|
446
|
+
|
|
447
|
+
```javascript
|
|
448
|
+
synth.playTone({
|
|
449
|
+
frequency: 120,
|
|
450
|
+
duration: 760,
|
|
451
|
+
attack: 1,
|
|
452
|
+
release: 580,
|
|
453
|
+
volume: 0.62,
|
|
454
|
+
waveType: WAVE.NOISE,
|
|
455
|
+
noiseRate: 12000,
|
|
456
|
+
noiseRateEnd: 650,
|
|
457
|
+
lowPassHz: 2500,
|
|
458
|
+
bitDepth: 8
|
|
459
|
+
})
|
|
460
|
+
```
|
|
461
|
+
|
|
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.
|
|
463
|
+
|
|
464
|
+
### Pluck
|
|
465
|
+
|
|
466
|
+
```javascript
|
|
467
|
+
synth.playTone({
|
|
468
|
+
note: 'E4',
|
|
469
|
+
duration: 900,
|
|
470
|
+
attack: 2,
|
|
471
|
+
decay: 260,
|
|
472
|
+
sustain: 0.18,
|
|
473
|
+
release: 380,
|
|
474
|
+
volume: 0.6,
|
|
475
|
+
waveType: WAVE.TRIANGLE
|
|
476
|
+
})
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
### Electric piano
|
|
480
|
+
|
|
481
|
+
```javascript
|
|
482
|
+
synth.playTone({
|
|
483
|
+
note: 'E3',
|
|
484
|
+
duration: 1800,
|
|
485
|
+
attack: 3,
|
|
486
|
+
decay: 700,
|
|
487
|
+
sustain: 0.3,
|
|
488
|
+
release: 600,
|
|
489
|
+
volume: 0.55,
|
|
490
|
+
waveType: WAVE.SINE,
|
|
491
|
+
fmRatio: 1,
|
|
492
|
+
fmDepth: 2.2,
|
|
493
|
+
fmDepthEnd: 0.25
|
|
494
|
+
})
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
The index falls from 2.2 to 0.25 while the level settles, so the attack is bright and the sustain is mellow.
|
|
498
|
+
|
|
499
|
+
### Bell
|
|
500
|
+
|
|
501
|
+
```javascript
|
|
502
|
+
synth.playTone({
|
|
503
|
+
note: 'C5',
|
|
504
|
+
duration: 2600,
|
|
505
|
+
attack: 1,
|
|
506
|
+
decay: 2000,
|
|
507
|
+
sustain: 0.02,
|
|
508
|
+
release: 500,
|
|
509
|
+
volume: 0.5,
|
|
510
|
+
waveType: WAVE.SINE,
|
|
511
|
+
fmRatio: 3.5,
|
|
512
|
+
fmDepth: 4,
|
|
513
|
+
fmDepthEnd: 0
|
|
514
|
+
})
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
The fractional ratio makes the overtones inharmonic. Try 1.4142 or 2.76 for a different metal.
|
|
518
|
+
|
|
519
|
+
### Wah
|
|
520
|
+
|
|
521
|
+
The wah in [Resonant filter](#resonant-filter) is also a recipe. Reverse the two cutoffs for a sound that closes instead of opening.
|
|
522
|
+
|
|
523
|
+
### Filtered laser
|
|
524
|
+
|
|
525
|
+
```javascript
|
|
526
|
+
synth.playTone({
|
|
527
|
+
frequency: 1600,
|
|
528
|
+
frequencyEnd: 160,
|
|
529
|
+
duration: 340,
|
|
530
|
+
attack: 2,
|
|
531
|
+
release: 90,
|
|
532
|
+
volume: 0.4,
|
|
533
|
+
waveType: WAVE.SAWTOOTH,
|
|
534
|
+
filterType: 'lowpass',
|
|
535
|
+
filterHz: 7000,
|
|
536
|
+
filterHzEnd: 500,
|
|
537
|
+
filterQ: 4
|
|
538
|
+
})
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
### Hi-hat
|
|
542
|
+
|
|
543
|
+
```javascript
|
|
544
|
+
synth.playTone({
|
|
545
|
+
duration: 80,
|
|
546
|
+
attack: 1,
|
|
547
|
+
decay: 30,
|
|
548
|
+
sustain: 0.25,
|
|
549
|
+
release: 45,
|
|
550
|
+
volume: 0.45,
|
|
551
|
+
waveType: WAVE.NOISE,
|
|
552
|
+
filterType: 'highpass',
|
|
553
|
+
filterHz: 7000
|
|
554
|
+
})
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
For an open hat, use `duration: 320`, `decay: 70` and `release: 220`.
|
|
558
|
+
|
|
559
|
+
### Thick pad
|
|
560
|
+
|
|
561
|
+
```javascript
|
|
562
|
+
synth.playChord({
|
|
563
|
+
notes: ['A3', 'E4', 'A3', 'E4', 'A3', 'E4'],
|
|
564
|
+
detunes: [-9, -9, 0, 0, 9, 9],
|
|
565
|
+
duration: 3200,
|
|
566
|
+
attack: 700,
|
|
567
|
+
release: 1300,
|
|
568
|
+
volume: 0.5,
|
|
569
|
+
waveType: WAVE.SAWTOOTH,
|
|
570
|
+
filterType: 'lowpass',
|
|
571
|
+
filterHz: 1500,
|
|
572
|
+
panSpread: 0.8
|
|
573
|
+
})
|
|
574
|
+
```
|
|
575
|
+
|
|
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.
|
|
577
|
+
|
|
578
|
+
### Kick
|
|
579
|
+
|
|
580
|
+
```javascript
|
|
581
|
+
synth.playTone({
|
|
582
|
+
frequency: 150,
|
|
583
|
+
frequencyEnd: 42,
|
|
584
|
+
duration: 320,
|
|
585
|
+
attack: 1,
|
|
586
|
+
decay: 150,
|
|
587
|
+
sustain: 0.3,
|
|
588
|
+
release: 140,
|
|
589
|
+
volume: 0.85,
|
|
590
|
+
waveType: WAVE.SINE
|
|
591
|
+
})
|
|
592
|
+
```
|
|
593
|
+
|
|
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.
|
|
391
595
|
|
|
392
596
|
### Simon-style cues
|
|
393
597
|
|
|
394
|
-
Use short, distinct square-wave pitches. Play one tone for the sequence and
|
|
395
|
-
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.
|
|
396
599
|
|
|
397
600
|
```javascript
|
|
398
601
|
var SIMON = [329.63, 440, 554.37, 659.25]
|
|
@@ -421,8 +624,25 @@ function playAllSimonPads () {
|
|
|
421
624
|
}
|
|
422
625
|
```
|
|
423
626
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
[
|
|
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.
|