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,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: audio-funk
|
|
3
|
+
description: Funk — slap bass, brass stabs, 16th-note hi-hats, syncopated groove that snaps
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TuneFrames — Funk
|
|
7
|
+
|
|
8
|
+
## Genre Profile
|
|
9
|
+
- BPM range: 95–115 (the pocket lives around 100–108)
|
|
10
|
+
- Key characteristics: Syncopated 16th-note grid, everything lands on or anticipates the beat, brass stabs on upbeats, rhythm guitar "chank" on the "and" of 2 and 4, bass owns the low end
|
|
11
|
+
- Typical instruments: Slap bass (Synth with fast attack + filter), brass stabs (PolySynth sawtooth, sharp envelope), rhythm guitar (PolySynth pluck), kick (MembraneSynth), tight snare (NoiseSynth), 16th hi-hats (MetalSynth)
|
|
12
|
+
- Mood: Infectious, locked-in, body-moving, celebratory — James Brown, Parliament, Daft Punk, Vulfpeck
|
|
13
|
+
|
|
14
|
+
## Core Pattern
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
// Funk core: 104 BPM, E minor groove
|
|
18
|
+
async function main() {
|
|
19
|
+
await Tone.start();
|
|
20
|
+
Tone.Transport.bpm.value = 104;
|
|
21
|
+
|
|
22
|
+
const s16 = Tone.Time('16n').toSeconds(); // one 16th note
|
|
23
|
+
const q = Tone.Time('4n').toSeconds(); // one beat
|
|
24
|
+
|
|
25
|
+
const dest = new Tone.Gain(1).toDestination();
|
|
26
|
+
const comp = new Tone.Compressor(-18, 4).connect(dest);
|
|
27
|
+
|
|
28
|
+
// ── Slap bass ────────────────────────────────────────────────────────
|
|
29
|
+
const bass = new Tone.Synth({
|
|
30
|
+
oscillator: { type: 'square' },
|
|
31
|
+
envelope: { attack: 0.001, decay: 0.08, sustain: 0.0, release: 0.05 }
|
|
32
|
+
}).connect(comp);
|
|
33
|
+
bass.volume.value = -6;
|
|
34
|
+
|
|
35
|
+
// Classic one-bar funk bass: E on 1, ghost note 16th early, pop on e2-and
|
|
36
|
+
const bassLine = [
|
|
37
|
+
{ note: 'E2', time: 0 },
|
|
38
|
+
{ note: 'E2', time: s16 * 2 },
|
|
39
|
+
{ note: 'G2', time: s16 * 3 },
|
|
40
|
+
{ note: 'E2', time: s16 * 4 },
|
|
41
|
+
{ note: 'A2', time: s16 * 6 },
|
|
42
|
+
{ note: 'E2', time: s16 * 8 },
|
|
43
|
+
{ note: 'D2', time: s16 * 10 },
|
|
44
|
+
{ note: 'E2', time: s16 * 12 },
|
|
45
|
+
{ note: 'G2', time: s16 * 14 },
|
|
46
|
+
];
|
|
47
|
+
bassLine.forEach(({ note, time }) => bass.triggerAttackRelease(note, '16n', time));
|
|
48
|
+
|
|
49
|
+
// ── Brass stabs (upbeat hits — "and" of 2 and 4) ──────────────────────
|
|
50
|
+
const brass = new Tone.PolySynth(Tone.Synth, {
|
|
51
|
+
oscillator: { type: 'sawtooth' },
|
|
52
|
+
envelope: { attack: 0.005, decay: 0.12, sustain: 0.0, release: 0.08 }
|
|
53
|
+
}).connect(comp);
|
|
54
|
+
brass.volume.value = -12;
|
|
55
|
+
|
|
56
|
+
// Stab on the "and" of beat 2 (s16*9) and "and" of beat 4 (s16*13)
|
|
57
|
+
brass.triggerAttackRelease(['E4','G4','B4'], '16n', s16 * 9);
|
|
58
|
+
brass.triggerAttackRelease(['E4','G4','B4'], '16n', s16 * 13);
|
|
59
|
+
|
|
60
|
+
// ── Tight kick ────────────────────────────────────────────────────────
|
|
61
|
+
const kick = new Tone.MembraneSynth({
|
|
62
|
+
pitchDecay: 0.04, octaves: 6,
|
|
63
|
+
envelope: { attack: 0.001, decay: 0.25, sustain: 0, release: 0.1 }
|
|
64
|
+
}).connect(dest);
|
|
65
|
+
kick.volume.value = -10;
|
|
66
|
+
kick.triggerAttackRelease('C1', '8n', 0); // beat 1
|
|
67
|
+
kick.triggerAttackRelease('C1', '8n', s16 * 10); // beat 2-and-a (syncopated)
|
|
68
|
+
|
|
69
|
+
// ── Snare on 3 ────────────────────────────────────────────────────────
|
|
70
|
+
const snare = new Tone.NoiseSynth({
|
|
71
|
+
noise: { type: 'white' },
|
|
72
|
+
envelope: { attack: 0.001, decay: 0.1, sustain: 0, release: 0.05 }
|
|
73
|
+
}).connect(new Tone.Filter(3500, 'bandpass').connect(dest));
|
|
74
|
+
snare.volume.value = -14;
|
|
75
|
+
snare.triggerAttackRelease('8n', s16 * 8); // beat 3
|
|
76
|
+
|
|
77
|
+
// ── 16th hi-hats ─────────────────────────────────────────────────────
|
|
78
|
+
const hat = new Tone.MetalSynth({
|
|
79
|
+
frequency: 600, harmonicity: 5.1, modulationIndex: 32,
|
|
80
|
+
resonance: 4000, octaves: 1.5,
|
|
81
|
+
envelope: { attack: 0.001, decay: 0.03, release: 0.01 }
|
|
82
|
+
}).connect(dest);
|
|
83
|
+
hat.volume.value = -20;
|
|
84
|
+
for (let i = 0; i < 16; i++) hat.triggerAttackRelease('32n', i * s16);
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Instrument Configuration
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
// Slap bass — square wave, nearly no sustain (percussive pluck feel)
|
|
92
|
+
const bass = new Tone.Synth({
|
|
93
|
+
oscillator: { type: 'square' },
|
|
94
|
+
envelope: { attack: 0.001, decay: 0.08, sustain: 0.0, release: 0.05 }
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Rhythm guitar "chank" — bright pluck, bandpass filtered, chord stab
|
|
98
|
+
const guitar = new Tone.PolySynth(Tone.Synth, {
|
|
99
|
+
oscillator: { type: 'triangle' },
|
|
100
|
+
envelope: { attack: 0.003, decay: 0.15, sustain: 0.0, release: 0.08 }
|
|
101
|
+
});
|
|
102
|
+
const guitarFilter = new Tone.Filter(2500, 'bandpass');
|
|
103
|
+
guitar.connect(guitarFilter);
|
|
104
|
+
|
|
105
|
+
// Brass stabs — sawtooth, instant attack, very short decay (punchy, no tail)
|
|
106
|
+
const brass = new Tone.PolySynth(Tone.Synth, {
|
|
107
|
+
oscillator: { type: 'sawtooth' },
|
|
108
|
+
envelope: { attack: 0.005, decay: 0.12, sustain: 0.0, release: 0.08 }
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Bus compressor glues everything together (the "pumping" funk feel)
|
|
112
|
+
const comp = new Tone.Compressor({ threshold: -18, ratio: 4, attack: 0.003, release: 0.1 });
|
|
113
|
+
comp.toDestination();
|
|
114
|
+
// Route everything through comp
|
|
115
|
+
[bass, brass, guitar].forEach(s => s.connect(comp));
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Composition Structure
|
|
119
|
+
|
|
120
|
+
1. **Intro (0–2s):** Bass alone on the groove, just root and fifth — establish the pocket
|
|
121
|
+
2. **Drums in (2–4s):** Kick + snare + 16th hats lock with the bass
|
|
122
|
+
3. **Full band (4–8s):** Brass stabs enter on upbeats, guitar chank fills the midrange
|
|
123
|
+
4. **Breakdown (8–10s):** Strip to bass + kick only — tension before the re-hit
|
|
124
|
+
5. **Re-hit (10–12s):** Everything back, maybe add a filter sweep on the bass up
|
|
125
|
+
|
|
126
|
+
## Example Variations
|
|
127
|
+
|
|
128
|
+
### 1 — Open hi-hat accent on beat 4
|
|
129
|
+
```js
|
|
130
|
+
const openHat = new Tone.MetalSynth({ envelope: { decay: 0.25 } }).connect(dest);
|
|
131
|
+
openHat.volume.value = -18;
|
|
132
|
+
openHat.triggerAttackRelease('8n', q * 3); // beat 4 open
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### 2 — Wah filter on brass (auto-wah effect)
|
|
136
|
+
```js
|
|
137
|
+
const autoWah = new Tone.AutoFilter({ frequency: 4, baseFrequency: 200, octaves: 4, wet: 0.8 });
|
|
138
|
+
autoWah.connect(dest);
|
|
139
|
+
autoWah.start(0);
|
|
140
|
+
brass.connect(autoWah);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 3 — Daft Punk-style filtered house funk
|
|
144
|
+
```js
|
|
145
|
+
// Add LP filter sweep on the bass from 300 Hz to 2000 Hz over 4 bars
|
|
146
|
+
const bassFilter = new Tone.Filter(300, 'lowpass').connect(comp);
|
|
147
|
+
bass.connect(bassFilter);
|
|
148
|
+
bassFilter.frequency.rampTo(2000, bar * 4);
|
|
149
|
+
```
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>TuneFrames — Funk</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":104,"duration":"12s"}</div>
|
|
9
|
+
<script>
|
|
10
|
+
async function main() {
|
|
11
|
+
await Tone.start();
|
|
12
|
+
Tone.Transport.bpm.value = 104;
|
|
13
|
+
|
|
14
|
+
const s16 = Tone.Time('16n').toSeconds(); // ~0.144s
|
|
15
|
+
const q = Tone.Time('4n').toSeconds(); // ~0.577s
|
|
16
|
+
const bar = Tone.Time('1n').toSeconds(); // ~2.308s
|
|
17
|
+
|
|
18
|
+
// ── Bus compressor (the funk glue) ──────────────────────────────────
|
|
19
|
+
const comp = new Tone.Compressor({ threshold: -16, ratio: 4, attack: 0.003, release: 0.12 }).toDestination();
|
|
20
|
+
const room = new Tone.Reverb({ decay: 0.6, wet: 0.12 }).connect(comp);
|
|
21
|
+
|
|
22
|
+
// ── Slap bass ───────────────────────────────────────────────────────
|
|
23
|
+
const bassFilter = new Tone.Filter(800, 'lowpass').connect(comp);
|
|
24
|
+
const bass = new Tone.Synth({
|
|
25
|
+
oscillator: { type: 'square' },
|
|
26
|
+
envelope: { attack: 0.001, decay: 0.09, sustain: 0.0, release: 0.04 }
|
|
27
|
+
}).connect(bassFilter);
|
|
28
|
+
bass.volume.value = -4;
|
|
29
|
+
|
|
30
|
+
// One-bar groove in E — repeated 5 times
|
|
31
|
+
const bassPattern = [
|
|
32
|
+
{ note: 'E2', t: 0 },
|
|
33
|
+
{ note: 'E2', t: s16*2 },
|
|
34
|
+
{ note: 'G2', t: s16*3 },
|
|
35
|
+
{ note: 'E2', t: s16*4 },
|
|
36
|
+
{ note: 'A2', t: s16*6 },
|
|
37
|
+
{ note: 'E2', t: s16*8 },
|
|
38
|
+
{ note: 'D2', t: s16*10 },
|
|
39
|
+
{ note: 'E2', t: s16*12 },
|
|
40
|
+
{ note: 'Gb2', t: s16*14 },
|
|
41
|
+
];
|
|
42
|
+
for (let rep = 0; rep < 5; rep++) {
|
|
43
|
+
bassPattern.forEach(({ note, t }) => bass.triggerAttackRelease(note, '16n', rep * bar + t));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ── Rhythm guitar chank (bandpass, hits on upbeats) ─────────────────
|
|
47
|
+
const guitFilter = new Tone.Filter(2200, 'bandpass').connect(room);
|
|
48
|
+
const guitar = new Tone.PolySynth(Tone.Synth, {
|
|
49
|
+
oscillator: { type: 'triangle' },
|
|
50
|
+
envelope: { attack: 0.003, decay: 0.1, sustain: 0.0, release: 0.06 }
|
|
51
|
+
}).connect(guitFilter);
|
|
52
|
+
guitar.volume.value = -14;
|
|
53
|
+
|
|
54
|
+
// Chank on "and" of 2 (s16*9) and "and" of 4 (s16*13) — classic funk chop
|
|
55
|
+
const chankTimes = [s16 * 9, s16 * 13];
|
|
56
|
+
for (let rep = 0; rep < 5; rep++) {
|
|
57
|
+
chankTimes.forEach(t => {
|
|
58
|
+
guitar.triggerAttackRelease(['E4','G4','B4'], '16n', rep * bar + t);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ── Brass stabs (upbeat, punchy) ────────────────────────────────────
|
|
63
|
+
const brassFilter = new Tone.Filter(4000, 'lowpass').connect(room);
|
|
64
|
+
const brass = new Tone.PolySynth(Tone.Synth, {
|
|
65
|
+
oscillator: { type: 'sawtooth' },
|
|
66
|
+
envelope: { attack: 0.006, decay: 0.13, sustain: 0.0, release: 0.07 }
|
|
67
|
+
}).connect(brassFilter);
|
|
68
|
+
brass.volume.value = -10;
|
|
69
|
+
|
|
70
|
+
// Stabs enter on bar 2 — same upbeat timing as guitar chank
|
|
71
|
+
for (let rep = 1; rep < 5; rep++) {
|
|
72
|
+
[s16 * 9, s16 * 13].forEach(t => {
|
|
73
|
+
brass.triggerAttackRelease(['E4','G4','B4'], '16n', rep * bar + t);
|
|
74
|
+
// Second stab chord a beat later for call-and-response
|
|
75
|
+
brass.triggerAttackRelease(['D4','F4','A4'], '16n', rep * bar + t + s16 * 2);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ── Kick drum ───────────────────────────────────────────────────────
|
|
80
|
+
const kick = new Tone.MembraneSynth({
|
|
81
|
+
pitchDecay: 0.04, octaves: 6,
|
|
82
|
+
envelope: { attack: 0.001, decay: 0.22, sustain: 0, release: 0.08 }
|
|
83
|
+
}).connect(comp);
|
|
84
|
+
kick.volume.value = -8;
|
|
85
|
+
|
|
86
|
+
// Kick pattern: beat 1, then syncopated 16th before beat 3 and on the "a" of 3
|
|
87
|
+
for (let rep = 0; rep < 5; rep++) {
|
|
88
|
+
kick.triggerAttackRelease('C1', '8n', rep * bar); // beat 1
|
|
89
|
+
kick.triggerAttackRelease('C1', '8n', rep * bar + s16 * 7); // "and-a" of 2
|
|
90
|
+
kick.triggerAttackRelease('C1', '8n', rep * bar + s16 * 10); // "and" of 3
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ── Snare on 2 and 4 (with ghost notes) ────────────────────────────
|
|
94
|
+
const snareBp = new Tone.Filter(3200, 'bandpass').connect(room);
|
|
95
|
+
const snare = new Tone.NoiseSynth({
|
|
96
|
+
noise: { type: 'white' },
|
|
97
|
+
envelope: { attack: 0.001, decay: 0.11, sustain: 0, release: 0.04 }
|
|
98
|
+
}).connect(snareBp);
|
|
99
|
+
snare.volume.value = -12;
|
|
100
|
+
|
|
101
|
+
const ghostSnare = new Tone.NoiseSynth({
|
|
102
|
+
noise: { type: 'white' },
|
|
103
|
+
envelope: { attack: 0.001, decay: 0.05, sustain: 0, release: 0.02 }
|
|
104
|
+
}).connect(snareBp);
|
|
105
|
+
ghostSnare.volume.value = -26; // quiet ghosts
|
|
106
|
+
|
|
107
|
+
for (let rep = 0; rep < 5; rep++) {
|
|
108
|
+
snare.triggerAttackRelease('8n', rep * bar + q); // beat 2
|
|
109
|
+
snare.triggerAttackRelease('8n', rep * bar + q * 3); // beat 4
|
|
110
|
+
// Ghost notes around beat 3
|
|
111
|
+
ghostSnare.triggerAttackRelease('16n', rep * bar + s16 * 9);
|
|
112
|
+
ghostSnare.triggerAttackRelease('16n', rep * bar + s16 * 11);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ── 16th hi-hats ───────────────────────────────────────────────────
|
|
116
|
+
const hat = new Tone.MetalSynth({
|
|
117
|
+
frequency: 600, harmonicity: 5.1, modulationIndex: 32,
|
|
118
|
+
resonance: 4000, octaves: 1.5,
|
|
119
|
+
envelope: { attack: 0.001, decay: 0.028, release: 0.008 }
|
|
120
|
+
}).connect(comp);
|
|
121
|
+
hat.volume.value = -19;
|
|
122
|
+
|
|
123
|
+
// Full 16th grid, slight accent on quarter notes
|
|
124
|
+
for (let rep = 0; rep < 5; rep++) {
|
|
125
|
+
for (let i = 0; i < 16; i++) {
|
|
126
|
+
hat.triggerAttackRelease('32n', rep * bar + i * s16);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ── Open hi-hat accent on beat 4 of every other bar ────────────────
|
|
131
|
+
const openHat = new Tone.MetalSynth({
|
|
132
|
+
frequency: 400, harmonicity: 3.1, modulationIndex: 12,
|
|
133
|
+
resonance: 2000, octaves: 1.0,
|
|
134
|
+
envelope: { attack: 0.001, decay: 0.22, release: 0.08 }
|
|
135
|
+
}).connect(room);
|
|
136
|
+
openHat.volume.value = -18;
|
|
137
|
+
|
|
138
|
+
for (let rep = 1; rep < 5; rep += 2) {
|
|
139
|
+
openHat.triggerAttackRelease('8n', rep * bar + q * 3);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
</script>
|
|
143
|
+
</body>
|
|
144
|
+
</html>
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: audio-future-bass
|
|
3
|
+
description: Future Bass — wide supersaw chords (fatsawtooth PolySynth), emotional melodic lead, pitched 808 sub, trap-influenced drums, massive chorus/reverb. Flume / San Holo energy. Big, emotional, wide stereo field.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TuneFrames — Future Bass
|
|
7
|
+
|
|
8
|
+
## Genre Profile
|
|
9
|
+
- BPM range: 140–160 (half-time groove feel, effective "heard" tempo closer to 70–80)
|
|
10
|
+
- Key characteristics: Supersaw chords (fatsawtooth oscillator, 5–7 unison voices, 30–50 spread), emotional minor-key melody, 808 sub bass (MembraneSynth with long pitchDecay), trap-influenced sparse kick, massive clap with reverb, hi-hat rolls with velocity variation, heavy chorus and reverb on chords
|
|
11
|
+
- Typical instruments: PolySynth/Synth fatsawtooth (supersaw chords), Synth/AMSynth (lead), MembraneSynth (808 + kick), NoiseSynth (clap), MetalSynth (hi-hats)
|
|
12
|
+
- Mood: Euphoric, emotional, uplifting, wide, festival-ready
|
|
13
|
+
|
|
14
|
+
## Core Pattern
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
// Future Bass — 150 BPM, A minor
|
|
18
|
+
// Structure: intro (bars 1–2) → drop (bar 3 onward)
|
|
19
|
+
// Chords: Am – F – C – G (1 bar each, looping)
|
|
20
|
+
// 808 sub follows chord roots with pitch glide
|
|
21
|
+
// Half-time drums: sparse kick, big clap on beat 3
|
|
22
|
+
|
|
23
|
+
Tone.Transport.bpm.value = 150;
|
|
24
|
+
|
|
25
|
+
// Supersaw chord synth — fatsawtooth is the key ingredient
|
|
26
|
+
const chordVerb = new Tone.Reverb({ decay: 4, wet: 0.55 }).toDestination();
|
|
27
|
+
const chordChorus = new Tone.Chorus({ frequency: 0.8, delayTime: 3.5, depth: 0.8, wet: 0.7 }).connect(chordVerb);
|
|
28
|
+
chordChorus.start();
|
|
29
|
+
const chords = new Tone.PolySynth(Tone.Synth, {
|
|
30
|
+
oscillator: { type: "fatsawtooth", count: 5, spread: 40 },
|
|
31
|
+
envelope: { attack: 0.08, decay: 0.2, sustain: 0.75, release: 2.0 },
|
|
32
|
+
volume: -10
|
|
33
|
+
}).connect(chordChorus);
|
|
34
|
+
|
|
35
|
+
// 808 sub — MembraneSynth with long pitch glide
|
|
36
|
+
const sub808Dist = new Tone.Distortion({ distortion: 0.1, wet: 0.25 }).toDestination();
|
|
37
|
+
const sub808 = new Tone.MembraneSynth({
|
|
38
|
+
pitchDecay: 0.45, // the "808 fall" — pitch glides from note down
|
|
39
|
+
octaves: 3,
|
|
40
|
+
envelope: { attack: 0.001, decay: 0.85, sustain: 0, release: 0.4 }
|
|
41
|
+
}).connect(sub808Dist);
|
|
42
|
+
|
|
43
|
+
// Schedule supersaw chords at the drop (bar 3+)
|
|
44
|
+
// One-bar chord stabs with full sustain
|
|
45
|
+
const dropChords = [
|
|
46
|
+
{ time: "2m", notes: ["A2","E3","A3","C4","E4"] }, // Am
|
|
47
|
+
{ time: "3m", notes: ["F2","C3","F3","A3","C4"] }, // F
|
|
48
|
+
{ time: "4m", notes: ["C3","G3","C4","E4","G4"] }, // C
|
|
49
|
+
{ time: "5m", notes: ["G2","D3","G3","B3","D4"] }, // G
|
|
50
|
+
];
|
|
51
|
+
dropChords.forEach(({ time, notes }) => {
|
|
52
|
+
chords.triggerAttackRelease(notes, "1m", time);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// 808 bass hits on chord roots — enters with drop
|
|
56
|
+
const subHits = [
|
|
57
|
+
{ time: "2m", note: "A2" }, { time: "3m", note: "F2" },
|
|
58
|
+
{ time: "4m", note: "C2" }, { time: "5m", note: "G2" },
|
|
59
|
+
];
|
|
60
|
+
subHits.forEach(({ time, note }) => {
|
|
61
|
+
sub808.triggerAttackRelease(note, "2n", time);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
Tone.Transport.start();
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Instrument Configuration
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
// Supersaw chords — fatsawtooth + wide chorus is the defining sound
|
|
71
|
+
const chordVerb = new Tone.Reverb({ decay: 4.0, wet: 0.55 }).toDestination();
|
|
72
|
+
const chordChorus = new Tone.Chorus({ frequency: 0.8, delayTime: 3.5, depth: 0.8, wet: 0.7 });
|
|
73
|
+
chordChorus.connect(chordVerb);
|
|
74
|
+
chordChorus.start();
|
|
75
|
+
const chords = new Tone.PolySynth(Tone.Synth, {
|
|
76
|
+
oscillator: { type: "fatsawtooth", count: 5, spread: 40 },
|
|
77
|
+
envelope: { attack: 0.08, decay: 0.2, sustain: 0.75, release: 2.0 },
|
|
78
|
+
volume: -10
|
|
79
|
+
}).connect(chordChorus);
|
|
80
|
+
|
|
81
|
+
// Lead melody — bright, emotional
|
|
82
|
+
const leadVerb = new Tone.Reverb({ decay: 2.5, wet: 0.4 }).toDestination();
|
|
83
|
+
const leadDelay = new Tone.FeedbackDelay({ delayTime: "8n", feedback: 0.3, wet: 0.25 }).connect(leadVerb);
|
|
84
|
+
const lead = new Tone.Synth({
|
|
85
|
+
oscillator: { type: "triangle" },
|
|
86
|
+
envelope: { attack: 0.02, decay: 0.3, sustain: 0.6, release: 1.0 },
|
|
87
|
+
volume: -6
|
|
88
|
+
}).connect(leadDelay);
|
|
89
|
+
|
|
90
|
+
// 808 sub — long pitch decay = the glide
|
|
91
|
+
const sub808Dist = new Tone.Distortion({ distortion: 0.1, wet: 0.25 }).toDestination();
|
|
92
|
+
const sub808 = new Tone.MembraneSynth({
|
|
93
|
+
pitchDecay: 0.45,
|
|
94
|
+
octaves: 3,
|
|
95
|
+
envelope: { attack: 0.001, decay: 0.85, sustain: 0, release: 0.4 },
|
|
96
|
+
volume: 2
|
|
97
|
+
}).connect(sub808Dist);
|
|
98
|
+
|
|
99
|
+
// Trap kick — on beat 1 (half-time feel)
|
|
100
|
+
const kick = new Tone.MembraneSynth({
|
|
101
|
+
pitchDecay: 0.08, octaves: 8,
|
|
102
|
+
envelope: { attack: 0.001, decay: 0.35, sustain: 0, release: 0.1 },
|
|
103
|
+
volume: 0
|
|
104
|
+
}).toDestination();
|
|
105
|
+
|
|
106
|
+
// Clap — big noise transient with long reverb
|
|
107
|
+
const clapVerb = new Tone.Reverb({ decay: 2.8, wet: 0.55 }).toDestination();
|
|
108
|
+
const clap = new Tone.NoiseSynth({
|
|
109
|
+
noise: { type: "white" },
|
|
110
|
+
envelope: { attack: 0.001, decay: 0.18, sustain: 0, release: 0.08 },
|
|
111
|
+
volume: -4
|
|
112
|
+
}).connect(clapVerb);
|
|
113
|
+
|
|
114
|
+
// Hi-hat — trap roll with velocity variation
|
|
115
|
+
const hat = new Tone.MetalSynth({
|
|
116
|
+
frequency: 600, harmonicity: 5.1, modulationIndex: 32,
|
|
117
|
+
resonance: 4500, octaves: 1.5,
|
|
118
|
+
envelope: { attack: 0.001, decay: 0.03, release: 0.01 },
|
|
119
|
+
volume: -12
|
|
120
|
+
}).toDestination();
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Composition Structure
|
|
124
|
+
|
|
125
|
+
- **Intro (bars 1–2):** Lead melody only with light hi-hats — emotional hook, no chords yet
|
|
126
|
+
- **Build (end of bar 2):** Riser noise (NoiseSynth with rising filter) creates anticipation
|
|
127
|
+
- **Drop (bar 3):** Supersaw chords crash in simultaneously with kick, clap, and 808 sub
|
|
128
|
+
- **Drop loop:** Am–F–C–G chord cycle (1 bar each), 808 follows roots, hats roll
|
|
129
|
+
- **Variation:** Add pitch bend on lead (modulate `lead.detune.value` ±50 cents over 1 bar)
|
|
130
|
+
- **Breakdown:** Mute drums, let chords ring with reverb tail — rebuild tension
|
|
131
|
+
- **Second drop:** Higher register chord voicing, lead octave up
|
|
132
|
+
|
|
133
|
+
Emotional tension technique: during the bar before the drop, cut all sound (or just leave a white noise riser), then everything hits at once — maximum impact.
|
|
134
|
+
|
|
135
|
+
## Example Variations
|
|
136
|
+
|
|
137
|
+
### 1 — Wider supersaw (more voices)
|
|
138
|
+
```js
|
|
139
|
+
// More unison voices = fatter, more expensive but more enormous
|
|
140
|
+
const chords = new Tone.PolySynth(Tone.Synth, {
|
|
141
|
+
oscillator: { type: "fatsawtooth", count: 7, spread: 55 },
|
|
142
|
+
// ...
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 2 — Filter sweep on drop entry
|
|
147
|
+
```js
|
|
148
|
+
// Chords start filtered and open up over the first bar of the drop
|
|
149
|
+
const chordFilter = new Tone.Filter({ frequency: 300, type: "lowpass", Q: 2 });
|
|
150
|
+
// chain: chords → chordFilter → chordChorus → chordVerb
|
|
151
|
+
const sweepStart = Tone.Time("2m").toSeconds();
|
|
152
|
+
chordFilter.frequency.setValueAtTime(300, sweepStart);
|
|
153
|
+
chordFilter.frequency.exponentialRampToValueAtTime(12000, sweepStart + Tone.Time("1m").toSeconds());
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 3 — Pitched chord stab (one-hit per bar)
|
|
157
|
+
```js
|
|
158
|
+
// Instead of sustained chords, use a sharp pluck attack then let the reverb carry
|
|
159
|
+
const chords = new Tone.PolySynth(Tone.Synth, {
|
|
160
|
+
oscillator: { type: "fatsawtooth", count: 5, spread: 40 },
|
|
161
|
+
envelope: { attack: 0.005, decay: 0.15, sustain: 0, release: 2.5 }, // no sustain = pluck
|
|
162
|
+
});
|
|
163
|
+
```
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>TuneFrames — Future Bass</title>
|
|
6
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<!-- TuneFrames Metadata -->
|
|
10
|
+
<div id="tuneframes" style="display:none">{"bpm":150,"duration":"12s"}</div>
|
|
11
|
+
|
|
12
|
+
<script>
|
|
13
|
+
async function main() {
|
|
14
|
+
await Tone.start();
|
|
15
|
+
Tone.Transport.bpm.value = 150;
|
|
16
|
+
|
|
17
|
+
// --- MASTER BUS ---
|
|
18
|
+
const masterLimiter = new Tone.Limiter(-1).toDestination();
|
|
19
|
+
const masterGain = new Tone.Gain(0.82).connect(masterLimiter);
|
|
20
|
+
|
|
21
|
+
// --- SUPERSAW CHORDS — fatsawtooth + wide chorus + huge reverb ---
|
|
22
|
+
const chordVerb = new Tone.Reverb({ decay: 4.0, wet: 0.55 }).connect(masterGain);
|
|
23
|
+
const chordChorus = new Tone.Chorus({ frequency: 0.8, delayTime: 3.5, depth: 0.8, wet: 0.7 }).connect(chordVerb);
|
|
24
|
+
chordChorus.start();
|
|
25
|
+
const chordGain = new Tone.Gain(0.45).connect(chordChorus);
|
|
26
|
+
const chords = new Tone.PolySynth(Tone.Synth, {
|
|
27
|
+
oscillator: { type: "fatsawtooth", count: 5, spread: 40 },
|
|
28
|
+
envelope: { attack: 0.08, decay: 0.2, sustain: 0.75, release: 2.0 },
|
|
29
|
+
volume: 0
|
|
30
|
+
}).connect(chordGain);
|
|
31
|
+
|
|
32
|
+
// --- LEAD — emotional melodic line, triangle + delay ---
|
|
33
|
+
const leadVerb = new Tone.Reverb({ decay: 2.5, wet: 0.4 }).connect(masterGain);
|
|
34
|
+
const leadDelay = new Tone.FeedbackDelay({ delayTime: "8n", feedback: 0.28, wet: 0.25 }).connect(leadVerb);
|
|
35
|
+
const leadGain = new Tone.Gain(0.6).connect(leadDelay);
|
|
36
|
+
const lead = new Tone.Synth({
|
|
37
|
+
oscillator: { type: "triangle" },
|
|
38
|
+
envelope: { attack: 0.02, decay: 0.3, sustain: 0.6, release: 1.0 },
|
|
39
|
+
volume: 0
|
|
40
|
+
}).connect(leadGain);
|
|
41
|
+
|
|
42
|
+
// --- 808 SUB — long pitch glide, saturated ---
|
|
43
|
+
const sub808Dist = new Tone.Distortion({ distortion: 0.12, wet: 0.3 }).connect(masterGain);
|
|
44
|
+
const sub808Gain = new Tone.Gain(0.85).connect(sub808Dist);
|
|
45
|
+
const sub808 = new Tone.MembraneSynth({
|
|
46
|
+
pitchDecay: 0.45,
|
|
47
|
+
octaves: 3,
|
|
48
|
+
envelope: { attack: 0.001, decay: 0.9, sustain: 0, release: 0.4 }
|
|
49
|
+
}).connect(sub808Gain);
|
|
50
|
+
|
|
51
|
+
// --- KICK — trap: sparse, heavy ---
|
|
52
|
+
const kickGain = new Tone.Gain(0.9).connect(masterGain);
|
|
53
|
+
const kick = new Tone.MembraneSynth({
|
|
54
|
+
pitchDecay: 0.08,
|
|
55
|
+
octaves: 8,
|
|
56
|
+
envelope: { attack: 0.001, decay: 0.38, sustain: 0, release: 0.1 }
|
|
57
|
+
}).connect(kickGain);
|
|
58
|
+
|
|
59
|
+
// --- CLAP — big noise with long reverb ---
|
|
60
|
+
const clapVerb = new Tone.Reverb({ decay: 2.8, wet: 0.55 }).connect(masterGain);
|
|
61
|
+
const clapGain = new Tone.Gain(0.55).connect(clapVerb);
|
|
62
|
+
const clap = new Tone.NoiseSynth({
|
|
63
|
+
noise: { type: "white" },
|
|
64
|
+
envelope: { attack: 0.001, decay: 0.18, sustain: 0, release: 0.08 }
|
|
65
|
+
}).connect(clapGain);
|
|
66
|
+
|
|
67
|
+
// --- HI-HAT — trap roll, heavy velocity variation ---
|
|
68
|
+
const hatGain = new Tone.Gain(0.35).connect(masterGain);
|
|
69
|
+
const hat = new Tone.MetalSynth({
|
|
70
|
+
frequency: 600,
|
|
71
|
+
envelope: { attack: 0.001, decay: 0.03, release: 0.01 },
|
|
72
|
+
harmonicity: 5.1,
|
|
73
|
+
modulationIndex: 32,
|
|
74
|
+
resonance: 4500,
|
|
75
|
+
octaves: 1.5
|
|
76
|
+
}).connect(hatGain);
|
|
77
|
+
|
|
78
|
+
// --- PATTERNS ---
|
|
79
|
+
|
|
80
|
+
// Intro lead melody (bars 1–2): emotional minor pentatonic line
|
|
81
|
+
const introLead = [
|
|
82
|
+
{ time: "0:0:0", note: "A4", dur: "4n" },
|
|
83
|
+
{ time: "0:1:0", note: "G4", dur: "8n" },
|
|
84
|
+
{ time: "0:1:2", note: "E4", dur: "8n" },
|
|
85
|
+
{ time: "0:2:0", note: "C5", dur: "4n" },
|
|
86
|
+
{ time: "0:3:0", note: "A4", dur: "4n" },
|
|
87
|
+
{ time: "1:0:0", note: "E4", dur: "2n" },
|
|
88
|
+
{ time: "1:2:0", note: "G4", dur: "4n" },
|
|
89
|
+
{ time: "1:3:0", note: "A4", dur: "4n" },
|
|
90
|
+
];
|
|
91
|
+
introLead.forEach(({ time, note, dur }) => {
|
|
92
|
+
lead.triggerAttackRelease(note, dur, time);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Drop lead melody (bars 3–6): brighter, higher register
|
|
96
|
+
const dropLead = [
|
|
97
|
+
{ time: "2:0:0", note: "A5", dur: "4n" },
|
|
98
|
+
{ time: "2:1:0", note: "G5", dur: "8n" },
|
|
99
|
+
{ time: "2:1:2", note: "E5", dur: "8n" },
|
|
100
|
+
{ time: "2:2:0", note: "C5", dur: "4n" },
|
|
101
|
+
{ time: "2:3:0", note: "A4", dur: "4n" },
|
|
102
|
+
{ time: "3:0:0", note: "F5", dur: "2n" },
|
|
103
|
+
{ time: "3:2:0", note: "E5", dur: "4n" },
|
|
104
|
+
{ time: "3:3:0", note: "D5", dur: "4n" },
|
|
105
|
+
{ time: "4:0:0", note: "C5", dur: "2n" },
|
|
106
|
+
{ time: "4:2:0", note: "E5", dur: "4n" },
|
|
107
|
+
{ time: "4:3:0", note: "G5", dur: "4n" },
|
|
108
|
+
{ time: "5:0:0", note: "G5", dur: "2n" },
|
|
109
|
+
{ time: "5:2:0", note: "A5", dur: "4n" },
|
|
110
|
+
{ time: "5:3:0", note: "G5", dur: "4n" },
|
|
111
|
+
];
|
|
112
|
+
dropLead.forEach(({ time, note, dur }) => {
|
|
113
|
+
lead.triggerAttackRelease(note, dur, time);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Drop supersaw chords — Am, F, C, G (1 bar each, wide voicings)
|
|
117
|
+
const dropChords = [
|
|
118
|
+
{ time: "2m", notes: ["A2","E3","A3","C4","E4"] },
|
|
119
|
+
{ time: "3m", notes: ["F2","C3","F3","A3","C4"] },
|
|
120
|
+
{ time: "4m", notes: ["C3","G3","C4","E4","G4"] },
|
|
121
|
+
{ time: "5m", notes: ["G2","D3","G3","B3","D4"] },
|
|
122
|
+
];
|
|
123
|
+
dropChords.forEach(({ time, notes }) => {
|
|
124
|
+
chords.triggerAttackRelease(notes, "1m", time);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// 808 sub bass — follows chord roots, enters with the drop
|
|
128
|
+
const subHits = [
|
|
129
|
+
{ time: "2m", note: "A2" },
|
|
130
|
+
{ time: "3m", note: "F2" },
|
|
131
|
+
{ time: "4m", note: "C2" },
|
|
132
|
+
{ time: "5m", note: "G2" },
|
|
133
|
+
];
|
|
134
|
+
subHits.forEach(({ time, note }) => {
|
|
135
|
+
sub808.triggerAttackRelease(note, "2n", time);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Kick: half-time feel — beat 1 every bar, plus extra on bar 2 beat 3
|
|
139
|
+
const kickSeq = new Tone.Sequence((time, val) => {
|
|
140
|
+
if (val) kick.triggerAttackRelease("C1", "8n", time);
|
|
141
|
+
}, ["C1",null,null,null,null,null,null,null,"C1",null,null,null,null,null,"C1",null], "16n");
|
|
142
|
+
kickSeq.start("2m");
|
|
143
|
+
|
|
144
|
+
// Clap: half-time snare on beat 3 (step 8 in 16-step)
|
|
145
|
+
const clapSeq = new Tone.Sequence((time, val) => {
|
|
146
|
+
if (val) clap.triggerAttackRelease("8n", time);
|
|
147
|
+
}, [null,null,null,null,null,null,null,null,1,null,null,null,null,null,null,null], "16n");
|
|
148
|
+
clapSeq.start("2m");
|
|
149
|
+
|
|
150
|
+
// Hi-hat: trap roll with velocity variation throughout
|
|
151
|
+
const hatVels = [0.8,0.3,0.7,0.2,0.6,0.3,0.8,0.2,0.7,0.3,0.6,0.2,0.9,0.3,1.0,0.2];
|
|
152
|
+
let hatStep = 0;
|
|
153
|
+
const hatSeq = new Tone.Sequence((time) => {
|
|
154
|
+
hat.triggerAttackRelease("32n", time, hatVels[hatStep % 16]);
|
|
155
|
+
hatStep++;
|
|
156
|
+
}, new Array(16).fill(1), "16n");
|
|
157
|
+
hatSeq.start(0);
|
|
158
|
+
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
main();
|
|
162
|
+
</script>
|
|
163
|
+
</body>
|
|
164
|
+
</html>
|