tuneframes 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +167 -199
- package/README.md +150 -97
- package/examples/example-ai-dj-chill.html +167 -0
- package/examples/example-ai-dj-dark.html +167 -0
- package/examples/example-ai-dj-energetic.html +167 -0
- package/examples/example-ai-dj-happy.html +167 -0
- package/examples/example-ai-dj.html +167 -0
- package/examples/example-ambient.html +63 -63
- package/examples/example-ambient.mp3 +0 -0
- package/examples/example-bass.html +48 -0
- package/examples/example-lofi.html +45 -45
- package/examples/example-lofi.mp3 +0 -0
- package/examples/example-minimal.html +21 -19
- package/examples/example-minimal.mp3 +0 -0
- package/examples/example-orchestral.html +67 -67
- package/examples/example-orchestral.mp3 +0 -0
- package/examples/example-piano.html +53 -0
- package/examples/example-piano.mp3 +0 -0
- package/examples/example-techno.html +69 -69
- package/examples/example-techno.mp3 +0 -0
- package/package.json +42 -24
- package/registry/presets/bass-electric.html +67 -0
- package/registry/presets/bass-saw.html +22 -0
- package/registry/presets/chord-progression.html +22 -0
- package/registry/presets/drums-808.html +85 -0
- package/registry/presets/drums-lofi.html +35 -0
- package/registry/presets/lead-piano.html +26 -0
- package/registry/presets/piano-salamander.html +69 -0
- package/registry/presets/reverb-warm.html +11 -0
- package/registry/samples.json +226 -0
- package/skills/audio-ambient/SKILL.md +141 -0
- package/skills/audio-ambient/example.html +113 -0
- package/skills/audio-boss-battle/SKILL.md +157 -0
- package/skills/audio-boss-battle/example.html +185 -0
- package/skills/audio-boss-battle/example.mp3 +0 -0
- package/skills/audio-chillwave/SKILL.md +142 -0
- package/skills/audio-chillwave/example.html +144 -0
- package/skills/audio-cinematic/SKILL.md +147 -0
- package/skills/audio-cinematic/example.html +123 -0
- package/skills/audio-classical/SKILL.md +138 -0
- package/skills/audio-classical/example.html +145 -0
- package/skills/audio-dnb/SKILL.md +142 -0
- package/skills/audio-dnb/example.html +124 -0
- package/skills/audio-downtempo/SKILL.md +152 -0
- package/skills/audio-downtempo/example.html +164 -0
- package/skills/audio-folk/SKILL.md +139 -0
- package/skills/audio-folk/example.html +132 -0
- package/skills/audio-funk/SKILL.md +149 -0
- package/skills/audio-funk/example.html +144 -0
- package/skills/audio-future-bass/SKILL.md +163 -0
- package/skills/audio-future-bass/example.html +164 -0
- package/skills/audio-hip-hop/SKILL.md +133 -0
- package/skills/audio-hip-hop/example.html +129 -0
- package/skills/audio-house/SKILL.md +147 -0
- package/skills/audio-house/example.html +128 -0
- package/skills/audio-indie-pop/SKILL.md +150 -0
- package/skills/audio-indie-pop/example.html +121 -0
- package/skills/audio-jazz/SKILL.md +141 -0
- package/skills/audio-jazz/example.html +146 -0
- package/skills/audio-lofi/SKILL.md +140 -0
- package/skills/audio-lofi/example.html +135 -0
- package/skills/audio-minimal/SKILL.md +155 -0
- package/skills/audio-minimal/example.html +118 -0
- package/skills/audio-orchestral/SKILL.md +156 -0
- package/skills/audio-orchestral/example.html +140 -0
- package/skills/audio-r-and-b/SKILL.md +134 -0
- package/skills/audio-r-and-b/example.html +154 -0
- package/skills/audio-r-and-b/example.mp3 +0 -0
- package/skills/audio-techno/SKILL.md +140 -0
- package/skills/audio-techno/example.html +125 -0
- package/skills/audio-trap/SKILL.md +123 -0
- package/skills/audio-trap/example.html +119 -0
- package/skills/render-retest/example.html +115 -0
- package/skills/tuneframes/SKILL.md +221 -0
- package/skills/tuneframes-cli/SKILL.md +46 -0
- package/skills/verify/example.html +104 -0
- package/src/cli.js +261 -89
- package/src/render.js +134 -7
|
@@ -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":"
|
|
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,19 +1,21 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
<title>TuneFrames — Minimal
|
|
5
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
|
|
6
|
-
</head>
|
|
7
|
-
<body>
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
async function main() {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
//
|
|
15
|
-
synth.triggerAttackRelease('C4', '4n', 0);
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
</
|
|
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
|
+
</html>
|
|
Binary file
|
|
@@ -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":"
|
|
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>
|
|
Binary file
|
|
@@ -0,0 +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>
|
|
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":"
|
|
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,24 +1,42 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "tuneframes",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
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>
|