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,125 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>TuneFrames — Techno</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":136,"duration":"12s"}</div>
|
|
11
|
+
|
|
12
|
+
<script>
|
|
13
|
+
async function main() {
|
|
14
|
+
await Tone.start();
|
|
15
|
+
Tone.Transport.bpm.value = 136;
|
|
16
|
+
|
|
17
|
+
// --- MASTER BUS ---
|
|
18
|
+
const masterLimiter = new Tone.Limiter(-1).toDestination();
|
|
19
|
+
const masterGain = new Tone.Gain(0.85).connect(masterLimiter);
|
|
20
|
+
|
|
21
|
+
// --- KICK — hard MembraneSynth ---
|
|
22
|
+
const kickGain = new Tone.Gain(0.9).connect(masterGain);
|
|
23
|
+
const kick = new Tone.MembraneSynth({
|
|
24
|
+
pitchDecay: 0.07,
|
|
25
|
+
octaves: 10,
|
|
26
|
+
envelope: { attack: 0.001, decay: 0.38, sustain: 0, release: 0.1 }
|
|
27
|
+
}).connect(kickGain);
|
|
28
|
+
|
|
29
|
+
// --- ACID BASS — MonoSynth sawtooth through distortion ---
|
|
30
|
+
const bassLimiter = new Tone.Limiter(-4).connect(masterGain);
|
|
31
|
+
const bassDist = new Tone.Distortion({ distortion: 0.55, wet: 0.7 }).connect(bassLimiter);
|
|
32
|
+
const bassFilter = new Tone.Filter({ frequency: 400, type: "lowpass", rolloff: -24, Q: 3 }).connect(bassDist);
|
|
33
|
+
const bass = new Tone.MonoSynth({
|
|
34
|
+
oscillator: { type: "sawtooth" },
|
|
35
|
+
filter: { Q: 7, type: "lowpass", rolloff: -24 },
|
|
36
|
+
filterEnvelope: {
|
|
37
|
+
attack: 0.002, decay: 0.25, sustain: 0.2, release: 0.5,
|
|
38
|
+
baseFrequency: 70, octaves: 3.5
|
|
39
|
+
},
|
|
40
|
+
envelope: { attack: 0.001, decay: 0.25, sustain: 0.5, release: 0.3 },
|
|
41
|
+
volume: -2
|
|
42
|
+
}).connect(bassFilter);
|
|
43
|
+
|
|
44
|
+
// --- HI-HAT — tight MetalSynth ---
|
|
45
|
+
const hatGain = new Tone.Gain(0.45).connect(masterGain);
|
|
46
|
+
const hat = new Tone.MetalSynth({
|
|
47
|
+
frequency: 400,
|
|
48
|
+
envelope: { attack: 0.001, decay: 0.045, release: 0.01 },
|
|
49
|
+
harmonicity: 5.1,
|
|
50
|
+
modulationIndex: 32,
|
|
51
|
+
resonance: 4200,
|
|
52
|
+
octaves: 1.5
|
|
53
|
+
}).connect(hatGain);
|
|
54
|
+
|
|
55
|
+
// --- STAB — sparse cold PolySynth ---
|
|
56
|
+
const stabReverb = new Tone.Reverb({ decay: 1.8, wet: 0.35 }).connect(masterGain);
|
|
57
|
+
const stab = new Tone.PolySynth(Tone.Synth, {
|
|
58
|
+
oscillator: { type: "square4" },
|
|
59
|
+
envelope: { attack: 0.001, decay: 0.12, sustain: 0, release: 0.08 },
|
|
60
|
+
volume: -14
|
|
61
|
+
}).connect(stabReverb);
|
|
62
|
+
|
|
63
|
+
// Stab chords — dissonant, industrial
|
|
64
|
+
const stabNotes = [["A2", "Eb3"], ["A2", "D3"], null, ["G2", "C#3"]];
|
|
65
|
+
|
|
66
|
+
// --- SEQUENCES ---
|
|
67
|
+
|
|
68
|
+
// Kick: 4-on-the-floor with ghost on bar 2 last 16th
|
|
69
|
+
const kickPattern = new Tone.Sequence((time, val) => {
|
|
70
|
+
if (val) kick.triggerAttackRelease("C1", "8n", time);
|
|
71
|
+
}, ["C1", null, null, null, "C1", null, null, null,
|
|
72
|
+
"C1", null, null, null, "C1", null, "C1", null], "16n");
|
|
73
|
+
kickPattern.start(0);
|
|
74
|
+
|
|
75
|
+
// Hi-hat: 16th notes, velocity variation
|
|
76
|
+
let hatStep = 0;
|
|
77
|
+
const hatVels = [0.75, 0.35, 0.75, 0.35, 0.75, 0.35, 0.95, 0.35,
|
|
78
|
+
0.75, 0.35, 0.75, 0.35, 0.75, 0.45, 0.95, 0.55];
|
|
79
|
+
const hatSeq = new Tone.Sequence((time) => {
|
|
80
|
+
hat.triggerAttackRelease("32n", time, hatVels[hatStep % 16]);
|
|
81
|
+
hatStep++;
|
|
82
|
+
}, new Array(16).fill(1), "16n");
|
|
83
|
+
hatSeq.start(0);
|
|
84
|
+
|
|
85
|
+
// Bass: acid pattern — A1 root with chromatic movement
|
|
86
|
+
const bassLine = [
|
|
87
|
+
{ note: "A1", dur: "8n" }, { note: null },
|
|
88
|
+
{ note: "A1", dur: "16n" }, { note: "A1", dur: "16n" },
|
|
89
|
+
{ note: null }, { note: "C2", dur: "8n" },
|
|
90
|
+
{ note: null }, { note: "A1", dur: "16n" },
|
|
91
|
+
{ note: "A1", dur: "8n" }, { note: null },
|
|
92
|
+
{ note: "Bb1", dur: "16n" }, { note: "A1", dur: "16n" },
|
|
93
|
+
{ note: null }, { note: "G1", dur: "16n" },
|
|
94
|
+
{ note: null }, { note: "A1", dur: "8n" }
|
|
95
|
+
];
|
|
96
|
+
let bassStep = 0;
|
|
97
|
+
const bassSeq = new Tone.Sequence((time) => {
|
|
98
|
+
const step = bassLine[bassStep % bassLine.length];
|
|
99
|
+
if (step.note) bass.triggerAttackRelease(step.note, step.dur, time);
|
|
100
|
+
bassStep++;
|
|
101
|
+
}, new Array(16).fill(1), "16n");
|
|
102
|
+
bassSeq.start("2m"); // bass enters at bar 3
|
|
103
|
+
|
|
104
|
+
// Stabs: every 2 bars on beat 3
|
|
105
|
+
let stabStep = 0;
|
|
106
|
+
const stabSeq = new Tone.Sequence((time) => {
|
|
107
|
+
const chord = stabNotes[stabStep % stabNotes.length];
|
|
108
|
+
if (chord) stab.triggerAttackRelease(chord, "32n", time);
|
|
109
|
+
stabStep++;
|
|
110
|
+
}, [1, null, null, null, null, null, null, null], "2n");
|
|
111
|
+
stabSeq.start("4m"); // stabs enter at bar 5
|
|
112
|
+
|
|
113
|
+
// --- BASS FILTER SWEEP — signature acid movement ---
|
|
114
|
+
const sweepStart = Tone.Time("2m").toSeconds();
|
|
115
|
+
bassFilter.frequency.setValueAtTime(200, sweepStart);
|
|
116
|
+
bassFilter.frequency.linearRampToValueAtTime(2800, sweepStart + 4);
|
|
117
|
+
bassFilter.frequency.linearRampToValueAtTime(150, sweepStart + 4.5);
|
|
118
|
+
bassFilter.frequency.linearRampToValueAtTime(1800, sweepStart + 8);
|
|
119
|
+
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
main();
|
|
123
|
+
</script>
|
|
124
|
+
</body>
|
|
125
|
+
</html>
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: audio-trap
|
|
3
|
+
description: Trap — 808 sub bass with pitch bend, snare on 2 and 4, rapid hi-hat rolls with velocity variation, sparse orchestral strings for drama.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TuneFrames — Trap
|
|
7
|
+
|
|
8
|
+
## Genre Profile
|
|
9
|
+
- BPM range: 130–160 (half-time feel makes it sit like 65–80)
|
|
10
|
+
- Key characteristics: 808 sub bass with long decay and downward pitch bend, snare on beats 2 and 4, hi-hat rolls in 16th/32nd triplets with strong velocity variation, heavy low end, atmospheric elements
|
|
11
|
+
- Typical instruments: MembraneSynth (808 sub), NoiseSynth (snare), MetalSynth (hi-hats), PolySynth (strings/pads)
|
|
12
|
+
- Mood: Menacing, cinematic, heavy, dramatic
|
|
13
|
+
|
|
14
|
+
## Core Pattern
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
// Trap — 140 BPM, half-time feel
|
|
18
|
+
// 808: long decay sub with downward pitch glide
|
|
19
|
+
// Snare: beat 2 and 4 (half-time = bars 1 and 3)
|
|
20
|
+
// Hi-hats: rapid rolls, velocity builds and drops — the signature
|
|
21
|
+
// Strings: sparse atmospheric chords
|
|
22
|
+
|
|
23
|
+
// 808 sub — MembraneSynth with pitch envelope = signature glide
|
|
24
|
+
const sub = new Tone.MembraneSynth({
|
|
25
|
+
pitchDecay: 0.5, // long pitch glide (the "808 fall")
|
|
26
|
+
octaves: 3, // moderate pitch drop
|
|
27
|
+
envelope: { attack: 0.001, decay: 1.2, sustain: 0, release: 0.5 }
|
|
28
|
+
}).toDestination();
|
|
29
|
+
|
|
30
|
+
// Trigger 808 on beat 1 — pitch starts at C2 then falls
|
|
31
|
+
sub.triggerAttackRelease("C2", "2n", time);
|
|
32
|
+
|
|
33
|
+
// Hi-hat roll — 32nd notes, velocity variation = the trap signature
|
|
34
|
+
const hatVelocities = [
|
|
35
|
+
0.9, 0.3, 0.5, 0.2, 0.7, 0.2, 0.4, 0.2, // 8-step build
|
|
36
|
+
0.6, 0.3, 0.8, 0.2, 0.5, 0.3, 1.0, 0.2 // accent on last
|
|
37
|
+
];
|
|
38
|
+
new Tone.Sequence((time, vel) => {
|
|
39
|
+
hat.triggerAttackRelease("32n", time, vel);
|
|
40
|
+
}, hatVelocities, "32n").start(0);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Instrument Configuration
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
// 808 Sub — long pitchDecay is the defining characteristic
|
|
47
|
+
const dist808 = new Tone.Distortion({ distortion: 0.15, wet: 0.3 }).toDestination();
|
|
48
|
+
const sub = new Tone.MembraneSynth({
|
|
49
|
+
pitchDecay: 0.45, // how long the pitch "falls" — the 808 glide
|
|
50
|
+
octaves: 3, // semitones of pitch drop
|
|
51
|
+
volume: 2,
|
|
52
|
+
envelope: { attack: 0.001, decay: 1.0, sustain: 0, release: 0.6 }
|
|
53
|
+
}).connect(dist808); // light saturation adds warmth to the sub
|
|
54
|
+
|
|
55
|
+
// Snare — tight white noise, minimal reverb
|
|
56
|
+
const snareVerb = new Tone.Reverb({ decay: 0.8, wet: 0.2 }).toDestination();
|
|
57
|
+
const snare = new Tone.NoiseSynth({
|
|
58
|
+
noise: { type: "white" },
|
|
59
|
+
envelope: { attack: 0.001, decay: 0.18, sustain: 0, release: 0.06 },
|
|
60
|
+
volume: -2
|
|
61
|
+
}).connect(snareVerb);
|
|
62
|
+
|
|
63
|
+
// Hi-hat — very short decay, high frequency
|
|
64
|
+
const hat = new Tone.MetalSynth({
|
|
65
|
+
frequency: 800,
|
|
66
|
+
envelope: { attack: 0.001, decay: 0.03, release: 0.008 },
|
|
67
|
+
harmonicity: 5.1,
|
|
68
|
+
modulationIndex: 32,
|
|
69
|
+
resonance: 5000,
|
|
70
|
+
octaves: 2,
|
|
71
|
+
volume: -8
|
|
72
|
+
}).toDestination();
|
|
73
|
+
|
|
74
|
+
// Orchestral strings — sparse, atmospheric
|
|
75
|
+
const stringsVerb = new Tone.Reverb({ decay: 4.0, wet: 0.6 }).toDestination();
|
|
76
|
+
const strings = new Tone.PolySynth(Tone.Synth, {
|
|
77
|
+
oscillator: { type: "sawtooth4" },
|
|
78
|
+
envelope: { attack: 0.4, decay: 1.0, sustain: 0.7, release: 1.5 },
|
|
79
|
+
volume: -12
|
|
80
|
+
}).connect(stringsVerb);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Composition Structure
|
|
84
|
+
|
|
85
|
+
- **Bar 1:** 808 hit on beat 1, snare on beat 3 (half-time), hi-hat roll building
|
|
86
|
+
- **Bar 2:** 808 on beat 1 again (the sub is still decaying from bar 1), hi-hat variation
|
|
87
|
+
- **Bars 1-4:** Drum loop establishes half-time feel with rolls escalating
|
|
88
|
+
- **Bar 5-8:** Add strings — sparse pad chord, lots of reverb
|
|
89
|
+
- **Bar 9-16:** Full arrangement — 808 pattern + hi-hat rolls + strings harmony
|
|
90
|
+
- **Build:** Hi-hat velocity increases toward drop, 32nd note flurry
|
|
91
|
+
- **Drop:** 808 hits hard, hi-hats sparse, strings cut — maximum impact
|
|
92
|
+
|
|
93
|
+
Hi-hat roll anatomy: Start sparse (quarter notes), build to 8ths, then 16ths, then 32nds on the approach to the drop. This escalation is the genre's primary tension tool.
|
|
94
|
+
|
|
95
|
+
## Example Variations
|
|
96
|
+
|
|
97
|
+
### 1 — Classic 808 Double Hit
|
|
98
|
+
```js
|
|
99
|
+
// 808 hits twice in a bar — beat 1 and the "and" of beat 3
|
|
100
|
+
const subPattern = ["C2", null, null, null, null, null, "C1", null];
|
|
101
|
+
// Second hit is higher (C1 vs C2) — common trap technique
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 2 — Hi-Hat Triplet Roll
|
|
105
|
+
```js
|
|
106
|
+
// Triplet 16ths (12 per bar instead of 16) — more organic feel
|
|
107
|
+
new Tone.Sequence((time, vel) => {
|
|
108
|
+
if (vel) hat.triggerAttackRelease("32n", time, vel);
|
|
109
|
+
}, [0.9, 0.3, 0.6, 0.9, 0.3, 0.6, 0.9, 0.3, 0.6, 0.9, 0.3, 0.9], "8t").start(0);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 3 — Sliding 808 Pitch (Tone.js portamento)
|
|
113
|
+
```js
|
|
114
|
+
// Use portamento on MonoSynth for more dramatic pitch slide
|
|
115
|
+
const slideSub = new Tone.MonoSynth({
|
|
116
|
+
oscillator: { type: "sine" },
|
|
117
|
+
portamento: 0.4, // glide time in seconds
|
|
118
|
+
envelope: { attack: 0.001, decay: 1.2, sustain: 0, release: 0.5 },
|
|
119
|
+
filter: { type: "lowpass", frequency: 200 }
|
|
120
|
+
});
|
|
121
|
+
slideSub.triggerAttackRelease("C3", "2n", time);
|
|
122
|
+
// Then after portamento, note has already slid down
|
|
123
|
+
```
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>TuneFrames — Trap</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":140,"duration":"12s"}</div>
|
|
11
|
+
|
|
12
|
+
<script>
|
|
13
|
+
async function main() {
|
|
14
|
+
await Tone.start();
|
|
15
|
+
Tone.Transport.bpm.value = 140;
|
|
16
|
+
|
|
17
|
+
// --- MASTER BUS ---
|
|
18
|
+
const masterLimiter = new Tone.Limiter(-1).toDestination();
|
|
19
|
+
const masterGain = new Tone.Gain(0.85).connect(masterLimiter);
|
|
20
|
+
|
|
21
|
+
// --- 808 SUB — MembraneSynth with long pitchDecay for the signature glide ---
|
|
22
|
+
const subSaturate = new Tone.Distortion({ distortion: 0.12, wet: 0.25 }).connect(masterGain);
|
|
23
|
+
const subGain = new Tone.Gain(1.1).connect(subSaturate);
|
|
24
|
+
const sub = new Tone.MembraneSynth({
|
|
25
|
+
pitchDecay: 0.48, // the 808 "fall" — longer = more dramatic
|
|
26
|
+
octaves: 3, // pitch range of the glide
|
|
27
|
+
envelope: { attack: 0.001, decay: 1.1, sustain: 0, release: 0.5 }
|
|
28
|
+
}).connect(subGain);
|
|
29
|
+
|
|
30
|
+
// --- SNARE — punchy white noise, dry ---
|
|
31
|
+
const snareVerb = new Tone.Reverb({ decay: 0.7, wet: 0.18 }).connect(masterGain);
|
|
32
|
+
const snareGain = new Tone.Gain(0.7).connect(snareVerb);
|
|
33
|
+
const snare = new Tone.NoiseSynth({
|
|
34
|
+
noise: { type: "white" },
|
|
35
|
+
envelope: { attack: 0.001, decay: 0.16, sustain: 0, release: 0.05 }
|
|
36
|
+
}).connect(snareGain);
|
|
37
|
+
|
|
38
|
+
// --- HI-HAT — very tight, high frequency ---
|
|
39
|
+
const hatGain = new Tone.Gain(0.42).connect(masterGain);
|
|
40
|
+
const hat = new Tone.MetalSynth({
|
|
41
|
+
frequency: 820,
|
|
42
|
+
envelope: { attack: 0.001, decay: 0.028, release: 0.008 },
|
|
43
|
+
harmonicity: 5.1,
|
|
44
|
+
modulationIndex: 32,
|
|
45
|
+
resonance: 5200,
|
|
46
|
+
octaves: 2
|
|
47
|
+
}).connect(hatGain);
|
|
48
|
+
|
|
49
|
+
// --- STRINGS — atmospheric PolySynth, heavy reverb ---
|
|
50
|
+
const stringsVerb = new Tone.Reverb({ decay: 4.5, wet: 0.65 }).connect(masterGain);
|
|
51
|
+
const stringsGain = new Tone.Gain(0.5).connect(stringsVerb);
|
|
52
|
+
const strings = new Tone.PolySynth(Tone.Synth, {
|
|
53
|
+
oscillator: { type: "sawtooth4" },
|
|
54
|
+
envelope: { attack: 0.5, decay: 1.2, sustain: 0.65, release: 2.0 },
|
|
55
|
+
volume: -2
|
|
56
|
+
}).connect(stringsGain);
|
|
57
|
+
|
|
58
|
+
// --- 808 PATTERN — half-time feel, beat 1 of every bar ---
|
|
59
|
+
// Pattern: hit on beat 1, ghost on beat 3.5
|
|
60
|
+
const subPattern = new Tone.Sequence((time, note) => {
|
|
61
|
+
if (note === "C2") {
|
|
62
|
+
sub.triggerAttackRelease("C2", "2n", time);
|
|
63
|
+
} else if (note === "G1") {
|
|
64
|
+
sub.triggerAttackRelease("G1", "4n", time);
|
|
65
|
+
}
|
|
66
|
+
}, ["C2", null, null, null, null, null, "G1", null,
|
|
67
|
+
"C2", null, null, null, null, null, null, null], "16n");
|
|
68
|
+
subPattern.start(0);
|
|
69
|
+
|
|
70
|
+
// --- SNARE PATTERN — half-time: beat 3 of the bar (step 8 of 16) ---
|
|
71
|
+
const snareSeq = new Tone.Sequence((time, val) => {
|
|
72
|
+
if (val) snare.triggerAttackRelease("16n", time);
|
|
73
|
+
}, [null, null, null, null, null, null, null, null,
|
|
74
|
+
1, null, null, null, null, null, null, null], "16n");
|
|
75
|
+
snareSeq.start(0);
|
|
76
|
+
|
|
77
|
+
// --- HI-HAT ROLLS — the trap signature ---
|
|
78
|
+
// 32nd-note grid: velocity variation creates rolls and accents
|
|
79
|
+
// Pattern evolves every bar: starts sparse, builds to dense rolls
|
|
80
|
+
let hatStep = 0;
|
|
81
|
+
|
|
82
|
+
// Bar 1-2 velocity map: sparse 16th pattern (every 2 32nds)
|
|
83
|
+
const hatBar1 = [
|
|
84
|
+
0.8, 0, 0.4, 0, 0.8, 0, 0.4, 0,
|
|
85
|
+
0.8, 0, 0.4, 0, 0.8, 0.3, 0.9, 0.3
|
|
86
|
+
];
|
|
87
|
+
// Bar 3-4: 16th rolls with triplet accent flurry
|
|
88
|
+
const hatBar2 = [
|
|
89
|
+
0.8, 0.3, 0.6, 0.2, 0.8, 0.3, 0.5, 0.2,
|
|
90
|
+
0.7, 0.3, 0.6, 0.2, 0.9, 0.5, 0.8, 0.5
|
|
91
|
+
];
|
|
92
|
+
// Bar 5-6: dense 32nd roll building to drop
|
|
93
|
+
const hatBar3 = [
|
|
94
|
+
0.6, 0.3, 0.7, 0.3, 0.6, 0.4, 0.8, 0.3,
|
|
95
|
+
0.6, 0.4, 0.7, 0.5, 0.8, 0.6, 0.9, 0.7
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
// Merge all bars into one 96-step (6-bar) loop for 32nds at 140bpm
|
|
99
|
+
const fullHatPattern = [...hatBar1, ...hatBar1, ...hatBar2, ...hatBar2, ...hatBar3, ...hatBar3];
|
|
100
|
+
|
|
101
|
+
const hatSeq = new Tone.Sequence((time, vel) => {
|
|
102
|
+
if (vel > 0) hat.triggerAttackRelease("32n", time, vel);
|
|
103
|
+
}, fullHatPattern, "32n");
|
|
104
|
+
hatSeq.start(0);
|
|
105
|
+
|
|
106
|
+
// --- STRINGS — atmospheric minor chord, enters at bar 3 ---
|
|
107
|
+
// Cm: C3-Eb3-G3 + high B4 tension note
|
|
108
|
+
const stringNotes = ["C3", "Eb3", "G3", "Bb3"];
|
|
109
|
+
strings.triggerAttackRelease(stringNotes, "2m", "2m");
|
|
110
|
+
// Resolution chord at bar 5
|
|
111
|
+
strings.triggerAttackRelease(["Ab2", "C3", "Eb3", "G3"], "2m", "4m");
|
|
112
|
+
strings.triggerAttackRelease(["C3", "Eb3", "G3", "Bb3"], "2m", "6m");
|
|
113
|
+
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
main();
|
|
117
|
+
</script>
|
|
118
|
+
</body>
|
|
119
|
+
</html>
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>TuneFrames — Render Retest</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":120,"duration":"10s"}</div>
|
|
11
|
+
|
|
12
|
+
<script>
|
|
13
|
+
async function main() {
|
|
14
|
+
await Tone.start();
|
|
15
|
+
|
|
16
|
+
// 120 BPM: quarter note = 0.5s, bar = 2.0s, 10s = 5 bars exactly.
|
|
17
|
+
// All note times are absolute seconds from 0. No Tone.now(), no Transport.start().
|
|
18
|
+
const beat = 0.5;
|
|
19
|
+
const bar = 2.0;
|
|
20
|
+
|
|
21
|
+
// --- MASTER BUS ---
|
|
22
|
+
const limiter = new Tone.Limiter(-1).toDestination();
|
|
23
|
+
const masterGain = new Tone.Gain(0.82).connect(limiter);
|
|
24
|
+
|
|
25
|
+
// --- REVERB connected to master ---
|
|
26
|
+
const reverb = new Tone.Reverb({ decay: 2.5, wet: 0.30 }).connect(masterGain);
|
|
27
|
+
|
|
28
|
+
// --- DELAY connected to master (parallel with reverb, not chained) ---
|
|
29
|
+
const delay = new Tone.FeedbackDelay(beat * 0.5, 0.25).connect(masterGain);
|
|
30
|
+
|
|
31
|
+
// --- KICK (MembraneSynth) ---
|
|
32
|
+
const kickGain = new Tone.Gain(0.88).connect(masterGain);
|
|
33
|
+
const kick = new Tone.MembraneSynth({
|
|
34
|
+
pitchDecay: 0.06,
|
|
35
|
+
octaves: 9,
|
|
36
|
+
envelope: { attack: 0.001, decay: 0.34, sustain: 0, release: 0.1 }
|
|
37
|
+
}).connect(kickGain);
|
|
38
|
+
|
|
39
|
+
// --- HI-HAT (MetalSynth) ---
|
|
40
|
+
// MetalSynth.triggerAttackRelease(duration, time, velocity) -- no pitch arg
|
|
41
|
+
const hatGain = new Tone.Gain(0.36).connect(masterGain);
|
|
42
|
+
const hat = new Tone.MetalSynth({
|
|
43
|
+
frequency: 440,
|
|
44
|
+
envelope: { attack: 0.001, decay: 0.05, release: 0.01 },
|
|
45
|
+
harmonicity: 5.1,
|
|
46
|
+
modulationIndex: 32,
|
|
47
|
+
resonance: 4000,
|
|
48
|
+
octaves: 1.5
|
|
49
|
+
}).connect(hatGain);
|
|
50
|
+
|
|
51
|
+
// --- CHORDS (PolySynth) through reverb ---
|
|
52
|
+
const padGain = new Tone.Gain(0.60).connect(reverb);
|
|
53
|
+
const pad = new Tone.PolySynth(Tone.Synth, {
|
|
54
|
+
oscillator: { type: "sine" },
|
|
55
|
+
envelope: { attack: 0.40, decay: 0.5, sustain: 0.60, release: 1.8 },
|
|
56
|
+
volume: -8
|
|
57
|
+
}).connect(padGain);
|
|
58
|
+
|
|
59
|
+
// --- MELODY (Synth) through delay + reverb ---
|
|
60
|
+
const melGain = new Tone.Gain(0.72).connect(delay);
|
|
61
|
+
melGain.connect(reverb);
|
|
62
|
+
const mel = new Tone.Synth({
|
|
63
|
+
oscillator: { type: "triangle" },
|
|
64
|
+
envelope: { attack: 0.012, decay: 0.20, sustain: 0.45, release: 0.65 },
|
|
65
|
+
volume: -5
|
|
66
|
+
}).connect(melGain);
|
|
67
|
+
|
|
68
|
+
// --- KICK: 4-on-the-floor, every 0.5s, 20 hits, last at 9.5s ---
|
|
69
|
+
for (let i = 0; i < 20; i++) {
|
|
70
|
+
kick.triggerAttackRelease("C1", "8n", i * beat);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// --- HI-HAT: 8th notes every 0.25s, on-beat louder, 40 hits, last at 9.75s ---
|
|
74
|
+
for (let i = 0; i < 40; i++) {
|
|
75
|
+
const vel = (i % 2 === 0) ? 0.60 : 0.35;
|
|
76
|
+
hat.triggerAttackRelease("16n", i * (beat * 0.5), vel);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// --- CHORDS: Dm -> Bb -> F -> C, one bar each, enter at bar 2 (2s) ---
|
|
80
|
+
// "1n" = whole note = 2.0s at 120 BPM = exactly one bar
|
|
81
|
+
const progression = [
|
|
82
|
+
{ notes: ["D2","F2","A2"], time: bar }, // Dm @ 2s
|
|
83
|
+
{ notes: ["Bb2","D3","F3"], time: bar * 2 }, // Bb @ 4s
|
|
84
|
+
{ notes: ["F2","A2","C3"], time: bar * 3 }, // F @ 6s
|
|
85
|
+
{ notes: ["C3","E3","G3"], time: bar * 4 }, // C @ 8s
|
|
86
|
+
];
|
|
87
|
+
progression.forEach(({ notes, time }) => {
|
|
88
|
+
pad.triggerAttackRelease(notes, "1n", time);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// --- MELODY: D minor pentatonic, enters at bar 3 (4s), last note at 9.0s ---
|
|
92
|
+
const melody = [
|
|
93
|
+
{ note: "F4", dur: "4n", t: 4.0 },
|
|
94
|
+
{ note: "E4", dur: "8n", t: 4.5 },
|
|
95
|
+
{ note: "D4", dur: "4n", t: 5.0 },
|
|
96
|
+
{ note: "A4", dur: "4n", t: 5.5 },
|
|
97
|
+
{ note: "G4", dur: "2n", t: 6.0 },
|
|
98
|
+
{ note: "F4", dur: "4n", t: 7.0 },
|
|
99
|
+
{ note: "E4", dur: "8n", t: 7.5 },
|
|
100
|
+
{ note: "D4", dur: "4n", t: 8.0 },
|
|
101
|
+
{ note: "F4", dur: "8n", t: 8.5 },
|
|
102
|
+
{ note: "A4", dur: "4n", t: 9.0 },
|
|
103
|
+
];
|
|
104
|
+
melody.forEach(({ note, dur, t }) => {
|
|
105
|
+
mel.triggerAttackRelease(note, dur, t);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// NOTE: do NOT call Tone.Transport.start() here.
|
|
109
|
+
// Tone.Offline manages the transport internally.
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
main();
|
|
113
|
+
</script>
|
|
114
|
+
</body>
|
|
115
|
+
</html>
|