tuneframes 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +166 -166
- package/README.md +158 -55
- package/examples/example-ai-dj-chill.html +167 -0
- package/examples/example-ai-dj-dark.html +167 -0
- package/examples/example-ai-dj-energetic.html +167 -0
- package/examples/example-ai-dj-happy.html +167 -0
- package/examples/example-ai-dj.html +167 -0
- package/examples/example-ambient.html +63 -63
- package/examples/example-ambient.mp3 +0 -0
- package/examples/example-bass.html +47 -47
- package/examples/example-lofi.html +45 -45
- package/examples/example-lofi.mp3 +0 -0
- package/examples/example-minimal.html +20 -20
- package/examples/example-orchestral.html +67 -67
- package/examples/example-orchestral.mp3 +0 -0
- package/examples/example-piano.html +52 -52
- package/examples/example-piano.mp3 +0 -0
- package/examples/example-techno.html +69 -69
- package/examples/example-techno.mp3 +0 -0
- package/package.json +42 -37
- package/registry/presets/bass-electric.html +67 -0
- package/registry/presets/bass-saw.html +22 -0
- package/registry/presets/chord-progression.html +22 -0
- package/registry/presets/drums-808.html +85 -0
- package/registry/presets/drums-lofi.html +35 -0
- package/registry/presets/lead-piano.html +26 -0
- package/registry/presets/piano-salamander.html +69 -0
- package/registry/presets/reverb-warm.html +11 -0
- package/registry/samples.json +226 -0
- package/skills/audio-ambient/SKILL.md +141 -0
- package/skills/audio-ambient/example.html +113 -0
- package/skills/audio-boss-battle/SKILL.md +157 -0
- package/skills/audio-boss-battle/example.html +185 -0
- package/skills/audio-boss-battle/example.mp3 +0 -0
- package/skills/audio-chillwave/SKILL.md +142 -0
- package/skills/audio-chillwave/example.html +144 -0
- package/skills/audio-cinematic/SKILL.md +147 -0
- package/skills/audio-cinematic/example.html +123 -0
- package/skills/audio-classical/SKILL.md +138 -0
- package/skills/audio-classical/example.html +145 -0
- package/skills/audio-dnb/SKILL.md +142 -0
- package/skills/audio-dnb/example.html +124 -0
- package/skills/audio-downtempo/SKILL.md +152 -0
- package/skills/audio-downtempo/example.html +164 -0
- package/skills/audio-folk/SKILL.md +139 -0
- package/skills/audio-folk/example.html +132 -0
- package/skills/audio-funk/SKILL.md +149 -0
- package/skills/audio-funk/example.html +144 -0
- package/skills/audio-future-bass/SKILL.md +163 -0
- package/skills/audio-future-bass/example.html +164 -0
- package/skills/audio-hip-hop/SKILL.md +133 -0
- package/skills/audio-hip-hop/example.html +129 -0
- package/skills/audio-house/SKILL.md +147 -0
- package/skills/audio-house/example.html +128 -0
- package/skills/audio-indie-pop/SKILL.md +150 -0
- package/skills/audio-indie-pop/example.html +121 -0
- package/skills/audio-jazz/SKILL.md +141 -0
- package/skills/audio-jazz/example.html +146 -0
- package/skills/audio-lofi/SKILL.md +140 -0
- package/skills/audio-lofi/example.html +135 -0
- package/skills/audio-minimal/SKILL.md +155 -0
- package/skills/audio-minimal/example.html +118 -0
- package/skills/audio-orchestral/SKILL.md +156 -0
- package/skills/audio-orchestral/example.html +140 -0
- package/skills/audio-r-and-b/SKILL.md +134 -0
- package/skills/audio-r-and-b/example.html +154 -0
- package/skills/audio-r-and-b/example.mp3 +0 -0
- package/skills/audio-techno/SKILL.md +140 -0
- package/skills/audio-techno/example.html +125 -0
- package/skills/audio-trap/SKILL.md +123 -0
- package/skills/audio-trap/example.html +119 -0
- package/skills/render-retest/example.html +115 -0
- package/skills/tuneframes/SKILL.md +221 -0
- package/skills/tuneframes-cli/SKILL.md +46 -0
- package/skills/verify/example.html +104 -0
- package/src/cli.js +260 -151
- package/src/render.js +134 -7
- package/examples/example-bass.mp3 +0 -0
- package/examples/example-demo-beat.wav +0 -0
- package/examples/example-orchestral.wav +0 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
<div id="tuneframes" style="display:none">{"bpm":80,"duration":"14s"}</div>
|
|
2
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
|
|
3
|
+
<script>
|
|
4
|
+
// TuneFrames — Orchestral
|
|
5
|
+
// D minor: strings carry melody, brass confirm harmony,
|
|
6
|
+
// woodwinds add color, timpani on downbeats
|
|
7
|
+
// Proper voice leading — each section has a role
|
|
8
|
+
|
|
9
|
+
async function main() {
|
|
10
|
+
await Tone.start();
|
|
11
|
+
|
|
12
|
+
const beat = 60 / 80; // 0.75s per quarter note
|
|
13
|
+
const half = beat * 2; // 1.5s per half note
|
|
14
|
+
|
|
15
|
+
// ── Hall reverb — all orchestral sound happens in a hall ──────────────────
|
|
16
|
+
const reverb = new Tone.Reverb({ decay: 3.5, preDelay: 0.05, wet: 0.7 });
|
|
17
|
+
await reverb.generate();
|
|
18
|
+
reverb.toDestination();
|
|
19
|
+
|
|
20
|
+
// ── STRINGS — PolySynth triangle, slow bow-attack ─────────────────────────
|
|
21
|
+
const strings = new Tone.PolySynth(Tone.Synth, {
|
|
22
|
+
oscillator: { type: 'triangle' },
|
|
23
|
+
envelope: { attack: 0.75, decay: 0.2, sustain: 0.95, release: 1.5 },
|
|
24
|
+
volume: -5,
|
|
25
|
+
});
|
|
26
|
+
strings.connect(reverb);
|
|
27
|
+
|
|
28
|
+
// ── BRASS — PolySynth sawtooth, medium breath-attack ─────────────────────
|
|
29
|
+
const brass = new Tone.PolySynth(Tone.Synth, {
|
|
30
|
+
oscillator: { type: 'sawtooth' },
|
|
31
|
+
envelope: { attack: 0.32, decay: 0.35, sustain: 0.72, release: 1.0 },
|
|
32
|
+
volume: -10,
|
|
33
|
+
});
|
|
34
|
+
brass.connect(reverb);
|
|
35
|
+
|
|
36
|
+
// ── WOODWINDS — AMSynth for reedy, breathy character ─────────────────────
|
|
37
|
+
const woodwinds = new Tone.AMSynth({
|
|
38
|
+
harmonicity: 1.0,
|
|
39
|
+
oscillator: { type: 'triangle' },
|
|
40
|
+
envelope: { attack: 0.14, decay: 0.1, sustain: 0.88, release: 0.6 },
|
|
41
|
+
modulation: { type: 'square' },
|
|
42
|
+
modulationEnvelope: { attack: 0.5, decay: 0.0, sustain: 0.3, release: 0.5 },
|
|
43
|
+
volume: -16,
|
|
44
|
+
});
|
|
45
|
+
woodwinds.connect(reverb);
|
|
46
|
+
|
|
47
|
+
// ── TIMPANI — MembraneSynth, long resonating pitch ───────────────────────
|
|
48
|
+
const timpani = new Tone.MembraneSynth({
|
|
49
|
+
pitchDecay: 0.38, octaves: 5,
|
|
50
|
+
envelope: { attack: 0.001, decay: 1.6, sustain: 0, release: 1.0 },
|
|
51
|
+
volume: -7,
|
|
52
|
+
});
|
|
53
|
+
timpani.connect(reverb);
|
|
54
|
+
|
|
55
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
56
|
+
// PHRASE 1 (0-6s): Statement — strings introduce D minor melody
|
|
57
|
+
// D minor scale ascending: D E F G A Bb A G
|
|
58
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
const phrase1 = [
|
|
61
|
+
{ note: 'D4', t: 0 },
|
|
62
|
+
{ note: 'E4', t: beat * 0.5 },
|
|
63
|
+
{ note: 'F4', t: beat * 1 },
|
|
64
|
+
{ note: 'G4', t: beat * 1.5 },
|
|
65
|
+
{ note: 'A4', t: beat * 2 },
|
|
66
|
+
{ note: 'Bb4', t: beat * 2.5 },
|
|
67
|
+
{ note: 'A4', t: beat * 3 },
|
|
68
|
+
{ note: 'G4', t: beat * 3.5 },
|
|
69
|
+
];
|
|
70
|
+
phrase1.forEach(({ note, t }) => strings.triggerAttackRelease(note, '4n', t));
|
|
71
|
+
|
|
72
|
+
// Brass: tonic Dm for 3 beats, then IV (Gm)
|
|
73
|
+
brass.triggerAttackRelease(['D3', 'F3', 'A3'], half, 0);
|
|
74
|
+
brass.triggerAttackRelease(['G2', 'Bb2', 'D3'], half, beat * 3);
|
|
75
|
+
|
|
76
|
+
// Timpani: downbeat on D2
|
|
77
|
+
timpani.triggerAttackRelease('D2', '8n', 0);
|
|
78
|
+
|
|
79
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
80
|
+
// PHRASE 2 (6-10s): Response — melody rises, woodwinds fill
|
|
81
|
+
// Half-cadence: ends on A major (V chord)
|
|
82
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
const p2start = beat * 8; // 6s
|
|
85
|
+
|
|
86
|
+
const phrase2 = [
|
|
87
|
+
{ note: 'F4', t: p2start },
|
|
88
|
+
{ note: 'A4', t: p2start + beat * 0.5 },
|
|
89
|
+
{ note: 'C5', t: p2start + beat * 1 },
|
|
90
|
+
{ note: 'D5', t: p2start + beat * 1.5 },
|
|
91
|
+
{ note: 'E5', t: p2start + beat * 2 },
|
|
92
|
+
{ note: 'F5', t: p2start + beat * 2.5 },
|
|
93
|
+
];
|
|
94
|
+
phrase2.forEach(({ note, t }) => strings.triggerAttackRelease(note, '4n', t));
|
|
95
|
+
|
|
96
|
+
// Woodwinds: fill inner voices between string notes
|
|
97
|
+
const woodwindNotes = [
|
|
98
|
+
{ note: 'A4', t: p2start + beat * 0.25 },
|
|
99
|
+
{ note: 'C5', t: p2start + beat * 1.25 },
|
|
100
|
+
{ note: 'F4', t: p2start + beat * 2.25 },
|
|
101
|
+
];
|
|
102
|
+
woodwindNotes.forEach(({ note, t }) => woodwinds.triggerAttackRelease(note, '4n', t));
|
|
103
|
+
|
|
104
|
+
// Brass: IV (Bb) → V (A major) — half cadence
|
|
105
|
+
brass.triggerAttackRelease(['Bb2', 'D3', 'F3'], half, p2start);
|
|
106
|
+
brass.triggerAttackRelease(['A2', 'C#3', 'E3'], half, p2start + beat * 3);
|
|
107
|
+
|
|
108
|
+
timpani.triggerAttackRelease('G2', '8n', p2start);
|
|
109
|
+
timpani.triggerAttackRelease('A2', '8n', p2start + beat * 3);
|
|
110
|
+
|
|
111
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
112
|
+
// PHRASE 3 (10-14s): Resolution — full cadence I (Dm), all voices converge
|
|
113
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
114
|
+
|
|
115
|
+
const p3start = p2start + beat * 6; // ~10.5s
|
|
116
|
+
|
|
117
|
+
const phrase3 = [
|
|
118
|
+
{ note: 'F5', t: p3start },
|
|
119
|
+
{ note: 'E5', t: p3start + beat * 0.5 },
|
|
120
|
+
{ note: 'D5', t: p3start + beat * 1 },
|
|
121
|
+
{ note: 'C5', t: p3start + beat * 1.5 },
|
|
122
|
+
{ note: 'A4', t: p3start + beat * 2 },
|
|
123
|
+
{ note: 'F4', t: p3start + beat * 2.5 },
|
|
124
|
+
{ note: 'D4', t: p3start + beat * 3 },
|
|
125
|
+
];
|
|
126
|
+
phrase3.forEach(({ note, t }) => {
|
|
127
|
+
if (t < 14) strings.triggerAttackRelease(note, '4n', t);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Brass: full Dm resolution chord
|
|
131
|
+
brass.triggerAttackRelease(['D3', 'F3', 'A3', 'D4'], beat * 4, p3start);
|
|
132
|
+
|
|
133
|
+
// Woodwinds: hold the 5th above brass
|
|
134
|
+
woodwinds.triggerAttackRelease('F5', beat * 3, p3start);
|
|
135
|
+
|
|
136
|
+
// Timpani: final downbeats on D2
|
|
137
|
+
timpani.triggerAttackRelease('D2', '4n', p3start);
|
|
138
|
+
timpani.triggerAttackRelease('D2', '4n', p3start + beat * 3);
|
|
139
|
+
}
|
|
140
|
+
</script>
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: audio-r-and-b
|
|
3
|
+
description: R&B / Neo-Soul — lush extended chords, smooth Rhodes, laid-back syncopated groove
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TuneFrames — R&B / Neo-Soul
|
|
7
|
+
|
|
8
|
+
## Genre Profile
|
|
9
|
+
- BPM range: 70–95 (the groove breathes; never rushed)
|
|
10
|
+
- Key characteristics: Extended chord voicings (min9, maj9, add9, 11ths), Rhodes/electric piano feel, laid-back beat placement (drums sit just behind the grid), syncopated bass that locks with the kick, gentle compression on everything
|
|
11
|
+
- Typical instruments: Rhodes (PolySynth triangle), warm bass (Synth sine), soft kick (MembraneSynth), brush snare (NoiseSynth), 8th hi-hats with occasional 16th fills, optional synth pad underneath
|
|
12
|
+
- Mood: Warm, sensual, introspective, polished — D'Angelo, H.E.R., Frank Ocean, Erykah Badu, Sade
|
|
13
|
+
|
|
14
|
+
## Core Pattern
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
// Neo-Soul core: 85 BPM, Fm9–Dbmaj9–Ab–Eb progression
|
|
18
|
+
async function main() {
|
|
19
|
+
await Tone.start();
|
|
20
|
+
Tone.Transport.bpm.value = 85;
|
|
21
|
+
|
|
22
|
+
const bar = Tone.Time('1n').toSeconds();
|
|
23
|
+
const q = Tone.Time('4n').toSeconds();
|
|
24
|
+
const s8 = Tone.Time('8n').toSeconds();
|
|
25
|
+
|
|
26
|
+
// ── Warm compression + reverb ─────────────────────────────────────────
|
|
27
|
+
const comp = new Tone.Compressor({ threshold: -20, ratio: 3, attack: 0.01, release: 0.2 }).toDestination();
|
|
28
|
+
const reverb = new Tone.Reverb({ decay: 2.5, preDelay: 0.01, wet: 0.35 }).connect(comp);
|
|
29
|
+
|
|
30
|
+
// ── Rhodes — extended voicings ────────────────────────────────────────
|
|
31
|
+
const rhodes = new Tone.PolySynth(Tone.Synth, {
|
|
32
|
+
oscillator: { type: 'triangle' },
|
|
33
|
+
envelope: { attack: 0.03, decay: 0.5, sustain: 0.6, release: 2.0 }
|
|
34
|
+
}).connect(reverb);
|
|
35
|
+
rhodes.volume.value = -10;
|
|
36
|
+
|
|
37
|
+
// Fm9 – Dbmaj9 – Ab – Eb (rich neo-soul changes)
|
|
38
|
+
const chords = [
|
|
39
|
+
['F3','Ab3','C4','Eb4','G4'], // Fm9
|
|
40
|
+
['Db3','F3','Ab3','C4','Eb4'], // Dbmaj9
|
|
41
|
+
['Ab2','C3','Eb3','G3'], // Ab maj7
|
|
42
|
+
['Eb3','G3','Bb3','D4'], // Ebmaj7
|
|
43
|
+
];
|
|
44
|
+
chords.forEach((ch, i) => rhodes.triggerAttackRelease(ch, '1n', i * bar));
|
|
45
|
+
|
|
46
|
+
// ── Syncopated bass ───────────────────────────────────────────────────
|
|
47
|
+
const bass = new Tone.Synth({
|
|
48
|
+
oscillator: { type: 'sine' },
|
|
49
|
+
envelope: { attack: 0.015, decay: 0.15, sustain: 0.5, release: 0.3 }
|
|
50
|
+
}).connect(comp);
|
|
51
|
+
bass.volume.value = -7;
|
|
52
|
+
|
|
53
|
+
// Root on 1, then syncopated hits — lands a 16th early on beat 3
|
|
54
|
+
const bassLine = [
|
|
55
|
+
{ note: 'F2', time: 0 },
|
|
56
|
+
{ note: 'F2', time: q * 1.75 }, // 16th before beat 3
|
|
57
|
+
{ note: 'Eb2', time: q * 3 },
|
|
58
|
+
];
|
|
59
|
+
bassLine.forEach(({ note, time }) => bass.triggerAttackRelease(note, '8n', time));
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Instrument Configuration
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
// Rhodes feel — triangle wave, moderate attack, long release
|
|
67
|
+
const rhodes = new Tone.PolySynth(Tone.Synth, {
|
|
68
|
+
oscillator: { type: 'triangle' },
|
|
69
|
+
envelope: { attack: 0.03, decay: 0.5, sustain: 0.6, release: 2.0 }
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Warm bass — sine, slightly longer attack than funk (more laid-back)
|
|
73
|
+
const bass = new Tone.Synth({
|
|
74
|
+
oscillator: { type: 'sine' },
|
|
75
|
+
envelope: { attack: 0.015, decay: 0.15, sustain: 0.5, release: 0.3 }
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Pad underneath — very slow attack, barely audible, adds warmth
|
|
79
|
+
const pad = new Tone.PolySynth(Tone.Synth, {
|
|
80
|
+
oscillator: { type: 'sine' },
|
|
81
|
+
envelope: { attack: 2.0, decay: 0.5, sustain: 0.8, release: 4.0 }
|
|
82
|
+
});
|
|
83
|
+
pad.volume.value = -22; // subtle, under everything
|
|
84
|
+
|
|
85
|
+
// Chorus on the Rhodes for that electric piano shimmer
|
|
86
|
+
const chorus = new Tone.Chorus({ frequency: 1.5, delayTime: 3.5, depth: 0.4, wet: 0.5 });
|
|
87
|
+
chorus.start();
|
|
88
|
+
|
|
89
|
+
// Gentle compression — glues without pumping
|
|
90
|
+
const comp = new Tone.Compressor({ threshold: -20, ratio: 3, attack: 0.01, release: 0.2 });
|
|
91
|
+
|
|
92
|
+
// Chain: rhodes → chorus → reverb → comp → destination
|
|
93
|
+
const reverb = new Tone.Reverb({ decay: 2.5, wet: 0.35 });
|
|
94
|
+
rhodes.connect(chorus);
|
|
95
|
+
chorus.connect(reverb);
|
|
96
|
+
reverb.connect(comp);
|
|
97
|
+
comp.toDestination();
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Composition Structure
|
|
101
|
+
|
|
102
|
+
1. **Intro (0–3s):** Rhodes alone with pad underneath — establish the lush harmonic world
|
|
103
|
+
2. **Bass enters (3–6s):** Syncopated bass locks with the root, still no drums
|
|
104
|
+
3. **Drums drop (6–9s):** Soft kick on 1 and 3, snare on 2 and 4, 8th hats — laid-back, never aggressive
|
|
105
|
+
4. **Melody/hook (9–12s):** High Rhodes or separate synth carries a smooth melodic phrase
|
|
106
|
+
5. **Outro:** Strip back to Rhodes + pad, let the chord voicings breathe and decay
|
|
107
|
+
|
|
108
|
+
## Example Variations
|
|
109
|
+
|
|
110
|
+
### 1 — D'Angelo "Voodoo" feel (heavy laid-back drums)
|
|
111
|
+
```js
|
|
112
|
+
// Drums sit 20ms behind the grid — schedule everything slightly late
|
|
113
|
+
const LAG = 0.02; // seconds
|
|
114
|
+
kick.triggerAttackRelease('C1', '8n', 0 + LAG);
|
|
115
|
+
snare.triggerAttackRelease('8n', q + LAG);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### 2 — Sade-style sparse ballad (60 BPM, less is more)
|
|
119
|
+
```js
|
|
120
|
+
Tone.Transport.bpm.value = 60;
|
|
121
|
+
// Just Rhodes + bass, no drums, add a gentle delay on the melody
|
|
122
|
+
const delay = new Tone.FeedbackDelay({ delayTime: '4n', feedback: 0.2, wet: 0.25 }).connect(reverb);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 3 — Neo-soul key change (drop a half-step on bar 3)
|
|
126
|
+
```js
|
|
127
|
+
// Modulate from Fm to Em for bar 3 — creates emotional lift/tension
|
|
128
|
+
const chordsWithMod = [
|
|
129
|
+
['F3','Ab3','C4','Eb4','G4'], // Fm9
|
|
130
|
+
['Db3','F3','Ab3','C4','Eb4'], // Dbmaj9
|
|
131
|
+
['E3','G3','B3','D4','F#4'], // Em9 (half-step down = brighter surprise)
|
|
132
|
+
['C3','E3','G3','B3'], // Cmaj7
|
|
133
|
+
];
|
|
134
|
+
```
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>TuneFrames — R&B / Neo-Soul</title>
|
|
5
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
|
|
6
|
+
</head>
|
|
7
|
+
<body>
|
|
8
|
+
<div id="tuneframes" style="display:none">{"bpm":86,"duration":"12s"}</div>
|
|
9
|
+
<script>
|
|
10
|
+
async function main() {
|
|
11
|
+
await Tone.start();
|
|
12
|
+
Tone.Transport.bpm.value = 86;
|
|
13
|
+
|
|
14
|
+
const bar = Tone.Time('1n').toSeconds(); // ~2.79s
|
|
15
|
+
const q = Tone.Time('4n').toSeconds(); // ~0.698s
|
|
16
|
+
const s8 = Tone.Time('8n').toSeconds(); // ~0.349s
|
|
17
|
+
const s16 = Tone.Time('16n').toSeconds();
|
|
18
|
+
|
|
19
|
+
// ── Signal chain: everything → comp → destination ────────────────────
|
|
20
|
+
const comp = new Tone.Compressor({ threshold: -20, ratio: 3, attack: 0.008, release: 0.18 }).toDestination();
|
|
21
|
+
const reverb = new Tone.Freeverb({ roomSize: 0.72, dampening: 3500, wet: 0.38 }).connect(comp);
|
|
22
|
+
|
|
23
|
+
// ── Pad underneath (barely audible warmth) ───────────────────────────
|
|
24
|
+
const pad = new Tone.PolySynth(Tone.Synth, {
|
|
25
|
+
oscillator: { type: 'sine' },
|
|
26
|
+
envelope: { attack: 2.2, decay: 0.5, sustain: 0.85, release: 5.0 }
|
|
27
|
+
}).connect(reverb);
|
|
28
|
+
pad.volume.value = -24;
|
|
29
|
+
|
|
30
|
+
// Hold the tonic throughout
|
|
31
|
+
pad.triggerAttackRelease(['F2','C3','Eb3'], '2n', 0);
|
|
32
|
+
pad.triggerAttackRelease(['Db2','Ab2','C3'], '2n', bar);
|
|
33
|
+
pad.triggerAttackRelease(['Ab2','Eb3','G3'], '2n', bar * 2);
|
|
34
|
+
pad.triggerAttackRelease(['Eb2','Bb2','D3'], '2n', bar * 3);
|
|
35
|
+
|
|
36
|
+
// ── Rhodes — chorus shimmer + reverb ────────────────────────────────
|
|
37
|
+
const chorus = new Tone.Chorus({ frequency: 1.5, delayTime: 3.5, depth: 0.4, wet: 0.45 }).connect(reverb);
|
|
38
|
+
chorus.start();
|
|
39
|
+
const rhodes = new Tone.PolySynth(Tone.Synth, {
|
|
40
|
+
oscillator: { type: 'triangle' },
|
|
41
|
+
envelope: { attack: 0.025, decay: 0.5, sustain: 0.62, release: 2.2 }
|
|
42
|
+
}).connect(chorus);
|
|
43
|
+
rhodes.volume.value = -9;
|
|
44
|
+
|
|
45
|
+
// Fm9 – Dbmaj9 – Abmaj7 – Ebmaj7 (4-bar loop × 2 passes + variation)
|
|
46
|
+
const changes = [
|
|
47
|
+
{ chord: ['F3','Ab3','C4','Eb4','G4'], time: 0 }, // Fm9
|
|
48
|
+
{ chord: ['Db3','F3','Ab3','C4','Eb4'], time: bar }, // Dbmaj9
|
|
49
|
+
{ chord: ['Ab2','Eb3','G3','C4'], time: bar * 2 }, // Abmaj7
|
|
50
|
+
{ chord: ['Eb3','G3','Bb3','D4'], time: bar * 3 }, // Ebmaj7
|
|
51
|
+
// Second pass with richer upper voicing
|
|
52
|
+
{ chord: ['F3','Ab3','C4','G4'], time: bar * 4 }, // Fm add9
|
|
53
|
+
{ chord: ['Db3','Ab3','C4','F4'], time: bar * 4 + bar }, // Dbmaj9/Ab
|
|
54
|
+
];
|
|
55
|
+
changes.forEach(({ chord, time }) => rhodes.triggerAttackRelease(chord, '1n', time));
|
|
56
|
+
|
|
57
|
+
// ── Warm syncopated bass ─────────────────────────────────────────────
|
|
58
|
+
const bass = new Tone.Synth({
|
|
59
|
+
oscillator: { type: 'sine' },
|
|
60
|
+
envelope: { attack: 0.014, decay: 0.18, sustain: 0.48, release: 0.28 }
|
|
61
|
+
}).connect(comp);
|
|
62
|
+
bass.volume.value = -5;
|
|
63
|
+
|
|
64
|
+
// Neo-soul bass: root on 1, anticipation (16th early) into beat 3, fill at end
|
|
65
|
+
const bassPattern = (root, fifth, t) => [
|
|
66
|
+
{ note: root, time: t }, // beat 1
|
|
67
|
+
{ note: root, time: t + q + s16 }, // "and-a" of beat 2 (anticipates 3)
|
|
68
|
+
{ note: fifth, time: t + q * 2 }, // beat 3
|
|
69
|
+
{ note: root, time: t + q * 3 + s16 * 2 }, // syncopated fill before bar end
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
[
|
|
73
|
+
['F2', 'C3', 0 ],
|
|
74
|
+
['Db2', 'Ab2', bar ],
|
|
75
|
+
['Ab1', 'Eb2', bar * 2 ],
|
|
76
|
+
['Eb2', 'Bb2', bar * 3 ],
|
|
77
|
+
['F2', 'C3', bar * 4 ],
|
|
78
|
+
['Db2', 'Ab2', bar * 4 + bar],
|
|
79
|
+
].forEach(([root, fifth, t]) => {
|
|
80
|
+
bassPattern(root, fifth, t).forEach(({ note, time }) =>
|
|
81
|
+
bass.triggerAttackRelease(note, '8n', time));
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// ── Kick (soft, beat 1 and 3) ────────────────────────────────────────
|
|
85
|
+
const kick = new Tone.MembraneSynth({
|
|
86
|
+
pitchDecay: 0.055, octaves: 5,
|
|
87
|
+
envelope: { attack: 0.001, decay: 0.35, sustain: 0, release: 0.3 }
|
|
88
|
+
}).connect(comp);
|
|
89
|
+
kick.volume.value = -14;
|
|
90
|
+
|
|
91
|
+
// Kicks enter on bar 2 (let Rhodes establish first)
|
|
92
|
+
for (let rep = 1; rep < 5; rep++) {
|
|
93
|
+
kick.triggerAttackRelease('C1', '8n', rep * bar); // beat 1
|
|
94
|
+
kick.triggerAttackRelease('C1', '8n', rep * bar + q * 2); // beat 3
|
|
95
|
+
// Syncopated ghost kick on the "and" of 4 every other bar
|
|
96
|
+
if (rep % 2 === 0) kick.triggerAttackRelease('C1', '8n', rep * bar + q * 3.5);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ── Brush snare on 2 and 4 ──────────────────────────────────────────
|
|
100
|
+
const snareBp = new Tone.Filter(2600, 'bandpass').connect(reverb);
|
|
101
|
+
const snare = new Tone.NoiseSynth({
|
|
102
|
+
noise: { type: 'white' },
|
|
103
|
+
envelope: { attack: 0.003, decay: 0.13, sustain: 0, release: 0.06 }
|
|
104
|
+
}).connect(snareBp);
|
|
105
|
+
snare.volume.value = -20;
|
|
106
|
+
|
|
107
|
+
for (let rep = 1; rep < 5; rep++) {
|
|
108
|
+
snare.triggerAttackRelease('8n', rep * bar + q); // beat 2
|
|
109
|
+
snare.triggerAttackRelease('8n', rep * bar + q * 3); // beat 4
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ── Hi-hats: 8ths with 16th fill before beat 3 ──────────────────────
|
|
113
|
+
const hat = new Tone.MetalSynth({
|
|
114
|
+
frequency: 500, harmonicity: 5.1, modulationIndex: 24,
|
|
115
|
+
resonance: 4000, octaves: 1.5,
|
|
116
|
+
envelope: { attack: 0.001, decay: 0.05, release: 0.01 }
|
|
117
|
+
}).connect(reverb);
|
|
118
|
+
hat.volume.value = -24;
|
|
119
|
+
|
|
120
|
+
for (let rep = 1; rep < 5; rep++) {
|
|
121
|
+
// Regular 8th notes
|
|
122
|
+
for (let i = 0; i < 8; i++) hat.triggerAttackRelease('16n', rep * bar + i * s8);
|
|
123
|
+
// 16th-note fill going into beat 3 (soul fill)
|
|
124
|
+
hat.triggerAttackRelease('16n', rep * bar + q * 2 - s16 * 2);
|
|
125
|
+
hat.triggerAttackRelease('16n', rep * bar + q * 2 - s16);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ── Melody (enters bar 3) — smooth Rhodes phrase ─────────────────────
|
|
129
|
+
const melDelay = new Tone.FeedbackDelay({ delayTime: '8n', feedback: 0.18, wet: 0.22 }).connect(reverb);
|
|
130
|
+
const melody = new Tone.Synth({
|
|
131
|
+
oscillator: { type: 'triangle' },
|
|
132
|
+
envelope: { attack: 0.04, decay: 0.35, sustain: 0.5, release: 1.5 }
|
|
133
|
+
}).connect(melDelay);
|
|
134
|
+
melody.volume.value = -13;
|
|
135
|
+
|
|
136
|
+
// Silky descending/ascending phrase — Fm pentatonic flavour
|
|
137
|
+
const phrase = [
|
|
138
|
+
{ note: 'C5', time: bar * 2, dur: '8n' },
|
|
139
|
+
{ note: 'Ab4', time: bar * 2 + s8, dur: '8n' },
|
|
140
|
+
{ note: 'G4', time: bar * 2 + s8*2, dur: '4n' },
|
|
141
|
+
{ note: 'Eb4', time: bar * 2 + s8*4, dur: '8n' },
|
|
142
|
+
{ note: 'F4', time: bar * 2 + s8*5, dur: '4n.' },
|
|
143
|
+
{ note: 'G4', time: bar * 3 + q, dur: '8n' },
|
|
144
|
+
{ note: 'Ab4', time: bar * 3 + q + s8, dur: '8n' },
|
|
145
|
+
{ note: 'C5', time: bar * 3 + q*2, dur: '2n' },
|
|
146
|
+
{ note: 'Bb4', time: bar * 4 + s8, dur: '4n' },
|
|
147
|
+
{ note: 'Ab4', time: bar * 4 + s8*3, dur: '4n' },
|
|
148
|
+
{ note: 'G4', time: bar * 4 + s8*5, dur: '2n' },
|
|
149
|
+
];
|
|
150
|
+
phrase.forEach(({ note, time, dur }) => melody.triggerAttackRelease(note, dur, time));
|
|
151
|
+
}
|
|
152
|
+
</script>
|
|
153
|
+
</body>
|
|
154
|
+
</html>
|
|
Binary file
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: audio-techno
|
|
3
|
+
description: Berlin-style techno — hard MembraneSynth kick, acid MonoSynth bass, industrial hi-hats, sparse stabs. Dark and mechanical.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TuneFrames — Techno
|
|
7
|
+
|
|
8
|
+
## Genre Profile
|
|
9
|
+
- BPM range: 130–145
|
|
10
|
+
- Key characteristics: Hard kick on every beat, relentless forward momentum, hypnotic repetition, industrial textures, minimal melody
|
|
11
|
+
- Typical instruments: MembraneSynth (kick), MonoSynth (acid bass), MetalSynth or NoiseSynth (hi-hats), PolySynth (sparse stabs)
|
|
12
|
+
- Mood: Dark, mechanical, hypnotic, relentless
|
|
13
|
+
|
|
14
|
+
## Core Pattern
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
// Berlin Techno — 4/4 at 136 BPM
|
|
18
|
+
// Kick on every beat (4-on-the-floor)
|
|
19
|
+
// Acid bass follows kick with filter sweep
|
|
20
|
+
// Hi-hats: closed on 8ths, open on off-beats
|
|
21
|
+
// Stabs: sparse, dissonant, 2-bar cycle
|
|
22
|
+
|
|
23
|
+
const kick = new Tone.MembraneSynth({
|
|
24
|
+
pitchDecay: 0.08,
|
|
25
|
+
octaves: 10,
|
|
26
|
+
envelope: { attack: 0.001, decay: 0.4, sustain: 0, release: 0.1 }
|
|
27
|
+
}).toDestination();
|
|
28
|
+
|
|
29
|
+
// Acid bass through distortion
|
|
30
|
+
const dist = new Tone.Distortion(0.6).toDestination();
|
|
31
|
+
const bass = new Tone.MonoSynth({
|
|
32
|
+
oscillator: { type: "sawtooth" },
|
|
33
|
+
filter: { Q: 6, type: "lowpass", rolloff: -24 },
|
|
34
|
+
filterEnvelope: {
|
|
35
|
+
attack: 0.001, decay: 0.2, sustain: 0.3, release: 0.5,
|
|
36
|
+
baseFrequency: 80, octaves: 4
|
|
37
|
+
},
|
|
38
|
+
envelope: { attack: 0.001, decay: 0.3, sustain: 0.4, release: 0.2 }
|
|
39
|
+
}).connect(dist);
|
|
40
|
+
|
|
41
|
+
// Industrial closed hi-hat
|
|
42
|
+
const hat = new Tone.MetalSynth({
|
|
43
|
+
frequency: 400, envelope: { attack: 0.001, decay: 0.04, release: 0.01 },
|
|
44
|
+
harmonicity: 5.1, modulationIndex: 32, resonance: 4000, octaves: 1.5
|
|
45
|
+
}).toDestination();
|
|
46
|
+
|
|
47
|
+
// Scheduling — 1-bar loop
|
|
48
|
+
new Tone.Sequence((time, note) => {
|
|
49
|
+
kick.triggerAttackRelease("C1", "8n", time);
|
|
50
|
+
}, ["C1", null, null, null, "C1", null, null, null,
|
|
51
|
+
"C1", null, null, null, "C1", null, null, null], "16n").start(0);
|
|
52
|
+
|
|
53
|
+
new Tone.Sequence((time, note) => {
|
|
54
|
+
if (note) hat.triggerAttackRelease("16n", time, note);
|
|
55
|
+
}, [0.7, 0.4, 0.7, 0.4, 0.7, 0.4, 0.9, 0.4,
|
|
56
|
+
0.7, 0.4, 0.7, 0.4, 0.7, 0.5, 0.9, 0.6], "16n").start(0);
|
|
57
|
+
|
|
58
|
+
Tone.Transport.bpm.value = 136;
|
|
59
|
+
Tone.Transport.start();
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Instrument Configuration
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
// Kick — punchy, sub-heavy membrane
|
|
66
|
+
const kick = new Tone.MembraneSynth({
|
|
67
|
+
pitchDecay: 0.06,
|
|
68
|
+
octaves: 10,
|
|
69
|
+
volume: 2,
|
|
70
|
+
envelope: { attack: 0.001, decay: 0.35, sustain: 0, release: 0.1 }
|
|
71
|
+
}).toDestination();
|
|
72
|
+
|
|
73
|
+
// Acid bass — sawtooth through distortion + filter
|
|
74
|
+
const limiter = new Tone.Limiter(-3).toDestination();
|
|
75
|
+
const dist = new Tone.Distortion({ distortion: 0.5, wet: 0.8 }).connect(limiter);
|
|
76
|
+
const bass = new Tone.MonoSynth({
|
|
77
|
+
oscillator: { type: "sawtooth" },
|
|
78
|
+
filter: { Q: 8, type: "lowpass", rolloff: -24 },
|
|
79
|
+
filterEnvelope: {
|
|
80
|
+
attack: 0.002, decay: 0.3, sustain: 0.2, release: 0.6,
|
|
81
|
+
baseFrequency: 60, octaves: 3.5
|
|
82
|
+
},
|
|
83
|
+
envelope: { attack: 0.001, decay: 0.2, sustain: 0.5, release: 0.3 },
|
|
84
|
+
volume: -4
|
|
85
|
+
}).connect(dist);
|
|
86
|
+
|
|
87
|
+
// Hi-hat — metallic, tight
|
|
88
|
+
const hat = new Tone.MetalSynth({
|
|
89
|
+
frequency: 380, harmonicity: 5.1, modulationIndex: 32,
|
|
90
|
+
resonance: 4000, octaves: 1.5,
|
|
91
|
+
envelope: { attack: 0.001, decay: 0.05, release: 0.01 },
|
|
92
|
+
volume: -8
|
|
93
|
+
}).toDestination();
|
|
94
|
+
|
|
95
|
+
// Stab — cold PolySynth, short decay
|
|
96
|
+
const reverb = new Tone.Reverb({ decay: 1.5, wet: 0.3 }).toDestination();
|
|
97
|
+
const stab = new Tone.PolySynth(Tone.Synth, {
|
|
98
|
+
oscillator: { type: "square" },
|
|
99
|
+
envelope: { attack: 0.001, decay: 0.15, sustain: 0, release: 0.1 },
|
|
100
|
+
volume: -10
|
|
101
|
+
}).connect(reverb);
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Composition Structure
|
|
105
|
+
|
|
106
|
+
- **Bar 1-2:** Kick only — establish the pulse
|
|
107
|
+
- **Bar 3-4:** Add hi-hats — density builds
|
|
108
|
+
- **Bar 5-8:** Add acid bass — filter opens over 4 bars (automation)
|
|
109
|
+
- **Bar 9-16:** Full loop — kick + hats + bass + sparse stabs every 2 bars
|
|
110
|
+
- **Bar 17+:** Variation — mute elements in/out for tension/release
|
|
111
|
+
- **Breakdown:** Drop kick, keep hats, let bass filter sweep down — then kick re-enters hard
|
|
112
|
+
- **Outro:** Strip back to kick only, fade hats
|
|
113
|
+
|
|
114
|
+
Key technique: filter automation on the bass is the main expressive tool. Sweep `bass.filter.frequency` from 80Hz up to 2kHz over 8 bars, then drop back.
|
|
115
|
+
|
|
116
|
+
## Example Variations
|
|
117
|
+
|
|
118
|
+
### 1 — Stutter Kick (double kick on last 16th)
|
|
119
|
+
```js
|
|
120
|
+
// Standard 4-on-floor with double hit before the beat
|
|
121
|
+
const kickPattern = ["C1", null, null, null, "C1", null, null, null,
|
|
122
|
+
"C1", null, null, null, "C1", null, "C1", null];
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 2 — Acid Filter Sweep (automation)
|
|
126
|
+
```js
|
|
127
|
+
// Automate bass filter opening over 2 bars
|
|
128
|
+
const now = Tone.now();
|
|
129
|
+
bass.filter.frequency.setValueAtTime(80, now);
|
|
130
|
+
bass.filter.frequency.linearRampToValueAtTime(3000, now + 4);
|
|
131
|
+
bass.filter.frequency.linearRampToValueAtTime(120, now + 4.5);
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 3 — Ride + Open Hat Pattern
|
|
135
|
+
```js
|
|
136
|
+
// More complex hat pattern: closed-closed-open on the off
|
|
137
|
+
const hatVelocities = [0.8, 0.3, 0.8, 0.3, 0.8, 0.3, 1.0, 0.2,
|
|
138
|
+
0.8, 0.3, 0.8, 0.3, 0.8, 0.4, 1.0, 0.5];
|
|
139
|
+
// Longer decay on accented hits for "open hat" feel
|
|
140
|
+
```
|