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,167 @@
|
|
|
1
|
+
<script src="https://unpkg.com/tone@14.7.77/build/Tone.js"></script>
|
|
2
|
+
<script>
|
|
3
|
+
// ─── AI DJ — mood-parameterized Tone.js composition ────────────────────────
|
|
4
|
+
// Renders different music based on ?mood= query param or window.MOOD
|
|
5
|
+
// Supported moods: chill | energetic | dark | happy
|
|
6
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
const MOOD = (() => {
|
|
9
|
+
const p = new URLSearchParams(window.location.search).get('mood');
|
|
10
|
+
return ['chill', 'energetic', 'dark', 'happy'].includes(p) ? p : 'chill';
|
|
11
|
+
})();
|
|
12
|
+
|
|
13
|
+
const CONFIG = {
|
|
14
|
+
chill: {
|
|
15
|
+
bpm: 72, duration: '12s',
|
|
16
|
+
scale: ['D3', 'F3', 'A3', 'C4'],
|
|
17
|
+
chords: [['D3','F3','A3'],['C3','E3','G3'],['D3','F3','A3'],['Bb2','D3','F3']],
|
|
18
|
+
leadNotes: ['D4','F4','A4','C5','D4','Bb3'],
|
|
19
|
+
leadRhythm: ['4n','4n','4n','4n','4n','4n'],
|
|
20
|
+
filterFreq: 800, reverbWet: 0.85, delayWet: 0.4, delayTime: '8n.',
|
|
21
|
+
kickTimes: [0, 2, 4, 6, 8, 10],
|
|
22
|
+
hatTimes: [1, 3, 5, 7, 9, 11],
|
|
23
|
+
snareTimes: [2, 6, 10],
|
|
24
|
+
},
|
|
25
|
+
energetic: {
|
|
26
|
+
bpm: 140, duration: '8s',
|
|
27
|
+
scale: ['C4', 'E4', 'G4', 'A4'],
|
|
28
|
+
chords: [['C4','E4','G4'],['A3','C4','E4'],['F3','A3','C4'],['G3','B3','D4']],
|
|
29
|
+
leadNotes: ['C5','E5','G5','C5','D5','E5'],
|
|
30
|
+
leadRhythm: ['8n','8n','8n','8n','8n','8n'],
|
|
31
|
+
filterFreq: 4000, reverbWet: 0.2, delayWet: 0.1, delayTime: '16n',
|
|
32
|
+
kickTimes: [0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 7, 7.5],
|
|
33
|
+
hatTimes: [0, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.25, 2.5, 2.75, 3, 3.25, 3.5, 3.75, 4, 4.25, 4.5, 4.75, 5, 5.25, 5.5, 5.75, 6, 6.25, 6.5, 6.75, 7, 7.25, 7.5, 7.75],
|
|
34
|
+
snareTimes: [1, 3, 5, 7],
|
|
35
|
+
},
|
|
36
|
+
dark: {
|
|
37
|
+
bpm: 80, duration: '10s',
|
|
38
|
+
scale: ['A2', 'C3', 'E3', 'G3'],
|
|
39
|
+
chords: [['A2','C3','E3'],['E2','G2','B2'],['A2','C3','E3'],['D2','F2','A2']],
|
|
40
|
+
leadNotes: ['A3','G3','E3','C3','A3','E3'],
|
|
41
|
+
leadRhythm: ['2n','2n','2n','2n','2n','2n'],
|
|
42
|
+
filterFreq: 600, reverbWet: 0.9, delayWet: 0.5, delayTime: '4n',
|
|
43
|
+
kickTimes: [0, 3, 6, 9],
|
|
44
|
+
hatTimes: [1.5, 4.5, 7.5],
|
|
45
|
+
snareTimes: [1.5, 5.5, 9.5],
|
|
46
|
+
},
|
|
47
|
+
happy: {
|
|
48
|
+
bpm: 120, duration: '8s',
|
|
49
|
+
scale: ['C4', 'E4', 'G4', 'A4'],
|
|
50
|
+
chords: [['C4','E4','G4'],['F3','A3','C4'],['G3','B3','D4'],['C4','E4','G4']],
|
|
51
|
+
leadNotes: ['C5','E5','G5','E5','D5','C5'],
|
|
52
|
+
leadRhythm: ['8n.','8n.','8n.','8n.','8n.','8n.'],
|
|
53
|
+
filterFreq: 5000, reverbWet: 0.3, delayWet: 0.2, delayTime: '8n',
|
|
54
|
+
kickTimes: [0, 1, 2, 3, 4, 5, 6, 7],
|
|
55
|
+
hatTimes: [0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 7, 7.5],
|
|
56
|
+
snareTimes: [0.5, 2.5, 4.5, 6.5],
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const cfg = CONFIG[MOOD];
|
|
61
|
+
|
|
62
|
+
async function main() {
|
|
63
|
+
await Tone.start();
|
|
64
|
+
|
|
65
|
+
const pad = new Tone.PolySynth(Tone.Synth, {
|
|
66
|
+
oscillator: { type: 'sine' },
|
|
67
|
+
envelope: { attack: 2.0, decay: 1, sustain: 0.8, release: 3 },
|
|
68
|
+
volume: -6,
|
|
69
|
+
}).toDestination();
|
|
70
|
+
|
|
71
|
+
const lead = new Tone.Synth({
|
|
72
|
+
oscillator: { type: MOOD === 'dark' ? 'sawtooth' : MOOD === 'energetic' ? 'square' : 'triangle' },
|
|
73
|
+
envelope: { attack: 0.02, decay: 0.1, sustain: 0.3, release: 0.5 },
|
|
74
|
+
volume: -4,
|
|
75
|
+
}).toDestination();
|
|
76
|
+
|
|
77
|
+
const bass = new Tone.MonoSynth({
|
|
78
|
+
oscillator: { type: 'sawtooth' },
|
|
79
|
+
filter: { Q: 2, type: 'lowpass', frequency: cfg.filterFreq },
|
|
80
|
+
envelope: { attack: 0.01, decay: 0.3, sustain: 0.4, release: 0.4 },
|
|
81
|
+
volume: -8,
|
|
82
|
+
}).toDestination();
|
|
83
|
+
|
|
84
|
+
const reverb = new Tone.Reverb({ decay: 4.5, wet: cfg.reverbWet }).toDestination();
|
|
85
|
+
const delay = new Tone.FeedbackDelay({ delayTime: cfg.delayTime, feedback: 0.3, wet: cfg.delayWet }).toDestination();
|
|
86
|
+
pad.connect(reverb);
|
|
87
|
+
lead.connect(delay);
|
|
88
|
+
delay.toDestination();
|
|
89
|
+
|
|
90
|
+
// Chord pads
|
|
91
|
+
new Tone.Sequence((time, chord) => {
|
|
92
|
+
pad.triggerAttackRelease(chord, '2n', time);
|
|
93
|
+
}, cfg.chords, 0).start(0);
|
|
94
|
+
|
|
95
|
+
// Lead melody
|
|
96
|
+
new Tone.Sequence((time, note) => {
|
|
97
|
+
lead.triggerAttackRelease(note, '8n.', time);
|
|
98
|
+
}, cfg.leadNotes, 0).start(0);
|
|
99
|
+
|
|
100
|
+
// Bass
|
|
101
|
+
new Tone.Sequence((time, note) => {
|
|
102
|
+
bass.triggerAttackRelease(note, '4n', time);
|
|
103
|
+
}, cfg.chords.map(c => c[0]), 0).start(0);
|
|
104
|
+
|
|
105
|
+
// Kick
|
|
106
|
+
const kick = new Tone.MembraneSynth({
|
|
107
|
+
pitchDecay: 0.05, octaves: 6,
|
|
108
|
+
envelope: { attack: 0.001, decay: 0.4, sustain: 0, release: 0.1 },
|
|
109
|
+
volume: MOOD === 'chill' ? -12 : MOOD === 'dark' ? -14 : -6,
|
|
110
|
+
}).toDestination();
|
|
111
|
+
cfg.kickTimes.forEach(t => {
|
|
112
|
+
Tone.Transport.scheduleOnce((time) => {
|
|
113
|
+
kick.triggerAttackRelease('C1', '8n', time);
|
|
114
|
+
}, t);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Hi-hat
|
|
118
|
+
const hihat = new Tone.NoiseSynth({
|
|
119
|
+
noise: { type: 'white' },
|
|
120
|
+
envelope: { attack: 0.001, decay: 0.05, sustain: 0, release: 0.01 },
|
|
121
|
+
volume: MOOD === 'chill' ? -18 : -10,
|
|
122
|
+
}).toDestination();
|
|
123
|
+
cfg.hatTimes.forEach(t => {
|
|
124
|
+
Tone.Transport.scheduleOnce((time) => {
|
|
125
|
+
hihat.triggerAttackRelease('16n', time);
|
|
126
|
+
}, t);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Snare
|
|
130
|
+
const snare = new Tone.NoiseSynth({
|
|
131
|
+
noise: { type: 'pink' },
|
|
132
|
+
envelope: { attack: 0.001, decay: 0.15, sustain: 0, release: 0.1 },
|
|
133
|
+
volume: MOOD === 'chill' ? -16 : MOOD === 'dark' ? -14 : -8,
|
|
134
|
+
}).toDestination();
|
|
135
|
+
cfg.snareTimes.forEach(t => {
|
|
136
|
+
Tone.Transport.scheduleOnce((time) => {
|
|
137
|
+
snare.triggerAttackRelease('8n', time);
|
|
138
|
+
}, t);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
Tone.Transport.bpm.value = cfg.bpm;
|
|
142
|
+
Tone.Transport.start();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
window.renderComposition = async function(wavPath) {
|
|
146
|
+
let attempts = 0;
|
|
147
|
+
while (typeof Tone === 'undefined' && attempts < 50) {
|
|
148
|
+
await new Promise(r => setTimeout(r, 100));
|
|
149
|
+
attempts++;
|
|
150
|
+
}
|
|
151
|
+
if (typeof Tone === 'undefined') throw new Error('Tone not loaded');
|
|
152
|
+
|
|
153
|
+
const durationSec = Math.max(Tone.Time(cfg.duration).toSeconds() + 0.5, 2);
|
|
154
|
+
|
|
155
|
+
const buffer = await Tone.Offline(async () => { await main(); }, durationSec, 1, 44100, cfg.bpm);
|
|
156
|
+
const wav = audioBufferToWav(buffer);
|
|
157
|
+
window.writeFile(wavPath, Array.from(new Uint8Array(wav)));
|
|
158
|
+
return wav.byteLength;
|
|
159
|
+
};
|
|
160
|
+
</script>
|
|
161
|
+
|
|
162
|
+
<!-- Metadata driven by JS so each mood gets correct BPM + duration -->
|
|
163
|
+
<script>
|
|
164
|
+
document.open();
|
|
165
|
+
document.write(`<div id="tuneframes" style="display:none">{"bpm":${cfg.bpm},"duration":"${cfg.duration}"}</div>`);
|
|
166
|
+
document.close();
|
|
167
|
+
</script>
|
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
<title>TuneFrames — Ambient</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":60,"duration":"16s"}</div>
|
|
9
|
-
<script>
|
|
10
|
-
async function main() {
|
|
11
|
-
await Tone.start();
|
|
12
|
-
|
|
13
|
-
const verb = new Tone.Reverb({ decay: 8, wet: 0.8 }).toDestination();
|
|
14
|
-
const delay = new Tone.PingPongDelay('4n', 0.3).toDestination();
|
|
15
|
-
|
|
16
|
-
// Warm pad
|
|
17
|
-
const pad = new Tone.PolySynth(Tone.Synth, {
|
|
18
|
-
oscillator: { type: 'sine' },
|
|
19
|
-
envelope: { attack: 3, decay: 1, sustain: 0.8, release: 4 }
|
|
20
|
-
}).connect(verb);
|
|
21
|
-
pad.volume.value = -8;
|
|
22
|
-
|
|
23
|
-
// Crystal high texture
|
|
24
|
-
const crystal = new Tone.PolySynth(Tone.Synth, {
|
|
25
|
-
oscillator: { type: 'sine' },
|
|
26
|
-
envelope: { attack: 4, decay: 0.5, sustain: 1, release: 3 }
|
|
27
|
-
}).connect(delay);
|
|
28
|
-
crystal.volume.value = -18;
|
|
29
|
-
|
|
30
|
-
// D minor progression — hypnotic and spacious
|
|
31
|
-
const chords = [
|
|
32
|
-
['D3', 'F3', 'A3'], // Dm
|
|
33
|
-
['C3', 'E3', 'G3'], // C
|
|
34
|
-
['Bb2', 'D3', 'F3'], // Bb
|
|
35
|
-
['A2', 'C3', 'E3'], // Am
|
|
36
|
-
];
|
|
37
|
-
|
|
38
|
-
const beat = Tone.Time('1n').toSeconds();
|
|
39
|
-
|
|
40
|
-
chords.forEach((chord, i) => {
|
|
41
|
-
pad.triggerAttackRelease(chord, '1n', i * beat);
|
|
42
|
-
// Add crystal shimmer an octave up, delayed
|
|
43
|
-
crystal.triggerAttackRelease(
|
|
44
|
-
chord.map(n => {
|
|
45
|
-
const octave = parseInt(n.match(/\d/)[0]);
|
|
46
|
-
return n.replace(/\d/, octave + 1);
|
|
47
|
-
}),
|
|
48
|
-
'2n', i * beat + 0.05
|
|
49
|
-
);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
// Slow bass drone
|
|
53
|
-
const drone = new Tone.MonoSynth({
|
|
54
|
-
oscillator: { type: 'sine' },
|
|
55
|
-
envelope: { attack: 2, decay: 0, sustain: 1, release: 3 }
|
|
56
|
-
}).toDestination();
|
|
57
|
-
drone.volume.value = -20;
|
|
58
|
-
drone.triggerAttackRelease('D1', '1n', 0);
|
|
59
|
-
drone.triggerAttackRelease('D1', '1n', beat * 2);
|
|
60
|
-
}
|
|
61
|
-
</script>
|
|
62
|
-
</body>
|
|
63
|
-
</html>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>TuneFrames — Ambient</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":60,"duration":"16s"}</div>
|
|
9
|
+
<script>
|
|
10
|
+
async function main() {
|
|
11
|
+
await Tone.start();
|
|
12
|
+
|
|
13
|
+
const verb = new Tone.Reverb({ decay: 8, wet: 0.8 }).toDestination();
|
|
14
|
+
const delay = new Tone.PingPongDelay('4n', 0.3).toDestination();
|
|
15
|
+
|
|
16
|
+
// Warm pad
|
|
17
|
+
const pad = new Tone.PolySynth(Tone.Synth, {
|
|
18
|
+
oscillator: { type: 'sine' },
|
|
19
|
+
envelope: { attack: 3, decay: 1, sustain: 0.8, release: 4 }
|
|
20
|
+
}).connect(verb);
|
|
21
|
+
pad.volume.value = -8;
|
|
22
|
+
|
|
23
|
+
// Crystal high texture
|
|
24
|
+
const crystal = new Tone.PolySynth(Tone.Synth, {
|
|
25
|
+
oscillator: { type: 'sine' },
|
|
26
|
+
envelope: { attack: 4, decay: 0.5, sustain: 1, release: 3 }
|
|
27
|
+
}).connect(delay);
|
|
28
|
+
crystal.volume.value = -18;
|
|
29
|
+
|
|
30
|
+
// D minor progression — hypnotic and spacious
|
|
31
|
+
const chords = [
|
|
32
|
+
['D3', 'F3', 'A3'], // Dm
|
|
33
|
+
['C3', 'E3', 'G3'], // C
|
|
34
|
+
['Bb2', 'D3', 'F3'], // Bb
|
|
35
|
+
['A2', 'C3', 'E3'], // Am
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const beat = Tone.Time('1n').toSeconds();
|
|
39
|
+
|
|
40
|
+
chords.forEach((chord, i) => {
|
|
41
|
+
pad.triggerAttackRelease(chord, '1n', i * beat);
|
|
42
|
+
// Add crystal shimmer an octave up, delayed
|
|
43
|
+
crystal.triggerAttackRelease(
|
|
44
|
+
chord.map(n => {
|
|
45
|
+
const octave = parseInt(n.match(/\d/)[0]);
|
|
46
|
+
return n.replace(/\d/, octave + 1);
|
|
47
|
+
}),
|
|
48
|
+
'2n', i * beat + 0.05
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Slow bass drone
|
|
53
|
+
const drone = new Tone.MonoSynth({
|
|
54
|
+
oscillator: { type: 'sine' },
|
|
55
|
+
envelope: { attack: 2, decay: 0, sustain: 1, release: 3 }
|
|
56
|
+
}).toDestination();
|
|
57
|
+
drone.volume.value = -20;
|
|
58
|
+
drone.triggerAttackRelease('D1', '1n', 0);
|
|
59
|
+
drone.triggerAttackRelease('D1', '1n', beat * 2);
|
|
60
|
+
}
|
|
61
|
+
</script>
|
|
62
|
+
</body>
|
|
63
|
+
</html>
|
|
Binary file
|
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
<title>TuneFrames — Bass</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":120,"duration":"4s"}</div>
|
|
9
|
-
<script>
|
|
10
|
-
async function main() {
|
|
11
|
-
await Tone.start();
|
|
12
|
-
const beat = Tone.Time('4n').toSeconds(); // 0.5s at 120 BPM
|
|
13
|
-
|
|
14
|
-
// Warm, rounded bass tone
|
|
15
|
-
const bass = new Tone.MonoSynth({
|
|
16
|
-
oscillator: { type: 'sawtooth' },
|
|
17
|
-
filter: { Q: 2, type: 'lowpass', rolloff: -12 },
|
|
18
|
-
envelope: { attack: 0.01, decay: 0.2, sustain: 0.4, release: 0.3 },
|
|
19
|
-
filterEnvelope: { attack: 0.01, decay: 0.1, sustain: 0.3, release: 0.2, baseFrequency: 100, octaves: 2 }
|
|
20
|
-
}).toDestination();
|
|
21
|
-
bass.volume.value = -6;
|
|
22
|
-
|
|
23
|
-
// Walking bass line: beats 0-4 (0 to 2s) = Dm, beats 4-7 (2 to 3.5s) = Gm
|
|
24
|
-
// All notes scheduled within duration=4s — notes at time >= 4s are dropped
|
|
25
|
-
const walk = [
|
|
26
|
-
// Bar 1 — Dm: root, 5th, 7th, root / approach, 3rd, 4th, 5th, root
|
|
27
|
-
{ note: 'D2', time: 0 },
|
|
28
|
-
{ note: 'A2', time: beat },
|
|
29
|
-
{ note: 'C3', time: 2 * beat },
|
|
30
|
-
{ note: 'D3', time: 3 * beat },
|
|
31
|
-
{ note: 'E2', time: 4 * beat },
|
|
32
|
-
{ note: 'F2', time: 5 * beat },
|
|
33
|
-
{ note: 'G2', time: 6 * beat },
|
|
34
|
-
{ note: 'A2', time: 7 * beat },
|
|
35
|
-
// Bar 2 — Gm: root, 5th, 7th, root
|
|
36
|
-
{ note: 'G2', time: 8 * beat },
|
|
37
|
-
{ note: 'D3', time: 9 * beat },
|
|
38
|
-
{ note: 'F3', time: 10 * beat },
|
|
39
|
-
{ note: 'G3', time: 11 * beat }
|
|
40
|
-
];
|
|
41
|
-
|
|
42
|
-
walk.forEach(({ note, time }) => {
|
|
43
|
-
if (time < 4) bass.triggerAttackRelease(note, '8n', time);
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
</script>
|
|
47
|
-
</body>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>TuneFrames — Bass</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":120,"duration":"4s"}</div>
|
|
9
|
+
<script>
|
|
10
|
+
async function main() {
|
|
11
|
+
await Tone.start();
|
|
12
|
+
const beat = Tone.Time('4n').toSeconds(); // 0.5s at 120 BPM
|
|
13
|
+
|
|
14
|
+
// Warm, rounded bass tone
|
|
15
|
+
const bass = new Tone.MonoSynth({
|
|
16
|
+
oscillator: { type: 'sawtooth' },
|
|
17
|
+
filter: { Q: 2, type: 'lowpass', rolloff: -12 },
|
|
18
|
+
envelope: { attack: 0.01, decay: 0.2, sustain: 0.4, release: 0.3 },
|
|
19
|
+
filterEnvelope: { attack: 0.01, decay: 0.1, sustain: 0.3, release: 0.2, baseFrequency: 100, octaves: 2 }
|
|
20
|
+
}).toDestination();
|
|
21
|
+
bass.volume.value = -6;
|
|
22
|
+
|
|
23
|
+
// Walking bass line: beats 0-4 (0 to 2s) = Dm, beats 4-7 (2 to 3.5s) = Gm
|
|
24
|
+
// All notes scheduled within duration=4s — notes at time >= 4s are dropped
|
|
25
|
+
const walk = [
|
|
26
|
+
// Bar 1 — Dm: root, 5th, 7th, root / approach, 3rd, 4th, 5th, root
|
|
27
|
+
{ note: 'D2', time: 0 },
|
|
28
|
+
{ note: 'A2', time: beat },
|
|
29
|
+
{ note: 'C3', time: 2 * beat },
|
|
30
|
+
{ note: 'D3', time: 3 * beat },
|
|
31
|
+
{ note: 'E2', time: 4 * beat },
|
|
32
|
+
{ note: 'F2', time: 5 * beat },
|
|
33
|
+
{ note: 'G2', time: 6 * beat },
|
|
34
|
+
{ note: 'A2', time: 7 * beat },
|
|
35
|
+
// Bar 2 — Gm: root, 5th, 7th, root
|
|
36
|
+
{ note: 'G2', time: 8 * beat },
|
|
37
|
+
{ note: 'D3', time: 9 * beat },
|
|
38
|
+
{ note: 'F3', time: 10 * beat },
|
|
39
|
+
{ note: 'G3', time: 11 * beat }
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
walk.forEach(({ note, time }) => {
|
|
43
|
+
if (time < 4) bass.triggerAttackRelease(note, '8n', time);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
</script>
|
|
47
|
+
</body>
|
|
48
48
|
</html>
|
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
<title>TuneFrames — Lofi Example</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":80,"duration":"10s"}</div>
|
|
9
|
-
<script>
|
|
10
|
-
async function main() {
|
|
11
|
-
await Tone.start();
|
|
12
|
-
|
|
13
|
-
const reverb = new Tone.Reverb({ decay: 4, wet: 0.6 }).toDestination();
|
|
14
|
-
const pad = new Tone.PolySynth(Tone.Synth, {
|
|
15
|
-
oscillator: { type: 'triangle' },
|
|
16
|
-
envelope: { attack: 0.5, decay: 0.3, sustain: 0.8, release: 2 }
|
|
17
|
-
}).connect(reverb);
|
|
18
|
-
|
|
19
|
-
// Am - F - C - G chord progression
|
|
20
|
-
const chords = [
|
|
21
|
-
['A3', 'C4', 'E4'],
|
|
22
|
-
['F3', 'A3', 'C4'],
|
|
23
|
-
['C3', 'E3', 'G3'],
|
|
24
|
-
['G3', 'B3', 'D4'],
|
|
25
|
-
];
|
|
26
|
-
|
|
27
|
-
// Play each chord for 2 beats at 80 BPM
|
|
28
|
-
for (let i = 0; i < chords.length; i++) {
|
|
29
|
-
pad.triggerAttackRelease(chords[i], '2n', i * Tone.Time('2n').toSeconds());
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Simple melody
|
|
33
|
-
const melody = new Tone.Synth({
|
|
34
|
-
oscillator: { type: 'sine' },
|
|
35
|
-
envelope: { attack: 0.01, decay: 0.1, sustain: 0.3, release: 0.5 }
|
|
36
|
-
}).toDestination();
|
|
37
|
-
|
|
38
|
-
melody.triggerAttackRelease('C5', '8n', Tone.Time('2n').toSeconds() * 0.5);
|
|
39
|
-
melody.triggerAttackRelease('E5', '8n', Tone.Time('2n').toSeconds() * 1.5);
|
|
40
|
-
melody.triggerAttackRelease('G5', '8n', Tone.Time('2n').toSeconds() * 2.5);
|
|
41
|
-
melody.triggerAttackRelease('C5', '4n', Tone.Time('2n').toSeconds() * 3.5);
|
|
42
|
-
}
|
|
43
|
-
</script>
|
|
44
|
-
</body>
|
|
45
|
-
</html>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>TuneFrames — Lofi Example</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":80,"duration":"10s"}</div>
|
|
9
|
+
<script>
|
|
10
|
+
async function main() {
|
|
11
|
+
await Tone.start();
|
|
12
|
+
|
|
13
|
+
const reverb = new Tone.Reverb({ decay: 4, wet: 0.6 }).toDestination();
|
|
14
|
+
const pad = new Tone.PolySynth(Tone.Synth, {
|
|
15
|
+
oscillator: { type: 'triangle' },
|
|
16
|
+
envelope: { attack: 0.5, decay: 0.3, sustain: 0.8, release: 2 }
|
|
17
|
+
}).connect(reverb);
|
|
18
|
+
|
|
19
|
+
// Am - F - C - G chord progression
|
|
20
|
+
const chords = [
|
|
21
|
+
['A3', 'C4', 'E4'],
|
|
22
|
+
['F3', 'A3', 'C4'],
|
|
23
|
+
['C3', 'E3', 'G3'],
|
|
24
|
+
['G3', 'B3', 'D4'],
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
// Play each chord for 2 beats at 80 BPM
|
|
28
|
+
for (let i = 0; i < chords.length; i++) {
|
|
29
|
+
pad.triggerAttackRelease(chords[i], '2n', i * Tone.Time('2n').toSeconds());
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Simple melody
|
|
33
|
+
const melody = new Tone.Synth({
|
|
34
|
+
oscillator: { type: 'sine' },
|
|
35
|
+
envelope: { attack: 0.01, decay: 0.1, sustain: 0.3, release: 0.5 }
|
|
36
|
+
}).toDestination();
|
|
37
|
+
|
|
38
|
+
melody.triggerAttackRelease('C5', '8n', Tone.Time('2n').toSeconds() * 0.5);
|
|
39
|
+
melody.triggerAttackRelease('E5', '8n', Tone.Time('2n').toSeconds() * 1.5);
|
|
40
|
+
melody.triggerAttackRelease('G5', '8n', Tone.Time('2n').toSeconds() * 2.5);
|
|
41
|
+
melody.triggerAttackRelease('C5', '4n', Tone.Time('2n').toSeconds() * 3.5);
|
|
42
|
+
}
|
|
43
|
+
</script>
|
|
44
|
+
</body>
|
|
45
|
+
</html>
|
|
Binary file
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
<title>TuneFrames — Minimal</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":120,"duration":"2s"}</div>
|
|
9
|
-
<script>
|
|
10
|
-
async function main() {
|
|
11
|
-
await Tone.start();
|
|
12
|
-
const synth = new Tone.Synth().toDestination();
|
|
13
|
-
const beat = Tone.Time('4n').toSeconds();
|
|
14
|
-
// C major arpeggio — three notes, clean and simple
|
|
15
|
-
synth.triggerAttackRelease('C4', '4n', 0);
|
|
16
|
-
synth.triggerAttackRelease('E4', '4n', beat);
|
|
17
|
-
synth.triggerAttackRelease('G4', '4n', beat * 2);
|
|
18
|
-
}
|
|
19
|
-
</script>
|
|
20
|
-
</body>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>TuneFrames — Minimal</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":120,"duration":"2s"}</div>
|
|
9
|
+
<script>
|
|
10
|
+
async function main() {
|
|
11
|
+
await Tone.start();
|
|
12
|
+
const synth = new Tone.Synth().toDestination();
|
|
13
|
+
const beat = Tone.Time('4n').toSeconds();
|
|
14
|
+
// C major arpeggio — three notes, clean and simple
|
|
15
|
+
synth.triggerAttackRelease('C4', '4n', 0);
|
|
16
|
+
synth.triggerAttackRelease('E4', '4n', beat);
|
|
17
|
+
synth.triggerAttackRelease('G4', '4n', beat * 2);
|
|
18
|
+
}
|
|
19
|
+
</script>
|
|
20
|
+
</body>
|
|
21
21
|
</html>
|
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
<title>TuneFrames — Orchestral</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":72,"duration":"14s"}</div>
|
|
9
|
-
<script>
|
|
10
|
-
async function main() {
|
|
11
|
-
await Tone.start();
|
|
12
|
-
|
|
13
|
-
const verb = new Tone.Reverb({ decay: 6, wet: 0.5 }).toDestination();
|
|
14
|
-
|
|
15
|
-
// Strings section
|
|
16
|
-
const strings = new Tone.PolySynth(Tone.Synth, {
|
|
17
|
-
oscillator: { type: 'sawtooth' },
|
|
18
|
-
envelope: { attack: 1.5, decay: 0.3, sustain: 0.9, release: 3 }
|
|
19
|
-
}).connect(verb);
|
|
20
|
-
strings.volume.value = -12;
|
|
21
|
-
|
|
22
|
-
// Brass
|
|
23
|
-
const brass = new Tone.PolySynth(Tone.Synth, {
|
|
24
|
-
oscillator: { type: 'sawtooth' },
|
|
25
|
-
envelope: { attack: 0.3, decay: 0.2, sustain: 0.7, release: 1.5 }
|
|
26
|
-
}).connect(verb);
|
|
27
|
-
brass.volume.value = -10;
|
|
28
|
-
|
|
29
|
-
// Timpani
|
|
30
|
-
const timpani = new Tone.MembraneSynth({
|
|
31
|
-
pitchDecay: 0.05,
|
|
32
|
-
octaves: 4,
|
|
33
|
-
oscillator: { type: 'sine' },
|
|
34
|
-
envelope: { attack: 0.001, decay: 1, sustain: 0, release: 2 }
|
|
35
|
-
}).toDestination();
|
|
36
|
-
timpani.volume.value = -8;
|
|
37
|
-
|
|
38
|
-
const beat = Tone.Time('1n').toSeconds();
|
|
39
|
-
|
|
40
|
-
// Strings: swell on every bar
|
|
41
|
-
const stringChords = [
|
|
42
|
-
['D3', 'F3', 'A3', 'C4'], // Dm7
|
|
43
|
-
['C3', 'E3', 'G3', 'B3'], // Cmaj7
|
|
44
|
-
['Bb2', 'D3', 'F3', 'A3'], // Bbmaj7
|
|
45
|
-
['A2', 'C3', 'E3', 'G3'], // Am7
|
|
46
|
-
];
|
|
47
|
-
stringChords.forEach((chord, i) => {
|
|
48
|
-
strings.triggerAttackRelease(chord, '1n', i * beat);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
// Brass: enters on bar 3 with fanfare
|
|
52
|
-
const fanfares = [
|
|
53
|
-
['D4', 'F4', 'A4'],
|
|
54
|
-
['C4', 'E4', 'G4'],
|
|
55
|
-
];
|
|
56
|
-
fanfares.forEach((chord, i) => {
|
|
57
|
-
brass.triggerAttackRelease(chord, '2n', (i + 2) * beat);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
// Timpani: tonic hits on bars 1 and 3
|
|
61
|
-
timpani.triggerAttackRelease('D2', '1n', 0);
|
|
62
|
-
timpani.triggerAttackRelease('D2', '1n', beat * 2);
|
|
63
|
-
timpani.triggerAttackRelease('A1', '2n', beat * 3.5);
|
|
64
|
-
}
|
|
65
|
-
</script>
|
|
66
|
-
</body>
|
|
67
|
-
</html>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>TuneFrames — Orchestral</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":72,"duration":"14s"}</div>
|
|
9
|
+
<script>
|
|
10
|
+
async function main() {
|
|
11
|
+
await Tone.start();
|
|
12
|
+
|
|
13
|
+
const verb = new Tone.Reverb({ decay: 6, wet: 0.5 }).toDestination();
|
|
14
|
+
|
|
15
|
+
// Strings section
|
|
16
|
+
const strings = new Tone.PolySynth(Tone.Synth, {
|
|
17
|
+
oscillator: { type: 'sawtooth' },
|
|
18
|
+
envelope: { attack: 1.5, decay: 0.3, sustain: 0.9, release: 3 }
|
|
19
|
+
}).connect(verb);
|
|
20
|
+
strings.volume.value = -12;
|
|
21
|
+
|
|
22
|
+
// Brass
|
|
23
|
+
const brass = new Tone.PolySynth(Tone.Synth, {
|
|
24
|
+
oscillator: { type: 'sawtooth' },
|
|
25
|
+
envelope: { attack: 0.3, decay: 0.2, sustain: 0.7, release: 1.5 }
|
|
26
|
+
}).connect(verb);
|
|
27
|
+
brass.volume.value = -10;
|
|
28
|
+
|
|
29
|
+
// Timpani
|
|
30
|
+
const timpani = new Tone.MembraneSynth({
|
|
31
|
+
pitchDecay: 0.05,
|
|
32
|
+
octaves: 4,
|
|
33
|
+
oscillator: { type: 'sine' },
|
|
34
|
+
envelope: { attack: 0.001, decay: 1, sustain: 0, release: 2 }
|
|
35
|
+
}).toDestination();
|
|
36
|
+
timpani.volume.value = -8;
|
|
37
|
+
|
|
38
|
+
const beat = Tone.Time('1n').toSeconds();
|
|
39
|
+
|
|
40
|
+
// Strings: swell on every bar
|
|
41
|
+
const stringChords = [
|
|
42
|
+
['D3', 'F3', 'A3', 'C4'], // Dm7
|
|
43
|
+
['C3', 'E3', 'G3', 'B3'], // Cmaj7
|
|
44
|
+
['Bb2', 'D3', 'F3', 'A3'], // Bbmaj7
|
|
45
|
+
['A2', 'C3', 'E3', 'G3'], // Am7
|
|
46
|
+
];
|
|
47
|
+
stringChords.forEach((chord, i) => {
|
|
48
|
+
strings.triggerAttackRelease(chord, '1n', i * beat);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Brass: enters on bar 3 with fanfare
|
|
52
|
+
const fanfares = [
|
|
53
|
+
['D4', 'F4', 'A4'],
|
|
54
|
+
['C4', 'E4', 'G4'],
|
|
55
|
+
];
|
|
56
|
+
fanfares.forEach((chord, i) => {
|
|
57
|
+
brass.triggerAttackRelease(chord, '2n', (i + 2) * beat);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Timpani: tonic hits on bars 1 and 3
|
|
61
|
+
timpani.triggerAttackRelease('D2', '1n', 0);
|
|
62
|
+
timpani.triggerAttackRelease('D2', '1n', beat * 2);
|
|
63
|
+
timpani.triggerAttackRelease('A1', '2n', beat * 3.5);
|
|
64
|
+
}
|
|
65
|
+
</script>
|
|
66
|
+
</body>
|
|
67
|
+
</html>
|