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,185 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>TuneFrames — Boss Battle</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":165,"duration":"12s","genre":"boss-battle","key":"D minor"}</div>
|
|
11
|
+
|
|
12
|
+
<script>
|
|
13
|
+
async function main() {
|
|
14
|
+
await Tone.start();
|
|
15
|
+
Tone.Transport.bpm.value = 165;
|
|
16
|
+
|
|
17
|
+
// --- MASTER BUS ---
|
|
18
|
+
const masterLimiter = new Tone.Limiter(-1).toDestination();
|
|
19
|
+
const masterGain = new Tone.Gain(0.8).connect(masterLimiter);
|
|
20
|
+
|
|
21
|
+
// --- BASS OSTINATO — driving 16th notes, sawtooth growl ---
|
|
22
|
+
const bassVerb = new Tone.Reverb({ decay: 0.4, wet: 0.1 }).connect(masterGain);
|
|
23
|
+
const bassGain = new Tone.Gain(0.75).connect(bassVerb);
|
|
24
|
+
const bass = new Tone.MonoSynth({
|
|
25
|
+
oscillator: { type: "sawtooth" },
|
|
26
|
+
filter: { Q: 3, type: "lowpass", rolloff: -24 },
|
|
27
|
+
filterEnvelope: {
|
|
28
|
+
attack: 0.001, decay: 0.1, sustain: 0.5, release: 0.2,
|
|
29
|
+
baseFrequency: 80, octaves: 2
|
|
30
|
+
},
|
|
31
|
+
envelope: { attack: 0.001, decay: 0.1, sustain: 0.7, release: 0.1 },
|
|
32
|
+
volume: -2
|
|
33
|
+
}).connect(bassGain);
|
|
34
|
+
|
|
35
|
+
// --- POWER CHORD STABS — square wave, very punchy ---
|
|
36
|
+
const stabReverb = new Tone.Reverb({ decay: 0.6, wet: 0.22 }).connect(masterGain);
|
|
37
|
+
const stabGain = new Tone.Gain(0.5).connect(stabReverb);
|
|
38
|
+
const stabs = new Tone.PolySynth(Tone.Synth, {
|
|
39
|
+
oscillator: { type: "square" },
|
|
40
|
+
envelope: { attack: 0.001, decay: 0.12, sustain: 0.0, release: 0.08 },
|
|
41
|
+
volume: 0
|
|
42
|
+
}).connect(stabGain);
|
|
43
|
+
|
|
44
|
+
// --- BRASS MELODY — AMSynth: sawtooth + sine AM = heroic blat ---
|
|
45
|
+
const brassVerb = new Tone.Reverb({ decay: 1.5, wet: 0.3 }).connect(masterGain);
|
|
46
|
+
const brassGain = new Tone.Gain(0.55).connect(brassVerb);
|
|
47
|
+
const brass = new Tone.AMSynth({
|
|
48
|
+
oscillator: { type: "sawtooth" },
|
|
49
|
+
envelope: { attack: 0.04, decay: 0.1, sustain: 0.85, release: 0.4 },
|
|
50
|
+
modulation: { type: "sine" },
|
|
51
|
+
modulationEnvelope: { attack: 0.5, decay: 0, sustain: 1, release: 0.5 },
|
|
52
|
+
harmonicity: 1,
|
|
53
|
+
volume: 0
|
|
54
|
+
}).connect(brassGain);
|
|
55
|
+
|
|
56
|
+
// --- TIMPANI — low-tuned MembraneSynth, long decay ---
|
|
57
|
+
const timpsVerb = new Tone.Reverb({ decay: 1.2, wet: 0.35 }).connect(masterGain);
|
|
58
|
+
const timpsGain = new Tone.Gain(0.65).connect(timpsVerb);
|
|
59
|
+
const timps = new Tone.MembraneSynth({
|
|
60
|
+
pitchDecay: 0.2,
|
|
61
|
+
octaves: 4,
|
|
62
|
+
envelope: { attack: 0.001, decay: 0.6, sustain: 0, release: 0.3 }
|
|
63
|
+
}).connect(timpsGain);
|
|
64
|
+
|
|
65
|
+
// --- KICK — hard, driving ---
|
|
66
|
+
const kickGain = new Tone.Gain(0.9).connect(masterGain);
|
|
67
|
+
const kick = new Tone.MembraneSynth({
|
|
68
|
+
pitchDecay: 0.06,
|
|
69
|
+
octaves: 8,
|
|
70
|
+
envelope: { attack: 0.001, decay: 0.28, sustain: 0, release: 0.08 }
|
|
71
|
+
}).connect(kickGain);
|
|
72
|
+
|
|
73
|
+
// --- SNARE — sharp crack ---
|
|
74
|
+
const snareVerb = new Tone.Reverb({ decay: 0.5, wet: 0.3 }).connect(masterGain);
|
|
75
|
+
const snareGain = new Tone.Gain(0.48).connect(snareVerb);
|
|
76
|
+
const snare = new Tone.NoiseSynth({
|
|
77
|
+
noise: { type: "white" },
|
|
78
|
+
envelope: { attack: 0.001, decay: 0.1, sustain: 0, release: 0.04 }
|
|
79
|
+
}).connect(snareGain);
|
|
80
|
+
|
|
81
|
+
// --- COUNTER ARPEGGIO — fast ascending D minor runs ---
|
|
82
|
+
const arpVerb = new Tone.Reverb({ decay: 0.8, wet: 0.25 }).connect(masterGain);
|
|
83
|
+
const arpGain = new Tone.Gain(0.3).connect(arpVerb);
|
|
84
|
+
const arpSynth = new Tone.PolySynth(Tone.Synth, {
|
|
85
|
+
oscillator: { type: "sawtooth4" },
|
|
86
|
+
envelope: { attack: 0.001, decay: 0.08, sustain: 0, release: 0.1 },
|
|
87
|
+
volume: 0
|
|
88
|
+
}).connect(arpGain);
|
|
89
|
+
|
|
90
|
+
// --- PATTERNS ---
|
|
91
|
+
|
|
92
|
+
// Bass ostinato: 16th-note D minor figure with neighbor movement (from bar 1)
|
|
93
|
+
const bassLine = [
|
|
94
|
+
"D2","D2","F2","D2", "G2","D2","F2","E2",
|
|
95
|
+
"D2","D2","F2","D2", "G2","F2","Eb2","D2"
|
|
96
|
+
];
|
|
97
|
+
let bassStep = 0;
|
|
98
|
+
const bassSeq = new Tone.Sequence((time) => {
|
|
99
|
+
bass.triggerAttackRelease(bassLine[bassStep % bassLine.length], "16n", time);
|
|
100
|
+
bassStep++;
|
|
101
|
+
}, new Array(16).fill(1), "16n");
|
|
102
|
+
bassSeq.start(0);
|
|
103
|
+
|
|
104
|
+
// Timpani: D1 on bar 1, A1 on beat 3, D1 on bar 3 — enters bar 2
|
|
105
|
+
// Use bars:beats:sixteenths (0-indexed) to avoid mixed-notation parsing errors.
|
|
106
|
+
const timpsHits = [
|
|
107
|
+
{ time: "1:0:0", note: "D1" },
|
|
108
|
+
{ time: "1:2:0", note: "A1" },
|
|
109
|
+
{ time: "2:0:0", note: "D1" },
|
|
110
|
+
{ time: "2:2:0", note: "A1" },
|
|
111
|
+
{ time: "3:0:0", note: "D1" },
|
|
112
|
+
{ time: "3:2:0", note: "A1" },
|
|
113
|
+
{ time: "4:0:0", note: "D1" },
|
|
114
|
+
{ time: "5:0:0", note: "D1" },
|
|
115
|
+
{ time: "6:0:0", note: "D1" },
|
|
116
|
+
{ time: "7:0:0", note: "D1" },
|
|
117
|
+
];
|
|
118
|
+
timpsHits.forEach(({ time, note }) => {
|
|
119
|
+
timps.triggerAttackRelease(note, "4n", time);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Power chord stabs: cycling Dm–F–G–Bb on offbeats, enters bar 3
|
|
123
|
+
const stabChords = [["D3","A3"], ["F3","C4"], ["G3","D4"], ["Bb3","F4"]];
|
|
124
|
+
let stabStep = 0;
|
|
125
|
+
const stabSeq = new Tone.Sequence((time, val) => {
|
|
126
|
+
if (val) {
|
|
127
|
+
stabs.triggerAttackRelease(stabChords[stabStep % stabChords.length], "16n", time);
|
|
128
|
+
stabStep++;
|
|
129
|
+
}
|
|
130
|
+
}, [null,null,1,null,null,null,null,null,null,null,1,null,null,null,null,null], "16n");
|
|
131
|
+
stabSeq.start("2m");
|
|
132
|
+
|
|
133
|
+
// Kick: driving 8th notes, enters bar 3
|
|
134
|
+
const kickSeq = new Tone.Sequence((time, val) => {
|
|
135
|
+
if (val) kick.triggerAttackRelease("C1", "8n", time);
|
|
136
|
+
}, ["C1",null,null,null,"C1",null,null,null,"C1",null,null,null,"C1",null,null,null], "16n");
|
|
137
|
+
kickSeq.start("2m");
|
|
138
|
+
|
|
139
|
+
// Snare: beats 2 and 4, enters bar 3
|
|
140
|
+
const snareSeq = new Tone.Sequence((time, val) => {
|
|
141
|
+
if (val) snare.triggerAttackRelease("8n", time);
|
|
142
|
+
}, [null,null,null,null,1,null,null,null,null,null,null,null,1,null,null,null], "16n");
|
|
143
|
+
snareSeq.start("2m");
|
|
144
|
+
|
|
145
|
+
// Counter arpeggio: D minor ascending 16th runs (D F A C, repeat), enters bar 5
|
|
146
|
+
const arpNotes = ["D4","F4","A4","C5","A4","F4","D4","F4",
|
|
147
|
+
"D4","F4","A4","C5","D5","C5","A4","F4"];
|
|
148
|
+
let arpStep = 0;
|
|
149
|
+
const arpSeq = new Tone.Sequence((time) => {
|
|
150
|
+
arpSynth.triggerAttackRelease(arpNotes[arpStep % arpNotes.length], "16n", time);
|
|
151
|
+
arpStep++;
|
|
152
|
+
}, new Array(16).fill(1), "16n");
|
|
153
|
+
arpSeq.start("4m");
|
|
154
|
+
|
|
155
|
+
// Heroic brass melody: D minor scale, ascending then descending, enters bar 5
|
|
156
|
+
const melodyNotes = [
|
|
157
|
+
// Bar 4: D ascending triad + 7th
|
|
158
|
+
{ time: "4:0:0", note: "D4", dur: "4n" },
|
|
159
|
+
{ time: "4:1:0", note: "F4", dur: "4n" },
|
|
160
|
+
{ time: "4:2:0", note: "A4", dur: "4n" },
|
|
161
|
+
{ time: "4:3:0", note: "C5", dur: "4n" },
|
|
162
|
+
// Bar 5: descend
|
|
163
|
+
{ time: "5:0:0", note: "Bb4", dur: "4n" },
|
|
164
|
+
{ time: "5:1:0", note: "A4", dur: "4n" },
|
|
165
|
+
{ time: "5:2:0", note: "G4", dur: "2n" },
|
|
166
|
+
// Bar 6: climb again, higher
|
|
167
|
+
{ time: "6:0:0", note: "A4", dur: "4n" },
|
|
168
|
+
{ time: "6:1:0", note: "C5", dur: "4n" },
|
|
169
|
+
{ time: "6:2:0", note: "D5", dur: "4n" },
|
|
170
|
+
{ time: "6:3:0", note: "F5", dur: "4n" },
|
|
171
|
+
// Bar 7: final resolve
|
|
172
|
+
{ time: "7:0:0", note: "Eb5", dur: "4n" },
|
|
173
|
+
{ time: "7:1:0", note: "D5", dur: "4n" },
|
|
174
|
+
{ time: "7:2:0", note: "A4", dur: "2n" },
|
|
175
|
+
];
|
|
176
|
+
melodyNotes.forEach(({ time, note, dur }) => {
|
|
177
|
+
brass.triggerAttackRelease(note, dur, time);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
main();
|
|
183
|
+
</script>
|
|
184
|
+
</body>
|
|
185
|
+
</html>
|
|
Binary file
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: audio-chillwave
|
|
3
|
+
description: Chillwave / Retrowave — hazy 80s nostalgia, dreamy arpeggio over gated drum machine, warm chorus + delay, minor chords
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TuneFrames — Chillwave / Retrowave
|
|
7
|
+
|
|
8
|
+
## Genre Profile
|
|
9
|
+
- BPM range: 90-110 (laid back, never rushed)
|
|
10
|
+
- Key characteristics: minor key arpeggios, 80s drum machine pattern (four-on-the-floor kick, clap on 2/4, 16th hi-hats), chorus on synths, slight ping-pong delay, tape-saturated warmth
|
|
11
|
+
- Typical instruments: PolySynth (sawtooth with chorus), NoiseSynth hi-hats, MembraneSynth kick, FMSynth for clap texture
|
|
12
|
+
- Mood: nostalgic, washed-out, sun-bleached, driving at 2am
|
|
13
|
+
|
|
14
|
+
## Core Pattern
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
// Chillwave core: minor arpeggio over 80s drum machine
|
|
18
|
+
// Key = A minor, BPM = 98
|
|
19
|
+
Tone.Transport.bpm.value = 98;
|
|
20
|
+
|
|
21
|
+
// Warm chorus on all melodic synths
|
|
22
|
+
const chorus = new Tone.Chorus({ frequency: 1.5, delayTime: 3.5, depth: 0.7, wet: 0.6 });
|
|
23
|
+
chorus.start(); // Chorus must be started manually
|
|
24
|
+
const reverb = new Tone.Reverb({ decay: 3.5, wet: 0.55 });
|
|
25
|
+
const delay = new Tone.FeedbackDelay({ delayTime: '8n.', feedback: 0.35, wet: 0.4 });
|
|
26
|
+
reverb.toDestination();
|
|
27
|
+
delay.connect(reverb);
|
|
28
|
+
chorus.connect(delay);
|
|
29
|
+
|
|
30
|
+
// Main arpeggio synth — the neon heartbeat
|
|
31
|
+
const arp = new Tone.Synth({
|
|
32
|
+
oscillator: { type: 'sawtooth' },
|
|
33
|
+
envelope: { attack: 0.02, decay: 0.15, sustain: 0.3, release: 0.5 },
|
|
34
|
+
volume: -8,
|
|
35
|
+
});
|
|
36
|
+
arp.connect(chorus);
|
|
37
|
+
|
|
38
|
+
// A minor arpeggio pattern: Am - F - C - G
|
|
39
|
+
const arpNotes = ['A4','C5','E5','A5', 'F4','A4','C5','F5', 'C4','E4','G4','C5', 'G4','B4','D5','G5'];
|
|
40
|
+
let arpIdx = 0;
|
|
41
|
+
new Tone.Sequence((time) => {
|
|
42
|
+
arp.triggerAttackRelease(arpNotes[arpIdx % arpNotes.length], '16n', time);
|
|
43
|
+
arpIdx++;
|
|
44
|
+
}, [null], '16n').start(0);
|
|
45
|
+
// ^ Simpler: use scheduleRepeat or explicit schedule below
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Instrument Configuration
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
// Chorus — the defining warmth of chillwave
|
|
52
|
+
const chorus = new Tone.Chorus({ frequency: 1.5, delayTime: 3.5, depth: 0.7, wet: 0.6 }).start();
|
|
53
|
+
|
|
54
|
+
// Tape-style reverb
|
|
55
|
+
const reverb = new Tone.Reverb({ decay: 3.0, preDelay: 0.02, wet: 0.5 });
|
|
56
|
+
await reverb.generate();
|
|
57
|
+
|
|
58
|
+
// Slapback-style delay (dotted 8th = that 80s bounce)
|
|
59
|
+
const delay = new Tone.FeedbackDelay({ delayTime: '8n.', feedback: 0.3, wet: 0.38 });
|
|
60
|
+
|
|
61
|
+
// Signal chain: arp → chorus → delay → reverb → destination
|
|
62
|
+
reverb.toDestination();
|
|
63
|
+
delay.connect(reverb);
|
|
64
|
+
chorus.connect(delay);
|
|
65
|
+
|
|
66
|
+
// Arpeggio synth — sawtooth for that retrowave shimmer
|
|
67
|
+
const arp = new Tone.Synth({
|
|
68
|
+
oscillator: { type: 'sawtooth' },
|
|
69
|
+
envelope: { attack: 0.01, decay: 0.1, sustain: 0.25, release: 0.4 },
|
|
70
|
+
volume: -6,
|
|
71
|
+
});
|
|
72
|
+
arp.connect(chorus);
|
|
73
|
+
|
|
74
|
+
// Pad behind the arp — PolySynth with longer attack
|
|
75
|
+
const pad = new Tone.PolySynth(Tone.Synth, {
|
|
76
|
+
oscillator: { type: 'triangle' },
|
|
77
|
+
envelope: { attack: 0.6, decay: 0.5, sustain: 0.7, release: 2.0 },
|
|
78
|
+
volume: -16,
|
|
79
|
+
});
|
|
80
|
+
pad.connect(reverb);
|
|
81
|
+
|
|
82
|
+
// Kick — punchy 4-on-the-floor
|
|
83
|
+
const kick = new Tone.MembraneSynth({
|
|
84
|
+
pitchDecay: 0.06, octaves: 8,
|
|
85
|
+
envelope: { attack: 0.001, decay: 0.35, sustain: 0, release: 0.1 },
|
|
86
|
+
volume: -4,
|
|
87
|
+
}).toDestination();
|
|
88
|
+
|
|
89
|
+
// Hi-hats — 16th-note machine pattern, slightly gated
|
|
90
|
+
const hihat = new Tone.NoiseSynth({
|
|
91
|
+
noise: { type: 'white' },
|
|
92
|
+
envelope: { attack: 0.001, decay: 0.04, sustain: 0, release: 0.01 },
|
|
93
|
+
volume: -16,
|
|
94
|
+
}).toDestination();
|
|
95
|
+
|
|
96
|
+
// Clap — pink noise, sits on 2 and 4
|
|
97
|
+
const clap = new Tone.NoiseSynth({
|
|
98
|
+
noise: { type: 'pink' },
|
|
99
|
+
envelope: { attack: 0.001, decay: 0.12, sustain: 0, release: 0.08 },
|
|
100
|
+
volume: -10,
|
|
101
|
+
}).toDestination();
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Composition Structure
|
|
105
|
+
|
|
106
|
+
1. **Intro (0-2s):** Arp enters alone — no drums yet. Establishes the hazy tone.
|
|
107
|
+
2. **Drop-in (2-4s):** Kick and hi-hat join. Clap enters on 2 and 4.
|
|
108
|
+
3. **Full loop (4-12s):** All elements running. Pad adds chord warmth behind the arp.
|
|
109
|
+
4. **Variation (optional):** Drop the kick for 2 bars, let the arp breathe.
|
|
110
|
+
|
|
111
|
+
**Chord cycle (4 chords, each 1 bar):**
|
|
112
|
+
- Am → F → C → G (classic 80s nostalgia loop)
|
|
113
|
+
- Em → C → G → D (brighter alternative)
|
|
114
|
+
- Dm → Bb → F → C (darker, more melancholic)
|
|
115
|
+
|
|
116
|
+
## Example Variations
|
|
117
|
+
|
|
118
|
+
### Variation 1: Slower / dreamier
|
|
119
|
+
```js
|
|
120
|
+
Tone.Transport.bpm.value = 88;
|
|
121
|
+
// Longer attack on arp: 0.04
|
|
122
|
+
// Chorus depth: 0.85, wet: 0.7
|
|
123
|
+
// Reverb decay: 5.0 — more smear
|
|
124
|
+
// Drop hi-hats to 8th notes instead of 16th
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Variation 2: More synthwave, less chill
|
|
128
|
+
```js
|
|
129
|
+
Tone.Transport.bpm.value = 108;
|
|
130
|
+
// Square oscillator instead of sawtooth
|
|
131
|
+
// Add a bass line: MonoSynth root notes, sawtooth, lowpass filter at 400hz
|
|
132
|
+
// Kick volume up: -2
|
|
133
|
+
// Less reverb: 0.3 wet
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Variation 3: Pure pads (no drums)
|
|
137
|
+
```js
|
|
138
|
+
// Just the chord pad + arp, massive chorus + reverb
|
|
139
|
+
// Pad attack: 2.0s, release: 4.0s
|
|
140
|
+
// This is the most "chill" direction — remove percussion entirely
|
|
141
|
+
// Use a second delayed arp an octave higher for shimmer
|
|
142
|
+
```
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
<div id="tuneframes" style="display:none">{"bpm":98,"duration":"12s"}</div>
|
|
2
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
|
|
3
|
+
<script>
|
|
4
|
+
// TuneFrames — Chillwave / Retrowave
|
|
5
|
+
// A minor arpeggio over 80s drum machine, warm chorus + delay
|
|
6
|
+
// Nostalgic, hazy, neon-at-midnight
|
|
7
|
+
|
|
8
|
+
async function main() {
|
|
9
|
+
await Tone.start();
|
|
10
|
+
|
|
11
|
+
Tone.Transport.bpm.value = 98;
|
|
12
|
+
|
|
13
|
+
// ── Effects chain ──────────────────────────────────────────────────────────
|
|
14
|
+
const reverb = new Tone.Reverb({ decay: 3.5, preDelay: 0.02, wet: 0.5 });
|
|
15
|
+
await reverb.generate();
|
|
16
|
+
reverb.toDestination();
|
|
17
|
+
|
|
18
|
+
// Dotted-8th delay = the defining retrowave slap
|
|
19
|
+
const delay = new Tone.FeedbackDelay({ delayTime: '8n.', feedback: 0.32, wet: 0.38 });
|
|
20
|
+
delay.connect(reverb);
|
|
21
|
+
|
|
22
|
+
// Chorus — smears everything into warm tape haze
|
|
23
|
+
const chorus = new Tone.Chorus({ frequency: 1.4, delayTime: 3.5, depth: 0.72, wet: 0.62 }).start();
|
|
24
|
+
chorus.connect(delay);
|
|
25
|
+
|
|
26
|
+
// ── Arpeggio synth — sawtooth for shimmer ─────────────────────────────────
|
|
27
|
+
const arp = new Tone.Synth({
|
|
28
|
+
oscillator: { type: 'sawtooth' },
|
|
29
|
+
envelope: { attack: 0.01, decay: 0.12, sustain: 0.28, release: 0.45 },
|
|
30
|
+
volume: -6,
|
|
31
|
+
});
|
|
32
|
+
arp.connect(chorus);
|
|
33
|
+
|
|
34
|
+
// ── Background pad — PolySynth triangle, long attack ─────────────────────
|
|
35
|
+
const pad = new Tone.PolySynth(Tone.Synth, {
|
|
36
|
+
oscillator: { type: 'triangle' },
|
|
37
|
+
envelope: { attack: 0.8, decay: 0.5, sustain: 0.75, release: 2.5 },
|
|
38
|
+
volume: -18,
|
|
39
|
+
});
|
|
40
|
+
pad.connect(reverb);
|
|
41
|
+
|
|
42
|
+
// ── Kick — four-on-the-floor, enters at 2s ────────────────────────────────
|
|
43
|
+
const kick = new Tone.MembraneSynth({
|
|
44
|
+
pitchDecay: 0.06, octaves: 8,
|
|
45
|
+
envelope: { attack: 0.001, decay: 0.38, sustain: 0, release: 0.1 },
|
|
46
|
+
volume: -4,
|
|
47
|
+
}).toDestination();
|
|
48
|
+
|
|
49
|
+
// ── Hi-hat — 16th notes, slightly gated ───────────────────────────────────
|
|
50
|
+
const hihat = new Tone.NoiseSynth({
|
|
51
|
+
noise: { type: 'white' },
|
|
52
|
+
envelope: { attack: 0.001, decay: 0.04, sustain: 0, release: 0.01 },
|
|
53
|
+
volume: -17,
|
|
54
|
+
}).toDestination();
|
|
55
|
+
|
|
56
|
+
// ── Clap — sits on 2 and 4 ───────────────────────────────────────────────
|
|
57
|
+
const clap = new Tone.NoiseSynth({
|
|
58
|
+
noise: { type: 'pink' },
|
|
59
|
+
envelope: { attack: 0.001, decay: 0.13, sustain: 0, release: 0.08 },
|
|
60
|
+
volume: -10,
|
|
61
|
+
}).toDestination();
|
|
62
|
+
|
|
63
|
+
// ── Schedule arpeggio ─────────────────────────────────────────────────────
|
|
64
|
+
// A minor → F → C → G, 16th-note arpeggio pattern
|
|
65
|
+
// At 98 BPM, one 16th note = 60/98/4 ≈ 0.153s
|
|
66
|
+
const sixteenth = 60 / 98 / 4;
|
|
67
|
+
const arpNotes = [
|
|
68
|
+
// Am: A4 C5 E5 A5
|
|
69
|
+
'A4','C5','E5','A5',
|
|
70
|
+
// F: F4 A4 C5 F5
|
|
71
|
+
'F4','A4','C5','F5',
|
|
72
|
+
// C: C4 E4 G4 C5
|
|
73
|
+
'C4','E4','G4','C5',
|
|
74
|
+
// G: G4 B4 D5 G5
|
|
75
|
+
'G4','B4','D5','G5',
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
// Four passes of the 16-note arpeggio
|
|
79
|
+
for (let pass = 0; pass < 4; pass++) {
|
|
80
|
+
const passStart = pass * arpNotes.length * sixteenth;
|
|
81
|
+
arpNotes.forEach((note, i) => {
|
|
82
|
+
arp.triggerAttackRelease(note, '16n', passStart + i * sixteenth);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ── Pad chords — one per bar (Am, F, C, G, repeating) ────────────────────
|
|
87
|
+
const beat = 60 / 98;
|
|
88
|
+
const padChords = [
|
|
89
|
+
{ notes: ['A3','C4','E4'], t: 0.8 },
|
|
90
|
+
{ notes: ['F3','A3','C4'], t: 0.8 + beat * 4 },
|
|
91
|
+
{ notes: ['C3','E3','G3'], t: 0.8 + beat * 8 },
|
|
92
|
+
{ notes: ['G2','B2','D3'], t: 0.8 + beat * 12 },
|
|
93
|
+
];
|
|
94
|
+
padChords.forEach(({ notes, t }) => {
|
|
95
|
+
pad.triggerAttackRelease(notes, '2n', t);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// ── Drums — enter at 2s ───────────────────────────────────────────────────
|
|
99
|
+
const drumStart = 2.0;
|
|
100
|
+
|
|
101
|
+
// Four-on-the-floor kick
|
|
102
|
+
for (let i = 0; i < 10; i++) {
|
|
103
|
+
const t = drumStart + i * beat;
|
|
104
|
+
if (t < 12) {
|
|
105
|
+
kick.triggerAttackRelease('C1', '8n', t);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// 16th hi-hats
|
|
110
|
+
for (let i = 0; i < 40; i++) {
|
|
111
|
+
const t = drumStart + i * sixteenth;
|
|
112
|
+
if (t < 12) {
|
|
113
|
+
hihat.triggerAttackRelease('16n', t);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Clap on 2 and 4 (every other beat, starting on beat 2)
|
|
118
|
+
for (let bar = 0; bar < 3; bar++) {
|
|
119
|
+
[beat, beat * 3].forEach((offset) => {
|
|
120
|
+
const t = drumStart + bar * beat * 4 + offset;
|
|
121
|
+
if (t < 12) {
|
|
122
|
+
clap.triggerAttackRelease('8n', t);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
window.renderComposition = async function(wavPath) {
|
|
129
|
+
let attempts = 0;
|
|
130
|
+
while (typeof Tone === 'undefined' && attempts < 50) {
|
|
131
|
+
await new Promise(r => setTimeout(r, 100));
|
|
132
|
+
attempts++;
|
|
133
|
+
}
|
|
134
|
+
if (typeof Tone === 'undefined') throw new Error('Tone not loaded');
|
|
135
|
+
|
|
136
|
+
const meta = JSON.parse(document.getElementById('tuneframes').textContent);
|
|
137
|
+
const durationSec = parseFloat(meta.duration) + 1.0;
|
|
138
|
+
|
|
139
|
+
const buffer = await Tone.Offline(async () => { await main(); }, durationSec, 1, 44100);
|
|
140
|
+
const wav = audioBufferToWav(buffer);
|
|
141
|
+
window.writeFile(wavPath, Array.from(new Uint8Array(wav)));
|
|
142
|
+
return wav.byteLength;
|
|
143
|
+
};
|
|
144
|
+
</script>
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: audio-cinematic
|
|
3
|
+
description: Cinematic Score — epic orchestral builds, pad swells, ostinato, tension and release
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TuneFrames — Cinematic Score
|
|
7
|
+
|
|
8
|
+
## Genre Profile
|
|
9
|
+
- BPM range: 60–90 (slow and grand; let notes breathe)
|
|
10
|
+
- Key characteristics: Slow harmonic rhythm, long-attack pads, ostinato string figures, brass swells on climax, no drums until climax (if at all), layered dynamics from pp to ff
|
|
11
|
+
- Typical instruments: String pads (PolySynth sawtooth, long attack), brass (PolySynth sawtooth, fast attack), choir pad (PolySynth sine), low piano (Synth), timpani (MembraneSynth)
|
|
12
|
+
- Mood: Epic, emotional, suspenseful, grand, sweeping — Zimmer / Morricone / Williams
|
|
13
|
+
|
|
14
|
+
## Core Pattern
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
// Cinematic core: 72 BPM, Dm–Bb–F–C (minor epic progression)
|
|
18
|
+
async function main() {
|
|
19
|
+
await Tone.start();
|
|
20
|
+
Tone.Transport.bpm.value = 72;
|
|
21
|
+
|
|
22
|
+
const bar = Tone.Time('1n').toSeconds(); // ~3.33s at 72 BPM
|
|
23
|
+
|
|
24
|
+
// ── Grand reverb hall ────────────────────────────────────────────────
|
|
25
|
+
const hall = new Tone.Reverb({ decay: 6, preDelay: 0.05, wet: 0.6 }).toDestination();
|
|
26
|
+
const hall2 = new Tone.Reverb({ decay: 4, wet: 0.4 }).toDestination();
|
|
27
|
+
|
|
28
|
+
// ── Strings pad — slow attack, swell ─────────────────────────────────
|
|
29
|
+
const strings = new Tone.PolySynth(Tone.Synth, {
|
|
30
|
+
oscillator: { type: 'sawtooth' },
|
|
31
|
+
envelope: { attack: 1.8, decay: 0.5, sustain: 0.85, release: 4.0 }
|
|
32
|
+
}).connect(hall);
|
|
33
|
+
strings.volume.value = -14;
|
|
34
|
+
|
|
35
|
+
// Dm–Bb–F–C progression (1 bar each)
|
|
36
|
+
const prog = [
|
|
37
|
+
['D3','F3','A3'], // Dm
|
|
38
|
+
['Bb2','D3','F3'], // Bb
|
|
39
|
+
['F2','A2','C3'], // F
|
|
40
|
+
['C3','E3','G3'], // C
|
|
41
|
+
];
|
|
42
|
+
prog.forEach((ch, i) => strings.triggerAttackRelease(ch, '1n', i * bar));
|
|
43
|
+
|
|
44
|
+
// ── Piano ostinato (enters bar 2, driving 8ths) ───────────────────────
|
|
45
|
+
const piano = new Tone.Synth({
|
|
46
|
+
oscillator: { type: 'sine' },
|
|
47
|
+
envelope: { attack: 0.005, decay: 0.3, sustain: 0.1, release: 0.8 }
|
|
48
|
+
}).connect(hall2);
|
|
49
|
+
piano.volume.value = -16;
|
|
50
|
+
|
|
51
|
+
const q = Tone.Time('4n').toSeconds();
|
|
52
|
+
const ostinato = ['D3','A3','D4','A3','F3','A3','D4','A3']; // D-A-D-A pattern
|
|
53
|
+
ostinato.forEach((n, i) => piano.triggerAttackRelease(n, '8n', bar + i * q * 0.5));
|
|
54
|
+
|
|
55
|
+
// ── Brass swell (enters bar 3) ────────────────────────────────────────
|
|
56
|
+
const brass = new Tone.PolySynth(Tone.Synth, {
|
|
57
|
+
oscillator: { type: 'sawtooth' },
|
|
58
|
+
envelope: { attack: 0.4, decay: 0.2, sustain: 0.8, release: 2.0 }
|
|
59
|
+
}).connect(hall);
|
|
60
|
+
brass.volume.value = -18;
|
|
61
|
+
|
|
62
|
+
brass.triggerAttackRelease(['F3','A3','C4'], '2n', bar * 2);
|
|
63
|
+
brass.triggerAttackRelease(['C3','E3','G3'], '2n', bar * 3);
|
|
64
|
+
|
|
65
|
+
// ── Timpani on climax ─────────────────────────────────────────────────
|
|
66
|
+
const timp = new Tone.MembraneSynth({
|
|
67
|
+
pitchDecay: 0.08, octaves: 5,
|
|
68
|
+
envelope: { attack: 0.001, decay: 1.2, sustain: 0, release: 2.0 }
|
|
69
|
+
}).connect(hall);
|
|
70
|
+
timp.volume.value = -10;
|
|
71
|
+
|
|
72
|
+
timp.triggerAttackRelease('D2', '4n', bar * 2);
|
|
73
|
+
timp.triggerAttackRelease('A1', '4n', bar * 3);
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Instrument Configuration
|
|
78
|
+
|
|
79
|
+
```js
|
|
80
|
+
// Strings — sawtooth for harmonic richness, very slow attack for swell
|
|
81
|
+
const strings = new Tone.PolySynth(Tone.Synth, {
|
|
82
|
+
oscillator: { type: 'sawtooth' },
|
|
83
|
+
envelope: { attack: 1.8, decay: 0.5, sustain: 0.85, release: 4.0 }
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Choir/pad layer — pure sine, even slower attack for the "ahh" effect
|
|
87
|
+
const choir = new Tone.PolySynth(Tone.Synth, {
|
|
88
|
+
oscillator: { type: 'sine' },
|
|
89
|
+
envelope: { attack: 2.5, decay: 0.3, sustain: 0.9, release: 5.0 }
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Brass — faster attack, shorter release (punchy swell)
|
|
93
|
+
const brass = new Tone.PolySynth(Tone.Synth, {
|
|
94
|
+
oscillator: { type: 'sawtooth' },
|
|
95
|
+
envelope: { attack: 0.3, decay: 0.2, sustain: 0.8, release: 2.0 }
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Piano ostinato — percussive, short sustain, clean
|
|
99
|
+
const piano = new Tone.Synth({
|
|
100
|
+
oscillator: { type: 'sine' },
|
|
101
|
+
envelope: { attack: 0.005, decay: 0.3, sustain: 0.1, release: 0.8 }
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Hall reverb — long decay for cinematic space
|
|
105
|
+
const hall = new Tone.Reverb({ decay: 6, preDelay: 0.05, wet: 0.6 }).toDestination();
|
|
106
|
+
|
|
107
|
+
// Volume automation for the build:
|
|
108
|
+
strings.volume.rampTo(-8, 4); // swell from -14 to -8 over 4s
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Composition Structure
|
|
112
|
+
|
|
113
|
+
1. **Silence / soft pad intro (0–3s):** Choir pad alone on tonic, barely audible — establishes space
|
|
114
|
+
2. **Strings enter (3–6s):** Slow-attack string swell on Dm, then Bb — harmonic foundation
|
|
115
|
+
3. **Ostinato begins (6–9s):** Piano or cello ostinato (repeating 8th-note figure) adds momentum
|
|
116
|
+
4. **Tension build (9–12s):** Strings move to dissonant chord (dim or sus), dynamics rise
|
|
117
|
+
5. **Brass climax (12–15s):** Full brass swell on the tonic, timpani hit, everything fortissimo
|
|
118
|
+
6. **Release / decay (final 2s):** Let hall reverb tail ring out — cut instruments, tail continues
|
|
119
|
+
|
|
120
|
+
## Example Variations
|
|
121
|
+
|
|
122
|
+
### 1 — Tension chord (diminished, no resolution)
|
|
123
|
+
```js
|
|
124
|
+
// Hold on a dim7 for maximum suspense before resolving
|
|
125
|
+
const dim = ['F#3','A3','C4','Eb4']; // F#dim7
|
|
126
|
+
strings.triggerAttackRelease(dim, '2n', tensionTime);
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 2 — Morricone-style solo melody (over strings)
|
|
130
|
+
```js
|
|
131
|
+
// High, yearning single note melody in the upper register
|
|
132
|
+
const solo = new Tone.Synth({
|
|
133
|
+
oscillator: { type: 'sine' },
|
|
134
|
+
envelope: { attack: 0.3, decay: 0.2, sustain: 0.9, release: 2.0 }
|
|
135
|
+
});
|
|
136
|
+
// Phrase: D5–C5–Bb4–A4 (descending minor scale, emotional)
|
|
137
|
+
const morriconeLine = ['D5','C5','Bb4','A4','F4','A4','D5'];
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 3 — Ostinato acceleration (builds tension)
|
|
141
|
+
```js
|
|
142
|
+
// Start with quarter-note ostinato, switch to 8ths on bar 3, 16ths on bar 4
|
|
143
|
+
const oSlow = Tone.Time('4n').toSeconds();
|
|
144
|
+
const oFast = Tone.Time('8n').toSeconds();
|
|
145
|
+
const oFast2 = Tone.Time('16n').toSeconds();
|
|
146
|
+
// Schedule the same note at progressively tighter intervals
|
|
147
|
+
```
|