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
|
Binary file
|
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
<title>TuneFrames — Piano</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":100,"duration":"5s"}</div>
|
|
9
|
-
<script>
|
|
10
|
-
async function main() {
|
|
11
|
-
await Tone.start();
|
|
12
|
-
const beat = Tone.Time('4n').toSeconds();
|
|
13
|
-
|
|
14
|
-
const piano = new Tone.PolySynth(Tone.Synth, {
|
|
15
|
-
oscillator: { type: 'triangle' },
|
|
16
|
-
envelope: { attack: 0.005, decay: 0.3, sustain: 0.3, release: 1.2 }
|
|
17
|
-
}).toDestination();
|
|
18
|
-
piano.volume.value = -8;
|
|
19
|
-
|
|
20
|
-
// Dm - Am - Bb - F chord progression, 2 bars each
|
|
21
|
-
const chords = [
|
|
22
|
-
['D3', 'F3', 'A3'], // Dm
|
|
23
|
-
['A2', 'C3', 'E3'], // Am
|
|
24
|
-
['Bb2', 'D3', 'F3'], // Bb
|
|
25
|
-
['F2', 'A2', 'C3'] // F
|
|
26
|
-
];
|
|
27
|
-
|
|
28
|
-
chords.forEach((chord, i) => {
|
|
29
|
-
piano.triggerAttackRelease(chord, '2n', i * 2 * beat);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
// Melodic line: simple pentatonic over the changes
|
|
33
|
-
const melody = [
|
|
34
|
-
{ note: 'D4', time: 0 },
|
|
35
|
-
{ note: 'F4', time: beat },
|
|
36
|
-
{ note: 'A4', time: beat * 2 },
|
|
37
|
-
{ note: 'C4', time: 2 * beat },
|
|
38
|
-
{ note: 'E4', time: 2.5 * beat },
|
|
39
|
-
{ note: 'D4', time: 3 * beat },
|
|
40
|
-
{ note: 'F4', time: 4 * beat },
|
|
41
|
-
{ note: 'G4', time: 4.5 * beat },
|
|
42
|
-
{ note: 'A4', time: 5 * beat },
|
|
43
|
-
{ note: 'Bb4', time: 6 * beat },
|
|
44
|
-
{ note: 'F4', time: 7 * beat }
|
|
45
|
-
];
|
|
46
|
-
|
|
47
|
-
melody.forEach(({ note, time }) => {
|
|
48
|
-
if (time < 5) piano.triggerAttackRelease(note, '8n', time);
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
</script>
|
|
52
|
-
</body>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>TuneFrames — Piano</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":100,"duration":"5s"}</div>
|
|
9
|
+
<script>
|
|
10
|
+
async function main() {
|
|
11
|
+
await Tone.start();
|
|
12
|
+
const beat = Tone.Time('4n').toSeconds();
|
|
13
|
+
|
|
14
|
+
const piano = new Tone.PolySynth(Tone.Synth, {
|
|
15
|
+
oscillator: { type: 'triangle' },
|
|
16
|
+
envelope: { attack: 0.005, decay: 0.3, sustain: 0.3, release: 1.2 }
|
|
17
|
+
}).toDestination();
|
|
18
|
+
piano.volume.value = -8;
|
|
19
|
+
|
|
20
|
+
// Dm - Am - Bb - F chord progression, 2 bars each
|
|
21
|
+
const chords = [
|
|
22
|
+
['D3', 'F3', 'A3'], // Dm
|
|
23
|
+
['A2', 'C3', 'E3'], // Am
|
|
24
|
+
['Bb2', 'D3', 'F3'], // Bb
|
|
25
|
+
['F2', 'A2', 'C3'] // F
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
chords.forEach((chord, i) => {
|
|
29
|
+
piano.triggerAttackRelease(chord, '2n', i * 2 * beat);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Melodic line: simple pentatonic over the changes
|
|
33
|
+
const melody = [
|
|
34
|
+
{ note: 'D4', time: 0 },
|
|
35
|
+
{ note: 'F4', time: beat },
|
|
36
|
+
{ note: 'A4', time: beat * 2 },
|
|
37
|
+
{ note: 'C4', time: 2 * beat },
|
|
38
|
+
{ note: 'E4', time: 2.5 * beat },
|
|
39
|
+
{ note: 'D4', time: 3 * beat },
|
|
40
|
+
{ note: 'F4', time: 4 * beat },
|
|
41
|
+
{ note: 'G4', time: 4.5 * beat },
|
|
42
|
+
{ note: 'A4', time: 5 * beat },
|
|
43
|
+
{ note: 'Bb4', time: 6 * beat },
|
|
44
|
+
{ note: 'F4', time: 7 * beat }
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
melody.forEach(({ note, time }) => {
|
|
48
|
+
if (time < 5) piano.triggerAttackRelease(note, '8n', time);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
</script>
|
|
52
|
+
</body>
|
|
53
53
|
</html>
|
|
Binary file
|
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
<title>TuneFrames - Techno</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":130,"duration":"4s"}</div>
|
|
9
|
-
<script>
|
|
10
|
-
async function main() {
|
|
11
|
-
await Tone.start();
|
|
12
|
-
const beat = Tone.Time('4n').toSeconds();
|
|
13
|
-
|
|
14
|
-
const comp = new Tone.Compressor(-18, 5).toDestination();
|
|
15
|
-
const verb = new Tone.Reverb({ decay: 1, wet: 0.3 }).toDestination();
|
|
16
|
-
|
|
17
|
-
// Kick - 4-on-the-floor
|
|
18
|
-
const kick = new Tone.MembraneSynth({
|
|
19
|
-
pitchDecay: 0.05, octaves: 6,
|
|
20
|
-
oscillator: { type: 'sine' },
|
|
21
|
-
envelope: { attack: 0.001, decay: 0.3, sustain: 0, release: 0.1 }
|
|
22
|
-
}).connect(comp);
|
|
23
|
-
|
|
24
|
-
// Hi-hat: NoiseSynth (white noise + filter envelope)
|
|
25
|
-
const hihat = new Tone.NoiseSynth({
|
|
26
|
-
noise: { type: 'white' },
|
|
27
|
-
envelope: { attack: 0.001, decay: 0.03, sustain: 0, release: 0.01 }
|
|
28
|
-
}).toDestination();
|
|
29
|
-
hihat.volume.value = -10;
|
|
30
|
-
|
|
31
|
-
// Bass: detuned saws
|
|
32
|
-
const bass = new Tone.MonoSynth({
|
|
33
|
-
oscillator: { type: 'sawtooth' },
|
|
34
|
-
filter: { Q: 3, type: 'lowpass', rolloff: -24 },
|
|
35
|
-
envelope: { attack: 0.005, decay: 0.15, sustain: 0.5, release: 0.1 },
|
|
36
|
-
filterEnvelope: { attack: 0.001, decay: 0.1, sustain: 0.3, release: 0.2, baseFrequency: 80, octaves: 4 }
|
|
37
|
-
}).connect(comp);
|
|
38
|
-
|
|
39
|
-
// Pad: parallel saw chords
|
|
40
|
-
const pad = new Tone.PolySynth(Tone.Synth, {
|
|
41
|
-
oscillator: { type: 'sawtooth' },
|
|
42
|
-
envelope: { attack: 0.2, decay: 0.4, sustain: 0.7, release: 0.8 }
|
|
43
|
-
}).connect(verb);
|
|
44
|
-
pad.volume.value = -14;
|
|
45
|
-
|
|
46
|
-
// Kick: 4-on-the-floor every beat
|
|
47
|
-
for (let i = 0; i < 8; i++) {
|
|
48
|
-
kick.triggerAttackRelease('C1', '8n', i * beat);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Hi-hat: offbeat 16ths (16 events)
|
|
52
|
-
for (let i = 0; i < 16; i++) {
|
|
53
|
-
hihat.triggerAttackRelease('16n', (i + 0.5) * beat * 0.5);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Bass: syncopated
|
|
57
|
-
const bassPat = ['E1', 'E1', null, 'G1', 'A1', null, 'B1', 'A1', 'G1', null, 'E1', 'E1'];
|
|
58
|
-
bassPat.forEach((note, i) => {
|
|
59
|
-
if (note) bass.triggerAttackRelease(note, '8n', i * beat * 0.5);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
// Pad: slow chord stabs
|
|
63
|
-
pad.triggerAttackRelease(['E2', 'G2', 'B2'], '2n', 0);
|
|
64
|
-
pad.triggerAttackRelease(['C2', 'E2', 'G2'], '2n', beat * 4);
|
|
65
|
-
pad.triggerAttackRelease(['D2', 'F2', 'A2'], '2n', beat * 6);
|
|
66
|
-
}
|
|
67
|
-
</script>
|
|
68
|
-
</body>
|
|
69
|
-
</html>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>TuneFrames - Techno</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":130,"duration":"4s"}</div>
|
|
9
|
+
<script>
|
|
10
|
+
async function main() {
|
|
11
|
+
await Tone.start();
|
|
12
|
+
const beat = Tone.Time('4n').toSeconds();
|
|
13
|
+
|
|
14
|
+
const comp = new Tone.Compressor(-18, 5).toDestination();
|
|
15
|
+
const verb = new Tone.Reverb({ decay: 1, wet: 0.3 }).toDestination();
|
|
16
|
+
|
|
17
|
+
// Kick - 4-on-the-floor
|
|
18
|
+
const kick = new Tone.MembraneSynth({
|
|
19
|
+
pitchDecay: 0.05, octaves: 6,
|
|
20
|
+
oscillator: { type: 'sine' },
|
|
21
|
+
envelope: { attack: 0.001, decay: 0.3, sustain: 0, release: 0.1 }
|
|
22
|
+
}).connect(comp);
|
|
23
|
+
|
|
24
|
+
// Hi-hat: NoiseSynth (white noise + filter envelope)
|
|
25
|
+
const hihat = new Tone.NoiseSynth({
|
|
26
|
+
noise: { type: 'white' },
|
|
27
|
+
envelope: { attack: 0.001, decay: 0.03, sustain: 0, release: 0.01 }
|
|
28
|
+
}).toDestination();
|
|
29
|
+
hihat.volume.value = -10;
|
|
30
|
+
|
|
31
|
+
// Bass: detuned saws
|
|
32
|
+
const bass = new Tone.MonoSynth({
|
|
33
|
+
oscillator: { type: 'sawtooth' },
|
|
34
|
+
filter: { Q: 3, type: 'lowpass', rolloff: -24 },
|
|
35
|
+
envelope: { attack: 0.005, decay: 0.15, sustain: 0.5, release: 0.1 },
|
|
36
|
+
filterEnvelope: { attack: 0.001, decay: 0.1, sustain: 0.3, release: 0.2, baseFrequency: 80, octaves: 4 }
|
|
37
|
+
}).connect(comp);
|
|
38
|
+
|
|
39
|
+
// Pad: parallel saw chords
|
|
40
|
+
const pad = new Tone.PolySynth(Tone.Synth, {
|
|
41
|
+
oscillator: { type: 'sawtooth' },
|
|
42
|
+
envelope: { attack: 0.2, decay: 0.4, sustain: 0.7, release: 0.8 }
|
|
43
|
+
}).connect(verb);
|
|
44
|
+
pad.volume.value = -14;
|
|
45
|
+
|
|
46
|
+
// Kick: 4-on-the-floor every beat
|
|
47
|
+
for (let i = 0; i < 8; i++) {
|
|
48
|
+
kick.triggerAttackRelease('C1', '8n', i * beat);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Hi-hat: offbeat 16ths (16 events)
|
|
52
|
+
for (let i = 0; i < 16; i++) {
|
|
53
|
+
hihat.triggerAttackRelease('16n', (i + 0.5) * beat * 0.5);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Bass: syncopated
|
|
57
|
+
const bassPat = ['E1', 'E1', null, 'G1', 'A1', null, 'B1', 'A1', 'G1', null, 'E1', 'E1'];
|
|
58
|
+
bassPat.forEach((note, i) => {
|
|
59
|
+
if (note) bass.triggerAttackRelease(note, '8n', i * beat * 0.5);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Pad: slow chord stabs
|
|
63
|
+
pad.triggerAttackRelease(['E2', 'G2', 'B2'], '2n', 0);
|
|
64
|
+
pad.triggerAttackRelease(['C2', 'E2', 'G2'], '2n', beat * 4);
|
|
65
|
+
pad.triggerAttackRelease(['D2', 'F2', 'A2'], '2n', beat * 6);
|
|
66
|
+
}
|
|
67
|
+
</script>
|
|
68
|
+
</body>
|
|
69
|
+
</html>
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,37 +1,42 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "tuneframes",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Agent-native music generation. Write Tone.js, render to audio.",
|
|
5
|
-
"main": "src/cli.js",
|
|
6
|
-
"bin": {
|
|
7
|
-
"tuneframes": "src/cli.js"
|
|
8
|
-
},
|
|
9
|
-
"scripts": {
|
|
10
|
-
"render": "node src/cli.js render",
|
|
11
|
-
"preview": "node src/cli.js preview"
|
|
12
|
-
},
|
|
13
|
-
"files": [
|
|
14
|
-
"src",
|
|
15
|
-
"examples"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "tuneframes",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Agent-native music generation. Write Tone.js, render to audio.",
|
|
5
|
+
"main": "src/cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"tuneframes": "src/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"render": "node src/cli.js render",
|
|
11
|
+
"preview": "node src/cli.js preview"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"src",
|
|
15
|
+
"examples",
|
|
16
|
+
"registry",
|
|
17
|
+
"skills"
|
|
18
|
+
],
|
|
19
|
+
"keywords": [
|
|
20
|
+
"tonejs",
|
|
21
|
+
"music",
|
|
22
|
+
"audio",
|
|
23
|
+
"generation",
|
|
24
|
+
"agents",
|
|
25
|
+
"web-audio",
|
|
26
|
+
"soundfont",
|
|
27
|
+
"sampler",
|
|
28
|
+
"instruments"
|
|
29
|
+
],
|
|
30
|
+
"author": "Nathan Shepherd",
|
|
31
|
+
"license": "Apache-2.0",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/Shepherd217/TuneFrames.git"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"playwright": "^1.44.0"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
|
|
5
|
+
</head>
|
|
6
|
+
<body>
|
|
7
|
+
<!-- Walking bass line in C minor — acoustic_bass from gleitz CDN -->
|
|
8
|
+
<!-- 2 bars at 100 BPM: 1 beat = 0.6s, 8 quarter notes = 4.8s -->
|
|
9
|
+
<div id="tuneframes" style="display:none">{"bpm":100,"duration":"6s"}</div>
|
|
10
|
+
<script>
|
|
11
|
+
async function main() {
|
|
12
|
+
await Tone.start();
|
|
13
|
+
|
|
14
|
+
// Load acoustic bass samples from gleitz FluidR3_GM CDN
|
|
15
|
+
const bass = new Tone.Sampler({
|
|
16
|
+
urls: {
|
|
17
|
+
C1: 'C1.mp3',
|
|
18
|
+
Ds1: 'Ds1.mp3',
|
|
19
|
+
Fs1: 'Fs1.mp3',
|
|
20
|
+
A1: 'A1.mp3',
|
|
21
|
+
C2: 'C2.mp3',
|
|
22
|
+
Ds2: 'Ds2.mp3',
|
|
23
|
+
Fs2: 'Fs2.mp3',
|
|
24
|
+
A2: 'A2.mp3',
|
|
25
|
+
C3: 'C3.mp3',
|
|
26
|
+
Ds3: 'Ds3.mp3',
|
|
27
|
+
Fs3: 'Fs3.mp3',
|
|
28
|
+
A3: 'A3.mp3',
|
|
29
|
+
C4: 'C4.mp3'
|
|
30
|
+
},
|
|
31
|
+
baseUrl: 'https://gleitz.github.io/midi-js-soundfonts/FluidR3_GM/acoustic_bass-mp3/'
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Subtle compression to even out the pluck dynamics
|
|
35
|
+
const comp = new Tone.Compressor({ threshold: -18, ratio: 3 }).toDestination();
|
|
36
|
+
bass.connect(comp);
|
|
37
|
+
|
|
38
|
+
// CRITICAL: wait for samples to load before scheduling any notes
|
|
39
|
+
await Tone.loaded();
|
|
40
|
+
|
|
41
|
+
const beat = Tone.Time('4n').toSeconds(); // 0.6s at 100 BPM
|
|
42
|
+
const dur = '4n'; // each walking note lasts one quarter note
|
|
43
|
+
|
|
44
|
+
// Walking bass in C minor — 2 bars, 4 notes per bar
|
|
45
|
+
// Bar 1: root → approach from below → fifth → seventh (classic walking move)
|
|
46
|
+
// Bar 2: resolve back down with chromatic approach to the root
|
|
47
|
+
const line = [
|
|
48
|
+
// Bar 1 — Cm feel
|
|
49
|
+
'C2', // root
|
|
50
|
+
'Eb2', // minor third
|
|
51
|
+
'G2', // fifth
|
|
52
|
+
'Bb2', // minor seventh
|
|
53
|
+
|
|
54
|
+
// Bar 2 — Fm → G7 turnaround, walking back to root
|
|
55
|
+
'Ab2', // flat-six (Fm)
|
|
56
|
+
'G2', // fifth (G dominant)
|
|
57
|
+
'F2', // approach
|
|
58
|
+
'Eb2' // land on minor third, implies return to Cm
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
line.forEach((note, i) => {
|
|
62
|
+
bass.triggerAttackRelease(note, dur, i * beat);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
</script>
|
|
66
|
+
</body>
|
|
67
|
+
</html>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!-- TuneFrames preset: bass-saw
|
|
2
|
+
Detuned saw MonoSynth bass. Paste inside <script>.
|
|
3
|
+
Set duration to cover the number of beats you've scheduled. -->
|
|
4
|
+
<script>
|
|
5
|
+
const beat = Tone.Time('4n').toSeconds();
|
|
6
|
+
|
|
7
|
+
const bass = new Tone.MonoSynth({
|
|
8
|
+
oscillator: { type: 'sawtooth' },
|
|
9
|
+
filter: { Q: 4, type: 'lowpass', rolloff: -24 },
|
|
10
|
+
envelope: { attack: 0.005, decay: 0.2, sustain: 0.6, release: 0.15 },
|
|
11
|
+
filterEnvelope: {
|
|
12
|
+
attack: 0.001, decay: 0.1, sustain: 0.4, release: 0.2,
|
|
13
|
+
baseFrequency: 80, octaves: 4
|
|
14
|
+
}
|
|
15
|
+
}).toDestination();
|
|
16
|
+
bass.volume.value = -6;
|
|
17
|
+
|
|
18
|
+
const pattern = ['E1', null, 'E1', 'G1', 'A1', null, 'B1', 'A1', 'G1', null, 'E1', 'E1'];
|
|
19
|
+
pattern.forEach((note, i) => {
|
|
20
|
+
if (note) bass.triggerAttackRelease(note, '8n', i * beat * 0.5);
|
|
21
|
+
});
|
|
22
|
+
</script>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!-- TuneFrames preset: chord-progression
|
|
2
|
+
Dm → C → Bb → Am, 1 measure each.
|
|
3
|
+
Paste inside <script>. Set metadata duration to "4s" per chord (16s total for all 4). -->
|
|
4
|
+
<script>
|
|
5
|
+
const measure = Tone.Time('1n').toSeconds();
|
|
6
|
+
|
|
7
|
+
const pad = new Tone.PolySynth(Tone.Synth, {
|
|
8
|
+
oscillator: { type: 'sawtooth' },
|
|
9
|
+
envelope: { attack: 0.5, decay: 0.3, sustain: 0.7, release: 1.5 }
|
|
10
|
+
}).toDestination();
|
|
11
|
+
|
|
12
|
+
const progression = [
|
|
13
|
+
{ chord: ['D3', 'F3', 'A3'], name: 'Dm' },
|
|
14
|
+
{ chord: ['C3', 'E3', 'G3'], name: 'C' },
|
|
15
|
+
{ chord: ['Bb2', 'D3', 'F3'], name: 'Bb' },
|
|
16
|
+
{ chord: ['A2', 'C3', 'E3'], name: 'Am' },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
progression.forEach(({ chord }, i) => {
|
|
20
|
+
pad.triggerAttackRelease(chord, '1n', i * measure);
|
|
21
|
+
});
|
|
22
|
+
</script>
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
|
|
5
|
+
</head>
|
|
6
|
+
<body>
|
|
7
|
+
<!-- Classic trap 808 pattern — synthesis only, no CDN required -->
|
|
8
|
+
<!-- 2 bars at 140 BPM: 1 bar = 4 beats × (60/140)s ≈ 1.714s → 2 bars ≈ 3.43s -->
|
|
9
|
+
<div id="tuneframes" style="display:none">{"bpm":140,"duration":"7s"}</div>
|
|
10
|
+
<script>
|
|
11
|
+
async function main() {
|
|
12
|
+
await Tone.start();
|
|
13
|
+
|
|
14
|
+
// --- 808 Kick: pitched MembraneSynth with long pitch fall ---
|
|
15
|
+
const kick808 = new Tone.MembraneSynth({
|
|
16
|
+
pitchDecay: 0.5,
|
|
17
|
+
octaves: 8,
|
|
18
|
+
envelope: { attack: 0.001, decay: 0.8, sustain: 0, release: 0.4 }
|
|
19
|
+
}).toDestination();
|
|
20
|
+
kick808.volume.value = 2;
|
|
21
|
+
|
|
22
|
+
// --- Snare: layered noise (body) + click (transient) ---
|
|
23
|
+
const snareNoise = new Tone.NoiseSynth({
|
|
24
|
+
noise: { type: 'white' },
|
|
25
|
+
envelope: { attack: 0.001, decay: 0.18, sustain: 0, release: 0.05 }
|
|
26
|
+
});
|
|
27
|
+
const snareFilter = new Tone.Filter({ frequency: 3500, type: 'highpass' }).toDestination();
|
|
28
|
+
snareNoise.connect(snareFilter);
|
|
29
|
+
snareNoise.volume.value = -4;
|
|
30
|
+
|
|
31
|
+
// --- Hi-hat: MetalSynth for crisp metallic transient ---
|
|
32
|
+
const hihat = new Tone.MetalSynth({
|
|
33
|
+
frequency: 400,
|
|
34
|
+
envelope: { attack: 0.001, decay: 0.04, release: 0.01 },
|
|
35
|
+
harmonicity: 5.1,
|
|
36
|
+
modulationIndex: 32,
|
|
37
|
+
resonance: 4000,
|
|
38
|
+
octaves: 1.5
|
|
39
|
+
}).toDestination();
|
|
40
|
+
hihat.volume.value = -10;
|
|
41
|
+
|
|
42
|
+
// Open hi-hat: longer decay
|
|
43
|
+
const openHat = new Tone.MetalSynth({
|
|
44
|
+
frequency: 500,
|
|
45
|
+
envelope: { attack: 0.001, decay: 0.3, release: 0.1 },
|
|
46
|
+
harmonicity: 5.1,
|
|
47
|
+
modulationIndex: 32,
|
|
48
|
+
resonance: 4000,
|
|
49
|
+
octaves: 1.5
|
|
50
|
+
}).toDestination();
|
|
51
|
+
openHat.volume.value = -14;
|
|
52
|
+
|
|
53
|
+
const beat = Tone.Time('4n').toSeconds(); // one beat in seconds at 140 BPM
|
|
54
|
+
const bar = beat * 4;
|
|
55
|
+
|
|
56
|
+
// Render 2 bars of trap pattern
|
|
57
|
+
for (let b = 0; b < 2; b++) {
|
|
58
|
+
const o = b * bar;
|
|
59
|
+
|
|
60
|
+
// --- 808 kick: beats 1, 2.5, 3 (classic trap syncopation) ---
|
|
61
|
+
kick808.triggerAttackRelease('C1', '8n', o);
|
|
62
|
+
kick808.triggerAttackRelease('C1', '8n', o + beat * 1.5);
|
|
63
|
+
kick808.triggerAttackRelease('C1', '8n', o + beat * 2);
|
|
64
|
+
|
|
65
|
+
// --- Snare: beat 3 (half-time feel) ---
|
|
66
|
+
snareNoise.triggerAttackRelease('8n', o + beat * 2);
|
|
67
|
+
|
|
68
|
+
// --- Hi-hats: 16th notes throughout, open hat on beat 3-and ---
|
|
69
|
+
const sixteenth = beat / 4;
|
|
70
|
+
for (let s = 0; s < 16; s++) {
|
|
71
|
+
// Skip the snare slot (beat 3) on closed hats so open hat rings
|
|
72
|
+
if (s === 8) {
|
|
73
|
+
openHat.triggerAttackRelease('C5', '8n', o + s * sixteenth);
|
|
74
|
+
} else {
|
|
75
|
+
// Accent downbeats slightly
|
|
76
|
+
const vel = (s % 4 === 0) ? 1.0 : (s % 2 === 0 ? 0.6 : 0.35);
|
|
77
|
+
hihat.volume.value = -10 + (vel - 1) * 4;
|
|
78
|
+
hihat.triggerAttackRelease('C5', '32n', o + s * sixteenth);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
</script>
|
|
84
|
+
</body>
|
|
85
|
+
</html>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<!-- TuneFrames preset: drums-lofi
|
|
2
|
+
Kick + snare + hi-hat loop. Paste inside <script> in your composition. -->
|
|
3
|
+
<script>
|
|
4
|
+
const beat = Tone.Time('4n').toSeconds();
|
|
5
|
+
|
|
6
|
+
const kick = new Tone.MembraneSynth({
|
|
7
|
+
pitchDecay: 0.05, octaves: 6,
|
|
8
|
+
oscillator: { type: 'sine' },
|
|
9
|
+
envelope: { attack: 0.001, decay: 0.4, sustain: 0, release: 0.1 }
|
|
10
|
+
}).toDestination();
|
|
11
|
+
|
|
12
|
+
const snare = new Tone.NoiseSynth({
|
|
13
|
+
noise: { type: 'white' },
|
|
14
|
+
envelope: { attack: 0.001, decay: 0.2, sustain: 0, release: 0.1 }
|
|
15
|
+
}).toDestination();
|
|
16
|
+
|
|
17
|
+
const hihat = new Tone.NoiseSynth({
|
|
18
|
+
noise: { type: 'white' },
|
|
19
|
+
envelope: { attack: 0.001, decay: 0.04, sustain: 0, release: 0.01 }
|
|
20
|
+
}).toDestination();
|
|
21
|
+
hihat.volume.value = -14;
|
|
22
|
+
|
|
23
|
+
// Kick on 1, 2, 3, 4
|
|
24
|
+
for (let i = 0; i < 4; i++) {
|
|
25
|
+
kick.triggerAttackRelease('C1', '4n', i * beat);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Snare on 2 and 4
|
|
29
|
+
[1, 3].forEach(i => snare.triggerAttackRelease('C3', '4n', i * beat));
|
|
30
|
+
|
|
31
|
+
// Hi-hat on every 8th note
|
|
32
|
+
for (let i = 0; i < 8; i++) {
|
|
33
|
+
hihat.triggerAttackRelease('16n', i * beat * 0.5);
|
|
34
|
+
}
|
|
35
|
+
</script>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<!-- TuneFrames preset: lead-piano
|
|
2
|
+
Simple 8-bar piano melody. Paste inside <script>. -->
|
|
3
|
+
<script>
|
|
4
|
+
const beat = Tone.Time('4n').toSeconds();
|
|
5
|
+
|
|
6
|
+
const piano = new Tone.PolySynth(Tone.Synth, {
|
|
7
|
+
oscillator: { type: 'triangle' },
|
|
8
|
+
envelope: { attack: 0.005, decay: 0.3, sustain: 0.4, release: 0.8 }
|
|
9
|
+
}).toDestination();
|
|
10
|
+
piano.volume.value = -8;
|
|
11
|
+
|
|
12
|
+
const melody = [
|
|
13
|
+
{ note: 'C5', time: 0 },
|
|
14
|
+
{ note: 'E5', time: 1 },
|
|
15
|
+
{ note: 'G5', time: 2 },
|
|
16
|
+
{ note: 'E5', time: 3 },
|
|
17
|
+
{ note: 'D5', time: 4 },
|
|
18
|
+
{ note: 'F5', time: 5 },
|
|
19
|
+
{ note: 'A5', time: 6 },
|
|
20
|
+
{ note: 'F5', time: 7 },
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
melody.forEach(({ note, time }) => {
|
|
24
|
+
piano.triggerAttackRelease(note, '4n', time * beat);
|
|
25
|
+
});
|
|
26
|
+
</script>
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
|
|
5
|
+
</head>
|
|
6
|
+
<body>
|
|
7
|
+
<!-- Am - F - C - G chord progression, 4 chords × 2s = 8s -->
|
|
8
|
+
<div id="tuneframes" style="display:none">{"bpm":120,"duration":"9s"}</div>
|
|
9
|
+
<script>
|
|
10
|
+
async function main() {
|
|
11
|
+
await Tone.start();
|
|
12
|
+
|
|
13
|
+
// Load acoustic grand piano samples from gleitz FluidR3_GM CDN
|
|
14
|
+
const piano = new Tone.Sampler({
|
|
15
|
+
urls: {
|
|
16
|
+
A0: 'A0.mp3',
|
|
17
|
+
C2: 'C2.mp3',
|
|
18
|
+
Ds2: 'Ds2.mp3',
|
|
19
|
+
Fs2: 'Fs2.mp3',
|
|
20
|
+
A2: 'A2.mp3',
|
|
21
|
+
C3: 'C3.mp3',
|
|
22
|
+
Ds3: 'Ds3.mp3',
|
|
23
|
+
Fs3: 'Fs3.mp3',
|
|
24
|
+
A3: 'A3.mp3',
|
|
25
|
+
C4: 'C4.mp3',
|
|
26
|
+
Ds4: 'Ds4.mp3',
|
|
27
|
+
Fs4: 'Fs4.mp3',
|
|
28
|
+
A4: 'A4.mp3',
|
|
29
|
+
C5: 'C5.mp3',
|
|
30
|
+
Ds5: 'Ds5.mp3',
|
|
31
|
+
Fs5: 'Fs5.mp3',
|
|
32
|
+
A5: 'A5.mp3',
|
|
33
|
+
C6: 'C6.mp3',
|
|
34
|
+
A6: 'A6.mp3',
|
|
35
|
+
C7: 'C7.mp3',
|
|
36
|
+
C8: 'C8.mp3'
|
|
37
|
+
},
|
|
38
|
+
baseUrl: 'https://gleitz.github.io/midi-js-soundfonts/FluidR3_GM/acoustic_grand_piano-mp3/'
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Light reverb for hall ambience
|
|
42
|
+
const reverb = new Tone.Reverb({ decay: 2.5, wet: 0.25 }).toDestination();
|
|
43
|
+
piano.connect(reverb);
|
|
44
|
+
|
|
45
|
+
// CRITICAL: wait for all samples to load before scheduling any notes
|
|
46
|
+
await Tone.loaded();
|
|
47
|
+
|
|
48
|
+
// Am - F - C - G, 2 seconds each, 8 seconds total
|
|
49
|
+
// Voicings are close-position triads with a bass note for warmth
|
|
50
|
+
const chords = [
|
|
51
|
+
{ notes: ['A2', 'E3', 'A3', 'C4', 'E4'], label: 'Am' },
|
|
52
|
+
{ notes: ['F2', 'C3', 'F3', 'A3', 'C4'], label: 'F' },
|
|
53
|
+
{ notes: ['C3', 'G3', 'C4', 'E4', 'G4'], label: 'C' },
|
|
54
|
+
{ notes: ['G2', 'D3', 'G3', 'B3', 'D4'], label: 'G' }
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
const chordDuration = 2; // seconds per chord
|
|
58
|
+
const noteDuration = '1n'; // sustain each chord for a whole note
|
|
59
|
+
|
|
60
|
+
chords.forEach((chord, i) => {
|
|
61
|
+
const t = i * chordDuration;
|
|
62
|
+
// Arpeggiate the bass note very slightly ahead of the chord for a natural feel
|
|
63
|
+
piano.triggerAttackRelease(chord.notes[0], noteDuration, t);
|
|
64
|
+
piano.triggerAttackRelease(chord.notes.slice(1), noteDuration, t + 0.04);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
</script>
|
|
68
|
+
</body>
|
|
69
|
+
</html>
|