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.
Files changed (80) hide show
  1. package/LICENSE +166 -166
  2. package/README.md +158 -55
  3. package/examples/example-ai-dj-chill.html +167 -0
  4. package/examples/example-ai-dj-dark.html +167 -0
  5. package/examples/example-ai-dj-energetic.html +167 -0
  6. package/examples/example-ai-dj-happy.html +167 -0
  7. package/examples/example-ai-dj.html +167 -0
  8. package/examples/example-ambient.html +63 -63
  9. package/examples/example-ambient.mp3 +0 -0
  10. package/examples/example-bass.html +47 -47
  11. package/examples/example-lofi.html +45 -45
  12. package/examples/example-lofi.mp3 +0 -0
  13. package/examples/example-minimal.html +20 -20
  14. package/examples/example-orchestral.html +67 -67
  15. package/examples/example-orchestral.mp3 +0 -0
  16. package/examples/example-piano.html +52 -52
  17. package/examples/example-piano.mp3 +0 -0
  18. package/examples/example-techno.html +69 -69
  19. package/examples/example-techno.mp3 +0 -0
  20. package/package.json +42 -37
  21. package/registry/presets/bass-electric.html +67 -0
  22. package/registry/presets/bass-saw.html +22 -0
  23. package/registry/presets/chord-progression.html +22 -0
  24. package/registry/presets/drums-808.html +85 -0
  25. package/registry/presets/drums-lofi.html +35 -0
  26. package/registry/presets/lead-piano.html +26 -0
  27. package/registry/presets/piano-salamander.html +69 -0
  28. package/registry/presets/reverb-warm.html +11 -0
  29. package/registry/samples.json +226 -0
  30. package/skills/audio-ambient/SKILL.md +141 -0
  31. package/skills/audio-ambient/example.html +113 -0
  32. package/skills/audio-boss-battle/SKILL.md +157 -0
  33. package/skills/audio-boss-battle/example.html +185 -0
  34. package/skills/audio-boss-battle/example.mp3 +0 -0
  35. package/skills/audio-chillwave/SKILL.md +142 -0
  36. package/skills/audio-chillwave/example.html +144 -0
  37. package/skills/audio-cinematic/SKILL.md +147 -0
  38. package/skills/audio-cinematic/example.html +123 -0
  39. package/skills/audio-classical/SKILL.md +138 -0
  40. package/skills/audio-classical/example.html +145 -0
  41. package/skills/audio-dnb/SKILL.md +142 -0
  42. package/skills/audio-dnb/example.html +124 -0
  43. package/skills/audio-downtempo/SKILL.md +152 -0
  44. package/skills/audio-downtempo/example.html +164 -0
  45. package/skills/audio-folk/SKILL.md +139 -0
  46. package/skills/audio-folk/example.html +132 -0
  47. package/skills/audio-funk/SKILL.md +149 -0
  48. package/skills/audio-funk/example.html +144 -0
  49. package/skills/audio-future-bass/SKILL.md +163 -0
  50. package/skills/audio-future-bass/example.html +164 -0
  51. package/skills/audio-hip-hop/SKILL.md +133 -0
  52. package/skills/audio-hip-hop/example.html +129 -0
  53. package/skills/audio-house/SKILL.md +147 -0
  54. package/skills/audio-house/example.html +128 -0
  55. package/skills/audio-indie-pop/SKILL.md +150 -0
  56. package/skills/audio-indie-pop/example.html +121 -0
  57. package/skills/audio-jazz/SKILL.md +141 -0
  58. package/skills/audio-jazz/example.html +146 -0
  59. package/skills/audio-lofi/SKILL.md +140 -0
  60. package/skills/audio-lofi/example.html +135 -0
  61. package/skills/audio-minimal/SKILL.md +155 -0
  62. package/skills/audio-minimal/example.html +118 -0
  63. package/skills/audio-orchestral/SKILL.md +156 -0
  64. package/skills/audio-orchestral/example.html +140 -0
  65. package/skills/audio-r-and-b/SKILL.md +134 -0
  66. package/skills/audio-r-and-b/example.html +154 -0
  67. package/skills/audio-r-and-b/example.mp3 +0 -0
  68. package/skills/audio-techno/SKILL.md +140 -0
  69. package/skills/audio-techno/example.html +125 -0
  70. package/skills/audio-trap/SKILL.md +123 -0
  71. package/skills/audio-trap/example.html +119 -0
  72. package/skills/render-retest/example.html +115 -0
  73. package/skills/tuneframes/SKILL.md +221 -0
  74. package/skills/tuneframes-cli/SKILL.md +46 -0
  75. package/skills/verify/example.html +104 -0
  76. package/src/cli.js +260 -151
  77. package/src/render.js +134 -7
  78. package/examples/example-bass.mp3 +0 -0
  79. package/examples/example-demo-beat.wav +0 -0
  80. 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>