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.
Files changed (78) hide show
  1. package/LICENSE +167 -199
  2. package/README.md +150 -97
  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 +48 -0
  11. package/examples/example-lofi.html +45 -45
  12. package/examples/example-lofi.mp3 +0 -0
  13. package/examples/example-minimal.html +21 -19
  14. package/examples/example-minimal.mp3 +0 -0
  15. package/examples/example-orchestral.html +67 -67
  16. package/examples/example-orchestral.mp3 +0 -0
  17. package/examples/example-piano.html +53 -0
  18. package/examples/example-piano.mp3 +0 -0
  19. package/examples/example-techno.html +69 -69
  20. package/examples/example-techno.mp3 +0 -0
  21. package/package.json +42 -24
  22. package/registry/presets/bass-electric.html +67 -0
  23. package/registry/presets/bass-saw.html +22 -0
  24. package/registry/presets/chord-progression.html +22 -0
  25. package/registry/presets/drums-808.html +85 -0
  26. package/registry/presets/drums-lofi.html +35 -0
  27. package/registry/presets/lead-piano.html +26 -0
  28. package/registry/presets/piano-salamander.html +69 -0
  29. package/registry/presets/reverb-warm.html +11 -0
  30. package/registry/samples.json +226 -0
  31. package/skills/audio-ambient/SKILL.md +141 -0
  32. package/skills/audio-ambient/example.html +113 -0
  33. package/skills/audio-boss-battle/SKILL.md +157 -0
  34. package/skills/audio-boss-battle/example.html +185 -0
  35. package/skills/audio-boss-battle/example.mp3 +0 -0
  36. package/skills/audio-chillwave/SKILL.md +142 -0
  37. package/skills/audio-chillwave/example.html +144 -0
  38. package/skills/audio-cinematic/SKILL.md +147 -0
  39. package/skills/audio-cinematic/example.html +123 -0
  40. package/skills/audio-classical/SKILL.md +138 -0
  41. package/skills/audio-classical/example.html +145 -0
  42. package/skills/audio-dnb/SKILL.md +142 -0
  43. package/skills/audio-dnb/example.html +124 -0
  44. package/skills/audio-downtempo/SKILL.md +152 -0
  45. package/skills/audio-downtempo/example.html +164 -0
  46. package/skills/audio-folk/SKILL.md +139 -0
  47. package/skills/audio-folk/example.html +132 -0
  48. package/skills/audio-funk/SKILL.md +149 -0
  49. package/skills/audio-funk/example.html +144 -0
  50. package/skills/audio-future-bass/SKILL.md +163 -0
  51. package/skills/audio-future-bass/example.html +164 -0
  52. package/skills/audio-hip-hop/SKILL.md +133 -0
  53. package/skills/audio-hip-hop/example.html +129 -0
  54. package/skills/audio-house/SKILL.md +147 -0
  55. package/skills/audio-house/example.html +128 -0
  56. package/skills/audio-indie-pop/SKILL.md +150 -0
  57. package/skills/audio-indie-pop/example.html +121 -0
  58. package/skills/audio-jazz/SKILL.md +141 -0
  59. package/skills/audio-jazz/example.html +146 -0
  60. package/skills/audio-lofi/SKILL.md +140 -0
  61. package/skills/audio-lofi/example.html +135 -0
  62. package/skills/audio-minimal/SKILL.md +155 -0
  63. package/skills/audio-minimal/example.html +118 -0
  64. package/skills/audio-orchestral/SKILL.md +156 -0
  65. package/skills/audio-orchestral/example.html +140 -0
  66. package/skills/audio-r-and-b/SKILL.md +134 -0
  67. package/skills/audio-r-and-b/example.html +154 -0
  68. package/skills/audio-r-and-b/example.mp3 +0 -0
  69. package/skills/audio-techno/SKILL.md +140 -0
  70. package/skills/audio-techno/example.html +125 -0
  71. package/skills/audio-trap/SKILL.md +123 -0
  72. package/skills/audio-trap/example.html +119 -0
  73. package/skills/render-retest/example.html +115 -0
  74. package/skills/tuneframes/SKILL.md +221 -0
  75. package/skills/tuneframes-cli/SKILL.md +46 -0
  76. package/skills/verify/example.html +104 -0
  77. package/src/cli.js +261 -89
  78. package/src/render.js +134 -7
@@ -0,0 +1,144 @@
1
+ <div id="tuneframes" style="display:none">{"bpm":98,"duration":"12s"}</div>
2
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
3
+ <script>
4
+ // TuneFrames — Chillwave / Retrowave
5
+ // A minor arpeggio over 80s drum machine, warm chorus + delay
6
+ // Nostalgic, hazy, neon-at-midnight
7
+
8
+ async function main() {
9
+ await Tone.start();
10
+
11
+ Tone.Transport.bpm.value = 98;
12
+
13
+ // ── Effects chain ──────────────────────────────────────────────────────────
14
+ const reverb = new Tone.Reverb({ decay: 3.5, preDelay: 0.02, wet: 0.5 });
15
+ await reverb.generate();
16
+ reverb.toDestination();
17
+
18
+ // Dotted-8th delay = the defining retrowave slap
19
+ const delay = new Tone.FeedbackDelay({ delayTime: '8n.', feedback: 0.32, wet: 0.38 });
20
+ delay.connect(reverb);
21
+
22
+ // Chorus — smears everything into warm tape haze
23
+ const chorus = new Tone.Chorus({ frequency: 1.4, delayTime: 3.5, depth: 0.72, wet: 0.62 }).start();
24
+ chorus.connect(delay);
25
+
26
+ // ── Arpeggio synth — sawtooth for shimmer ─────────────────────────────────
27
+ const arp = new Tone.Synth({
28
+ oscillator: { type: 'sawtooth' },
29
+ envelope: { attack: 0.01, decay: 0.12, sustain: 0.28, release: 0.45 },
30
+ volume: -6,
31
+ });
32
+ arp.connect(chorus);
33
+
34
+ // ── Background pad — PolySynth triangle, long attack ─────────────────────
35
+ const pad = new Tone.PolySynth(Tone.Synth, {
36
+ oscillator: { type: 'triangle' },
37
+ envelope: { attack: 0.8, decay: 0.5, sustain: 0.75, release: 2.5 },
38
+ volume: -18,
39
+ });
40
+ pad.connect(reverb);
41
+
42
+ // ── Kick — four-on-the-floor, enters at 2s ────────────────────────────────
43
+ const kick = new Tone.MembraneSynth({
44
+ pitchDecay: 0.06, octaves: 8,
45
+ envelope: { attack: 0.001, decay: 0.38, sustain: 0, release: 0.1 },
46
+ volume: -4,
47
+ }).toDestination();
48
+
49
+ // ── Hi-hat — 16th notes, slightly gated ───────────────────────────────────
50
+ const hihat = new Tone.NoiseSynth({
51
+ noise: { type: 'white' },
52
+ envelope: { attack: 0.001, decay: 0.04, sustain: 0, release: 0.01 },
53
+ volume: -17,
54
+ }).toDestination();
55
+
56
+ // ── Clap — sits on 2 and 4 ───────────────────────────────────────────────
57
+ const clap = new Tone.NoiseSynth({
58
+ noise: { type: 'pink' },
59
+ envelope: { attack: 0.001, decay: 0.13, sustain: 0, release: 0.08 },
60
+ volume: -10,
61
+ }).toDestination();
62
+
63
+ // ── Schedule arpeggio ─────────────────────────────────────────────────────
64
+ // A minor → F → C → G, 16th-note arpeggio pattern
65
+ // At 98 BPM, one 16th note = 60/98/4 ≈ 0.153s
66
+ const sixteenth = 60 / 98 / 4;
67
+ const arpNotes = [
68
+ // Am: A4 C5 E5 A5
69
+ 'A4','C5','E5','A5',
70
+ // F: F4 A4 C5 F5
71
+ 'F4','A4','C5','F5',
72
+ // C: C4 E4 G4 C5
73
+ 'C4','E4','G4','C5',
74
+ // G: G4 B4 D5 G5
75
+ 'G4','B4','D5','G5',
76
+ ];
77
+
78
+ // Four passes of the 16-note arpeggio
79
+ for (let pass = 0; pass < 4; pass++) {
80
+ const passStart = pass * arpNotes.length * sixteenth;
81
+ arpNotes.forEach((note, i) => {
82
+ arp.triggerAttackRelease(note, '16n', passStart + i * sixteenth);
83
+ });
84
+ }
85
+
86
+ // ── Pad chords — one per bar (Am, F, C, G, repeating) ────────────────────
87
+ const beat = 60 / 98;
88
+ const padChords = [
89
+ { notes: ['A3','C4','E4'], t: 0.8 },
90
+ { notes: ['F3','A3','C4'], t: 0.8 + beat * 4 },
91
+ { notes: ['C3','E3','G3'], t: 0.8 + beat * 8 },
92
+ { notes: ['G2','B2','D3'], t: 0.8 + beat * 12 },
93
+ ];
94
+ padChords.forEach(({ notes, t }) => {
95
+ pad.triggerAttackRelease(notes, '2n', t);
96
+ });
97
+
98
+ // ── Drums — enter at 2s ───────────────────────────────────────────────────
99
+ const drumStart = 2.0;
100
+
101
+ // Four-on-the-floor kick
102
+ for (let i = 0; i < 10; i++) {
103
+ const t = drumStart + i * beat;
104
+ if (t < 12) {
105
+ kick.triggerAttackRelease('C1', '8n', t);
106
+ }
107
+ }
108
+
109
+ // 16th hi-hats
110
+ for (let i = 0; i < 40; i++) {
111
+ const t = drumStart + i * sixteenth;
112
+ if (t < 12) {
113
+ hihat.triggerAttackRelease('16n', t);
114
+ }
115
+ }
116
+
117
+ // Clap on 2 and 4 (every other beat, starting on beat 2)
118
+ for (let bar = 0; bar < 3; bar++) {
119
+ [beat, beat * 3].forEach((offset) => {
120
+ const t = drumStart + bar * beat * 4 + offset;
121
+ if (t < 12) {
122
+ clap.triggerAttackRelease('8n', t);
123
+ }
124
+ });
125
+ }
126
+ }
127
+
128
+ window.renderComposition = async function(wavPath) {
129
+ let attempts = 0;
130
+ while (typeof Tone === 'undefined' && attempts < 50) {
131
+ await new Promise(r => setTimeout(r, 100));
132
+ attempts++;
133
+ }
134
+ if (typeof Tone === 'undefined') throw new Error('Tone not loaded');
135
+
136
+ const meta = JSON.parse(document.getElementById('tuneframes').textContent);
137
+ const durationSec = parseFloat(meta.duration) + 1.0;
138
+
139
+ const buffer = await Tone.Offline(async () => { await main(); }, durationSec, 1, 44100);
140
+ const wav = audioBufferToWav(buffer);
141
+ window.writeFile(wavPath, Array.from(new Uint8Array(wav)));
142
+ return wav.byteLength;
143
+ };
144
+ </script>
@@ -0,0 +1,147 @@
1
+ ---
2
+ name: audio-cinematic
3
+ description: Cinematic Score — epic orchestral builds, pad swells, ostinato, tension and release
4
+ ---
5
+
6
+ # TuneFrames — Cinematic Score
7
+
8
+ ## Genre Profile
9
+ - BPM range: 60–90 (slow and grand; let notes breathe)
10
+ - Key characteristics: Slow harmonic rhythm, long-attack pads, ostinato string figures, brass swells on climax, no drums until climax (if at all), layered dynamics from pp to ff
11
+ - Typical instruments: String pads (PolySynth sawtooth, long attack), brass (PolySynth sawtooth, fast attack), choir pad (PolySynth sine), low piano (Synth), timpani (MembraneSynth)
12
+ - Mood: Epic, emotional, suspenseful, grand, sweeping — Zimmer / Morricone / Williams
13
+
14
+ ## Core Pattern
15
+
16
+ ```js
17
+ // Cinematic core: 72 BPM, Dm–Bb–F–C (minor epic progression)
18
+ async function main() {
19
+ await Tone.start();
20
+ Tone.Transport.bpm.value = 72;
21
+
22
+ const bar = Tone.Time('1n').toSeconds(); // ~3.33s at 72 BPM
23
+
24
+ // ── Grand reverb hall ────────────────────────────────────────────────
25
+ const hall = new Tone.Reverb({ decay: 6, preDelay: 0.05, wet: 0.6 }).toDestination();
26
+ const hall2 = new Tone.Reverb({ decay: 4, wet: 0.4 }).toDestination();
27
+
28
+ // ── Strings pad — slow attack, swell ─────────────────────────────────
29
+ const strings = new Tone.PolySynth(Tone.Synth, {
30
+ oscillator: { type: 'sawtooth' },
31
+ envelope: { attack: 1.8, decay: 0.5, sustain: 0.85, release: 4.0 }
32
+ }).connect(hall);
33
+ strings.volume.value = -14;
34
+
35
+ // Dm–Bb–F–C progression (1 bar each)
36
+ const prog = [
37
+ ['D3','F3','A3'], // Dm
38
+ ['Bb2','D3','F3'], // Bb
39
+ ['F2','A2','C3'], // F
40
+ ['C3','E3','G3'], // C
41
+ ];
42
+ prog.forEach((ch, i) => strings.triggerAttackRelease(ch, '1n', i * bar));
43
+
44
+ // ── Piano ostinato (enters bar 2, driving 8ths) ───────────────────────
45
+ const piano = new Tone.Synth({
46
+ oscillator: { type: 'sine' },
47
+ envelope: { attack: 0.005, decay: 0.3, sustain: 0.1, release: 0.8 }
48
+ }).connect(hall2);
49
+ piano.volume.value = -16;
50
+
51
+ const q = Tone.Time('4n').toSeconds();
52
+ const ostinato = ['D3','A3','D4','A3','F3','A3','D4','A3']; // D-A-D-A pattern
53
+ ostinato.forEach((n, i) => piano.triggerAttackRelease(n, '8n', bar + i * q * 0.5));
54
+
55
+ // ── Brass swell (enters bar 3) ────────────────────────────────────────
56
+ const brass = new Tone.PolySynth(Tone.Synth, {
57
+ oscillator: { type: 'sawtooth' },
58
+ envelope: { attack: 0.4, decay: 0.2, sustain: 0.8, release: 2.0 }
59
+ }).connect(hall);
60
+ brass.volume.value = -18;
61
+
62
+ brass.triggerAttackRelease(['F3','A3','C4'], '2n', bar * 2);
63
+ brass.triggerAttackRelease(['C3','E3','G3'], '2n', bar * 3);
64
+
65
+ // ── Timpani on climax ─────────────────────────────────────────────────
66
+ const timp = new Tone.MembraneSynth({
67
+ pitchDecay: 0.08, octaves: 5,
68
+ envelope: { attack: 0.001, decay: 1.2, sustain: 0, release: 2.0 }
69
+ }).connect(hall);
70
+ timp.volume.value = -10;
71
+
72
+ timp.triggerAttackRelease('D2', '4n', bar * 2);
73
+ timp.triggerAttackRelease('A1', '4n', bar * 3);
74
+ }
75
+ ```
76
+
77
+ ## Instrument Configuration
78
+
79
+ ```js
80
+ // Strings — sawtooth for harmonic richness, very slow attack for swell
81
+ const strings = new Tone.PolySynth(Tone.Synth, {
82
+ oscillator: { type: 'sawtooth' },
83
+ envelope: { attack: 1.8, decay: 0.5, sustain: 0.85, release: 4.0 }
84
+ });
85
+
86
+ // Choir/pad layer — pure sine, even slower attack for the "ahh" effect
87
+ const choir = new Tone.PolySynth(Tone.Synth, {
88
+ oscillator: { type: 'sine' },
89
+ envelope: { attack: 2.5, decay: 0.3, sustain: 0.9, release: 5.0 }
90
+ });
91
+
92
+ // Brass — faster attack, shorter release (punchy swell)
93
+ const brass = new Tone.PolySynth(Tone.Synth, {
94
+ oscillator: { type: 'sawtooth' },
95
+ envelope: { attack: 0.3, decay: 0.2, sustain: 0.8, release: 2.0 }
96
+ });
97
+
98
+ // Piano ostinato — percussive, short sustain, clean
99
+ const piano = new Tone.Synth({
100
+ oscillator: { type: 'sine' },
101
+ envelope: { attack: 0.005, decay: 0.3, sustain: 0.1, release: 0.8 }
102
+ });
103
+
104
+ // Hall reverb — long decay for cinematic space
105
+ const hall = new Tone.Reverb({ decay: 6, preDelay: 0.05, wet: 0.6 }).toDestination();
106
+
107
+ // Volume automation for the build:
108
+ strings.volume.rampTo(-8, 4); // swell from -14 to -8 over 4s
109
+ ```
110
+
111
+ ## Composition Structure
112
+
113
+ 1. **Silence / soft pad intro (0–3s):** Choir pad alone on tonic, barely audible — establishes space
114
+ 2. **Strings enter (3–6s):** Slow-attack string swell on Dm, then Bb — harmonic foundation
115
+ 3. **Ostinato begins (6–9s):** Piano or cello ostinato (repeating 8th-note figure) adds momentum
116
+ 4. **Tension build (9–12s):** Strings move to dissonant chord (dim or sus), dynamics rise
117
+ 5. **Brass climax (12–15s):** Full brass swell on the tonic, timpani hit, everything fortissimo
118
+ 6. **Release / decay (final 2s):** Let hall reverb tail ring out — cut instruments, tail continues
119
+
120
+ ## Example Variations
121
+
122
+ ### 1 — Tension chord (diminished, no resolution)
123
+ ```js
124
+ // Hold on a dim7 for maximum suspense before resolving
125
+ const dim = ['F#3','A3','C4','Eb4']; // F#dim7
126
+ strings.triggerAttackRelease(dim, '2n', tensionTime);
127
+ ```
128
+
129
+ ### 2 — Morricone-style solo melody (over strings)
130
+ ```js
131
+ // High, yearning single note melody in the upper register
132
+ const solo = new Tone.Synth({
133
+ oscillator: { type: 'sine' },
134
+ envelope: { attack: 0.3, decay: 0.2, sustain: 0.9, release: 2.0 }
135
+ });
136
+ // Phrase: D5–C5–Bb4–A4 (descending minor scale, emotional)
137
+ const morriconeLine = ['D5','C5','Bb4','A4','F4','A4','D5'];
138
+ ```
139
+
140
+ ### 3 — Ostinato acceleration (builds tension)
141
+ ```js
142
+ // Start with quarter-note ostinato, switch to 8ths on bar 3, 16ths on bar 4
143
+ const oSlow = Tone.Time('4n').toSeconds();
144
+ const oFast = Tone.Time('8n').toSeconds();
145
+ const oFast2 = Tone.Time('16n').toSeconds();
146
+ // Schedule the same note at progressively tighter intervals
147
+ ```
@@ -0,0 +1,123 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>TuneFrames — Cinematic Score</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":"15s"}</div>
9
+ <script>
10
+ async function main() {
11
+ await Tone.start();
12
+ Tone.Transport.bpm.value = 72;
13
+
14
+ const bar = Tone.Time('1n').toSeconds(); // ~3.33s per bar at 72 BPM
15
+ const q = Tone.Time('4n').toSeconds(); // ~0.83s per beat
16
+
17
+ // ── Reverb halls ─────────────────────────────────────────────────────
18
+ const hall = new Tone.Reverb({ decay: 7, preDelay: 0.06, wet: 0.65 });
19
+ const room = new Tone.Reverb({ decay: 3, preDelay: 0.01, wet: 0.35 });
20
+ // Await IR generation before offline rendering begins — required in Tone.Offline
21
+ await Promise.all([hall.ready, room.ready]);
22
+ hall.toDestination();
23
+ room.toDestination();
24
+
25
+ // ── Choir pad (enters immediately, very soft) ─────────────────────────
26
+ const choir = new Tone.PolySynth(Tone.Synth, {
27
+ oscillator: { type: 'sine' },
28
+ envelope: { attack: 2.8, decay: 0.4, sustain: 0.85, release: 6.0 }
29
+ }).connect(hall);
30
+ choir.volume.value = -22;
31
+
32
+ // Dm open fifth — ethereal, thin, creates space
33
+ choir.triggerAttackRelease(['D3','A3'], '2n', 0);
34
+
35
+ // ── Strings — slow attack swell ──────────────────────────────────────
36
+ const strings = new Tone.PolySynth(Tone.Synth, {
37
+ oscillator: { type: 'sawtooth' },
38
+ envelope: { attack: 2.0, decay: 0.5, sustain: 0.85, release: 5.0 }
39
+ }).connect(hall);
40
+ strings.volume.value = -16;
41
+
42
+ // Dm – Bb – F – C (the cinematic minor-to-relative-major arc)
43
+ const prog = [
44
+ ['D3','F3','A3','D4'], // Dm (bar 1)
45
+ ['Bb2','D3','F3','Bb3'], // Bb (bar 2)
46
+ ['F2','C3','F3','A3'], // F (bar 3)
47
+ ['C3','G3','C4','E4'], // C (bar 4)
48
+ ];
49
+ prog.forEach((ch, i) => strings.triggerAttackRelease(ch, '1n', i * bar));
50
+
51
+ // ── Cello ostinato (enters bar 2 — adds forward motion) ──────────────
52
+ const cello = new Tone.Synth({
53
+ oscillator: { type: 'sawtooth' },
54
+ envelope: { attack: 0.12, decay: 0.2, sustain: 0.7, release: 0.8 }
55
+ }).connect(hall);
56
+ cello.volume.value = -18;
57
+
58
+ // D–A–D pattern driving through bars 2–4
59
+ const ostinato = ['D3','A3','D3','A3','D3','A3','D3','F3'];
60
+ ostinato.forEach((n, i) => cello.triggerAttackRelease(n, '8n', bar + i * (q * 0.5)));
61
+
62
+ const ostinato2 = ['Bb2','F3','Bb2','F3','Bb2','F3','C3','G3'];
63
+ ostinato2.forEach((n, i) => cello.triggerAttackRelease(n, '8n', bar * 2 + i * (q * 0.5)));
64
+
65
+ const ostinato3 = ['F2','C3','F3','C3','F2','C3','G2','C3'];
66
+ ostinato3.forEach((n, i) => cello.triggerAttackRelease(n, '8n', bar * 3 + i * (q * 0.5)));
67
+
68
+ // ── Low piano (enters bar 1, punctuates downbeats) ───────────────────
69
+ const piano = new Tone.Synth({
70
+ oscillator: { type: 'sine' },
71
+ envelope: { attack: 0.01, decay: 1.2, sustain: 0.1, release: 2.0 }
72
+ }).connect(room);
73
+ piano.volume.value = -14;
74
+
75
+ piano.triggerAttackRelease('D2', '2n', 0);
76
+ piano.triggerAttackRelease('Bb1', '2n', bar);
77
+ piano.triggerAttackRelease('F1', '2n', bar * 2);
78
+ piano.triggerAttackRelease('C2', '2n', bar * 3);
79
+
80
+ // ── Yearning solo melody (enters bar 2, high register) ───────────────
81
+ const solo = new Tone.Synth({
82
+ oscillator: { type: 'sine' },
83
+ envelope: { attack: 0.35, decay: 0.3, sustain: 0.85, release: 2.5 }
84
+ }).connect(hall);
85
+ solo.volume.value = -14;
86
+
87
+ // Morricone-style descending phrase: D5–C5–Bb4–A4–F4–A4
88
+ const soloPhrase = [
89
+ { note: 'D5', time: bar * 1.5, dur: '4n' },
90
+ { note: 'C5', time: bar * 1.5 + q, dur: '4n' },
91
+ { note: 'Bb4', time: bar * 1.5 + q*2, dur: '2n' },
92
+ { note: 'A4', time: bar * 2.5, dur: '4n' },
93
+ { note: 'F4', time: bar * 2.5 + q, dur: '4n' },
94
+ { note: 'A4', time: bar * 2.5 + q*2, dur: '4n' },
95
+ { note: 'D5', time: bar * 3, dur: '2n.' },
96
+ { note: 'C5', time: bar * 3.75, dur: '4n' },
97
+ ];
98
+ soloPhrase.forEach(({ note, time, dur }) => solo.triggerAttackRelease(note, dur, time));
99
+
100
+ // ── Brass swell (enters bar 3 — the climax) ──────────────────────────
101
+ const brass = new Tone.PolySynth(Tone.Synth, {
102
+ oscillator: { type: 'sawtooth' },
103
+ envelope: { attack: 0.5, decay: 0.3, sustain: 0.85, release: 3.0 }
104
+ }).connect(hall);
105
+ brass.volume.value = -20;
106
+
107
+ brass.triggerAttackRelease(['F3','A3','C4','F4'], '2n', bar * 2);
108
+ brass.triggerAttackRelease(['C3','G3','C4','E4'], '1n', bar * 3);
109
+
110
+ // ── Timpani — dramatic hits on bars 3 and 4 ──────────────────────────
111
+ const timp = new Tone.MembraneSynth({
112
+ pitchDecay: 0.1, octaves: 6,
113
+ envelope: { attack: 0.001, decay: 1.5, sustain: 0, release: 2.5 }
114
+ }).connect(hall);
115
+ timp.volume.value = -8;
116
+
117
+ timp.triggerAttackRelease('F1', '4n', bar * 2);
118
+ timp.triggerAttackRelease('D1', '4n', bar * 3);
119
+ timp.triggerAttackRelease('C1', '4n', bar * 3 + q * 2);
120
+ }
121
+ </script>
122
+ </body>
123
+ </html>
@@ -0,0 +1,138 @@
1
+ ---
2
+ name: audio-classical
3
+ description: Classical solo piano — left hand Alberti bass or broken chords, right hand melody with ornaments, no effects beyond room reverb
4
+ ---
5
+
6
+ # TuneFrames — Classical Piano
7
+
8
+ ## Genre Profile
9
+ - BPM range: 80-140 (Allegro for Mozart, Andante for slow movements)
10
+ - Key characteristics: Alberti bass in left hand (bass-chord-chord pattern), melodic right hand in single notes or octaves, voice leading between the two hands, harmonic rhythm changes every half or full bar
11
+ - Typical instruments: Tone.Synth with triangle oscillator (cleanest piano approximation), slight room reverb only
12
+ - Mood: elegant, structured, emotionally direct, intellectually clear
13
+
14
+ ## Core Pattern
15
+
16
+ ```js
17
+ // Classical piano: right hand melody over left hand Alberti bass
18
+ // Key = C major, BPM = 104 (Allegro moderato)
19
+ // Alberti bass = bass note → inner chord → upper chord, repeating 8th notes
20
+ Tone.Transport.bpm.value = 104;
21
+
22
+ // Piano synth — triangle wave with tight envelope
23
+ const piano = new Tone.Synth({
24
+ oscillator: { type: 'triangle' },
25
+ envelope: { attack: 0.008, decay: 0.3, sustain: 0.2, release: 1.2 },
26
+ volume: -6,
27
+ });
28
+
29
+ // Gentle room reverb only
30
+ const reverb = new Tone.Reverb({ decay: 1.2, preDelay: 0.01, wet: 0.25 });
31
+ await reverb.generate();
32
+ reverb.toDestination();
33
+ piano.connect(reverb);
34
+
35
+ // A second instance for left hand (allows simultaneous notes)
36
+ const pianoL = new Tone.Synth({
37
+ oscillator: { type: 'triangle' },
38
+ envelope: { attack: 0.008, decay: 0.4, sustain: 0.15, release: 1.0 },
39
+ volume: -10, // left hand slightly quieter
40
+ });
41
+ pianoL.connect(reverb);
42
+
43
+ // RIGHT HAND: C major scale as 8th-note melody (C D E F G F E D)
44
+ const rhNotes = ['C5','D5','E5','F5','G5','F5','E5','D5'];
45
+ const rhTimes = [0, 0.29, 0.57, 0.86, 1.15, 1.44, 1.73, 2.02]; // 8th notes at 104 BPM
46
+ rhNotes.forEach((note, i) => {
47
+ Tone.Transport.scheduleOnce(time => {
48
+ piano.triggerAttackRelease(note, '8n', time);
49
+ }, rhTimes[i]);
50
+ });
51
+
52
+ // LEFT HAND: Alberti bass on C major chord (C3 → E3 → G3 repeating)
53
+ // Pattern: C3, G3, E3, G3 (bass → upper → inner → upper)
54
+ const lhPattern = ['C3','G3','E3','G3', 'F2','A2','F2','A2'];
55
+ lhPattern.forEach((note, i) => {
56
+ Tone.Transport.scheduleOnce(time => {
57
+ pianoL.triggerAttackRelease(note, '8n', time);
58
+ }, i * 0.29); // 8th note spacing at 104 BPM
59
+ });
60
+ ```
61
+
62
+ ## Instrument Configuration
63
+
64
+ ```js
65
+ // Piano approximation — triangle + tight decay, slight room
66
+ // Two instances: right hand (melody) and left hand (accompaniment)
67
+
68
+ // Room reverb — small, not a concert hall
69
+ const reverb = new Tone.Reverb({ decay: 1.1, preDelay: 0.008, wet: 0.22 });
70
+ await reverb.generate();
71
+ reverb.toDestination();
72
+
73
+ // Right hand — melody voice, slightly brighter
74
+ const rh = new Tone.Synth({
75
+ oscillator: { type: 'triangle' },
76
+ envelope: { attack: 0.006, decay: 0.25, sustain: 0.15, release: 1.4 },
77
+ volume: -4,
78
+ }).connect(reverb);
79
+
80
+ // Left hand — accompaniment, slightly softer and darker
81
+ const lh = new Tone.Synth({
82
+ oscillator: { type: 'triangle' },
83
+ envelope: { attack: 0.01, decay: 0.4, sustain: 0.1, release: 1.0 },
84
+ volume: -9,
85
+ }).connect(reverb);
86
+
87
+ // For chords in the left hand, use PolySynth
88
+ const lhChord = new Tone.PolySynth(Tone.Synth, {
89
+ oscillator: { type: 'triangle' },
90
+ envelope: { attack: 0.01, decay: 0.35, sustain: 0.15, release: 1.2 },
91
+ volume: -11,
92
+ }).connect(reverb);
93
+ ```
94
+
95
+ ## Composition Structure
96
+
97
+ **Classical follows phrase structure — typically 4-bar or 8-bar phrases:**
98
+
99
+ 1. **Antecedent phrase (0-4s):** Opening melody in right hand. Left hand Alberti bass on I chord (tonic). Ends on V (half cadence — tension).
100
+ 2. **Consequent phrase (4-8s):** Melody continues or develops. Left hand harmonic progression I→IV→V→I. Ends on I (full cadence — resolution).
101
+ 3. **Middle section / contrast (8-12s):** Move to relative minor or vi. Melody gets more chromatic or ornamental. Left hand may switch from Alberti to block chords.
102
+ 4. **Return (12s+):** Return to opening material. Final cadence: IV→V→I.
103
+
104
+ **Alberti bass pattern at any BPM:**
105
+ - Bass note (lowest note of chord) → Upper note → Middle note → Upper note
106
+ - Repeat for each 8th note subdivision
107
+ - C major: C3 → G3 → E3 → G3
108
+ - F major: F2 → C3 → A2 → C3
109
+ - G major: G2 → D3 → B2 → D3
110
+
111
+ ## Example Variations
112
+
113
+ ### Variation 1: Slow movement (Andante cantabile)
114
+ ```js
115
+ Tone.Transport.bpm.value = 72;
116
+ // Right hand: long notes (quarter and half notes) — singable melody
117
+ // Left hand: broken octaves instead of Alberti — just bass + high octave alternating
118
+ // Reverb slightly wetter: 0.35 — feels more intimate
119
+ // Envelope release: 2.0 — notes ring longer
120
+ ```
121
+
122
+ ### Variation 2: Fast passage work (Allegro vivace)
123
+ ```js
124
+ Tone.Transport.bpm.value = 132;
125
+ // Right hand: 16th-note scalar run (C major scale, 2 octaves up and down)
126
+ // Left hand: block chords on beats 1 and 3 only (not Alberti — too busy)
127
+ // Use PolySynth for left hand chords: ['C3','E3','G3'], ['G2','B2','D3'], etc.
128
+ // Shorter release: 0.6 — crisp, not muddy at fast tempo
129
+ ```
130
+
131
+ ### Variation 3: Ornamental turn
132
+ ```js
133
+ // Right hand melody includes a trill approximation
134
+ // Instead of one quarter note on E5, play: E5, F5, E5, D5, E5 as 32nd notes
135
+ // Schedule these rapid notes as very short triggerAttackRelease calls
136
+ // const trillNotes = ['E5','F5','E5','D5','E5'];
137
+ // trillNotes.forEach((n, i) => rh.triggerAttackRelease(n, '32n', time + i * 0.045));
138
+ ```