@hraness/dawg 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (275) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/DAWG.md +655 -141
  3. package/README.md +27 -25
  4. package/core/autotune.ts +1119 -0
  5. package/core/chords.ts +759 -23
  6. package/core/clips.ts +499 -0
  7. package/core/diff.ts +182 -99
  8. package/core/expression.ts +158 -6
  9. package/core/fx.ts +354 -32
  10. package/core/granular.ts +91 -0
  11. package/core/instruments.ts +180 -0
  12. package/core/keys.ts +573 -4
  13. package/core/loop.ts +5 -0
  14. package/core/lyrics.ts +297 -0
  15. package/core/master.ts +3 -3
  16. package/core/resonators.ts +306 -4
  17. package/core/routing.ts +165 -0
  18. package/core/score.ts +1181 -16
  19. package/core/sdk/eval-child.ts +7 -2
  20. package/core/sdk/eval.ts +35 -6
  21. package/core/sdk/print.ts +354 -6
  22. package/core/sdk/sync-lyrics.ts +49 -0
  23. package/core/sdk/v1.ts +3160 -69
  24. package/core/sections.ts +298 -24
  25. package/core/sing.ts +815 -0
  26. package/core/strings.ts +237 -2
  27. package/core/style-provenance.ts +80 -0
  28. package/core/styles/africa-mena-southasia.ts +2893 -0
  29. package/core/styles/americas.ts +3810 -0
  30. package/core/styles/art.ts +4993 -0
  31. package/core/styles/base.ts +123 -0
  32. package/core/styles/cycles.ts +106 -0
  33. package/core/styles/electronic.ts +2723 -0
  34. package/core/styles/europe-asia-pacific.ts +2838 -0
  35. package/core/styles/excerpt.ts +29 -0
  36. package/core/styles/gamelan.ts +283 -0
  37. package/core/styles/generate.ts +2199 -0
  38. package/core/styles/index.ts +515 -0
  39. package/core/styles/parts.ts +106 -0
  40. package/core/styles/pop.ts +3189 -0
  41. package/core/styles/rock.ts +2993 -0
  42. package/core/styles/roots.ts +4175 -0
  43. package/core/styles/schema.ts +429 -0
  44. package/core/styles/taxonomy.ts +940 -0
  45. package/core/styles/validate.ts +528 -0
  46. package/core/tempo.ts +32 -2
  47. package/core/tuning.ts +19 -3
  48. package/core/vocoder.ts +524 -0
  49. package/core/winds.ts +652 -0
  50. package/guides/agent.md +29 -0
  51. package/guides/arrange.md +30 -0
  52. package/guides/audition.md +20 -14
  53. package/guides/automation.md +12 -6
  54. package/guides/chords.md +16 -14
  55. package/guides/effects.md +17 -15
  56. package/guides/faders.md +20 -16
  57. package/guides/files.md +13 -9
  58. package/guides/getting-started.md +15 -11
  59. package/guides/keys.md +19 -15
  60. package/guides/media.md +17 -12
  61. package/guides/mix.md +12 -6
  62. package/guides/music.md +23 -8
  63. package/guides/notes.md +15 -9
  64. package/guides/performance.md +15 -10
  65. package/guides/play.md +21 -13
  66. package/guides/project.md +25 -8
  67. package/guides/providers.md +20 -14
  68. package/guides/resample.md +32 -0
  69. package/guides/rhythm.md +17 -13
  70. package/guides/sessions.md +17 -7
  71. package/guides/show-me.md +31 -0
  72. package/guides/sound.md +25 -9
  73. package/guides/sounds.md +15 -12
  74. package/guides/styles.md +31 -0
  75. package/guides/tempo.md +15 -10
  76. package/guides/tracks.md +15 -10
  77. package/guides/tuning.md +32 -0
  78. package/guides/voice.md +31 -0
  79. package/guides/web-search.md +18 -8
  80. package/native/prebuilt/darwin-arm64/libdawg_sink.dylib +0 -0
  81. package/native/prebuilt/darwin-x64/libdawg_sink.dylib +0 -0
  82. package/native/prebuilt/linux-arm64/libdawg_sink.so +0 -0
  83. package/native/prebuilt/linux-x64/libdawg_sink.so +0 -0
  84. package/native/prebuilt/manifest.json +21 -0
  85. package/package.json +5 -2
  86. package/src/agent/agent.ts +131 -13
  87. package/src/agent/calibration-tools.ts +53 -0
  88. package/src/agent/chord-tools.ts +153 -1
  89. package/src/agent/clip-tools.ts +453 -0
  90. package/src/agent/command-agent.ts +369 -0
  91. package/src/agent/drum-tools.ts +2 -2
  92. package/src/agent/expression-tools.ts +91 -0
  93. package/src/agent/gateway.ts +246 -60
  94. package/src/agent/models.ts +53 -12
  95. package/src/agent/ops.ts +15 -1
  96. package/src/agent/pack-tools.ts +1 -1
  97. package/src/agent/planner.ts +13 -0
  98. package/src/agent/portable-schema.ts +80 -0
  99. package/src/agent/preview-tool.ts +5 -1
  100. package/src/agent/provider.ts +22 -8
  101. package/src/agent/resample-tool.ts +131 -0
  102. package/src/agent/rhythm-tools.ts +1 -1
  103. package/src/agent/section-tools.ts +1 -1
  104. package/src/agent/show-me.ts +497 -0
  105. package/src/agent/steer.ts +15 -0
  106. package/src/agent/style-tools.ts +217 -0
  107. package/src/agent/tool-error.ts +12 -0
  108. package/src/agent/tools.ts +307 -37
  109. package/src/agent/usage.ts +2 -2
  110. package/src/agent/voice-tools.ts +925 -0
  111. package/src/agent/xcb-agent.ts +16 -6
  112. package/src/argv.ts +38 -0
  113. package/src/audio/analysis.ts +253 -0
  114. package/src/audio/arrange.ts +58 -3
  115. package/src/audio/autotune-engine.ts +101 -0
  116. package/src/audio/autotune.ts +640 -0
  117. package/src/audio/clips.ts +240 -0
  118. package/src/audio/doctor.ts +86 -0
  119. package/src/audio/dsp/bandbank.ts +138 -0
  120. package/src/audio/dsp/envelope.ts +171 -0
  121. package/src/audio/dsp/follow.ts +120 -0
  122. package/src/audio/dsp/formant.ts +427 -0
  123. package/src/audio/dsp/glottal.ts +243 -0
  124. package/src/audio/dsp/interp.ts +7 -2
  125. package/src/audio/dsp/lpc.ts +50 -0
  126. package/src/audio/dsp/periodicity.ts +59 -0
  127. package/src/audio/dsp/pitch.ts +995 -0
  128. package/src/audio/dsp/psola.ts +199 -0
  129. package/src/audio/dsp/shift.ts +256 -0
  130. package/src/audio/effects/chain.ts +21 -4
  131. package/src/audio/effects/common.ts +57 -0
  132. package/src/audio/effects/convolution.ts +113 -5
  133. package/src/audio/effects/filter.ts +48 -69
  134. package/src/audio/effects/formant.ts +263 -0
  135. package/src/audio/effects/gaze.ts +354 -0
  136. package/src/audio/engine.ts +206 -33
  137. package/src/audio/fit.ts +80 -3
  138. package/src/audio/granular.ts +352 -21
  139. package/src/audio/instrument-check.ts +67 -47
  140. package/src/audio/instruments.ts +36 -3
  141. package/src/audio/keys/calibration.ts +56 -0
  142. package/src/audio/keys/electric.ts +427 -0
  143. package/src/audio/keys/engine.ts +156 -20
  144. package/src/audio/keys/organ.ts +1335 -0
  145. package/src/audio/keys/piano.ts +72 -8
  146. package/src/audio/keys/sympathetic.ts +127 -0
  147. package/src/audio/kits.ts +135 -6
  148. package/src/audio/live-worker.ts +3 -1
  149. package/src/audio/live.ts +247 -26
  150. package/src/audio/native.ts +615 -0
  151. package/src/audio/preview.ts +45 -3
  152. package/src/audio/render-worker.ts +2 -0
  153. package/src/audio/renderer.ts +2 -0
  154. package/src/audio/resample.ts +286 -0
  155. package/src/audio/resonators.ts +10 -2
  156. package/src/audio/sampler.ts +252 -24
  157. package/src/audio/samples.ts +46 -3
  158. package/src/audio/sing/analysis.ts +193 -0
  159. package/src/audio/sing/engine.ts +949 -0
  160. package/src/audio/strings/body.ts +29 -16
  161. package/src/audio/strings/bow.ts +699 -0
  162. package/src/audio/strings/engine.ts +274 -22
  163. package/src/audio/strings/measure.test-helpers.ts +2 -0
  164. package/src/audio/synth/oscillators.ts +31 -21
  165. package/src/audio/synth/voice.ts +34 -1
  166. package/src/audio/vocoder/bank.ts +314 -0
  167. package/src/audio/vocoder/carrier.ts +165 -0
  168. package/src/audio/vocoder/control.ts +68 -0
  169. package/src/audio/vocoder/detect.ts +50 -0
  170. package/src/audio/vocoder/index.ts +304 -0
  171. package/src/audio/vocoder/talkbox.ts +143 -0
  172. package/src/audio/wav.ts +608 -85
  173. package/src/audio/winds/engine.ts +306 -0
  174. package/src/audio/winds/filters.ts +153 -0
  175. package/src/audio/winds/pitch.ts +104 -0
  176. package/src/audio/winds/trim.ts +104 -0
  177. package/src/audio/winds/trims.ts +917 -0
  178. package/src/audio/winds/trims1.ts +297 -0
  179. package/src/audio/winds/voice.ts +492 -0
  180. package/src/auth/cli.ts +38 -36
  181. package/src/auth/credentials.ts +30 -1
  182. package/src/auth/login.ts +15 -9
  183. package/src/auth/tui.ts +19 -10
  184. package/src/commands/arrange.ts +44 -29
  185. package/src/commands/autotune.ts +421 -0
  186. package/src/commands/calibration.ts +74 -0
  187. package/src/commands/clips.ts +887 -0
  188. package/src/commands/drums.ts +3 -2
  189. package/src/commands/edit.ts +11 -4
  190. package/src/commands/expression.ts +104 -23
  191. package/src/commands/formant.ts +221 -0
  192. package/src/commands/fx.ts +105 -33
  193. package/src/commands/grammar.ts +558 -0
  194. package/src/commands/granular.ts +7 -2
  195. package/src/commands/help.ts +652 -339
  196. package/src/commands/history.ts +18 -0
  197. package/src/commands/keys.ts +293 -17
  198. package/src/commands/modal.ts +67 -4
  199. package/src/commands/music.ts +1 -1
  200. package/src/commands/nearest.ts +53 -0
  201. package/src/commands/pack.ts +9 -2
  202. package/src/commands/param-range.ts +56 -0
  203. package/src/commands/parses.ts +130 -0
  204. package/src/commands/progression.ts +170 -0
  205. package/src/commands/resample.ts +281 -0
  206. package/src/commands/rhythm.ts +3 -0
  207. package/src/commands/rig.ts +8 -25
  208. package/src/commands/sample.ts +49 -2
  209. package/src/commands/shift.ts +119 -0
  210. package/src/commands/sing.ts +478 -0
  211. package/src/commands/string.ts +28 -2
  212. package/src/commands/strum.ts +485 -0
  213. package/src/commands/style.ts +415 -0
  214. package/src/commands/time.ts +6 -3
  215. package/src/commands/tuning.ts +3 -3
  216. package/src/commands/vocal-pitch.ts +616 -0
  217. package/src/commands/vocal.ts +147 -0
  218. package/src/commands/vocoder.ts +627 -0
  219. package/src/commands/wind.ts +244 -0
  220. package/src/fs/durable.ts +50 -0
  221. package/src/lang/glossary.ts +493 -0
  222. package/src/launch-args.ts +163 -0
  223. package/src/main.ts +1521 -255
  224. package/src/media/cli.ts +20 -3
  225. package/src/media/import.ts +3 -1
  226. package/src/project/check.ts +26 -3
  227. package/src/project/clip-pins.ts +72 -0
  228. package/src/project/init.ts +23 -8
  229. package/src/project/sync.ts +418 -86
  230. package/src/render.ts +36 -1
  231. package/src/session/daemon.ts +3 -0
  232. package/src/session/meta.ts +14 -0
  233. package/src/session/origin.ts +154 -0
  234. package/src/session/port.ts +22 -4
  235. package/src/session/presence.ts +50 -7
  236. package/src/session/protocol.ts +5 -1
  237. package/src/session/rebase.ts +18 -5
  238. package/src/session/receipt.ts +258 -0
  239. package/src/session/store.ts +65 -43
  240. package/src/tui/arrange-menu.ts +65 -31
  241. package/src/tui/audition.ts +1 -1
  242. package/src/tui/euclid.ts +18 -13
  243. package/src/tui/fader.ts +228 -41
  244. package/src/tui/granular-menu.ts +2 -4
  245. package/src/tui/menu-clips.ts +297 -0
  246. package/src/tui/menu-time.ts +20 -13
  247. package/src/tui/menu-voice.ts +405 -0
  248. package/src/tui/menu.ts +1377 -193
  249. package/src/tui/modal-menu.ts +38 -11
  250. package/src/tui/performance-menu.ts +46 -1
  251. package/src/tui/play-chords.ts +108 -5
  252. package/src/tui/play-mode.ts +16 -1
  253. package/src/tui/play-session.ts +81 -16
  254. package/src/tui/sing-menu.ts +278 -0
  255. package/src/tui/style-menu.ts +104 -0
  256. package/src/tui/vocoder-menu.ts +244 -0
  257. package/src/tui/wind-menu.ts +144 -0
  258. package/src/version.ts +8 -0
  259. package/src/web/fetch.ts +115 -29
  260. package/tui/activity.ts +180 -9
  261. package/tui/app.ts +274 -40
  262. package/tui/clip-row.ts +132 -0
  263. package/tui/delight.ts +144 -0
  264. package/tui/drawer.ts +70 -22
  265. package/tui/frame-gate.ts +76 -0
  266. package/tui/grammar.ts +112 -63
  267. package/tui/guide.ts +42 -4
  268. package/tui/highway.ts +269 -25
  269. package/tui/hints.ts +192 -0
  270. package/tui/input.ts +60 -9
  271. package/tui/keys.ts +1 -1
  272. package/tui/play-strip.ts +68 -14
  273. package/tui/prompt.ts +1 -1
  274. package/tui/screen.ts +144 -14
  275. package/tui/theme.ts +27 -0
@@ -0,0 +1,2723 @@
1
+ /**
2
+ * Electronic dance, ambient and experimental electronic (quality-08 family
3
+ * `electronic`, every branch except `breakbeat-family`).
4
+ *
5
+ * The root card holds what all machine music shares: a quantised 16-step
6
+ * grid, 8-bar hypermeter, loop harmony, a build/drop form whose breakdown
7
+ * drops the kit, synth voices and a club master. Each family card holds its
8
+ * defining pattern (four-on-the-floor with off-beat hats for house, a
9
+ * rolling off-beat bass for trance, distorted kicks for hardcore, no kit
10
+ * for ambient) and each leaf holds only its deltas. The `summary` of every
11
+ * card is its theory note: the concepts that define the style, searchable
12
+ * from `/style search`.
13
+ *
14
+ * Theory, not material: every rhythm is an abstract step grid and every
15
+ * progression a functional grammar or a modal vamp. No melody, bassline,
16
+ * lyric or sample of any record is encoded.
17
+ *
18
+ * Reference (root): Mark J. Butler, "Unlocking the Groove: Rhythm, Meter,
19
+ * and Musical Design in Electronic Dance Music" (Indiana UP, 2006).
20
+ */
21
+
22
+ import { grid, intervals, kit, maybe, role } from "./parts.ts";
23
+ import {
24
+ card,
25
+ type RoleName,
26
+ type RoleTexture,
27
+ type SectionKind,
28
+ type StyleCard,
29
+ } from "./schema.ts";
30
+
31
+ // ---------------------------------------------------------------------------
32
+ // Shared grids (16 steps = one 4/4 bar of 16ths)
33
+
34
+ /** Four-on-the-floor: a kick on every beat. */
35
+ const FOUR_FLOOR = grid("x...x...x...x...");
36
+ /** Backbeat: beats 2 and 4. */
37
+ const BACKBEAT = grid("....x.......x...");
38
+ /** Half-time backbeat: beat 3 only. */
39
+ const HALF_TIME = grid("........x.......");
40
+ /** Off-beat 8th: the "and" of every beat. */
41
+ const OFFBEAT = grid("..x...x...x...x.");
42
+ const EIGHTHS = grid("x.x.x.x.x.x.x.x.");
43
+ const SIXTEENTHS = grid("xxxxxxxxxxxxxxxx");
44
+ /** Rolling kick-bass: three 16ths after each kick, never on it. */
45
+ const ROLLING = grid(".xxx.xxx.xxx.xxx");
46
+ /** Tresillo: 3+3+2 sixteenths, twice per bar. */
47
+ const TRESILLO = grid("x..x..x.x..x..x.");
48
+ /** Syncopated electro kick (the 808 "boom ... boom-boom" shape). */
49
+ const ELECTRO_KICK = grid("x.....x...x.x...");
50
+ /** Two-step broken kick for downtempo and breaks. */
51
+ const BROKEN_KICK = grid("x.....x...x.....");
52
+ /**
53
+ * Gallop: the off-beat 8th plus the 16th after it, the rolling bass of
54
+ * progressive trance and melodic techno.
55
+ */
56
+ const GALLOP = grid("..xx..xx..xx..xx");
57
+ /** Pedal: one held note from the downbeat of each bar. */
58
+ const DOWNBEAT = grid("x...............");
59
+ /** Gated offbeat 16th shaker for house. */
60
+ const SHAKER_16 = grid("x.xxx.xxx.xxx.xx");
61
+
62
+ // ---------------------------------------------------------------------------
63
+ // Kits and voices
64
+
65
+ const voicesOf = (...kits: [string, number][]) =>
66
+ Object.freeze(kits.map(([name, w]) => kit(name, w)));
67
+ const machine = (required: boolean, ...kits: [string, number][]) =>
68
+ Object.freeze({ required, voices: voicesOf(...kits) });
69
+ const MACHINE = machine(true, ["syn909", 1], ["syn808", 0.4]);
70
+ const MACHINE_OPT = machine(false, ["syn909", 1], ["syn808", 0.4]);
71
+ const K808 = machine(true, ["syn808", 1]);
72
+ const K808_OPT = machine(false, ["syn808", 1]);
73
+ const ELECTRO = machine(true, ["electro", 1], ["syn808", 0.5]);
74
+ const LOFI = machine(true, ["lofi", 1]);
75
+ const LOFI_OPT = machine(false, ["lofi", 1]);
76
+ const TRAP = machine(true, ["trap", 1]);
77
+ const ACOUSTIC = machine(true, ["acoustic", 1]);
78
+ const ACOUSTIC_OPT = machine(false, ["acoustic", 1]);
79
+
80
+ /** Kit roles on one kit voice; the drum track takes the first role's kit. */
81
+ function kitOn(
82
+ voice: RoleTexture,
83
+ roles: readonly RoleName[],
84
+ optional: readonly RoleName[] = [],
85
+ ): Record<string, RoleTexture> {
86
+ const out: Record<string, RoleTexture> = {};
87
+ for (const r of roles) out[r] = voice;
88
+ for (const r of optional)
89
+ out[r] = Object.freeze({ required: false, voices: voice.voices });
90
+ return out;
91
+ }
92
+
93
+ /** No kit at all (ambient textures). */
94
+ const NO_KIT = Object.freeze({
95
+ kick: null,
96
+ snare: null,
97
+ clap: null,
98
+ rim: null,
99
+ tom: null,
100
+ hat: null,
101
+ openhat: null,
102
+ perc: null,
103
+ shaker: null,
104
+ });
105
+
106
+ /**
107
+ * Dance form: the breakdown drops the kit and bass (the defining tension
108
+ * of build-drop music), the intro is kit and bass for DJ mixing.
109
+ */
110
+ const DANCE_ROLEMAP: Readonly<Partial<Record<SectionKind, RoleName[]>>> =
111
+ Object.freeze({
112
+ breakdown: ["pad", "chords", "lead", "arp", "drone", "counter"],
113
+ intro: ["kick", "hat", "openhat", "perc", "shaker", "rim", "bass", "pad"],
114
+ });
115
+
116
+ const MINOR_SCALES = Object.freeze([
117
+ ["minor", 0.6],
118
+ ["dorian", 0.25],
119
+ ["phrygian", 0.15],
120
+ ] as const);
121
+
122
+ // ---------------------------------------------------------------------------
123
+ // Cards
124
+
125
+ const ROOT: readonly StyleCard[] = [
126
+ card({
127
+ id: "electronic",
128
+ abstract: true,
129
+ summary:
130
+ "loop-based machine music: quantised 16-step grid, 8-bar hypermeter, loop harmony, build and drop",
131
+ tempo: { bpm: [100, 140], typical: 124 },
132
+ meter: { hypermeter: [[8, 1]] },
133
+ groove: { subdivision: 4, humanize: { timingMs: 0, velocity: 0.02 } },
134
+ rhythm: {
135
+ onsets: { kick: FOUR_FLOOR, clap: BACKBEAT, hat: OFFBEAT },
136
+ fills: { every: 8, density: [0.3, 0.6] },
137
+ },
138
+ pitch: { scales: MINOR_SCALES },
139
+ harmony: {
140
+ presets: [
141
+ ["aeolian", 0.5],
142
+ ["dorian-vamp", 0.3],
143
+ ["sad-pop", 0.2],
144
+ ],
145
+ cadences: [
146
+ ["bVII-I", 0.5],
147
+ ["V-I", 0.3],
148
+ ["iv-I", 0.2],
149
+ ],
150
+ rhythm: [
151
+ [1, 0.5],
152
+ [0.5, 0.5],
153
+ ],
154
+ sevenths: 0.4,
155
+ },
156
+ melody: {
157
+ repetition: 0.8,
158
+ density: [2, 3],
159
+ phraseBars: [
160
+ [2, 0.5],
161
+ [4, 0.5],
162
+ ],
163
+ intervals: intervals(4, 3, 1.2, 1.2),
164
+ },
165
+ bass: {
166
+ behaviour: [
167
+ ["ostinato", 0.5],
168
+ ["octave", 0.3],
169
+ ["root", 0.2],
170
+ ],
171
+ onsets: OFFBEAT,
172
+ kickLock: 0,
173
+ },
174
+ form: {
175
+ plans: [
176
+ [["intro", "build", "drop", "breakdown", "build", "drop", "outro"], 1],
177
+ ],
178
+ archetype: "build-drop",
179
+ },
180
+ texture: {
181
+ roles: {
182
+ snare: null,
183
+ kick: MACHINE,
184
+ clap: MACHINE,
185
+ hat: MACHINE,
186
+ openhat: MACHINE_OPT,
187
+ bass: role("bass", "saw:0.4", "square:0.3"),
188
+ chords: role("keys", "saw:0.5"),
189
+ pad: maybe("strings", "granular:0.4"),
190
+ lead: role("lead", "pluck:0.6"),
191
+ },
192
+ },
193
+ expression: { dynamics: [0.6, 1] },
194
+ mix: {
195
+ levels: {
196
+ kick: 0,
197
+ clap: -5,
198
+ hat: -9,
199
+ openhat: -11,
200
+ bass: -3,
201
+ chords: -8,
202
+ pad: -12,
203
+ lead: -4,
204
+ },
205
+ space: 0.3,
206
+ loudness: "club",
207
+ },
208
+ }),
209
+ ];
210
+
211
+ // ---------------------------------------------------------------------------
212
+ // Disco and its heirs.
213
+ // Refs: Tim Lawrence, "Love Saves the Day" (Duke UP, 2003); Alice Echols,
214
+ // "Hot Stuff: Disco and the Remaking of American Culture" (2010).
215
+
216
+ const DISCO: readonly StyleCard[] = [
217
+ card({
218
+ id: "disco-family",
219
+ abstract: true,
220
+ summary:
221
+ "four-on-the-floor kick, open hat on every off-beat, octave-leaping 8th bass, string and horn pads over extended diatonic sevenths",
222
+ tempo: { bpm: [110, 130], typical: 120 },
223
+ rhythm: {
224
+ onsets: { hat: EIGHTHS, openhat: OFFBEAT },
225
+ locks: [{ kind: "avoid", a: "kick", b: "openhat" }],
226
+ },
227
+ pitch: {
228
+ scales: [
229
+ ["minor", 0.4],
230
+ ["dorian", 0.3],
231
+ ["major", 0.3],
232
+ ],
233
+ },
234
+ harmony: {
235
+ presets: [
236
+ ["dorian-vamp", 0.35],
237
+ ["ii-v-i", 0.25],
238
+ ["fifties", 0.2],
239
+ ["aeolian", 0.2],
240
+ ],
241
+ sevenths: 0.6,
242
+ },
243
+ bass: { behaviour: [["octave", 1]], onsets: EIGHTHS },
244
+ form: { roleMap: DANCE_ROLEMAP },
245
+ texture: {
246
+ roles: {
247
+ openhat: MACHINE,
248
+ chords: role("strings", "epiano:0.5"),
249
+ lead: role("strings", "lead:0.5"),
250
+ },
251
+ },
252
+ }),
253
+ card({
254
+ id: "disco",
255
+ summary:
256
+ "Philadelphia-rooted disco: live four-on-the-floor, 16th hi-hat lift, octave bass, string-section pads, ii-V and dorian vamps",
257
+ tempo: { bpm: [112, 128], typical: 118 },
258
+ groove: { humanize: { timingMs: 6, velocity: 0.06 } },
259
+ rhythm: { onsets: { hat: SIXTEENTHS, perc: grid("..x...x...x...x.") } },
260
+ texture: {
261
+ roles: {
262
+ kick: ACOUSTIC,
263
+ clap: ACOUSTIC,
264
+ hat: ACOUSTIC,
265
+ openhat: ACOUSTIC,
266
+ perc: maybe("bell"),
267
+ bass: role("ebass", "slap:0.5"),
268
+ chords: role("strings", "rhodes:0.4"),
269
+ lead: role("violins", "trumpet:0.5"),
270
+ },
271
+ },
272
+ mix: { loudness: "streaming" },
273
+ }),
274
+ card({
275
+ id: "euro-disco",
276
+ summary:
277
+ "European disco: sequenced 8th octave bass, minor-key pop progressions, string synth chords, steady drum machine",
278
+ tempo: { bpm: [115, 130], typical: 122 },
279
+ pitch: { scales: [["minor", 1]] },
280
+ harmony: {
281
+ presets: [
282
+ ["aeolian", 0.4],
283
+ ["sad-pop", 0.4],
284
+ ["andalusian", 0.2],
285
+ ],
286
+ sevenths: 0.2,
287
+ },
288
+ texture: {
289
+ roles: {
290
+ bass: role("saw", "bass:0.5"),
291
+ lead: role("lead", "strings:0.4"),
292
+ },
293
+ },
294
+ }),
295
+ card({
296
+ id: "italo-disco",
297
+ summary:
298
+ "Italo-disco: drum machine, 8th octave bass, arpeggiated minor triads, melancholic aeolian loops and square-wave hooks",
299
+ tempo: { bpm: [110, 125], typical: 118 },
300
+ pitch: { scales: [["minor", 1]] },
301
+ harmony: {
302
+ presets: [
303
+ ["aeolian", 0.5],
304
+ ["sad-pop", 0.5],
305
+ ],
306
+ sevenths: 0.1,
307
+ },
308
+ rhythm: { onsets: { hat: OFFBEAT, arp: SIXTEENTHS } },
309
+ texture: {
310
+ roles: {
311
+ kick: K808,
312
+ arp: role("pluck", "square:0.5"),
313
+ bass: role("saw"),
314
+ lead: role("square", "lead:0.5"),
315
+ },
316
+ },
317
+ }),
318
+ card({
319
+ id: "hi-nrg",
320
+ summary:
321
+ "Hi-NRG: fast 128-140 bpm four-on-the-floor, relentless 8th octave bass, minor triads, handclap backbeat",
322
+ tempo: { bpm: [128, 140], typical: 134 },
323
+ pitch: { scales: [["minor", 1]] },
324
+ harmony: {
325
+ presets: [
326
+ ["aeolian", 0.5],
327
+ ["andalusian", 0.3],
328
+ ["sad-pop", 0.2],
329
+ ],
330
+ rhythm: [[1, 1]],
331
+ sevenths: 0,
332
+ },
333
+ rhythm: { onsets: { clap: grid("....x.......x..x") } },
334
+ texture: { roles: { bass: role("saw", "square:0.5"), lead: role("lead") } },
335
+ }),
336
+ card({
337
+ id: "space-disco",
338
+ summary:
339
+ "Space disco: 16th sequencer bass and arpeggios instead of the octave bass, sweeping filter pads, dorian vamps, long phrases",
340
+ tempo: { bpm: [110, 122], typical: 116 },
341
+ meter: { hypermeter: [[16, 1]] },
342
+ harmony: { model: "modal", rhythm: [[0.5, 1]] },
343
+ bass: { behaviour: [["ostinato", 1]], onsets: SIXTEENTHS },
344
+ rhythm: { onsets: { arp: SIXTEENTHS } },
345
+ texture: {
346
+ roles: {
347
+ arp: role("pluck", "saw:0.5"),
348
+ pad: role("strings", "cloud:0.4"),
349
+ bass: role("saw"),
350
+ lead: role("lead", "square:0.4"),
351
+ },
352
+ },
353
+ mix: { fx: { pad: { autofilter: "slow-sweep" } }, space: 0.5 },
354
+ }),
355
+ card({
356
+ id: "nu-disco",
357
+ summary:
358
+ "Nu-disco: disco octave bass and off-beat hat at house tempo, filtered ninth chords, sidechain pump",
359
+ tempo: { bpm: [110, 124], typical: 118 },
360
+ groove: { swingRatio: [1, 1.2] },
361
+ harmony: { sevenths: 0.8 },
362
+ texture: {
363
+ roles: {
364
+ chords: role("rhodes", "keys:0.5"),
365
+ bass: role("ebass", "saw:0.5"),
366
+ lead: role("pluck", "lead:0.5"),
367
+ },
368
+ },
369
+ mix: { fx: { chords: { duck: "pump" } } },
370
+ }),
371
+ card({
372
+ id: "disco-polo",
373
+ summary:
374
+ "Disco polo: simple major-key I-V-vi-IV and I-vi-IV-V loops, oom-pah off-beat bass, bright keyboard lead",
375
+ tempo: { bpm: [120, 140], typical: 128 },
376
+ pitch: {
377
+ scales: [
378
+ ["major", 0.7],
379
+ ["minor", 0.3],
380
+ ],
381
+ },
382
+ harmony: {
383
+ presets: [
384
+ ["axis", 0.5],
385
+ ["fifties", 0.5],
386
+ ],
387
+ cadences: [["V-I", 1]],
388
+ sevenths: 0,
389
+ },
390
+ bass: { behaviour: [["root-fifth", 1]], onsets: FOUR_FLOOR },
391
+ texture: {
392
+ roles: {
393
+ chords: role("keys", "organ:0.4"),
394
+ lead: role("lead", "square:0.5"),
395
+ },
396
+ },
397
+ }),
398
+ card({
399
+ id: "freestyle",
400
+ summary:
401
+ "Latin freestyle: electro syncopated 808 kick, clave-like stabs, minor progressions, 16th hats",
402
+ tempo: { bpm: [108, 122], typical: 116 },
403
+ rhythm: {
404
+ onsets: {
405
+ kick: ELECTRO_KICK,
406
+ hat: SIXTEENTHS,
407
+ openhat: null,
408
+ perc: grid("x..x..x...x.x..."),
409
+ },
410
+ locks: [],
411
+ },
412
+ bass: { behaviour: [["ostinato", 1]], onsets: ELECTRO_KICK, kickLock: 0.8 },
413
+ harmony: {
414
+ presets: [
415
+ ["aeolian", 0.5],
416
+ ["andalusian", 0.5],
417
+ ],
418
+ },
419
+ texture: {
420
+ roles: {
421
+ kick: ELECTRO,
422
+ openhat: null,
423
+ perc: MACHINE_OPT,
424
+ bass: role("saw", "square:0.5"),
425
+ chords: role("keys"),
426
+ lead: role("lead", "square:0.5"),
427
+ },
428
+ },
429
+ }),
430
+ card({
431
+ id: "eurobeat",
432
+ summary:
433
+ "Eurobeat: 150-160 bpm four-on-the-floor, 8th octave bass, minor key with modal-interchange bVI-bVII-i climbs, brass-synth stabs",
434
+ tempo: { bpm: [148, 162], typical: 155 },
435
+ pitch: { scales: [["minor", 1]] },
436
+ harmony: {
437
+ presets: [
438
+ ["aeolian", 0.6],
439
+ ["andalusian", 0.4],
440
+ ],
441
+ cadences: [["bVII-I", 1]],
442
+ sevenths: 0,
443
+ },
444
+ texture: {
445
+ roles: {
446
+ bass: role("saw"),
447
+ chords: role("saw", "keys:0.5"),
448
+ lead: role("lead"),
449
+ },
450
+ },
451
+ }),
452
+ card({
453
+ id: "eurodance",
454
+ summary:
455
+ "Eurodance: 130-145 bpm four-on-the-floor, off-beat bass stabs, minor vi-IV-I-V loops, piano and supersaw hooks",
456
+ tempo: { bpm: [128, 145], typical: 138 },
457
+ pitch: { scales: [["minor", 1]] },
458
+ harmony: {
459
+ presets: [
460
+ ["sad-pop", 0.6],
461
+ ["aeolian", 0.4],
462
+ ],
463
+ sevenths: 0,
464
+ },
465
+ bass: { behaviour: [["root", 1]], onsets: OFFBEAT },
466
+ texture: {
467
+ roles: {
468
+ chords: role("piano", "saw:0.6"),
469
+ bass: role("saw"),
470
+ lead: role("saw", "lead:0.5"),
471
+ },
472
+ },
473
+ }),
474
+ card({
475
+ id: "electro-swing",
476
+ summary:
477
+ "Electro swing: triplet-swung 8ths over a four-on-the-floor kick, harmonic-minor ii-V-i, walking upright bass, clarinet and muted horns",
478
+ tempo: { bpm: [115, 130], typical: 124 },
479
+ groove: { subdivision: 2, swingRatio: [1.8, 2.2] },
480
+ rhythm: {
481
+ onsets: {
482
+ kick: grid("x.x.x.x."),
483
+ clap: grid("..x...x."),
484
+ hat: grid("x.x.x.x."),
485
+ openhat: grid(".x.x.x.x"),
486
+ },
487
+ },
488
+ pitch: {
489
+ scales: [
490
+ ["harmonic-minor", 0.6],
491
+ ["minor", 0.4],
492
+ ],
493
+ },
494
+ harmony: {
495
+ presets: [
496
+ ["minor-ii-v", 0.7],
497
+ ["andalusian", 0.3],
498
+ ],
499
+ cadences: [["V-I", 1]],
500
+ },
501
+ bass: {
502
+ behaviour: [["walking", 1]],
503
+ walk: { chordToneOnOne: 0.9, chromaticApproach: 0.3 },
504
+ },
505
+ texture: {
506
+ roles: {
507
+ bass: role("upright"),
508
+ chords: role("piano", "keys:0.3"),
509
+ lead: role("clarinet", "mutedtrumpet:0.6", "sax:0.4"),
510
+ },
511
+ },
512
+ }),
513
+ ];
514
+
515
+ // ---------------------------------------------------------------------------
516
+ // House.
517
+ // Refs: Hillegonda Rietveld, "This Is Our House" (Ashgate, 1998); Mark J.
518
+ // Butler (2006) on four-on-the-floor and off-beat hi-hat interlock.
519
+
520
+ const HOUSE: readonly StyleCard[] = [
521
+ card({
522
+ id: "house",
523
+ abstract: true,
524
+ summary:
525
+ "house: 118-130 bpm four-on-the-floor, clap on 2 and 4, open hat on every off-beat, piano or organ stabs, 8-bar loops",
526
+ tempo: { bpm: [118, 130], typical: 124 },
527
+ rhythm: {
528
+ onsets: { openhat: OFFBEAT, hat: grid("x.x.x.x.x.x.x.x.") },
529
+ locks: [{ kind: "avoid", a: "kick", b: "openhat" }],
530
+ },
531
+ form: { roleMap: DANCE_ROLEMAP },
532
+ texture: {
533
+ roles: { kick: MACHINE, chords: role("piano", "organ:0.5", "keys:0.5") },
534
+ },
535
+ }),
536
+ card({
537
+ id: "chicago-house",
538
+ summary:
539
+ "Chicago jack: raw 909/808 four-on-the-floor, swung 16th hats, clap on 2 and 4, two-chord piano vamp",
540
+ tempo: { bpm: [118, 128], typical: 122 },
541
+ groove: { swingRatio: [1.1, 1.3] },
542
+ rhythm: { onsets: { hat: SIXTEENTHS } },
543
+ harmony: {
544
+ presets: [["dorian-vamp", 1]],
545
+ rhythm: [[1, 1]],
546
+ },
547
+ texture: { roles: { kick: K808, chords: role("piano", "organ:0.4") } },
548
+ }),
549
+ card({
550
+ id: "acid-house",
551
+ summary:
552
+ "Acid house: 303 squelch bass as a 16th-note ostinato with resonant filter and accents, one-chord modal vamp, 909 jack",
553
+ tempo: { bpm: [118, 128], typical: 122 },
554
+ pitch: {
555
+ scales: [
556
+ ["phrygian", 0.4],
557
+ ["minor", 0.4],
558
+ ["dorian", 0.2],
559
+ ],
560
+ },
561
+ harmony: { model: "modal", rhythm: [[0.25, 1]] },
562
+ bass: {
563
+ behaviour: [["ostinato", 1]],
564
+ onsets: grid("x.xxx.x.xx.xx.x."),
565
+ range: [33, 57],
566
+ },
567
+ texture: {
568
+ roles: { bass: role("saw", "square:0.4"), chords: null, pad: null },
569
+ },
570
+ expression: {
571
+ articulation: {
572
+ bass: [
573
+ ["staccato", 0.6],
574
+ ["accent", 0.4],
575
+ ],
576
+ },
577
+ },
578
+ mix: { fx: { bass: { autofilter: "env-follow" } } },
579
+ }),
580
+ card({
581
+ id: "deep-house",
582
+ summary:
583
+ "Deep house: minor 7th and 9th dorian loops, swung 16ths (54-58 %), off-beat hat, syncopated bass that avoids the kick",
584
+ seedSalt: 1986,
585
+ tempo: { bpm: [118, 125], typical: 122 },
586
+ groove: { swingRatio: [1.18, 1.38], velocity: [1, 0.55, 0.8, 0.6] },
587
+ rhythm: {
588
+ onsets: {
589
+ kick: FOUR_FLOOR,
590
+ clap: BACKBEAT,
591
+ hat: grid("..x...x...x...x."),
592
+ openhat: grid("..1...1...1...1."),
593
+ shaker: SHAKER_16,
594
+ },
595
+ },
596
+ pitch: {
597
+ scales: [
598
+ ["dorian", 0.55],
599
+ ["minor", 0.45],
600
+ ],
601
+ },
602
+ harmony: {
603
+ presets: [
604
+ ["dorian-vamp", 0.6],
605
+ ["aeolian", 0.4],
606
+ ],
607
+ forms: [
608
+ [["i7", "iv7"], 0.5],
609
+ [["i9", "bVII", "iv7", "i7"], 0.5],
610
+ ],
611
+ sources: { presets: 1, forms: 2 },
612
+ rhythm: [
613
+ [1, 0.6],
614
+ [0.5, 0.4],
615
+ ],
616
+ sevenths: 0.9,
617
+ voicing: {
618
+ types: [
619
+ ["open", 0.6],
620
+ ["shell", 0.4],
621
+ ],
622
+ range: [52, 74],
623
+ notes: [4, 5],
624
+ },
625
+ },
626
+ melody: { density: [1, 2], ambitus: [5, 10], repetition: 0.85 },
627
+ bass: {
628
+ behaviour: [["ostinato", 1]],
629
+ range: [31, 48],
630
+ onsets: grid("..x..x....x..x.."),
631
+ kickLock: 0,
632
+ },
633
+ texture: {
634
+ roles: {
635
+ shaker: MACHINE_OPT,
636
+ chords: role("epiano", "organ:0.5"),
637
+ pad: maybe("strings"),
638
+ lead: maybe("pluck", "vibes:0.4"),
639
+ },
640
+ },
641
+ mix: { space: 0.4 },
642
+ }),
643
+ card({
644
+ id: "garage-house",
645
+ summary:
646
+ "Garage (NY) house: gospel organ and piano, major ii-V-I and turnaround changes with sevenths, swung hats, soulful lead",
647
+ tempo: { bpm: [118, 126], typical: 122 },
648
+ groove: { swingRatio: [1.15, 1.35] },
649
+ pitch: {
650
+ scales: [
651
+ ["major", 0.6],
652
+ ["mixolydian", 0.4],
653
+ ],
654
+ },
655
+ harmony: {
656
+ presets: [
657
+ ["ii-v-i", 0.5],
658
+ ["turnaround", 0.5],
659
+ ],
660
+ cadences: [
661
+ ["V-I", 0.7],
662
+ ["IV-I", 0.3],
663
+ ],
664
+ sevenths: 0.8,
665
+ },
666
+ texture: {
667
+ roles: {
668
+ chords: role("hammond", "piano:0.6"),
669
+ lead: role("choir", "rhodes:0.4"),
670
+ },
671
+ },
672
+ }),
673
+ card({
674
+ id: "hip-house",
675
+ summary:
676
+ "Hip house: four-on-the-floor under a hip-hop snare backbeat, rapped phrasing as dense 16th lead, sampled stab chords",
677
+ tempo: { bpm: [115, 126], typical: 120 },
678
+ rhythm: { onsets: { snare: BACKBEAT, clap: null } },
679
+ melody: { density: [3, 4], ambitus: [3, 7], repetition: 0.9 },
680
+ texture: {
681
+ roles: {
682
+ snare: K808,
683
+ clap: null,
684
+ kick: K808,
685
+ lead: role("lead", "vocoder:0.4"),
686
+ },
687
+ },
688
+ }),
689
+ card({
690
+ id: "italo-house",
691
+ summary:
692
+ "Italo house: rolling major piano stabs on off-beats, bright I-V-vi-IV loops, euphoric diva-style lead",
693
+ tempo: { bpm: [120, 128], typical: 124 },
694
+ pitch: {
695
+ scales: [
696
+ ["major", 0.7],
697
+ ["minor", 0.3],
698
+ ],
699
+ },
700
+ harmony: {
701
+ presets: [
702
+ ["axis", 0.5],
703
+ ["fifties", 0.5],
704
+ ],
705
+ cadences: [["V-I", 1]],
706
+ sevenths: 0.2,
707
+ },
708
+ rhythm: { onsets: { chords: grid("..x..x..x..x..x.") } },
709
+ texture: {
710
+ roles: { chords: role("piano"), lead: role("choir", "lead:0.5") },
711
+ },
712
+ }),
713
+ card({
714
+ id: "euro-house",
715
+ summary:
716
+ "Euro house: pop song form on four-on-the-floor, minor vi-IV-I-V loops, off-beat bass, synth riff hook",
717
+ tempo: { bpm: [122, 132], typical: 126 },
718
+ harmony: {
719
+ presets: [
720
+ ["sad-pop", 0.6],
721
+ ["aeolian", 0.4],
722
+ ],
723
+ sevenths: 0.1,
724
+ },
725
+ bass: { behaviour: [["root", 1]] },
726
+ texture: {
727
+ roles: {
728
+ chords: role("piano", "saw:0.4"),
729
+ lead: role("lead", "saw:0.4"),
730
+ },
731
+ },
732
+ }),
733
+ card({
734
+ id: "progressive-house",
735
+ summary:
736
+ "Progressive house: 16-bar hypermeter, syncopated rolling bass with 16th pickups, 16th shaker, layered plucked arpeggios, slow filter builds",
737
+ tempo: { bpm: [122, 128], typical: 126 },
738
+ meter: { hypermeter: [[16, 1]] },
739
+ rhythm: {
740
+ onsets: { arp: grid("x.xxx.xxx.xxx.xx"), shaker: SHAKER_16 },
741
+ },
742
+ bass: { onsets: grid("..x..xx...x..xx.") },
743
+ harmony: {
744
+ presets: [
745
+ ["aeolian", 0.6],
746
+ ["sad-pop", 0.4],
747
+ ],
748
+ rhythm: [[0.5, 1]],
749
+ },
750
+ texture: {
751
+ roles: {
752
+ shaker: MACHINE,
753
+ arp: role("pluck"),
754
+ pad: role("strings", "cloud:0.4"),
755
+ chords: role("saw"),
756
+ },
757
+ },
758
+ mix: { fx: { pad: { autofilter: "hpf-rise" } }, space: 0.45 },
759
+ }),
760
+ card({
761
+ id: "tribal-house",
762
+ summary:
763
+ "Tribal house: tom and conga polyrhythm over four-on-the-floor, tresillo perc, minimal one-chord harmony",
764
+ tempo: { bpm: [124, 130], typical: 127 },
765
+ harmony: { model: "modal", rhythm: [[0.25, 1]] },
766
+ rhythm: {
767
+ onsets: { tom: grid("..x..x....x..x.."), perc: TRESILLO, clap: null },
768
+ },
769
+ texture: {
770
+ roles: {
771
+ tom: MACHINE,
772
+ clap: null,
773
+ perc: role("framedrum", "tabla:0.3"),
774
+ chords: null,
775
+ },
776
+ },
777
+ }),
778
+ card({
779
+ id: "french-house",
780
+ summary:
781
+ "French house: filtered disco loop with sidechain pump, swung 16ths, dorian and major seventh vamps, phaser sweeps",
782
+ tempo: { bpm: [118, 126], typical: 123 },
783
+ groove: { swingRatio: [1.1, 1.3] },
784
+ harmony: {
785
+ presets: [
786
+ ["dorian-vamp", 0.5],
787
+ ["ii-v-i", 0.5],
788
+ ],
789
+ sevenths: 0.8,
790
+ },
791
+ bass: { behaviour: [["octave", 1]], onsets: EIGHTHS },
792
+ texture: {
793
+ roles: {
794
+ chords: role("rhodes", "strings:0.5"),
795
+ bass: role("ebass", "saw:0.5"),
796
+ },
797
+ },
798
+ mix: { fx: { chords: { djf: "dark", duck: "pump", phaser: "slow" } } },
799
+ }),
800
+ card({
801
+ id: "tech-house",
802
+ summary:
803
+ "Tech house: rolling 16th bass groove, percussive tops, shuffled hats, one-chord minimal harmony",
804
+ tempo: { bpm: [124, 128], typical: 126 },
805
+ groove: { swingRatio: [1.05, 1.25] },
806
+ harmony: { model: "modal", rhythm: [[0.25, 1]] },
807
+ rhythm: {
808
+ onsets: { perc: grid("...x..x....x..x."), rim: grid("......x.......x.") },
809
+ },
810
+ bass: { behaviour: [["ostinato", 1]], onsets: grid("..xx..x...xx..x.") },
811
+ texture: {
812
+ roles: {
813
+ perc: MACHINE_OPT,
814
+ rim: MACHINE_OPT,
815
+ chords: null,
816
+ lead: maybe("pluck"),
817
+ },
818
+ },
819
+ }),
820
+ card({
821
+ id: "hard-house",
822
+ summary:
823
+ "Hard house: 140-150 bpm, off-beat donk bass, distorted kick, minor riff stabs, hoover leads",
824
+ tempo: { bpm: [138, 152], typical: 145 },
825
+ bass: { behaviour: [["root", 1]], onsets: OFFBEAT },
826
+ texture: {
827
+ roles: { bass: role("square", "saw:0.5"), chords: role("saw") },
828
+ },
829
+ mix: { fx: { kick: { distort: "crunch" } }, loudness: "loud" },
830
+ }),
831
+ card({
832
+ id: "electro-house",
833
+ summary:
834
+ "Electro and big room house: distorted saw bass on off-beats, sparse big-room kick drop, simple minor hook, snare-roll builds",
835
+ tempo: { bpm: [126, 130], typical: 128 },
836
+ rhythm: {
837
+ fills: { every: 8, density: [0.6, 1] },
838
+ onsets: { snare: grid("....x.......x...") },
839
+ },
840
+ melody: { density: [1, 2], repetition: 0.95, ambitus: [3, 7] },
841
+ texture: {
842
+ roles: {
843
+ snare: MACHINE,
844
+ bass: role("saw"),
845
+ lead: role("saw", "lead:0.5"),
846
+ },
847
+ },
848
+ mix: { fx: { bass: { distort: "fold" } }, loudness: "loud" },
849
+ }),
850
+ card({
851
+ id: "minimal-house",
852
+ summary:
853
+ "Microhouse: clicks and micro-samples, sparse probabilistic perc, one-chord dub stab, low pitched density",
854
+ tempo: { bpm: [118, 126], typical: 122 },
855
+ harmony: { model: "modal", rhythm: [[0.25, 1]] },
856
+ rhythm: {
857
+ onsets: {
858
+ clap: grid("....5.......x..."),
859
+ rim: grid("..3..4...3...5.."),
860
+ hat: grid("..x...x...x...x."),
861
+ },
862
+ },
863
+ melody: { density: [0.5, 1] },
864
+ texture: {
865
+ roles: {
866
+ rim: MACHINE_OPT,
867
+ chords: role("keys"),
868
+ lead: maybe("grains", "microloop:0.5"),
869
+ },
870
+ },
871
+ mix: { fx: { rim: { crush: "lofi" } } },
872
+ }),
873
+ card({
874
+ id: "future-house",
875
+ summary:
876
+ "Future and bass house: metallic wobbling bass on syncopated off-beats, minor riffs, swung hats",
877
+ tempo: { bpm: [124, 128], typical: 126 },
878
+ groove: { swingRatio: [1.05, 1.2] },
879
+ bass: { behaviour: [["ostinato", 1]], onsets: grid("..x..x....x..xx.") },
880
+ texture: {
881
+ roles: { bass: role("saw", "square:0.5"), chords: maybe("pluck") },
882
+ },
883
+ mix: { fx: { bass: { autofilter: "wobble", distort: "fold" } } },
884
+ }),
885
+ card({
886
+ id: "tropical-house",
887
+ summary:
888
+ "Tropical house: 100-115 bpm, marimba and steelpan plucks, major I-V-vi-IV, soft four-on-the-floor",
889
+ tempo: { bpm: [100, 115], typical: 108 },
890
+ pitch: {
891
+ scales: [
892
+ ["major", 0.8],
893
+ ["major-pentatonic", 0.2],
894
+ ],
895
+ },
896
+ harmony: {
897
+ presets: [
898
+ ["axis", 0.6],
899
+ ["sad-pop", 0.4],
900
+ ],
901
+ cadences: [["IV-I", 1]],
902
+ sevenths: 0.2,
903
+ },
904
+ texture: {
905
+ roles: {
906
+ chords: role("keys", "nylon:0.5"),
907
+ lead: role("marimba", "steelpan:0.6", "flute:0.3"),
908
+ },
909
+ },
910
+ mix: { loudness: "streaming" },
911
+ }),
912
+ card({
913
+ id: "lofi-house",
914
+ summary:
915
+ "Lo-fi and outsider house: saturated lofi kit, tape wobble, swung 16ths, dorian seventh loops",
916
+ tempo: { bpm: [115, 125], typical: 120 },
917
+ groove: { swingRatio: [1.15, 1.35] },
918
+ harmony: { presets: [["dorian-vamp", 1]], sevenths: 0.8 },
919
+ texture: {
920
+ roles: {
921
+ kick: LOFI,
922
+ chords: role("epiano", "lofi:0.6"),
923
+ lead: maybe("pluck"),
924
+ },
925
+ },
926
+ mix: {
927
+ fx: { chords: { wobble: "tape", crush: "lofi" } },
928
+ loudness: "streaming",
929
+ },
930
+ }),
931
+ card({
932
+ id: "ghetto-house",
933
+ summary:
934
+ "Ghetto house: fast 808 four-on-the-floor, clap on every beat, 16th hats, chant-like repeated minor riff",
935
+ tempo: { bpm: [128, 140], typical: 132 },
936
+ rhythm: { onsets: { clap: FOUR_FLOOR, hat: SIXTEENTHS } },
937
+ harmony: { model: "modal", rhythm: [[0.25, 1]] },
938
+ melody: { repetition: 0.95, ambitus: [2, 5] },
939
+ texture: { roles: { kick: K808, chords: null } },
940
+ }),
941
+ card({
942
+ id: "ambient-house",
943
+ summary:
944
+ "Ambient house and Balearic: soft four-on-the-floor under long lydian and major-seventh pads, spacious reverb",
945
+ tempo: { bpm: [100, 118], typical: 110 },
946
+ pitch: {
947
+ scales: [
948
+ ["lydian", 0.5],
949
+ ["major", 0.5],
950
+ ],
951
+ },
952
+ harmony: {
953
+ presets: [
954
+ ["axis", 0.5],
955
+ ["canon", 0.5],
956
+ ],
957
+ cadences: [["IV-I", 1]],
958
+ rhythm: [[0.5, 1]],
959
+ sevenths: 0.7,
960
+ },
961
+ melody: { density: [0.5, 1.5] },
962
+ texture: {
963
+ roles: {
964
+ pad: role("strings", "cloud:0.6"),
965
+ lead: maybe("pluck", "nylon:0.4"),
966
+ },
967
+ },
968
+ mix: { space: 0.7, loudness: "streaming" },
969
+ }),
970
+ card({
971
+ id: "afro-house",
972
+ summary:
973
+ "Afro house: 3-3-2 bell timeline and conga interlock over four-on-the-floor, minor modal chant harmony",
974
+ tempo: { bpm: [118, 124], typical: 121 },
975
+ harmony: { model: "modal", rhythm: [[0.5, 1]] },
976
+ rhythm: {
977
+ onsets: {
978
+ bell: grid("x..x..x.x..x..x."),
979
+ perc: grid("..x..xx...x..xx."),
980
+ shaker: SHAKER_16,
981
+ },
982
+ },
983
+ texture: {
984
+ roles: {
985
+ bell: role("bell"),
986
+ perc: role("framedrum", "tabla:0.3"),
987
+ shaker: MACHINE_OPT,
988
+ chords: maybe("marimba", "keys:0.5"),
989
+ lead: role("marimba", "flute:0.4"),
990
+ },
991
+ },
992
+ }),
993
+ card({
994
+ id: "amapiano",
995
+ summary:
996
+ "Amapiano: 110-116 bpm, log-drum bass in tresillo syncopation, 16th shakers, jazzy major-seventh piano, sparse kick",
997
+ tempo: { bpm: [110, 116], typical: 113 },
998
+ groove: { swingRatio: [1.05, 1.25] },
999
+ rhythm: {
1000
+ onsets: {
1001
+ kick: grid("x.......x......."),
1002
+ clap: grid("....x.......x..."),
1003
+ shaker: SIXTEENTHS,
1004
+ },
1005
+ },
1006
+ pitch: {
1007
+ scales: [
1008
+ ["major", 0.5],
1009
+ ["dorian", 0.5],
1010
+ ],
1011
+ },
1012
+ harmony: {
1013
+ presets: [
1014
+ ["ii-v-i", 0.6],
1015
+ ["dorian-vamp", 0.4],
1016
+ ],
1017
+ sevenths: 0.9,
1018
+ },
1019
+ bass: { behaviour: [["ostinato", 1]], onsets: TRESILLO },
1020
+ texture: {
1021
+ roles: {
1022
+ shaker: MACHINE,
1023
+ bass: role("marimba", "bass:0.4"),
1024
+ chords: role("piano", "rhodes:0.5"),
1025
+ lead: maybe("flute", "sax:0.4"),
1026
+ },
1027
+ },
1028
+ }),
1029
+ card({
1030
+ id: "gqom",
1031
+ summary:
1032
+ "Gqom: no four-on-the-floor; broken 3-3-4-3-3 kick cells, dark minor drones, sparse metallic perc",
1033
+ tempo: { bpm: [120, 128], typical: 124 },
1034
+ rhythm: {
1035
+ onsets: {
1036
+ kick: grid("x..x..x...x..x.."),
1037
+ clap: grid("........x......."),
1038
+ hat: grid("..x...x...x...x."),
1039
+ tom: grid("...x.....x....x."),
1040
+ openhat: null,
1041
+ },
1042
+ locks: [],
1043
+ },
1044
+ pitch: {
1045
+ scales: [
1046
+ ["phrygian", 0.5],
1047
+ ["minor", 0.5],
1048
+ ],
1049
+ },
1050
+ harmony: { model: "drone" },
1051
+ texture: {
1052
+ roles: {
1053
+ tom: MACHINE,
1054
+ openhat: null,
1055
+ chords: null,
1056
+ drone: role("organ", "cloud:0.5"),
1057
+ },
1058
+ },
1059
+ }),
1060
+ card({
1061
+ id: "kwaito",
1062
+ summary:
1063
+ "Kwaito: slowed-down house at 100-115 bpm, heavy synth bass, call-and-response chant, minor loops",
1064
+ tempo: { bpm: [100, 115], typical: 108 },
1065
+ harmony: {
1066
+ presets: [
1067
+ ["aeolian", 0.6],
1068
+ ["dorian-vamp", 0.4],
1069
+ ],
1070
+ },
1071
+ bass: {
1072
+ behaviour: [["ostinato", 1]],
1073
+ onsets: grid("x..x..x...x....."),
1074
+ kickLock: 0,
1075
+ },
1076
+ texture: {
1077
+ roles: { kick: K808, bass: role("saw"), lead: role("lead", "choir:0.4") },
1078
+ },
1079
+ }),
1080
+ ];
1081
+
1082
+ // ---------------------------------------------------------------------------
1083
+ // Techno.
1084
+ // Refs: Dan Sicko, "Techno Rebels" (Billboard, 1999); Kodwo Eshun, "More
1085
+ // Brilliant than the Sun" (Quartet, 1998).
1086
+
1087
+ const TECHNO: readonly StyleCard[] = [
1088
+ card({
1089
+ id: "techno",
1090
+ abstract: true,
1091
+ summary:
1092
+ "techno: 125-140 bpm relentless four-on-the-floor, ride and hat ostinati, minimal modal harmony, timbre over chord change",
1093
+ tempo: { bpm: [125, 140], typical: 132 },
1094
+ harmony: { model: "modal", rhythm: [[0.25, 1]] },
1095
+ rhythm: {
1096
+ onsets: {
1097
+ clap: grid("....1.......x..."),
1098
+ perc: grid("..x..x..x...x..x"),
1099
+ },
1100
+ },
1101
+ form: { roleMap: DANCE_ROLEMAP },
1102
+ texture: {
1103
+ roles: {
1104
+ perc: MACHINE_OPT,
1105
+ chords: maybe("saw", "keys:0.5"),
1106
+ lead: role("saw", "square:0.5"),
1107
+ },
1108
+ },
1109
+ melody: { repetition: 0.9, density: [2, 4] },
1110
+ }),
1111
+ card({
1112
+ id: "detroit-techno",
1113
+ summary:
1114
+ "Detroit techno: string-pad minor ninth chords, syncopated 808/909 programming, dorian melancholy over a machine pulse",
1115
+ tempo: { bpm: [125, 135], typical: 130 },
1116
+ pitch: {
1117
+ scales: [
1118
+ ["dorian", 0.6],
1119
+ ["minor", 0.4],
1120
+ ],
1121
+ },
1122
+ harmony: {
1123
+ model: "functional",
1124
+ presets: [
1125
+ ["dorian-vamp", 0.6],
1126
+ ["aeolian", 0.4],
1127
+ ],
1128
+ rhythm: [[0.5, 1]],
1129
+ sevenths: 0.9,
1130
+ },
1131
+ rhythm: { onsets: { hat: SIXTEENTHS } },
1132
+ texture: {
1133
+ roles: {
1134
+ kick: K808,
1135
+ chords: role("strings", "saw:0.5"),
1136
+ pad: role("strings"),
1137
+ },
1138
+ },
1139
+ }),
1140
+ card({
1141
+ id: "minimal-techno",
1142
+ summary:
1143
+ "Minimal techno: reduction to kick, clicks and one evolving tone; probabilistic rim and perc, very low pitched density",
1144
+ tempo: { bpm: [124, 132], typical: 128 },
1145
+ rhythm: {
1146
+ onsets: {
1147
+ clap: grid("............5..."),
1148
+ rim: grid("...3..x....3..4."),
1149
+ perc: grid(".....3.....4...."),
1150
+ },
1151
+ },
1152
+ melody: { density: [0.5, 1], ambitus: [2, 5], repetition: 0.95 },
1153
+ texture: {
1154
+ roles: {
1155
+ rim: MACHINE_OPT,
1156
+ chords: null,
1157
+ pad: null,
1158
+ lead: role("pluck", "triangle:0.5"),
1159
+ },
1160
+ },
1161
+ }),
1162
+ card({
1163
+ id: "dub-techno",
1164
+ summary:
1165
+ "Dub techno: off-beat minor seventh chord stab drowned in dub delay and reverb, sub-bass pulse, hiss and space",
1166
+ tempo: { bpm: [115, 125], typical: 120 },
1167
+ harmony: {
1168
+ model: "functional",
1169
+ presets: [["dorian-vamp", 1]],
1170
+ sevenths: 1,
1171
+ rhythm: [[0.25, 1]],
1172
+ },
1173
+ rhythm: { onsets: { chords: grid("..x.......x....."), clap: null } },
1174
+ bass: { behaviour: [["root", 1]], onsets: grid("x.......x.......") },
1175
+ texture: {
1176
+ roles: {
1177
+ clap: null,
1178
+ chords: role("keys", "epiano:0.5"),
1179
+ lead: null,
1180
+ pad: maybe("cloud"),
1181
+ },
1182
+ },
1183
+ mix: { fx: { chords: { autofilter: "slow-sweep" } }, space: 0.7 },
1184
+ }),
1185
+ card({
1186
+ id: "hard-techno",
1187
+ summary:
1188
+ "Hard techno and schranz: 140-155 bpm distorted kick, looping 16th hat and perc stabs, atonal phrygian riffs",
1189
+ tempo: { bpm: [140, 155], typical: 148 },
1190
+ pitch: { scales: [["phrygian", 1]] },
1191
+ rhythm: { onsets: { hat: SIXTEENTHS, perc: grid("x.xx.xx.x.xx.xx.") } },
1192
+ texture: { roles: { chords: null, lead: role("square", "saw:0.5") } },
1193
+ mix: {
1194
+ fx: { kick: { distort: "crunch" }, perc: { distort: "fuzz" } },
1195
+ loudness: "loud",
1196
+ },
1197
+ }),
1198
+ card({
1199
+ id: "acid-techno",
1200
+ summary:
1201
+ "Acid techno: 303 resonant-filter bass ostinato with accents and slides, 135-145 bpm, phrygian one-chord vamp",
1202
+ tempo: { bpm: [133, 145], typical: 138 },
1203
+ pitch: {
1204
+ scales: [
1205
+ ["phrygian", 0.6],
1206
+ ["minor", 0.4],
1207
+ ],
1208
+ },
1209
+ bass: {
1210
+ behaviour: [["ostinato", 1]],
1211
+ onsets: grid("x.xxx.xxx.x.xx.x"),
1212
+ range: [33, 57],
1213
+ },
1214
+ texture: { roles: { chords: null, bass: role("saw", "square:0.4") } },
1215
+ expression: {
1216
+ articulation: {
1217
+ bass: [
1218
+ ["staccato", 0.5],
1219
+ ["accent", 0.5],
1220
+ ],
1221
+ },
1222
+ },
1223
+ mix: { fx: { bass: { autofilter: "env-follow", distort: "warm" } } },
1224
+ }),
1225
+ card({
1226
+ id: "industrial-techno",
1227
+ summary:
1228
+ "Industrial and warehouse techno: distorted rumble kick, metallic perc, dark phrygian drone, huge reverb",
1229
+ tempo: { bpm: [128, 140], typical: 134 },
1230
+ pitch: {
1231
+ scales: [
1232
+ ["phrygian", 0.6],
1233
+ ["locrian", 0.4],
1234
+ ],
1235
+ },
1236
+ harmony: { model: "drone" },
1237
+ texture: {
1238
+ roles: {
1239
+ chords: null,
1240
+ drone: role("organ", "swarm:0.5"),
1241
+ lead: maybe("square"),
1242
+ },
1243
+ },
1244
+ mix: {
1245
+ fx: { kick: { distort: "fuzz" }, perc: { distort: "crunch" } },
1246
+ space: 0.55,
1247
+ loudness: "loud",
1248
+ },
1249
+ }),
1250
+ card({
1251
+ id: "bleep-techno",
1252
+ summary:
1253
+ "Bleep techno: pure sine-like bleep melodies, deep sub-bass, sparse 808 kit, minor pentatonic cells",
1254
+ tempo: { bpm: [120, 130], typical: 125 },
1255
+ pitch: { scales: [["minor-pentatonic", 1]] },
1256
+ rhythm: { onsets: { hat: OFFBEAT } },
1257
+ bass: { behaviour: [["root", 1]], onsets: grid("x.....x...x.....") },
1258
+ texture: {
1259
+ roles: {
1260
+ kick: K808,
1261
+ chords: null,
1262
+ bass: role("triangle"),
1263
+ lead: role("triangle", "bell:0.4"),
1264
+ },
1265
+ },
1266
+ }),
1267
+ card({
1268
+ id: "new-beat",
1269
+ summary:
1270
+ "New beat: EBM slowed to 100-115 bpm, heavy snare backbeat, sequenced 8th bass, dark minor",
1271
+ tempo: { bpm: [98, 115], typical: 108 },
1272
+ rhythm: { onsets: { snare: BACKBEAT, clap: null, hat: EIGHTHS } },
1273
+ bass: { behaviour: [["ostinato", 1]], onsets: EIGHTHS },
1274
+ texture: { roles: { snare: MACHINE, clap: null, chords: maybe("saw") } },
1275
+ }),
1276
+ card({
1277
+ id: "ebm",
1278
+ summary:
1279
+ "EBM: sequenced 8th or 16th bass ostinato, militant snare backbeat, minor/phrygian riffs, barked short phrases",
1280
+ tempo: { bpm: [115, 130], typical: 122 },
1281
+ pitch: {
1282
+ scales: [
1283
+ ["minor", 0.5],
1284
+ ["phrygian", 0.5],
1285
+ ],
1286
+ },
1287
+ rhythm: { onsets: { snare: BACKBEAT, clap: null, hat: EIGHTHS } },
1288
+ bass: { behaviour: [["ostinato", 1]], onsets: SIXTEENTHS },
1289
+ texture: {
1290
+ roles: {
1291
+ snare: MACHINE,
1292
+ clap: null,
1293
+ bass: role("saw", "square:0.5"),
1294
+ chords: null,
1295
+ },
1296
+ },
1297
+ }),
1298
+ card({
1299
+ id: "electroclash",
1300
+ summary:
1301
+ "Electroclash: electro kit backbeat, square-wave bass, punk-simple two-chord minor riffs, deadpan short phrases",
1302
+ tempo: { bpm: [118, 130], typical: 124 },
1303
+ harmony: {
1304
+ model: "functional",
1305
+ presets: [
1306
+ ["aeolian", 0.5],
1307
+ ["dorian-vamp", 0.5],
1308
+ ],
1309
+ },
1310
+ rhythm: { onsets: { snare: BACKBEAT, clap: null, hat: EIGHTHS } },
1311
+ texture: {
1312
+ roles: {
1313
+ kick: ELECTRO,
1314
+ snare: ELECTRO,
1315
+ clap: null,
1316
+ bass: role("square"),
1317
+ lead: role("square", "vocoder:0.3"),
1318
+ },
1319
+ },
1320
+ }),
1321
+ card({
1322
+ id: "electro",
1323
+ summary:
1324
+ "Electro: syncopated 808 kick (not four-on-the-floor), snare on 2 and 4, 16th hats, vocoder and phrygian bass riffs",
1325
+ tempo: { bpm: [120, 135], typical: 128 },
1326
+ pitch: {
1327
+ scales: [
1328
+ ["phrygian", 0.5],
1329
+ ["minor", 0.5],
1330
+ ],
1331
+ },
1332
+ rhythm: {
1333
+ onsets: {
1334
+ kick: ELECTRO_KICK,
1335
+ snare: BACKBEAT,
1336
+ clap: null,
1337
+ hat: SIXTEENTHS,
1338
+ perc: null,
1339
+ },
1340
+ },
1341
+ bass: { behaviour: [["ostinato", 1]], onsets: ELECTRO_KICK, kickLock: 0.8 },
1342
+ texture: {
1343
+ roles: {
1344
+ kick: ELECTRO,
1345
+ snare: ELECTRO,
1346
+ clap: null,
1347
+ perc: null,
1348
+ lead: role("vocoder", "square:0.5"),
1349
+ },
1350
+ },
1351
+ }),
1352
+ card({
1353
+ id: "ghettotech",
1354
+ summary:
1355
+ "Ghettotech: 140-160 bpm 808 four-on-the-floor, rapid 16th hats and claps, chanted one-note hooks",
1356
+ tempo: { bpm: [140, 160], typical: 150 },
1357
+ rhythm: { onsets: { hat: SIXTEENTHS, clap: grid("....x..x....x.x.") } },
1358
+ melody: { repetition: 0.95, ambitus: [2, 4] },
1359
+ texture: { roles: { kick: K808, chords: null } },
1360
+ }),
1361
+ card({
1362
+ id: "skweee",
1363
+ summary:
1364
+ "Skweee: slow funky 90-110 bpm, square-wave leads, swung 16ths, sparse funk kick, chromatic-free minor pentatonic",
1365
+ tempo: { bpm: [90, 110], typical: 100 },
1366
+ groove: { swingRatio: [1.2, 1.5] },
1367
+ pitch: {
1368
+ scales: [
1369
+ ["minor-pentatonic", 0.6],
1370
+ ["dorian", 0.4],
1371
+ ],
1372
+ },
1373
+ rhythm: {
1374
+ onsets: { kick: BROKEN_KICK, snare: BACKBEAT, clap: null, perc: null },
1375
+ },
1376
+ texture: {
1377
+ roles: {
1378
+ snare: MACHINE,
1379
+ clap: null,
1380
+ perc: null,
1381
+ bass: role("square"),
1382
+ lead: role("square", "triangle:0.5"),
1383
+ },
1384
+ },
1385
+ }),
1386
+ card({
1387
+ id: "makina",
1388
+ summary:
1389
+ "Makina: 160-180 bpm four-on-the-floor, off-beat bass, hoover and piano riffs, bright minor-to-major hooks",
1390
+ tempo: { bpm: [160, 180], typical: 170 },
1391
+ harmony: {
1392
+ model: "functional",
1393
+ presets: [
1394
+ ["aeolian", 0.6],
1395
+ ["andalusian", 0.4],
1396
+ ],
1397
+ rhythm: [[1, 1]],
1398
+ },
1399
+ bass: { behaviour: [["root", 1]], onsets: OFFBEAT },
1400
+ texture: {
1401
+ roles: {
1402
+ chords: role("piano", "saw:0.5"),
1403
+ lead: role("saw", "lead:0.5"),
1404
+ },
1405
+ },
1406
+ mix: { loudness: "loud" },
1407
+ }),
1408
+ card({
1409
+ id: "bouncy-techno",
1410
+ summary:
1411
+ "Bouncy techno and scouse house: 150-170 bpm, off-beat donk bass, cheerful major riffs, four-on-the-floor",
1412
+ tempo: { bpm: [150, 170], typical: 160 },
1413
+ pitch: {
1414
+ scales: [
1415
+ ["major", 0.6],
1416
+ ["minor", 0.4],
1417
+ ],
1418
+ },
1419
+ harmony: {
1420
+ model: "functional",
1421
+ presets: [
1422
+ ["axis", 0.5],
1423
+ ["sad-pop", 0.5],
1424
+ ],
1425
+ cadences: [["V-I", 1]],
1426
+ },
1427
+ bass: { behaviour: [["root", 1]], onsets: OFFBEAT },
1428
+ texture: { roles: { bass: role("square"), lead: role("lead", "saw:0.4") } },
1429
+ mix: { loudness: "loud" },
1430
+ }),
1431
+ card({
1432
+ id: "melodic-techno",
1433
+ summary:
1434
+ "Melodic techno: 120-126 bpm, galloping 8th-plus-16th bass ostinato, closed 16th hats, minor and phrygian arpeggio sequences, long pads, one-chord modal stasis with dramatic builds",
1435
+ tempo: { bpm: [120, 126], typical: 123 },
1436
+ meter: { hypermeter: [[16, 1]] },
1437
+ pitch: {
1438
+ scales: [
1439
+ ["minor", 0.6],
1440
+ ["phrygian", 0.4],
1441
+ ],
1442
+ },
1443
+ harmony: { model: "modal", rhythm: [[0.25, 1]] },
1444
+ bass: { behaviour: [["ostinato", 1]], onsets: GALLOP, range: [31, 45] },
1445
+ rhythm: { onsets: { hat: SIXTEENTHS, arp: SIXTEENTHS } },
1446
+ texture: {
1447
+ roles: {
1448
+ arp: role("pluck", "saw:0.5"),
1449
+ pad: role("strings", "cloud:0.5"),
1450
+ lead: maybe("lead"),
1451
+ },
1452
+ },
1453
+ mix: { fx: { arp: { autofilter: "slow-sweep" } }, space: 0.6 },
1454
+ }),
1455
+ ];
1456
+
1457
+ // ---------------------------------------------------------------------------
1458
+ // Trance.
1459
+ // Refs: Graham St John, "Global Tribe: Technology, Spirituality and
1460
+ // Psytrance" (Equinox, 2012); Butler (2006) on breakdown-buildup form.
1461
+
1462
+ const TRANCE: readonly StyleCard[] = [
1463
+ card({
1464
+ id: "trance-family",
1465
+ abstract: true,
1466
+ summary:
1467
+ "trance: 130-145 bpm, rolling off-beat bass, gated supersaw chords, 16th arpeggios, long breakdown and build",
1468
+ tempo: { bpm: [130, 145], typical: 138 },
1469
+ meter: { hypermeter: [[16, 1]] },
1470
+ pitch: { scales: [["minor", 1]] },
1471
+ harmony: {
1472
+ presets: [
1473
+ ["aeolian", 0.6],
1474
+ ["sad-pop", 0.4],
1475
+ ],
1476
+ rhythm: [[0.5, 1]],
1477
+ },
1478
+ bass: {
1479
+ behaviour: [
1480
+ ["octave", 0.5],
1481
+ ["ostinato", 0.5],
1482
+ ],
1483
+ onsets: OFFBEAT,
1484
+ },
1485
+ form: { roleMap: DANCE_ROLEMAP },
1486
+ texture: { roles: { chords: role("saw"), arp: role("pluck") } },
1487
+ rhythm: { onsets: { arp: SHAKER_16 } },
1488
+ }),
1489
+ card({
1490
+ id: "trance",
1491
+ summary:
1492
+ "Classic and uplifting trance: aeolian vi-IV-I-V supersaw loops, off-beat bass, 16th gated pads, soaring stepwise lead",
1493
+ tempo: { bpm: [136, 140], typical: 138 },
1494
+ harmony: {
1495
+ presets: [
1496
+ ["sad-pop", 0.5],
1497
+ ["aeolian", 0.5],
1498
+ ],
1499
+ cadences: [
1500
+ ["bVII-I", 0.6],
1501
+ ["V-I", 0.4],
1502
+ ],
1503
+ },
1504
+ melody: {
1505
+ density: [1, 2],
1506
+ ambitus: [7, 12],
1507
+ intervals: intervals(4, 2.5, 1.5, 0.8, 1.2),
1508
+ },
1509
+ texture: {
1510
+ roles: { pad: role("strings", "saw:0.5"), lead: role("saw", "lead:0.5") },
1511
+ },
1512
+ mix: { fx: { chords: { tremolo: "eighth-chop" } }, space: 0.45 },
1513
+ }),
1514
+ card({
1515
+ id: "progressive-trance",
1516
+ summary:
1517
+ "Progressive trance: 128-134 bpm, galloping 8th-plus-16th bass, closed 16th hats, slow-evolving filtered arpeggio, sparse harmonic rhythm, restrained lead",
1518
+ tempo: { bpm: [128, 134], typical: 132 },
1519
+ rhythm: { onsets: { hat: SIXTEENTHS } },
1520
+ bass: { onsets: GALLOP },
1521
+ harmony: { rhythm: [[0.25, 1]] },
1522
+ melody: { density: [1, 2] },
1523
+ texture: { roles: { lead: maybe("pluck", "saw:0.4") } },
1524
+ mix: { fx: { arp: { autofilter: "slow-sweep" } } },
1525
+ }),
1526
+ card({
1527
+ id: "tech-trance",
1528
+ summary:
1529
+ "Tech trance: techno drive at trance tempo, percussive 16th stabs, modal one-chord harmony, minimal melody",
1530
+ tempo: { bpm: [135, 142], typical: 138 },
1531
+ harmony: { model: "modal", rhythm: [[0.25, 1]] },
1532
+ rhythm: { onsets: { hat: SIXTEENTHS, perc: grid("..x..x..x..x..x.") } },
1533
+ texture: {
1534
+ roles: {
1535
+ perc: MACHINE_OPT,
1536
+ chords: null,
1537
+ lead: role("saw", "square:0.5"),
1538
+ },
1539
+ },
1540
+ }),
1541
+ card({
1542
+ id: "hard-trance",
1543
+ summary:
1544
+ "Hard trance: 145-155 bpm, distorted kick, driving off-beat bass, harsh minor supersaw riffs",
1545
+ tempo: { bpm: [145, 155], typical: 150 },
1546
+ harmony: {
1547
+ presets: [
1548
+ ["aeolian", 0.6],
1549
+ ["andalusian", 0.4],
1550
+ ],
1551
+ },
1552
+ texture: { roles: { lead: role("saw") } },
1553
+ mix: { fx: { kick: { distort: "crunch" } }, loudness: "loud" },
1554
+ }),
1555
+ card({
1556
+ id: "goa-trance",
1557
+ summary:
1558
+ "Goa trance: phrygian and harmonic-minor modal riffs, 16th acid-like lead lines, rolling kick-bass, no chord changes",
1559
+ tempo: { bpm: [138, 150], typical: 145 },
1560
+ pitch: {
1561
+ scales: [
1562
+ ["phrygian", 0.5],
1563
+ ["harmonic-minor", 0.3],
1564
+ ["phrygian-dominant", 0.2],
1565
+ ],
1566
+ },
1567
+ harmony: { model: "modal", rhythm: [[0.25, 1]] },
1568
+ bass: { behaviour: [["ostinato", 1]], onsets: ROLLING },
1569
+ melody: {
1570
+ density: [3, 4],
1571
+ repetition: 0.9,
1572
+ intervals: intervals(5, 2, 0.8, 1),
1573
+ },
1574
+ texture: {
1575
+ roles: { chords: null, arp: role("saw", "pluck:0.5"), lead: role("saw") },
1576
+ },
1577
+ mix: { fx: { lead: { autofilter: "env-follow" } } },
1578
+ }),
1579
+ card({
1580
+ id: "psytrance",
1581
+ summary:
1582
+ "Psytrance: rolling kick-bass (three 16th bass notes after each kick, never on it), phrygian one-chord riffs, 140-148 bpm",
1583
+ tempo: { bpm: [140, 148], typical: 144 },
1584
+ pitch: {
1585
+ scales: [
1586
+ ["phrygian", 0.7],
1587
+ ["minor", 0.3],
1588
+ ],
1589
+ },
1590
+ harmony: { model: "modal", rhythm: [[0.25, 1]] },
1591
+ bass: { behaviour: [["ostinato", 1]], onsets: ROLLING, range: [31, 45] },
1592
+ rhythm: { onsets: { hat: OFFBEAT, perc: grid("..x...x...x..xx.") } },
1593
+ texture: {
1594
+ roles: {
1595
+ perc: MACHINE_OPT,
1596
+ chords: null,
1597
+ lead: role("saw", "square:0.5"),
1598
+ },
1599
+ },
1600
+ }),
1601
+ card({
1602
+ id: "progressive-psy",
1603
+ summary:
1604
+ "Progressive psytrance: 134-140 bpm, rolling kick-bass in a shorter two-note 16th pattern, minor not phrygian, hypnotic filtered riffs, sparser and deeper than full-on",
1605
+ tempo: { bpm: [134, 140], typical: 137 },
1606
+ meter: { hypermeter: [[16, 1]] },
1607
+ pitch: {
1608
+ scales: [
1609
+ ["minor", 0.7],
1610
+ ["dorian", 0.3],
1611
+ ],
1612
+ },
1613
+ harmony: { model: "modal", rhythm: [[0.25, 1]] },
1614
+ bass: {
1615
+ behaviour: [["ostinato", 1]],
1616
+ onsets: grid(".xx..xx..xx..xx."),
1617
+ range: [31, 45],
1618
+ },
1619
+ rhythm: { onsets: { hat: OFFBEAT, perc: grid("......x.......x.") } },
1620
+ texture: {
1621
+ roles: {
1622
+ perc: MACHINE_OPT,
1623
+ chords: null,
1624
+ pad: maybe("cloud"),
1625
+ lead: role("pluck", "saw:0.5"),
1626
+ },
1627
+ },
1628
+ mix: { fx: { lead: { autofilter: "slow-sweep" } }, space: 0.5 },
1629
+ }),
1630
+ card({
1631
+ id: "dark-psy",
1632
+ summary:
1633
+ "Dark psytrance and forest: 148-165 bpm rolling kick-bass, locrian and phrygian dissonance, chaotic perc",
1634
+ tempo: { bpm: [148, 165], typical: 155 },
1635
+ pitch: {
1636
+ scales: [
1637
+ ["locrian", 0.5],
1638
+ ["phrygian", 0.5],
1639
+ ],
1640
+ },
1641
+ harmony: { model: "drone" },
1642
+ bass: { behaviour: [["ostinato", 1]], onsets: ROLLING, range: [31, 45] },
1643
+ rhythm: { onsets: { perc: grid("x.3x.x3.x..x3.x.") } },
1644
+ texture: {
1645
+ roles: {
1646
+ perc: MACHINE_OPT,
1647
+ chords: null,
1648
+ arp: null,
1649
+ drone: role("swarm", "organ:0.5"),
1650
+ },
1651
+ },
1652
+ }),
1653
+ card({
1654
+ id: "suomisaundi",
1655
+ summary:
1656
+ "Suomisaundi: playful psytrance with mixolydian quirks, rolling bass, absurd stop-start perc",
1657
+ tempo: { bpm: [138, 150], typical: 145 },
1658
+ pitch: {
1659
+ scales: [
1660
+ ["mixolydian", 0.5],
1661
+ ["dorian", 0.5],
1662
+ ],
1663
+ },
1664
+ harmony: { model: "modal", rhythm: [[0.25, 1]] },
1665
+ bass: { behaviour: [["ostinato", 1]], onsets: ROLLING },
1666
+ rhythm: { onsets: { perc: grid("x..x.x....x.x..x") } },
1667
+ texture: {
1668
+ roles: {
1669
+ perc: MACHINE_OPT,
1670
+ chords: null,
1671
+ lead: role("square", "organ:0.4"),
1672
+ },
1673
+ },
1674
+ }),
1675
+ card({
1676
+ id: "psybient",
1677
+ summary:
1678
+ "Psybient and psydub: half-tempo psychedelic ambient, dub delays, phrygian drones, sparse kit",
1679
+ tempo: { bpm: [80, 110], typical: 95 },
1680
+ meter: { hypermeter: [[8, 1]] },
1681
+ pitch: {
1682
+ scales: [
1683
+ ["phrygian", 0.5],
1684
+ ["dorian", 0.5],
1685
+ ],
1686
+ },
1687
+ harmony: { model: "drone" },
1688
+ rhythm: { onsets: { kick: BROKEN_KICK, clap: HALF_TIME, hat: OFFBEAT } },
1689
+ bass: { behaviour: [["pedal", 1]] },
1690
+ texture: {
1691
+ roles: {
1692
+ chords: null,
1693
+ arp: null,
1694
+ drone: role("cloud", "organ:0.5"),
1695
+ pad: role("granular"),
1696
+ },
1697
+ },
1698
+ mix: { space: 0.75, loudness: "streaming" },
1699
+ }),
1700
+ ];
1701
+
1702
+ // ---------------------------------------------------------------------------
1703
+ // Hardcore and hard dance.
1704
+ // Refs: Simon Reynolds, "Energy Flash" (Picador, 1998); Butler (2006).
1705
+
1706
+ const HARDCORE: readonly StyleCard[] = [
1707
+ card({
1708
+ id: "hardcore-family",
1709
+ abstract: true,
1710
+ summary:
1711
+ "hardcore: 150-200 bpm, distorted overdriven kick as the lead instrument, minor riffs, loud master",
1712
+ tempo: { bpm: [150, 200], typical: 170 },
1713
+ harmony: {
1714
+ presets: [
1715
+ ["aeolian", 0.6],
1716
+ ["andalusian", 0.4],
1717
+ ],
1718
+ sevenths: 0,
1719
+ },
1720
+ form: { roleMap: DANCE_ROLEMAP },
1721
+ mix: { fx: { kick: { distort: "crunch" } }, loudness: "loud" },
1722
+ texture: { roles: { kick: MACHINE } },
1723
+ }),
1724
+ card({
1725
+ id: "rave-hardcore",
1726
+ summary:
1727
+ "Breakbeat hardcore: syncopated break snare against a four-floor-free kick, major piano stabs, rave hoover",
1728
+ tempo: { bpm: [140, 160], typical: 150 },
1729
+ rhythm: {
1730
+ onsets: {
1731
+ kick: BROKEN_KICK,
1732
+ snare: grid("....x..1.x..x..1"),
1733
+ clap: null,
1734
+ hat: EIGHTHS,
1735
+ },
1736
+ },
1737
+ pitch: {
1738
+ scales: [
1739
+ ["major", 0.5],
1740
+ ["minor", 0.5],
1741
+ ],
1742
+ },
1743
+ harmony: {
1744
+ presets: [
1745
+ ["axis", 0.5],
1746
+ ["aeolian", 0.5],
1747
+ ],
1748
+ },
1749
+ texture: {
1750
+ roles: {
1751
+ snare: MACHINE,
1752
+ clap: null,
1753
+ chords: role("piano"),
1754
+ lead: role("saw", "lead:0.5"),
1755
+ },
1756
+ },
1757
+ mix: { fx: { kick: { distort: "warm" } } },
1758
+ }),
1759
+ card({
1760
+ id: "happy-hardcore",
1761
+ summary:
1762
+ "Happy hardcore: 165-180 bpm, bright major I-V-vi-IV piano, off-beat bass stabs, euphoric stepwise leads",
1763
+ tempo: { bpm: [165, 180], typical: 172 },
1764
+ pitch: { scales: [["major", 1]] },
1765
+ harmony: {
1766
+ presets: [
1767
+ ["axis", 0.6],
1768
+ ["fifties", 0.4],
1769
+ ],
1770
+ cadences: [["V-I", 1]],
1771
+ },
1772
+ bass: { behaviour: [["root", 1]], onsets: OFFBEAT },
1773
+ texture: {
1774
+ roles: { chords: role("piano"), lead: role("saw", "lead:0.5") },
1775
+ },
1776
+ }),
1777
+ card({
1778
+ id: "gabber",
1779
+ summary:
1780
+ "Gabber: 160-200 bpm fuzz-distorted kick on every beat that is also the bass (no separate bassline), off-beat hats, dark minor stabs",
1781
+ tempo: { bpm: [160, 200], typical: 180 },
1782
+ bass: { behaviour: [["none", 1]] },
1783
+ harmony: { model: "modal", rhythm: [[0.25, 1]] },
1784
+ texture: { roles: { chords: maybe("saw"), lead: role("saw") } },
1785
+ mix: { fx: { kick: { distort: "fuzz" } } },
1786
+ }),
1787
+ card({
1788
+ id: "doomcore",
1789
+ summary:
1790
+ "Doomcore: slow hardcore 140-170 bpm, phrygian and locrian drones, distorted kick, dread over melody",
1791
+ tempo: { bpm: [140, 170], typical: 155 },
1792
+ pitch: {
1793
+ scales: [
1794
+ ["phrygian", 0.5],
1795
+ ["locrian", 0.5],
1796
+ ],
1797
+ },
1798
+ harmony: { model: "drone" },
1799
+ melody: { density: [0.5, 1.5] },
1800
+ texture: { roles: { chords: null, drone: role("organ", "swarm:0.5") } },
1801
+ mix: { space: 0.5 },
1802
+ }),
1803
+ card({
1804
+ id: "speedcore",
1805
+ summary:
1806
+ "Speedcore and extratone: 250-300 bpm, kick drum as a pitched buzz, 8th or 16th kick streams, atonal",
1807
+ tempo: { bpm: [250, 300], typical: 280 },
1808
+ harmony: { model: "drone" },
1809
+ rhythm: {
1810
+ onsets: { kick: EIGHTHS, clap: null, hat: null },
1811
+ fills: { every: 8, density: [0, 0.1] },
1812
+ },
1813
+ melody: { density: [0.5, 1] },
1814
+ texture: {
1815
+ roles: { clap: null, hat: null, chords: null, lead: maybe("square") },
1816
+ },
1817
+ mix: { fx: { kick: { distort: "fuzz" } } },
1818
+ }),
1819
+ card({
1820
+ id: "hardstyle",
1821
+ summary:
1822
+ "Hardstyle: 145-155 bpm, reverse bass tail on every off-beat, pitched distorted kick, epic minor supersaw leads",
1823
+ tempo: { bpm: [145, 155], typical: 150 },
1824
+ bass: { behaviour: [["root", 1]], onsets: OFFBEAT },
1825
+ texture: {
1826
+ roles: {
1827
+ bass: role("saw"),
1828
+ chords: role("saw"),
1829
+ lead: role("saw", "lead:0.5"),
1830
+ },
1831
+ },
1832
+ mix: { fx: { bass: { distort: "shape" } } },
1833
+ }),
1834
+ card({
1835
+ id: "jumpstyle",
1836
+ summary:
1837
+ "Jumpstyle: 138-145 bpm hard kick with off-beat bass, no hi-hat bed (off-beat percussion instead), two-chord minor stabs, shuffle-dance groove",
1838
+ tempo: { bpm: [138, 145], typical: 142 },
1839
+ groove: { swingRatio: [1.1, 1.3] },
1840
+ rhythm: { onsets: { hat: null, perc: grid("..x...x...x...x.") } },
1841
+ harmony: { presets: [["dorian-vamp", 1]] },
1842
+ bass: { behaviour: [["root", 1]], onsets: OFFBEAT },
1843
+ texture: { roles: { bass: role("square"), perc: MACHINE } },
1844
+ }),
1845
+ card({
1846
+ id: "frenchcore",
1847
+ summary:
1848
+ "Frenchcore and uptempo: 190-210 bpm distorted kick with an off-beat bass jump, harmonic-minor riffs",
1849
+ tempo: { bpm: [190, 210], typical: 200 },
1850
+ pitch: {
1851
+ scales: [
1852
+ ["harmonic-minor", 0.6],
1853
+ ["minor", 0.4],
1854
+ ],
1855
+ },
1856
+ bass: { behaviour: [["root", 1]], onsets: OFFBEAT },
1857
+ texture: { roles: { lead: role("saw", "violins:0.4") } },
1858
+ mix: { fx: { kick: { distort: "fuzz" } } },
1859
+ }),
1860
+ card({
1861
+ id: "j-core",
1862
+ summary:
1863
+ "J-core: 170-200 bpm, major anime-pop changes (canon and I-V-vi-IV), bright 16th arps, chopped breaks",
1864
+ tempo: { bpm: [170, 200], typical: 185 },
1865
+ pitch: { scales: [["major", 1]] },
1866
+ harmony: {
1867
+ presets: [
1868
+ ["canon", 0.5],
1869
+ ["axis", 0.5],
1870
+ ],
1871
+ cadences: [["V-I", 1]],
1872
+ },
1873
+ rhythm: { onsets: { arp: SIXTEENTHS } },
1874
+ texture: {
1875
+ roles: {
1876
+ arp: role("square", "pluck:0.5"),
1877
+ chords: role("piano", "saw:0.5"),
1878
+ lead: role("lead", "square:0.5"),
1879
+ },
1880
+ },
1881
+ }),
1882
+ card({
1883
+ id: "lento-violento",
1884
+ summary:
1885
+ "Lento violento: hardcore slowed to 90-110 bpm, heavy distorted kick, sludgy phrygian riffs",
1886
+ tempo: { bpm: [90, 110], typical: 100 },
1887
+ pitch: { scales: [["phrygian", 1]] },
1888
+ harmony: { model: "modal", rhythm: [[0.25, 1]] },
1889
+ texture: { roles: { chords: null, lead: role("saw") } },
1890
+ mix: { fx: { kick: { distort: "fuzz" } } },
1891
+ }),
1892
+ card({
1893
+ id: "freetekno",
1894
+ summary:
1895
+ "Freetekno: free-party 160-180 bpm, acid 303 bass, relentless four-on-the-floor, minimal modal harmony",
1896
+ tempo: { bpm: [160, 180], typical: 170 },
1897
+ harmony: { model: "modal", rhythm: [[0.25, 1]] },
1898
+ bass: { behaviour: [["ostinato", 1]], onsets: grid("x.xxx.xxx.xxx.xx") },
1899
+ texture: { roles: { chords: null, bass: role("saw") } },
1900
+ mix: {
1901
+ fx: { bass: { autofilter: "env-follow" }, kick: { distort: "warm" } },
1902
+ },
1903
+ }),
1904
+ ];
1905
+
1906
+ // ---------------------------------------------------------------------------
1907
+ // Downtempo and chill.
1908
+ // Refs: Reynolds (1998) on trip hop; Adam Harper, "Infinite Music"
1909
+ // (Zero, 2011) on vaporwave and hypnagogic pop.
1910
+
1911
+ const DOWNTEMPO: readonly StyleCard[] = [
1912
+ card({
1913
+ id: "downtempo-family",
1914
+ abstract: true,
1915
+ summary:
1916
+ "downtempo: 70-110 bpm, broken half-step beats, jazz-tinged seventh chords, dub space",
1917
+ tempo: { bpm: [70, 110], typical: 90 },
1918
+ groove: { swingRatio: [1, 1.3], humanize: { timingMs: 4, velocity: 0.05 } },
1919
+ rhythm: {
1920
+ onsets: {
1921
+ kick: BROKEN_KICK,
1922
+ snare: BACKBEAT,
1923
+ clap: null,
1924
+ },
1925
+ },
1926
+ harmony: { sevenths: 0.8 },
1927
+ form: {
1928
+ plans: [
1929
+ [
1930
+ ["intro", "verse", "chorus", "breakdown", "verse", "chorus", "outro"],
1931
+ 1,
1932
+ ],
1933
+ ],
1934
+ archetype: "verse-chorus",
1935
+ },
1936
+ texture: {
1937
+ roles: { snare: MACHINE, clap: null, chords: role("epiano", "keys:0.5") },
1938
+ },
1939
+ mix: { space: 0.5, loudness: "streaming" },
1940
+ }),
1941
+ card({
1942
+ id: "downtempo",
1943
+ summary:
1944
+ "Downtempo: relaxed broken beat at 85-100 bpm, minor ninth loops, warm electric piano, dub delay",
1945
+ tempo: { bpm: [85, 100], typical: 92 },
1946
+ texture: { roles: { kick: LOFI } },
1947
+ mix: { space: 0.6 },
1948
+ }),
1949
+ card({
1950
+ id: "trip-hop",
1951
+ summary:
1952
+ "Trip hop: slow swung boom-bap backbeat under 100 bpm, minor and harmonic-minor strings, vinyl-crackle space",
1953
+ tempo: { bpm: [70, 95], typical: 84 },
1954
+ groove: { swingRatio: [1.2, 1.5] },
1955
+ pitch: {
1956
+ scales: [
1957
+ ["minor", 0.6],
1958
+ ["harmonic-minor", 0.4],
1959
+ ],
1960
+ },
1961
+ harmony: {
1962
+ presets: [
1963
+ ["andalusian", 0.5],
1964
+ ["aeolian", 0.5],
1965
+ ],
1966
+ },
1967
+ texture: {
1968
+ roles: {
1969
+ kick: LOFI,
1970
+ chords: role("strings", "rhodes:0.5"),
1971
+ lead: role("violins", "lead:0.4"),
1972
+ },
1973
+ },
1974
+ mix: { fx: { chords: { crush: "lofi" } } },
1975
+ }),
1976
+ card({
1977
+ id: "illbient",
1978
+ summary:
1979
+ "Illbient: dub-wise dark urban ambient, half-time sparse beat, drone pedal, delay and reverb as form",
1980
+ tempo: { bpm: [70, 90], typical: 80 },
1981
+ harmony: { model: "drone" },
1982
+ rhythm: { onsets: { kick: grid("x.........5....."), snare: HALF_TIME } },
1983
+ bass: { behaviour: [["pedal", 1]] },
1984
+ texture: { roles: { chords: null, drone: role("organ", "swarm:0.5") } },
1985
+ mix: { space: 0.75 },
1986
+ }),
1987
+ card({
1988
+ id: "leftfield",
1989
+ summary:
1990
+ "Leftfield: eclectic style-blending electronica at 100-120 bpm, dub bass, unusual timbres over four-on-the-floor",
1991
+ tempo: { bpm: [100, 120], typical: 112 },
1992
+ rhythm: { onsets: { kick: FOUR_FLOOR, hat: OFFBEAT } },
1993
+ harmony: { model: "modal", rhythm: [[0.5, 1]] },
1994
+ texture: {
1995
+ roles: { lead: role("granular", "pluck:0.5"), chords: maybe("organ") },
1996
+ },
1997
+ }),
1998
+ card({
1999
+ id: "chillwave",
2000
+ summary:
2001
+ "Chillwave: hazy major-seventh loops, chorused synths, tape wobble, soft backbeat at 80-110 bpm",
2002
+ tempo: { bpm: [80, 110], typical: 96 },
2003
+ pitch: {
2004
+ scales: [
2005
+ ["major", 0.7],
2006
+ ["lydian", 0.3],
2007
+ ],
2008
+ },
2009
+ harmony: {
2010
+ presets: [
2011
+ ["axis", 0.5],
2012
+ ["canon", 0.5],
2013
+ ],
2014
+ cadences: [["IV-I", 1]],
2015
+ },
2016
+ rhythm: { onsets: { kick: grid("x.......x......."), hat: EIGHTHS } },
2017
+ texture: {
2018
+ roles: {
2019
+ kick: LOFI,
2020
+ chords: role("keys", "dreampop:0.5"),
2021
+ lead: role("lead"),
2022
+ },
2023
+ },
2024
+ mix: { fx: { chords: { chorus: "wide", wobble: "tape" } } },
2025
+ }),
2026
+ card({
2027
+ id: "vaporwave",
2028
+ summary:
2029
+ "Vaporwave: slowed smooth-jazz and muzak ii-V major ninths, pitched-down loop repetition, tape wobble, reverb wash",
2030
+ tempo: { bpm: [60, 90], typical: 75 },
2031
+ pitch: { scales: [["major", 1]] },
2032
+ harmony: {
2033
+ presets: [
2034
+ ["ii-v-i", 0.7],
2035
+ ["turnaround", 0.3],
2036
+ ],
2037
+ cadences: [["V-I", 1]],
2038
+ sevenths: 1,
2039
+ },
2040
+ melody: { repetition: 0.95 },
2041
+ texture: {
2042
+ roles: {
2043
+ chords: role("epiano", "sax:0.3"),
2044
+ lead: role("sax", "epiano:0.4"),
2045
+ },
2046
+ },
2047
+ mix: { fx: { chords: { wobble: "tape" } }, space: 0.7 },
2048
+ }),
2049
+ card({
2050
+ id: "future-funk",
2051
+ summary:
2052
+ "Future funk: chopped disco loops at 110-125 bpm, four-on-the-floor, major ninth ii-V vamps, slap bass, sidechain pump",
2053
+ tempo: { bpm: [110, 125], typical: 118 },
2054
+ groove: { swingRatio: [1, 1.15] },
2055
+ pitch: {
2056
+ scales: [
2057
+ ["major", 0.6],
2058
+ ["dorian", 0.4],
2059
+ ],
2060
+ },
2061
+ harmony: {
2062
+ presets: [
2063
+ ["ii-v-i", 0.5],
2064
+ ["dorian-vamp", 0.5],
2065
+ ],
2066
+ sevenths: 0.9,
2067
+ },
2068
+ rhythm: { onsets: { kick: FOUR_FLOOR, snare: BACKBEAT, hat: OFFBEAT } },
2069
+ bass: { behaviour: [["octave", 1]], onsets: EIGHTHS },
2070
+ texture: {
2071
+ roles: { bass: role("slap"), chords: role("rhodes", "strings:0.5") },
2072
+ },
2073
+ mix: { fx: { chords: { duck: "pump" } }, loudness: "club" },
2074
+ }),
2075
+ card({
2076
+ id: "witch-house",
2077
+ summary:
2078
+ "Witch house: half-time at 60-80 bpm (snare only on beat 3), triplet hats, dark phrygian drones, 808 sub",
2079
+ tempo: { bpm: [60, 80], typical: 70 },
2080
+ groove: { subdivision: 3, swingRatio: [1, 1] },
2081
+ pitch: {
2082
+ scales: [
2083
+ ["phrygian", 0.5],
2084
+ ["minor", 0.5],
2085
+ ],
2086
+ },
2087
+ harmony: { model: "drone" },
2088
+ rhythm: {
2089
+ onsets: {
2090
+ kick: grid("x.....x....."),
2091
+ snare: grid("......x....."),
2092
+ hat: grid("xxxxxxxxxxxx"),
2093
+ },
2094
+ fills: { every: 8, density: [0, 0] },
2095
+ },
2096
+ bass: { behaviour: [["pedal", 1]] },
2097
+ texture: {
2098
+ roles: { kick: TRAP, chords: null, drone: role("choir", "organ:0.5") },
2099
+ },
2100
+ mix: { space: 0.7 },
2101
+ }),
2102
+ ];
2103
+
2104
+ // ---------------------------------------------------------------------------
2105
+ // Ambient and synthesizer music.
2106
+ // Refs: Brian Eno, liner notes to "Ambient 1: Music for Airports" (1978);
2107
+ // Mark Prendergast, "The Ambient Century" (Bloomsbury, 2000).
2108
+
2109
+ const AMBIENT: readonly StyleCard[] = [
2110
+ card({
2111
+ id: "ambient-family",
2112
+ abstract: true,
2113
+ summary:
2114
+ "ambient: no kit, pedal-point drones and slow pads, modal stasis instead of cadence, long reverb tails",
2115
+ tempo: { bpm: [60, 100], typical: 80 },
2116
+ harmony: { model: "modal", rhythm: [[0.25, 1]] },
2117
+ melody: { density: [0, 1], repetition: 0.5 },
2118
+ bass: { behaviour: [["pedal", 1]], onsets: DOWNBEAT },
2119
+ form: {
2120
+ plans: [[["intro", "verse", "bridge", "verse", "outro"], 1]],
2121
+ archetype: "through-composed",
2122
+ },
2123
+ texture: {
2124
+ roles: {
2125
+ ...NO_KIT,
2126
+ chords: null,
2127
+ pad: role("strings", "granular:0.6"),
2128
+ drone: role("organ", "cloud:0.5"),
2129
+ lead: maybe("bell", "pluck:0.5"),
2130
+ },
2131
+ },
2132
+ mix: { space: 0.85, loudness: "ambient" },
2133
+ }),
2134
+ card({
2135
+ id: "ambient",
2136
+ summary:
2137
+ "Ambient: tonic pedal with slow major and lydian modal pads, sparse bell tones, music as environment",
2138
+ pitch: {
2139
+ scales: [
2140
+ ["major", 0.5],
2141
+ ["lydian", 0.5],
2142
+ ],
2143
+ },
2144
+ texture: { roles: { lead: maybe("bell", "celesta:0.5") } },
2145
+ }),
2146
+ card({
2147
+ id: "dark-ambient",
2148
+ summary:
2149
+ "Dark ambient: phrygian and locrian drones, low swarming textures, no pulse, dread through dissonant pedal",
2150
+ tempo: { bpm: [50, 80], typical: 65 },
2151
+ pitch: {
2152
+ scales: [
2153
+ ["phrygian", 0.5],
2154
+ ["locrian", 0.5],
2155
+ ],
2156
+ },
2157
+ harmony: { model: "drone" },
2158
+ melody: { density: [0, 0.5], range: [48, 72] },
2159
+ texture: {
2160
+ roles: {
2161
+ pad: role("swarm", "granular:0.5"),
2162
+ drone: role("organ"),
2163
+ lead: maybe("gong", "bowl:0.5"),
2164
+ },
2165
+ },
2166
+ }),
2167
+ card({
2168
+ id: "space-ambient",
2169
+ summary:
2170
+ "Space ambient: lydian raised-fourth shimmer over a sub pedal, slow cloud textures, vast reverb",
2171
+ tempo: { bpm: [48, 66], typical: 56 },
2172
+ pitch: { scales: [["lydian", 1]] },
2173
+ texture: {
2174
+ roles: {
2175
+ pad: role("cloud", "strings:0.5"),
2176
+ lead: maybe("sparkle", "bell:0.5"),
2177
+ },
2178
+ },
2179
+ mix: { space: 0.95 },
2180
+ }),
2181
+ card({
2182
+ id: "berlin-school",
2183
+ summary:
2184
+ "Berlin school: interlocking 16th sequencer arpeggios over an 8th sequencer bass ostinato, no kit, slow filter evolution, long modal sections",
2185
+ tempo: { bpm: [100, 130], typical: 115 },
2186
+ meter: { hypermeter: [[16, 1]] },
2187
+ pitch: {
2188
+ scales: [
2189
+ ["minor", 0.6],
2190
+ ["dorian", 0.4],
2191
+ ],
2192
+ },
2193
+ rhythm: { onsets: { arp: SIXTEENTHS } },
2194
+ bass: { behaviour: [["ostinato", 1]], onsets: EIGHTHS },
2195
+ melody: { density: [1, 2] },
2196
+ texture: {
2197
+ roles: {
2198
+ arp: role("saw", "pluck:0.5"),
2199
+ pad: role("strings"),
2200
+ lead: maybe("lead"),
2201
+ },
2202
+ },
2203
+ mix: { fx: { arp: { autofilter: "slow-sweep" } } },
2204
+ }),
2205
+ card({
2206
+ id: "new-age",
2207
+ summary:
2208
+ "New age: consonant major and pentatonic modes, harp and piano arpeggio over pads, gentle functional cadences",
2209
+ tempo: { bpm: [60, 90], typical: 72 },
2210
+ pitch: {
2211
+ scales: [
2212
+ ["major", 0.5],
2213
+ ["major-pentatonic", 0.5],
2214
+ ],
2215
+ },
2216
+ harmony: {
2217
+ model: "functional",
2218
+ presets: [
2219
+ ["canon", 0.5],
2220
+ ["axis", 0.5],
2221
+ ],
2222
+ cadences: [
2223
+ ["IV-I", 0.6],
2224
+ ["V-I", 0.4],
2225
+ ],
2226
+ rhythm: [[0.5, 1]],
2227
+ },
2228
+ texture: {
2229
+ roles: {
2230
+ chords: role("harp", "piano:0.6"),
2231
+ lead: role("flute", "piano:0.5"),
2232
+ },
2233
+ },
2234
+ }),
2235
+ card({
2236
+ id: "dungeon-synth",
2237
+ summary:
2238
+ "Dungeon synth: lo-fi organ and string synth in harmonic minor, i-iv-V and Andalusian descents, medieval drone",
2239
+ tempo: { bpm: [60, 100], typical: 80 },
2240
+ pitch: {
2241
+ scales: [
2242
+ ["harmonic-minor", 0.5],
2243
+ ["minor", 0.5],
2244
+ ],
2245
+ },
2246
+ harmony: {
2247
+ model: "functional",
2248
+ presets: [
2249
+ ["andalusian", 0.5],
2250
+ ["aeolian", 0.5],
2251
+ ],
2252
+ cadences: [["V-I", 1]],
2253
+ rhythm: [[1, 1]],
2254
+ sevenths: 0,
2255
+ },
2256
+ melody: { density: [1, 2] },
2257
+ texture: {
2258
+ roles: {
2259
+ chords: role("organ", "strings:0.5"),
2260
+ lead: role("flute", "organ:0.5"),
2261
+ drone: null,
2262
+ },
2263
+ },
2264
+ mix: { fx: { chords: { crush: "lofi" } } },
2265
+ }),
2266
+ card({
2267
+ id: "ambient-techno",
2268
+ summary:
2269
+ "Ambient techno: soft four-on-the-floor and off-beat hat under long dorian pads, dub chord echoes",
2270
+ tempo: { bpm: [110, 125], typical: 118 },
2271
+ pitch: {
2272
+ scales: [
2273
+ ["dorian", 0.6],
2274
+ ["minor", 0.4],
2275
+ ],
2276
+ },
2277
+ rhythm: { onsets: { kick: FOUR_FLOOR, hat: OFFBEAT } },
2278
+ texture: { roles: { kick: K808, hat: K808_OPT } },
2279
+ mix: { space: 0.7, loudness: "streaming" },
2280
+ }),
2281
+ card({
2282
+ id: "japanese-ambient",
2283
+ summary:
2284
+ "Kankyo ongaku: environmental music in major pentatonic, sparse marimba and piano cells, silence as structure",
2285
+ tempo: { bpm: [60, 90], typical: 72 },
2286
+ pitch: { scales: [["major-pentatonic", 1]] },
2287
+ melody: { density: [0.5, 1], repetition: 0.7 },
2288
+ texture: {
2289
+ roles: {
2290
+ pad: role("felt", "strings:0.4"),
2291
+ lead: role("marimba", "piano:0.5", "vibes:0.3"),
2292
+ drone: null,
2293
+ },
2294
+ },
2295
+ mix: { space: 0.6 },
2296
+ }),
2297
+ ];
2298
+
2299
+ // ---------------------------------------------------------------------------
2300
+ // IDM and experimental electronic.
2301
+ // Refs: Kim Cascone, "The Aesthetics of Failure" (Computer Music Journal
2302
+ // 24:4, 2000); S. Alexander Reed, "Assimilate: A Critical History of
2303
+ // Industrial Music" (Oxford UP, 2013).
2304
+
2305
+ const IDM: readonly StyleCard[] = [
2306
+ card({
2307
+ id: "idm-family",
2308
+ abstract: true,
2309
+ summary:
2310
+ "IDM: irregular programmed beats, additive 7/8 and 5/4 groupings, probabilistic ghost hits, modal color",
2311
+ meter: {
2312
+ signatures: [
2313
+ ["4/4", 0.6],
2314
+ ["7/8", 0.2],
2315
+ ["5/4", 0.2],
2316
+ ],
2317
+ },
2318
+ harmony: { model: "modal" },
2319
+ rhythm: {
2320
+ onsets: {
2321
+ kick: grid("x..1..x...1.x..."),
2322
+ clap: grid("....x..1....x.1."),
2323
+ hat: grid("x1x1x1x1x1x1x1x1"),
2324
+ },
2325
+ },
2326
+ }),
2327
+ card({
2328
+ id: "idm",
2329
+ summary:
2330
+ "IDM: displaced kick and snare, 16th ghost-note programming, additive meters, warm modal pads",
2331
+ texture: {
2332
+ roles: {
2333
+ pad: role("strings", "granular:0.4"),
2334
+ lead: role("pluck", "bell:0.4"),
2335
+ },
2336
+ },
2337
+ }),
2338
+ card({
2339
+ id: "glitch",
2340
+ summary:
2341
+ "Glitch electronica: clicks, skips and bit-crushed micro-edits as rhythm, aesthetics of failure, sparse modal pitch",
2342
+ rhythm: {
2343
+ onsets: { rim: grid("3.3..4.3.3..4..3"), perc: grid(".4..3.4...3.4..3") },
2344
+ },
2345
+ melody: { density: [0.5, 1.5] },
2346
+ texture: {
2347
+ roles: {
2348
+ rim: MACHINE_OPT,
2349
+ perc: MACHINE_OPT,
2350
+ lead: role("microloop", "grains:0.5"),
2351
+ },
2352
+ },
2353
+ mix: { fx: { rim: { crush: "destroy" }, perc: { crush: "8-bit" } } },
2354
+ }),
2355
+ card({
2356
+ id: "folktronica",
2357
+ summary:
2358
+ "Folktronica: fingerpicked acoustic guitar and folk modes (mixolydian, dorian) over programmed beats",
2359
+ tempo: { bpm: [85, 120], typical: 100 },
2360
+ pitch: {
2361
+ scales: [
2362
+ ["mixolydian", 0.5],
2363
+ ["dorian", 0.5],
2364
+ ],
2365
+ },
2366
+ harmony: {
2367
+ model: "functional",
2368
+ presets: [
2369
+ ["mixolydian-rock", 0.5],
2370
+ ["dorian-vamp", 0.5],
2371
+ ],
2372
+ },
2373
+ texture: {
2374
+ roles: {
2375
+ chords: role("nylon", "steel:0.5"),
2376
+ lead: role("flute", "kalimba:0.5"),
2377
+ },
2378
+ },
2379
+ }),
2380
+ card({
2381
+ id: "electronica",
2382
+ summary:
2383
+ "Electronica: song-length electronic pop structures, minor and dorian loops, clean programmed beats",
2384
+ meter: { signatures: [["4/4", 1]] },
2385
+ harmony: {
2386
+ model: "functional",
2387
+ presets: [
2388
+ ["aeolian", 0.5],
2389
+ ["sad-pop", 0.5],
2390
+ ],
2391
+ },
2392
+ texture: {
2393
+ roles: {
2394
+ chords: role("keys", "saw:0.5"),
2395
+ lead: role("lead", "pluck:0.5"),
2396
+ },
2397
+ },
2398
+ }),
2399
+ card({
2400
+ id: "industrial",
2401
+ summary:
2402
+ "Industrial: metallic perc and noise loops, distorted machine beats, phrygian and locrian riffs, harsh timbre",
2403
+ tempo: { bpm: [100, 130], typical: 115 },
2404
+ meter: { signatures: [["4/4", 1]] },
2405
+ pitch: {
2406
+ scales: [
2407
+ ["phrygian", 0.5],
2408
+ ["locrian", 0.5],
2409
+ ],
2410
+ },
2411
+ rhythm: {
2412
+ onsets: {
2413
+ kick: FOUR_FLOOR,
2414
+ snare: BACKBEAT,
2415
+ clap: null,
2416
+ perc: grid("x..x..x...x..x.."),
2417
+ },
2418
+ },
2419
+ texture: {
2420
+ roles: {
2421
+ snare: MACHINE,
2422
+ clap: null,
2423
+ perc: MACHINE_OPT,
2424
+ lead: role("saw"),
2425
+ },
2426
+ },
2427
+ mix: {
2428
+ fx: { snare: { distort: "fuzz" }, perc: { distort: "crunch" } },
2429
+ loudness: "loud",
2430
+ },
2431
+ }),
2432
+ card({
2433
+ id: "aggrotech",
2434
+ summary:
2435
+ "Aggrotech and dark electro: distorted four-on-the-floor EBM drive, harmonic-minor supersaw riffs, harsh snare",
2436
+ tempo: { bpm: [125, 140], typical: 132 },
2437
+ meter: { signatures: [["4/4", 1]] },
2438
+ pitch: {
2439
+ scales: [
2440
+ ["harmonic-minor", 0.5],
2441
+ ["minor", 0.5],
2442
+ ],
2443
+ },
2444
+ harmony: {
2445
+ model: "functional",
2446
+ presets: [
2447
+ ["aeolian", 0.5],
2448
+ ["andalusian", 0.5],
2449
+ ],
2450
+ },
2451
+ rhythm: {
2452
+ onsets: { kick: FOUR_FLOOR, snare: BACKBEAT, clap: null, hat: OFFBEAT },
2453
+ },
2454
+ texture: { roles: { snare: MACHINE, clap: null, lead: role("saw") } },
2455
+ mix: { fx: { kick: { distort: "crunch" } }, loudness: "loud" },
2456
+ }),
2457
+ card({
2458
+ id: "dark-wave",
2459
+ summary:
2460
+ "Darkwave: post-punk backbeat on drum machine, chorused minor bass melodies, aeolian loops, cold synth pads",
2461
+ tempo: { bpm: [100, 130], typical: 116 },
2462
+ meter: { signatures: [["4/4", 1]] },
2463
+ pitch: { scales: [["minor", 1]] },
2464
+ harmony: {
2465
+ model: "functional",
2466
+ presets: [
2467
+ ["aeolian", 0.6],
2468
+ ["andalusian", 0.4],
2469
+ ],
2470
+ },
2471
+ rhythm: {
2472
+ onsets: {
2473
+ kick: grid("x.......x.x....."),
2474
+ snare: BACKBEAT,
2475
+ clap: null,
2476
+ hat: EIGHTHS,
2477
+ },
2478
+ },
2479
+ bass: { behaviour: [["ostinato", 1]], onsets: EIGHTHS },
2480
+ texture: {
2481
+ roles: {
2482
+ snare: MACHINE,
2483
+ clap: null,
2484
+ bass: role("ebass"),
2485
+ chords: role("strings", "saw:0.5"),
2486
+ },
2487
+ },
2488
+ mix: { fx: { bass: { chorus: "wide" } } },
2489
+ }),
2490
+ card({
2491
+ id: "minimal-wave",
2492
+ summary:
2493
+ "Minimal synth: one or two monophonic synths over a bare drum machine, square bass ostinato, aeolian two-chord loops",
2494
+ tempo: { bpm: [100, 125], typical: 112 },
2495
+ meter: { signatures: [["4/4", 1]] },
2496
+ pitch: { scales: [["minor", 1]] },
2497
+ harmony: {
2498
+ model: "functional",
2499
+ presets: [
2500
+ ["dorian-vamp", 0.5],
2501
+ ["aeolian", 0.5],
2502
+ ],
2503
+ sevenths: 0,
2504
+ },
2505
+ rhythm: {
2506
+ onsets: {
2507
+ kick: grid("x.......x......."),
2508
+ snare: BACKBEAT,
2509
+ clap: null,
2510
+ hat: OFFBEAT,
2511
+ },
2512
+ },
2513
+ bass: { behaviour: [["ostinato", 1]], onsets: EIGHTHS },
2514
+ texture: {
2515
+ roles: {
2516
+ snare: MACHINE,
2517
+ clap: null,
2518
+ bass: role("square"),
2519
+ chords: null,
2520
+ lead: role("square", "triangle:0.5"),
2521
+ },
2522
+ },
2523
+ }),
2524
+ card({
2525
+ id: "neofolk",
2526
+ summary:
2527
+ "Neofolk: strummed acoustic guitar, martial snare and tom tattoo, aeolian and dorian modes, often in 3/4",
2528
+ tempo: { bpm: [70, 110], typical: 90 },
2529
+ meter: {
2530
+ signatures: [
2531
+ ["4/4", 0.5],
2532
+ ["3/4", 0.5],
2533
+ ],
2534
+ },
2535
+ groove: { humanize: { timingMs: 8, velocity: 0.08 } },
2536
+ pitch: {
2537
+ scales: [
2538
+ ["minor", 0.5],
2539
+ ["dorian", 0.5],
2540
+ ],
2541
+ },
2542
+ harmony: {
2543
+ model: "functional",
2544
+ presets: [
2545
+ ["aeolian", 0.5],
2546
+ ["andalusian", 0.5],
2547
+ ],
2548
+ voicing: {
2549
+ types: [["open", 1]],
2550
+ range: [45, 69],
2551
+ notes: [4, 6],
2552
+ strokes: [["folk", 1]],
2553
+ },
2554
+ },
2555
+ rhythm: {
2556
+ onsets: {
2557
+ kick: grid("x.......x......."),
2558
+ snare: grid("x.xx....x.xx...."),
2559
+ tom: grid("....x.......x..."),
2560
+ clap: null,
2561
+ hat: null,
2562
+ },
2563
+ },
2564
+ texture: {
2565
+ roles: {
2566
+ kick: ACOUSTIC,
2567
+ snare: ACOUSTIC,
2568
+ tom: ACOUSTIC_OPT,
2569
+ clap: null,
2570
+ hat: null,
2571
+ chords: role("steel", "nylon:0.5"),
2572
+ lead: role("violin", "flute:0.4"),
2573
+ },
2574
+ },
2575
+ mix: { loudness: "streaming" },
2576
+ }),
2577
+ ];
2578
+
2579
+ // ---------------------------------------------------------------------------
2580
+ // Synthwave and chip.
2581
+ // Refs: Karen Collins, "Game Sound" (MIT Press, 2008) on chip voices;
2582
+ // synthwave as retro-futurist pastiche per Harper (2011).
2583
+
2584
+ const SYNTHWAVE: readonly StyleCard[] = [
2585
+ card({
2586
+ id: "synthwave-family",
2587
+ abstract: true,
2588
+ summary:
2589
+ "synthwave and chip: gated backbeat snare, 16th-note octave bass, minor pop vi-IV-I-V and aeolian loops",
2590
+ tempo: { bpm: [80, 120], typical: 100 },
2591
+ rhythm: {
2592
+ onsets: {
2593
+ kick: grid("x.......x......."),
2594
+ snare: BACKBEAT,
2595
+ clap: null,
2596
+ hat: EIGHTHS,
2597
+ },
2598
+ },
2599
+ harmony: {
2600
+ presets: [
2601
+ ["sad-pop", 0.5],
2602
+ ["aeolian", 0.5],
2603
+ ],
2604
+ },
2605
+ bass: { behaviour: [["octave", 1]], onsets: SIXTEENTHS },
2606
+ form: {
2607
+ plans: [
2608
+ [
2609
+ [
2610
+ "intro",
2611
+ "verse",
2612
+ "chorus",
2613
+ "verse",
2614
+ "chorus",
2615
+ "bridge",
2616
+ "chorus",
2617
+ "outro",
2618
+ ],
2619
+ 1,
2620
+ ],
2621
+ ],
2622
+ archetype: "verse-chorus",
2623
+ },
2624
+ texture: {
2625
+ roles: {
2626
+ snare: MACHINE,
2627
+ clap: null,
2628
+ chords: role("saw"),
2629
+ lead: role("square", "lead:0.6"),
2630
+ },
2631
+ },
2632
+ mix: { loudness: "streaming" },
2633
+ }),
2634
+ card({
2635
+ id: "synthwave",
2636
+ summary:
2637
+ "Synthwave: retro 80s gated-reverb backbeat, 16th octave bass, aeolian and vi-IV-I-V pads, chorused saw leads",
2638
+ tempo: { bpm: [80, 118], typical: 100 },
2639
+ texture: {
2640
+ roles: {
2641
+ kick: K808,
2642
+ pad: role("strings", "saw:0.5"),
2643
+ lead: role("saw", "lead:0.5"),
2644
+ },
2645
+ },
2646
+ mix: { fx: { chords: { chorus: "wide" } } },
2647
+ }),
2648
+ card({
2649
+ id: "darksynth",
2650
+ summary:
2651
+ "Darksynth: aggressive synthwave with distorted bass, phrygian riffs, four-on-the-floor drive at 100-130 bpm",
2652
+ tempo: { bpm: [100, 130], typical: 118 },
2653
+ pitch: {
2654
+ scales: [
2655
+ ["phrygian", 0.5],
2656
+ ["minor", 0.5],
2657
+ ],
2658
+ },
2659
+ rhythm: { onsets: { kick: FOUR_FLOOR } },
2660
+ texture: { roles: { lead: role("saw") } },
2661
+ mix: { fx: { bass: { distort: "crunch" } }, loudness: "loud" },
2662
+ }),
2663
+ card({
2664
+ id: "chiptune",
2665
+ summary:
2666
+ "Chiptune: two pulse waves, a triangle bass and noise drums; fast arpeggios stand in for chords",
2667
+ tempo: { bpm: [120, 160], typical: 140 },
2668
+ pitch: {
2669
+ scales: [
2670
+ ["major", 0.5],
2671
+ ["minor", 0.5],
2672
+ ],
2673
+ },
2674
+ harmony: {
2675
+ presets: [
2676
+ ["axis", 0.4],
2677
+ ["canon", 0.3],
2678
+ ["aeolian", 0.3],
2679
+ ],
2680
+ },
2681
+ rhythm: {
2682
+ onsets: { arp: SIXTEENTHS, kick: grid("x.......x.x....."), hat: EIGHTHS },
2683
+ },
2684
+ bass: { behaviour: [["octave", 1]], onsets: EIGHTHS },
2685
+ texture: {
2686
+ roles: {
2687
+ chords: null,
2688
+ pad: null,
2689
+ arp: role("square"),
2690
+ bass: role("triangle"),
2691
+ lead: role("square"),
2692
+ },
2693
+ },
2694
+ mix: { fx: { lead: { crush: "8-bit" }, arp: { crush: "8-bit" } } },
2695
+ }),
2696
+ card({
2697
+ id: "future-pop",
2698
+ summary:
2699
+ "Future pop: EBM drive meets trance pads, four-on-the-floor at 120-135 bpm, minor vi-IV-I-V supersaw chords",
2700
+ tempo: { bpm: [120, 135], typical: 128 },
2701
+ rhythm: { onsets: { kick: FOUR_FLOOR, hat: OFFBEAT } },
2702
+ bass: { behaviour: [["ostinato", 1]], onsets: OFFBEAT },
2703
+ texture: {
2704
+ roles: {
2705
+ pad: role("strings", "saw:0.5"),
2706
+ lead: role("lead", "vocoder:0.3"),
2707
+ },
2708
+ },
2709
+ }),
2710
+ ];
2711
+
2712
+ export const ELECTRONIC_CARDS: readonly StyleCard[] = Object.freeze([
2713
+ ...ROOT,
2714
+ ...DISCO,
2715
+ ...HOUSE,
2716
+ ...TECHNO,
2717
+ ...TRANCE,
2718
+ ...HARDCORE,
2719
+ ...DOWNTEMPO,
2720
+ ...AMBIENT,
2721
+ ...IDM,
2722
+ ...SYNTHWAVE,
2723
+ ]);