tuneframes 0.1.0 → 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 +167 -199
- package/README.md +150 -97
- 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 +48 -0
- package/examples/example-lofi.html +45 -45
- package/examples/example-lofi.mp3 +0 -0
- package/examples/example-minimal.html +21 -19
- package/examples/example-minimal.mp3 +0 -0
- package/examples/example-orchestral.html +67 -67
- package/examples/example-orchestral.mp3 +0 -0
- package/examples/example-piano.html +53 -0
- 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 -24
- 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 +261 -89
- package/src/render.js +134 -7
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
<div id="tuneframes" style="display:none">{"bpm":104,"duration":"12s"}</div>
|
|
2
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
|
|
3
|
+
<script>
|
|
4
|
+
// TuneFrames — Classical Piano
|
|
5
|
+
// C major, Allegro moderato (104 BPM)
|
|
6
|
+
// Right hand: melodic line with ornamental turn
|
|
7
|
+
// Left hand: Alberti bass (bass-upper-inner-upper pattern)
|
|
8
|
+
// Harmony: I → V → vi → IV → V → I (complete progression)
|
|
9
|
+
|
|
10
|
+
async function main() {
|
|
11
|
+
await Tone.start();
|
|
12
|
+
|
|
13
|
+
Tone.Transport.bpm.value = 104;
|
|
14
|
+
|
|
15
|
+
const beat = 60 / 104; // 0.577s per quarter note
|
|
16
|
+
const eighth = beat / 2; // 0.288s per eighth note
|
|
17
|
+
const sixteenth = beat / 4; // 0.144s per sixteenth note
|
|
18
|
+
|
|
19
|
+
// ── Small room reverb — a piano in a parlour, not a concert hall ──────────
|
|
20
|
+
const reverb = new Tone.Reverb({ decay: 1.1, preDelay: 0.008, wet: 0.22 });
|
|
21
|
+
await reverb.generate();
|
|
22
|
+
reverb.toDestination();
|
|
23
|
+
|
|
24
|
+
// ── Right hand — melody voice, triangle wave ──────────────────────────────
|
|
25
|
+
const rh = new Tone.Synth({
|
|
26
|
+
oscillator: { type: 'triangle' },
|
|
27
|
+
envelope: { attack: 0.006, decay: 0.22, sustain: 0.18, release: 1.4 },
|
|
28
|
+
volume: -4,
|
|
29
|
+
});
|
|
30
|
+
rh.connect(reverb);
|
|
31
|
+
|
|
32
|
+
// ── Left hand single notes (bass) — slightly softer, same timbre ─────────
|
|
33
|
+
const lhBass = new Tone.Synth({
|
|
34
|
+
oscillator: { type: 'triangle' },
|
|
35
|
+
envelope: { attack: 0.008, decay: 0.35, sustain: 0.12, release: 1.0 },
|
|
36
|
+
volume: -10,
|
|
37
|
+
});
|
|
38
|
+
lhBass.connect(reverb);
|
|
39
|
+
|
|
40
|
+
// ── Left hand chord notes (inner voices of Alberti) ───────────────────────
|
|
41
|
+
const lhChord = new Tone.PolySynth(Tone.Synth, {
|
|
42
|
+
oscillator: { type: 'triangle' },
|
|
43
|
+
envelope: { attack: 0.008, decay: 0.3, sustain: 0.1, release: 0.9 },
|
|
44
|
+
volume: -13,
|
|
45
|
+
});
|
|
46
|
+
lhChord.connect(reverb);
|
|
47
|
+
|
|
48
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
49
|
+
// Helper: schedule an Alberti bass pattern for one bar using direct time
|
|
50
|
+
// root = bass note (e.g. 'C3'), inner = e.g. 'E3', upper = e.g. 'G3'
|
|
51
|
+
// Pattern of 8 eighth notes: root upper inner upper root upper inner upper
|
|
52
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
53
|
+
function alberti(root, inner, upper, startTime) {
|
|
54
|
+
const pattern = [root, upper, inner, upper, root, upper, inner, upper];
|
|
55
|
+
pattern.forEach((note, i) => {
|
|
56
|
+
const t = startTime + i * eighth;
|
|
57
|
+
if (i % 2 === 0) {
|
|
58
|
+
lhBass.triggerAttackRelease(note, '8n', t);
|
|
59
|
+
} else {
|
|
60
|
+
lhChord.triggerAttackRelease(note, '8n', t);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
66
|
+
// Harmony plan (one bar each, 4 beats = beat*4 per bar):
|
|
67
|
+
// Bar 1: I — C major (C3 E3 G3)
|
|
68
|
+
// Bar 2: V — G major (G2 B2 D3)
|
|
69
|
+
// Bar 3: vi — A minor (A2 C3 E3)
|
|
70
|
+
// Bar 4: IV — F major (F2 A2 C3)
|
|
71
|
+
// Bar 5: V — G major (G2 B2 D3)
|
|
72
|
+
// Bar 6: I — C major (C3 E3 G3) — final resolution
|
|
73
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
74
|
+
const barLen = beat * 4;
|
|
75
|
+
|
|
76
|
+
alberti('C3', 'E3', 'G3', 0); // I
|
|
77
|
+
alberti('G2', 'B2', 'D3', barLen); // V
|
|
78
|
+
alberti('A2', 'C3', 'E3', barLen * 2); // vi
|
|
79
|
+
alberti('F2', 'A2', 'C3', barLen * 3); // IV
|
|
80
|
+
alberti('G2', 'B2', 'D3', barLen * 4); // V
|
|
81
|
+
alberti('C3', 'E3', 'G3', barLen * 5); // I (final)
|
|
82
|
+
|
|
83
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
84
|
+
// Right hand melody — stepwise, fits the chord above
|
|
85
|
+
// Phrase 1 (bars 1-2): C major scale ascending, quarter + eighth mix
|
|
86
|
+
// Phrase 2 (bars 3-4): descent with ornamental turn on E5
|
|
87
|
+
// Phrase 3 (bars 5-6): cadential figure, landing on C5
|
|
88
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
89
|
+
|
|
90
|
+
// Phrase 1: ascending C major scale with rhythmic variety
|
|
91
|
+
const phrase1 = [
|
|
92
|
+
{ note: 'C5', dur: '4n', t: 0 },
|
|
93
|
+
{ note: 'D5', dur: '8n', t: beat },
|
|
94
|
+
{ note: 'E5', dur: '8n', t: beat + eighth },
|
|
95
|
+
{ note: 'F5', dur: '4n', t: beat * 2 },
|
|
96
|
+
{ note: 'G5', dur: '4n', t: beat * 3 },
|
|
97
|
+
// Bar 2: hold G5, then step up to A5
|
|
98
|
+
{ note: 'G5', dur: '8n', t: barLen },
|
|
99
|
+
{ note: 'A5', dur: '8n', t: barLen + eighth },
|
|
100
|
+
{ note: 'G5', dur: '4n', t: barLen + beat },
|
|
101
|
+
{ note: 'F5', dur: '4n', t: barLen + beat * 2 },
|
|
102
|
+
{ note: 'E5', dur: '4n', t: barLen + beat * 3 },
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
// Phrase 2: ornamental turn on E5 (F E D E), then descent
|
|
106
|
+
const turnStart = barLen * 2;
|
|
107
|
+
const turn = [
|
|
108
|
+
{ note: 'F5', dur: '16n', t: turnStart },
|
|
109
|
+
{ note: 'E5', dur: '16n', t: turnStart + sixteenth },
|
|
110
|
+
{ note: 'D5', dur: '16n', t: turnStart + sixteenth * 2 },
|
|
111
|
+
{ note: 'E5', dur: '8n', t: turnStart + sixteenth * 3 },
|
|
112
|
+
];
|
|
113
|
+
const phrase2 = [
|
|
114
|
+
...turn,
|
|
115
|
+
{ note: 'D5', dur: '4n', t: turnStart + beat },
|
|
116
|
+
{ note: 'C5', dur: '4n', t: turnStart + beat * 2 },
|
|
117
|
+
{ note: 'B4', dur: '4n', t: turnStart + beat * 3 },
|
|
118
|
+
// Bar 4:
|
|
119
|
+
{ note: 'A4', dur: '4n', t: barLen * 3 },
|
|
120
|
+
{ note: 'G4', dur: '8n', t: barLen * 3 + beat },
|
|
121
|
+
{ note: 'A4', dur: '8n', t: barLen * 3 + beat + eighth },
|
|
122
|
+
{ note: 'C5', dur: '2n', t: barLen * 3 + beat * 2 },
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
// Phrase 3: cadential figure V → I landing
|
|
126
|
+
const p3start = barLen * 4;
|
|
127
|
+
const phrase3 = [
|
|
128
|
+
{ note: 'B4', dur: '4n', t: p3start },
|
|
129
|
+
{ note: 'D5', dur: '4n', t: p3start + beat },
|
|
130
|
+
{ note: 'G5', dur: '4n', t: p3start + beat * 2 },
|
|
131
|
+
{ note: 'F5', dur: '4n', t: p3start + beat * 3 },
|
|
132
|
+
// Bar 6: resolution to I — long final note
|
|
133
|
+
{ note: 'E5', dur: '8n', t: barLen * 5 },
|
|
134
|
+
{ note: 'D5', dur: '8n', t: barLen * 5 + eighth },
|
|
135
|
+
{ note: 'C5', dur: '2n', t: barLen * 5 + beat },
|
|
136
|
+
];
|
|
137
|
+
|
|
138
|
+
// Schedule all right hand notes using direct time offsets (seconds)
|
|
139
|
+
[...phrase1, ...phrase2, ...phrase3].forEach(({ note, dur, t }) => {
|
|
140
|
+
if (t < 12) {
|
|
141
|
+
rh.triggerAttackRelease(note, dur, t);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
</script>
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: audio-dnb
|
|
3
|
+
description: Drum and Bass — syncopated Amen-style breakbeat, reese bass with chorus and filter, atmospheric pad, heavy sub frequencies. Hard hitting and kinetic.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TuneFrames — Drum and Bass
|
|
7
|
+
|
|
8
|
+
## Genre Profile
|
|
9
|
+
- BPM range: 165–180
|
|
10
|
+
- Key characteristics: Complex syncopated breakbeat (kick not on the beat — that's the point), heavy Reese bass (sawtooth + chorus + filter), atmospheric pad underneath, powerful sub frequencies, kinetic forward momentum
|
|
11
|
+
- Typical instruments: MembraneSynth (kick), NoiseSynth (snare), MetalSynth (hi-hat), FMSynth or MonoSynth (Reese bass), PolySynth (pad)
|
|
12
|
+
- Mood: Intense, kinetic, dark, cerebral, euphoric at the peak
|
|
13
|
+
|
|
14
|
+
## Core Pattern
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
// Drum and Bass — 174 BPM
|
|
18
|
+
// Breakbeat: kick/snare NOT on beats 1,2,3,4 — syncopated Amen-style
|
|
19
|
+
// Reese bass: sawtooth + heavy chorus = that "wobble" character
|
|
20
|
+
// Pad: slow attack, atmospheric underneath
|
|
21
|
+
|
|
22
|
+
// Classic DnB breakbeat (Amen-style, 16-step)
|
|
23
|
+
// K=kick, S=snare, .=rest
|
|
24
|
+
// K . . S . K K S . . K . S . K S (feel, not strict)
|
|
25
|
+
const breakPattern = [
|
|
26
|
+
"kick", null, null, "snare", null, "kick", "kick", "snare",
|
|
27
|
+
null, null, "kick", null, "snare", null, "kick", "snare"
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
new Tone.Sequence((time, hit) => {
|
|
31
|
+
if (hit === "kick") kick.triggerAttackRelease("C1", "8n", time);
|
|
32
|
+
if (hit === "snare") snare.triggerAttackRelease("16n", time);
|
|
33
|
+
}, breakPattern, "16n").start(0);
|
|
34
|
+
|
|
35
|
+
// Reese bass — sawtooth through chorus
|
|
36
|
+
const chorus = new Tone.Chorus({ frequency: 1.8, delayTime: 3.5, depth: 0.7, wet: 0.7 });
|
|
37
|
+
const filter = new Tone.Filter({ frequency: 800, type: "lowpass", Q: 2 });
|
|
38
|
+
const reese = new Tone.MonoSynth({
|
|
39
|
+
oscillator: { type: "sawtooth" },
|
|
40
|
+
envelope: { attack: 0.01, decay: 0.5, sustain: 0.8, release: 0.3 },
|
|
41
|
+
volume: -4
|
|
42
|
+
}).chain(chorus, filter, Tone.Destination);
|
|
43
|
+
chorus.start();
|
|
44
|
+
|
|
45
|
+
Tone.Transport.bpm.value = 174;
|
|
46
|
+
Tone.Transport.start();
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Instrument Configuration
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
// Kick — short, punchy, low
|
|
53
|
+
const kick = new Tone.MembraneSynth({
|
|
54
|
+
pitchDecay: 0.04,
|
|
55
|
+
octaves: 5,
|
|
56
|
+
envelope: { attack: 0.001, decay: 0.2, sustain: 0, release: 0.08 },
|
|
57
|
+
volume: 2
|
|
58
|
+
}).toDestination();
|
|
59
|
+
|
|
60
|
+
// Snare — crisp, with slight room
|
|
61
|
+
const snareVerb = new Tone.Reverb({ decay: 0.5, wet: 0.15 }).toDestination();
|
|
62
|
+
const snare = new Tone.NoiseSynth({
|
|
63
|
+
noise: { type: "white" },
|
|
64
|
+
envelope: { attack: 0.001, decay: 0.12, sustain: 0, release: 0.04 },
|
|
65
|
+
volume: -2
|
|
66
|
+
}).connect(snareVerb);
|
|
67
|
+
|
|
68
|
+
// Hi-hat — very fast, adds texture between beats
|
|
69
|
+
const hat = new Tone.MetalSynth({
|
|
70
|
+
frequency: 500, harmonicity: 5.1, modulationIndex: 32,
|
|
71
|
+
resonance: 4500, octaves: 1.5,
|
|
72
|
+
envelope: { attack: 0.001, decay: 0.035, release: 0.01 },
|
|
73
|
+
volume: -12
|
|
74
|
+
}).toDestination();
|
|
75
|
+
|
|
76
|
+
// Reese bass — the DnB defining sound
|
|
77
|
+
// Two detuned sawtooths + heavy chorus + filter
|
|
78
|
+
const reeseLimiter = new Tone.Limiter(-4).toDestination();
|
|
79
|
+
const reeseFilter = new Tone.Filter({ frequency: 700, type: "lowpass", Q: 3 }).connect(reeseLimiter);
|
|
80
|
+
const reeseChorus = new Tone.Chorus({
|
|
81
|
+
frequency: 1.8, // slow chorus = the "reese" movement
|
|
82
|
+
delayTime: 3.5,
|
|
83
|
+
depth: 0.75,
|
|
84
|
+
wet: 0.75
|
|
85
|
+
}).connect(reeseFilter);
|
|
86
|
+
reeseChorus.start();
|
|
87
|
+
const reese = new Tone.MonoSynth({
|
|
88
|
+
oscillator: { type: "sawtooth" },
|
|
89
|
+
filter: { Q: 1, type: "lowpass", frequency: 2000 },
|
|
90
|
+
filterEnvelope: { attack: 0.02, decay: 0.5, sustain: 0.5, release: 0.5,
|
|
91
|
+
baseFrequency: 200, octaves: 2 },
|
|
92
|
+
envelope: { attack: 0.01, decay: 0.3, sustain: 0.8, release: 0.3 },
|
|
93
|
+
volume: -2
|
|
94
|
+
}).connect(reeseChorus);
|
|
95
|
+
|
|
96
|
+
// Atmospheric pad — slow attack, long release
|
|
97
|
+
const padVerb = new Tone.Reverb({ decay: 5.0, wet: 0.7 }).toDestination();
|
|
98
|
+
const pad = new Tone.PolySynth(Tone.Synth, {
|
|
99
|
+
oscillator: { type: "sawtooth4" },
|
|
100
|
+
envelope: { attack: 0.6, decay: 1.0, sustain: 0.6, release: 2.5 },
|
|
101
|
+
volume: -14
|
|
102
|
+
}).connect(padVerb);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Composition Structure
|
|
106
|
+
|
|
107
|
+
- **Bar 1-2:** Breakbeat only — establish the syncopated groove immediately
|
|
108
|
+
- **Bar 3-4:** Add hi-hat fills between kick/snare — density builds
|
|
109
|
+
- **Bar 5-8:** Add Reese bass — low sustained notes, filter opens slowly
|
|
110
|
+
- **Bar 9-16:** Full arrangement — breakbeat + Reese + atmospheric pad
|
|
111
|
+
- **Variation bar:** Every 4 bars, drop a kick or add a snare roll for surprise
|
|
112
|
+
- **Reese movement:** Filter automation — close at bar 5, sweep open over bar 6-8, close and re-open
|
|
113
|
+
- **Outro:** Break strip — just pad remains, then breakbeat returns for exit
|
|
114
|
+
|
|
115
|
+
The breakbeat is the identity. The kick landing OFF the downbeat is what separates DnB from techno — resist putting the kick on beat 1. The snare DOES land around beats 2 and 4 but offset slightly.
|
|
116
|
+
|
|
117
|
+
## Example Variations
|
|
118
|
+
|
|
119
|
+
### 1 — Half-time feel bridge
|
|
120
|
+
```js
|
|
121
|
+
// Every 8 bars, drop to a 2-step pattern (kick-snare only) for 1 bar
|
|
122
|
+
// Builds tension before the full break returns
|
|
123
|
+
const halfTimeBar = ["kick", null, null, null, null, null, null, null,
|
|
124
|
+
null, null, null, null, "snare", null, null, null];
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 2 — Reese filter wobble (1/4 note LFO)
|
|
128
|
+
```js
|
|
129
|
+
// LFO modulating the Reese filter — creates rhythmic wobble
|
|
130
|
+
const lfo = new Tone.LFO({ frequency: "4n", min: 200, max: 2000, type: "sine" });
|
|
131
|
+
lfo.connect(reeseFilter.frequency);
|
|
132
|
+
lfo.start();
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### 3 — Snare roll into drop
|
|
136
|
+
```js
|
|
137
|
+
// 16th-note snare roll over the last bar before a new section
|
|
138
|
+
// Schedule 8 snare hits in last bar
|
|
139
|
+
for (let i = 0; i < 8; i++) {
|
|
140
|
+
snare.triggerAttackRelease("32n", `${barStart} + ${i * 0.107}`, 0.3 + i * 0.087);
|
|
141
|
+
}
|
|
142
|
+
```
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>TuneFrames — Drum and 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":174,"duration":"12s"}</div>
|
|
11
|
+
|
|
12
|
+
<script>
|
|
13
|
+
async function main() {
|
|
14
|
+
await Tone.start();
|
|
15
|
+
Tone.Transport.bpm.value = 174;
|
|
16
|
+
|
|
17
|
+
// --- MASTER BUS ---
|
|
18
|
+
const masterLimiter = new Tone.Limiter(-1).toDestination();
|
|
19
|
+
const masterGain = new Tone.Gain(0.87).connect(masterLimiter);
|
|
20
|
+
|
|
21
|
+
// --- KICK — short, punchy, sub-heavy ---
|
|
22
|
+
const kickGain = new Tone.Gain(0.9).connect(masterGain);
|
|
23
|
+
const kick = new Tone.MembraneSynth({
|
|
24
|
+
pitchDecay: 0.038,
|
|
25
|
+
octaves: 5,
|
|
26
|
+
envelope: { attack: 0.001, decay: 0.18, sustain: 0, release: 0.07 }
|
|
27
|
+
}).connect(kickGain);
|
|
28
|
+
|
|
29
|
+
// --- SNARE — crisp white noise ---
|
|
30
|
+
const snareVerb = new Tone.Reverb({ decay: 0.45, wet: 0.14 }).connect(masterGain);
|
|
31
|
+
const snareGain = new Tone.Gain(0.65).connect(snareVerb);
|
|
32
|
+
const snare = new Tone.NoiseSynth({
|
|
33
|
+
noise: { type: "white" },
|
|
34
|
+
envelope: { attack: 0.001, decay: 0.11, sustain: 0, release: 0.035 }
|
|
35
|
+
}).connect(snareGain);
|
|
36
|
+
|
|
37
|
+
// --- HI-HAT — fast texture between beats ---
|
|
38
|
+
const hatGain = new Tone.Gain(0.28).connect(masterGain);
|
|
39
|
+
const hat = new Tone.MetalSynth({
|
|
40
|
+
frequency: 520,
|
|
41
|
+
envelope: { attack: 0.001, decay: 0.032, release: 0.01 },
|
|
42
|
+
harmonicity: 5.1,
|
|
43
|
+
modulationIndex: 32,
|
|
44
|
+
resonance: 4600,
|
|
45
|
+
octaves: 1.5
|
|
46
|
+
}).connect(hatGain);
|
|
47
|
+
|
|
48
|
+
// --- REESE BASS — MonoSynth sawtooth + Chorus + Filter ---
|
|
49
|
+
// The Reese is the defining DnB sound: two slightly detuned saws = chorus
|
|
50
|
+
const reeseLimiter = new Tone.Limiter(-4).connect(masterGain);
|
|
51
|
+
const reeseFilter = new Tone.Filter({ frequency: 500, type: "lowpass", Q: 2.5 }).connect(reeseLimiter);
|
|
52
|
+
const reeseChorus = new Tone.Chorus({
|
|
53
|
+
frequency: 1.7,
|
|
54
|
+
delayTime: 3.5,
|
|
55
|
+
depth: 0.78,
|
|
56
|
+
wet: 0.72
|
|
57
|
+
}).connect(reeseFilter);
|
|
58
|
+
reeseChorus.start();
|
|
59
|
+
const reese = new Tone.MonoSynth({
|
|
60
|
+
oscillator: { type: "sawtooth" },
|
|
61
|
+
filter: { Q: 1.5, type: "lowpass", frequency: 3000 },
|
|
62
|
+
filterEnvelope: {
|
|
63
|
+
attack: 0.02, decay: 0.6, sustain: 0.5, release: 0.4,
|
|
64
|
+
baseFrequency: 180, octaves: 2.5
|
|
65
|
+
},
|
|
66
|
+
envelope: { attack: 0.008, decay: 0.4, sustain: 0.8, release: 0.35 },
|
|
67
|
+
volume: 0
|
|
68
|
+
}).connect(reeseChorus);
|
|
69
|
+
|
|
70
|
+
// --- ATMOSPHERIC PAD — slow attack, very wet reverb ---
|
|
71
|
+
const padVerb = new Tone.Reverb({ decay: 5.5, wet: 0.72 }).connect(masterGain);
|
|
72
|
+
const padGain = new Tone.Gain(0.45).connect(padVerb);
|
|
73
|
+
const pad = new Tone.PolySynth(Tone.Synth, {
|
|
74
|
+
oscillator: { type: "sawtooth4" },
|
|
75
|
+
envelope: { attack: 0.7, decay: 1.2, sustain: 0.6, release: 3.0 },
|
|
76
|
+
volume: -2
|
|
77
|
+
}).connect(padGain);
|
|
78
|
+
|
|
79
|
+
// --- BREAKBEAT PATTERN — syncopated Amen-style ---
|
|
80
|
+
// K=kick, S=snare, h=hat, .=rest
|
|
81
|
+
// Pattern: . . S . K K S . . K . S K . S h
|
|
82
|
+
const breakSeq = new Tone.Sequence((time, hit) => {
|
|
83
|
+
if (hit === "K") kick.triggerAttackRelease("C1", "8n", time);
|
|
84
|
+
if (hit === "S") snare.triggerAttackRelease("16n", time, 0.85);
|
|
85
|
+
if (hit === "s") snare.triggerAttackRelease("16n", time, 0.45); // ghost snare
|
|
86
|
+
if (hit === "h") hat.triggerAttackRelease("32n", time, 0.5);
|
|
87
|
+
}, [
|
|
88
|
+
null, "h", "S", null, "K", "K", "S", null,
|
|
89
|
+
"h", "K", null, "S", "K", "h", "S", "h"
|
|
90
|
+
], "16n");
|
|
91
|
+
breakSeq.start(0);
|
|
92
|
+
|
|
93
|
+
// --- REESE BASS LINE — D minor, sustained long notes ---
|
|
94
|
+
// Reese bass plays whole-bar notes — the filter movement IS the melody
|
|
95
|
+
const reeseLine = [
|
|
96
|
+
{ note: "D1", dur: "1m", time: "2m" },
|
|
97
|
+
{ note: "C1", dur: "1m", time: "3m" },
|
|
98
|
+
{ note: "Bb0", dur: "1m", time: "4m" },
|
|
99
|
+
{ note: "A0", dur: "1m", time: "5m" },
|
|
100
|
+
{ note: "D1", dur: "1m", time: "6m" },
|
|
101
|
+
{ note: "F1", dur: "1m", time: "7m" },
|
|
102
|
+
];
|
|
103
|
+
reeseLine.forEach(({ note, dur, time }) => {
|
|
104
|
+
reese.triggerAttackRelease(note, dur, time);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Filter sweep on Reese — opens over bars 3-5, closes back bar 6-7
|
|
108
|
+
const sweepStart = Tone.Time("2m").toSeconds();
|
|
109
|
+
reeseFilter.frequency.setValueAtTime(300, sweepStart);
|
|
110
|
+
reeseFilter.frequency.exponentialRampToValueAtTime(2200, sweepStart + 4);
|
|
111
|
+
reeseFilter.frequency.exponentialRampToValueAtTime(250, sweepStart + 5.5);
|
|
112
|
+
reeseFilter.frequency.exponentialRampToValueAtTime(1800, sweepStart + 9);
|
|
113
|
+
|
|
114
|
+
// --- PAD — atmospheric Dm chord, enters at bar 4 ---
|
|
115
|
+
const padNotes = ["D3", "F3", "A3", "C4"]; // Dm7
|
|
116
|
+
pad.triggerAttackRelease(padNotes, "4m", "3m");
|
|
117
|
+
pad.triggerAttackRelease(["C3", "Eb3", "G3", "Bb3"], "4m", "7m"); // Cm7
|
|
118
|
+
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
main();
|
|
122
|
+
</script>
|
|
123
|
+
</body>
|
|
124
|
+
</html>
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: audio-downtempo
|
|
3
|
+
description: Downtempo / Trip-Hop — dark cinematic groove, Portishead/Massive Attack vibes, breakbeat swing, vinyl texture, sub bass
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TuneFrames — Downtempo / Trip-Hop
|
|
7
|
+
|
|
8
|
+
## Genre Profile
|
|
9
|
+
- BPM range: 70-90 (always swung — 16th notes have a lazy late feel)
|
|
10
|
+
- Key characteristics: heavy breakbeat (kick NOT on every beat — syncopated), vinyl noise texture, sub bass movement on root notes, eerie/minor melodic line, gritty filter on everything
|
|
11
|
+
- Typical instruments: MembraneSynth kick, NoiseSynth snare + crackle, MonoSynth sub bass with lowpass filter, AMSynth for eerie melody, BitCrusher for lo-fi grit
|
|
12
|
+
- Mood: gritty, hypnotic, paranoid, late-night cinematic
|
|
13
|
+
|
|
14
|
+
## Core Pattern
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
// Trip-hop breakbeat — NOT four-on-the-floor
|
|
18
|
+
// Kick: 0, 3, 5 (in 8ths) — heavy, off-balance feel
|
|
19
|
+
// Snare: 2, 6 (on 2 and 4 but swung)
|
|
20
|
+
// BPM = 82, swung feel via manual timing offsets
|
|
21
|
+
Tone.Transport.bpm.value = 82;
|
|
22
|
+
|
|
23
|
+
// Lo-fi grit filter
|
|
24
|
+
const bitCrusher = new Tone.BitCrusher(6).toDestination();
|
|
25
|
+
const reverb = new Tone.Reverb({ decay: 4.0, wet: 0.6 });
|
|
26
|
+
reverb.toDestination();
|
|
27
|
+
const filter = new Tone.Filter({ frequency: 2000, type: 'lowpass', rolloff: -24 });
|
|
28
|
+
filter.connect(reverb);
|
|
29
|
+
|
|
30
|
+
// Sub bass — the backbone
|
|
31
|
+
const bass = new Tone.MonoSynth({
|
|
32
|
+
oscillator: { type: 'sine' },
|
|
33
|
+
filter: { Q: 2, frequency: 120, type: 'lowpass' },
|
|
34
|
+
envelope: { attack: 0.02, decay: 0.3, sustain: 0.6, release: 0.4 },
|
|
35
|
+
volume: -4,
|
|
36
|
+
}).toDestination();
|
|
37
|
+
|
|
38
|
+
// Eerie melody — AMSynth gives that trembling quality
|
|
39
|
+
const melody = new Tone.AMSynth({
|
|
40
|
+
harmonicity: 2.5,
|
|
41
|
+
envelope: { attack: 0.15, decay: 0.5, sustain: 0.3, release: 1.0 },
|
|
42
|
+
volume: -12,
|
|
43
|
+
});
|
|
44
|
+
melody.connect(filter);
|
|
45
|
+
|
|
46
|
+
// Breakbeat kick — syncopated
|
|
47
|
+
const kick = new Tone.MembraneSynth({
|
|
48
|
+
pitchDecay: 0.08, octaves: 10,
|
|
49
|
+
envelope: { attack: 0.001, decay: 0.5, sustain: 0, release: 0.1 },
|
|
50
|
+
volume: -2,
|
|
51
|
+
}).toDestination();
|
|
52
|
+
|
|
53
|
+
// Schedule kick on syncopated positions (seconds at 82 BPM, 1 beat = ~0.73s)
|
|
54
|
+
// Pattern over 2 bars (~5.85s): kick on beat 1, the "and" of 2, beat 3.5
|
|
55
|
+
const beatLen = 60 / 82;
|
|
56
|
+
[0, beatLen * 1.5, beatLen * 3, beatLen * 4, beatLen * 5.5, beatLen * 7].forEach(t => {
|
|
57
|
+
Tone.Transport.scheduleOnce(time => kick.triggerAttackRelease('C1', '8n', time), t);
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Instrument Configuration
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
// BitCrusher — vinyl artifact simulation
|
|
65
|
+
const bitCrusher = new Tone.BitCrusher(7); // lower = grittier
|
|
66
|
+
bitCrusher.toDestination();
|
|
67
|
+
|
|
68
|
+
// Dark reverb
|
|
69
|
+
const reverb = new Tone.Reverb({ decay: 5.0, preDelay: 0.03, wet: 0.65 });
|
|
70
|
+
await reverb.generate();
|
|
71
|
+
reverb.toDestination();
|
|
72
|
+
|
|
73
|
+
// Vinyl crackle — pink noise at very low volume, always running
|
|
74
|
+
const crackle = new Tone.NoiseSynth({
|
|
75
|
+
noise: { type: 'pink' },
|
|
76
|
+
envelope: { attack: 0.1, decay: 0.1, sustain: 1.0, release: 0.1 },
|
|
77
|
+
volume: -32,
|
|
78
|
+
}).toDestination();
|
|
79
|
+
crackle.triggerAttack(); // sustain forever
|
|
80
|
+
|
|
81
|
+
// Sub bass — pure sine, filtered hard
|
|
82
|
+
const bass = new Tone.MonoSynth({
|
|
83
|
+
oscillator: { type: 'sine' },
|
|
84
|
+
filter: { Q: 3, frequency: 100, type: 'lowpass', rolloff: -24 },
|
|
85
|
+
filterEnvelope: { attack: 0.05, decay: 0.5, sustain: 0.3, release: 0.8, baseFrequency: 60, octaves: 1.5 },
|
|
86
|
+
envelope: { attack: 0.02, decay: 0.2, sustain: 0.6, release: 0.5 },
|
|
87
|
+
volume: -3,
|
|
88
|
+
}).toDestination();
|
|
89
|
+
|
|
90
|
+
// Eerie lead — AMSynth with tremolo-like modulation
|
|
91
|
+
const lead = new Tone.AMSynth({
|
|
92
|
+
harmonicity: 3,
|
|
93
|
+
oscillator: { type: 'sawtooth' },
|
|
94
|
+
envelope: { attack: 0.2, decay: 0.8, sustain: 0.4, release: 2.0 },
|
|
95
|
+
modulation: { type: 'sine' },
|
|
96
|
+
modulationEnvelope: { attack: 0.5, decay: 0.0, sustain: 1.0, release: 0.5 },
|
|
97
|
+
volume: -14,
|
|
98
|
+
}).connect(reverb);
|
|
99
|
+
|
|
100
|
+
// Heavy kick
|
|
101
|
+
const kick = new Tone.MembraneSynth({
|
|
102
|
+
pitchDecay: 0.1, octaves: 12,
|
|
103
|
+
envelope: { attack: 0.001, decay: 0.6, sustain: 0, release: 0.15 },
|
|
104
|
+
volume: -1,
|
|
105
|
+
}).toDestination();
|
|
106
|
+
|
|
107
|
+
// Snare — layered noise (pink for body, white for crack)
|
|
108
|
+
const snare = new Tone.NoiseSynth({
|
|
109
|
+
noise: { type: 'pink' },
|
|
110
|
+
envelope: { attack: 0.001, decay: 0.18, sustain: 0, release: 0.12 },
|
|
111
|
+
volume: -8,
|
|
112
|
+
}).toDestination();
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Composition Structure
|
|
116
|
+
|
|
117
|
+
1. **Crackle intro (0-2s):** Vinyl crackle only — establishes grit and atmosphere.
|
|
118
|
+
2. **Bass enters (2-4s):** Sub bass plays the root. No other elements yet. Heavy.
|
|
119
|
+
3. **Beat drop (4-6s):** Kick + snare syncopated breakbeat joins. Still just bass + beat.
|
|
120
|
+
4. **Melody (6-12s):** AMSynth eerie melody over the groove. Should feel like it doesn't quite fit — that's correct.
|
|
121
|
+
5. **Full loop (12s+):** All elements. Melody repeats with slight variation.
|
|
122
|
+
|
|
123
|
+
**Bass movement (minor feel):**
|
|
124
|
+
- Root: D2 → A2 → F2 → G2 (4 beats each)
|
|
125
|
+
- Melody follows but at half speed — slower, more ominous
|
|
126
|
+
|
|
127
|
+
## Example Variations
|
|
128
|
+
|
|
129
|
+
### Variation 1: Pure Portishead (drum + bass only)
|
|
130
|
+
```js
|
|
131
|
+
// No melody — just kick, snare, bass, crackle
|
|
132
|
+
// Bass line has more movement: D2 → C2 → Bb1 → A1 (descending chromatic minor)
|
|
133
|
+
// Snare gets a slight BitCrusher: bitCrusher.connect(snare output)
|
|
134
|
+
// This is the most "hip-hop foundation" version
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Variation 2: Cinematic (add string texture)
|
|
138
|
+
```js
|
|
139
|
+
// PolySynth triangle wave simulating strings
|
|
140
|
+
// Very long attack: 3.0s, release: 4.0s
|
|
141
|
+
// Notes: Dm chord sustaining throughout (D3, F3, A3)
|
|
142
|
+
// Volume: -20 — barely audible shimmer behind the groove
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Variation 3: Faster / more aggressive
|
|
146
|
+
```js
|
|
147
|
+
Tone.Transport.bpm.value = 92;
|
|
148
|
+
// BitCrusher lower: 5 (crunchier)
|
|
149
|
+
// Kick volume: 0 (0dB — very present)
|
|
150
|
+
// Add 16th hi-hats at -24dB just for texture
|
|
151
|
+
// Lead melody shorter notes: 0.1s decay instead of 0.8s
|
|
152
|
+
```
|