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,125 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>TuneFrames — Techno</title>
6
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
7
+ </head>
8
+ <body>
9
+ <!-- TuneFrames Metadata -->
10
+ <div id="tuneframes" style="display:none">{"bpm":136,"duration":"12s"}</div>
11
+
12
+ <script>
13
+ async function main() {
14
+ await Tone.start();
15
+ Tone.Transport.bpm.value = 136;
16
+
17
+ // --- MASTER BUS ---
18
+ const masterLimiter = new Tone.Limiter(-1).toDestination();
19
+ const masterGain = new Tone.Gain(0.85).connect(masterLimiter);
20
+
21
+ // --- KICK — hard MembraneSynth ---
22
+ const kickGain = new Tone.Gain(0.9).connect(masterGain);
23
+ const kick = new Tone.MembraneSynth({
24
+ pitchDecay: 0.07,
25
+ octaves: 10,
26
+ envelope: { attack: 0.001, decay: 0.38, sustain: 0, release: 0.1 }
27
+ }).connect(kickGain);
28
+
29
+ // --- ACID BASS — MonoSynth sawtooth through distortion ---
30
+ const bassLimiter = new Tone.Limiter(-4).connect(masterGain);
31
+ const bassDist = new Tone.Distortion({ distortion: 0.55, wet: 0.7 }).connect(bassLimiter);
32
+ const bassFilter = new Tone.Filter({ frequency: 400, type: "lowpass", rolloff: -24, Q: 3 }).connect(bassDist);
33
+ const bass = new Tone.MonoSynth({
34
+ oscillator: { type: "sawtooth" },
35
+ filter: { Q: 7, type: "lowpass", rolloff: -24 },
36
+ filterEnvelope: {
37
+ attack: 0.002, decay: 0.25, sustain: 0.2, release: 0.5,
38
+ baseFrequency: 70, octaves: 3.5
39
+ },
40
+ envelope: { attack: 0.001, decay: 0.25, sustain: 0.5, release: 0.3 },
41
+ volume: -2
42
+ }).connect(bassFilter);
43
+
44
+ // --- HI-HAT — tight MetalSynth ---
45
+ const hatGain = new Tone.Gain(0.45).connect(masterGain);
46
+ const hat = new Tone.MetalSynth({
47
+ frequency: 400,
48
+ envelope: { attack: 0.001, decay: 0.045, release: 0.01 },
49
+ harmonicity: 5.1,
50
+ modulationIndex: 32,
51
+ resonance: 4200,
52
+ octaves: 1.5
53
+ }).connect(hatGain);
54
+
55
+ // --- STAB — sparse cold PolySynth ---
56
+ const stabReverb = new Tone.Reverb({ decay: 1.8, wet: 0.35 }).connect(masterGain);
57
+ const stab = new Tone.PolySynth(Tone.Synth, {
58
+ oscillator: { type: "square4" },
59
+ envelope: { attack: 0.001, decay: 0.12, sustain: 0, release: 0.08 },
60
+ volume: -14
61
+ }).connect(stabReverb);
62
+
63
+ // Stab chords — dissonant, industrial
64
+ const stabNotes = [["A2", "Eb3"], ["A2", "D3"], null, ["G2", "C#3"]];
65
+
66
+ // --- SEQUENCES ---
67
+
68
+ // Kick: 4-on-the-floor with ghost on bar 2 last 16th
69
+ const kickPattern = new Tone.Sequence((time, val) => {
70
+ if (val) kick.triggerAttackRelease("C1", "8n", time);
71
+ }, ["C1", null, null, null, "C1", null, null, null,
72
+ "C1", null, null, null, "C1", null, "C1", null], "16n");
73
+ kickPattern.start(0);
74
+
75
+ // Hi-hat: 16th notes, velocity variation
76
+ let hatStep = 0;
77
+ const hatVels = [0.75, 0.35, 0.75, 0.35, 0.75, 0.35, 0.95, 0.35,
78
+ 0.75, 0.35, 0.75, 0.35, 0.75, 0.45, 0.95, 0.55];
79
+ const hatSeq = new Tone.Sequence((time) => {
80
+ hat.triggerAttackRelease("32n", time, hatVels[hatStep % 16]);
81
+ hatStep++;
82
+ }, new Array(16).fill(1), "16n");
83
+ hatSeq.start(0);
84
+
85
+ // Bass: acid pattern — A1 root with chromatic movement
86
+ const bassLine = [
87
+ { note: "A1", dur: "8n" }, { note: null },
88
+ { note: "A1", dur: "16n" }, { note: "A1", dur: "16n" },
89
+ { note: null }, { note: "C2", dur: "8n" },
90
+ { note: null }, { note: "A1", dur: "16n" },
91
+ { note: "A1", dur: "8n" }, { note: null },
92
+ { note: "Bb1", dur: "16n" }, { note: "A1", dur: "16n" },
93
+ { note: null }, { note: "G1", dur: "16n" },
94
+ { note: null }, { note: "A1", dur: "8n" }
95
+ ];
96
+ let bassStep = 0;
97
+ const bassSeq = new Tone.Sequence((time) => {
98
+ const step = bassLine[bassStep % bassLine.length];
99
+ if (step.note) bass.triggerAttackRelease(step.note, step.dur, time);
100
+ bassStep++;
101
+ }, new Array(16).fill(1), "16n");
102
+ bassSeq.start("2m"); // bass enters at bar 3
103
+
104
+ // Stabs: every 2 bars on beat 3
105
+ let stabStep = 0;
106
+ const stabSeq = new Tone.Sequence((time) => {
107
+ const chord = stabNotes[stabStep % stabNotes.length];
108
+ if (chord) stab.triggerAttackRelease(chord, "32n", time);
109
+ stabStep++;
110
+ }, [1, null, null, null, null, null, null, null], "2n");
111
+ stabSeq.start("4m"); // stabs enter at bar 5
112
+
113
+ // --- BASS FILTER SWEEP — signature acid movement ---
114
+ const sweepStart = Tone.Time("2m").toSeconds();
115
+ bassFilter.frequency.setValueAtTime(200, sweepStart);
116
+ bassFilter.frequency.linearRampToValueAtTime(2800, sweepStart + 4);
117
+ bassFilter.frequency.linearRampToValueAtTime(150, sweepStart + 4.5);
118
+ bassFilter.frequency.linearRampToValueAtTime(1800, sweepStart + 8);
119
+
120
+ }
121
+
122
+ main();
123
+ </script>
124
+ </body>
125
+ </html>
@@ -0,0 +1,123 @@
1
+ ---
2
+ name: audio-trap
3
+ description: Trap — 808 sub bass with pitch bend, snare on 2 and 4, rapid hi-hat rolls with velocity variation, sparse orchestral strings for drama.
4
+ ---
5
+
6
+ # TuneFrames — Trap
7
+
8
+ ## Genre Profile
9
+ - BPM range: 130–160 (half-time feel makes it sit like 65–80)
10
+ - Key characteristics: 808 sub bass with long decay and downward pitch bend, snare on beats 2 and 4, hi-hat rolls in 16th/32nd triplets with strong velocity variation, heavy low end, atmospheric elements
11
+ - Typical instruments: MembraneSynth (808 sub), NoiseSynth (snare), MetalSynth (hi-hats), PolySynth (strings/pads)
12
+ - Mood: Menacing, cinematic, heavy, dramatic
13
+
14
+ ## Core Pattern
15
+
16
+ ```js
17
+ // Trap — 140 BPM, half-time feel
18
+ // 808: long decay sub with downward pitch glide
19
+ // Snare: beat 2 and 4 (half-time = bars 1 and 3)
20
+ // Hi-hats: rapid rolls, velocity builds and drops — the signature
21
+ // Strings: sparse atmospheric chords
22
+
23
+ // 808 sub — MembraneSynth with pitch envelope = signature glide
24
+ const sub = new Tone.MembraneSynth({
25
+ pitchDecay: 0.5, // long pitch glide (the "808 fall")
26
+ octaves: 3, // moderate pitch drop
27
+ envelope: { attack: 0.001, decay: 1.2, sustain: 0, release: 0.5 }
28
+ }).toDestination();
29
+
30
+ // Trigger 808 on beat 1 — pitch starts at C2 then falls
31
+ sub.triggerAttackRelease("C2", "2n", time);
32
+
33
+ // Hi-hat roll — 32nd notes, velocity variation = the trap signature
34
+ const hatVelocities = [
35
+ 0.9, 0.3, 0.5, 0.2, 0.7, 0.2, 0.4, 0.2, // 8-step build
36
+ 0.6, 0.3, 0.8, 0.2, 0.5, 0.3, 1.0, 0.2 // accent on last
37
+ ];
38
+ new Tone.Sequence((time, vel) => {
39
+ hat.triggerAttackRelease("32n", time, vel);
40
+ }, hatVelocities, "32n").start(0);
41
+ ```
42
+
43
+ ## Instrument Configuration
44
+
45
+ ```js
46
+ // 808 Sub — long pitchDecay is the defining characteristic
47
+ const dist808 = new Tone.Distortion({ distortion: 0.15, wet: 0.3 }).toDestination();
48
+ const sub = new Tone.MembraneSynth({
49
+ pitchDecay: 0.45, // how long the pitch "falls" — the 808 glide
50
+ octaves: 3, // semitones of pitch drop
51
+ volume: 2,
52
+ envelope: { attack: 0.001, decay: 1.0, sustain: 0, release: 0.6 }
53
+ }).connect(dist808); // light saturation adds warmth to the sub
54
+
55
+ // Snare — tight white noise, minimal reverb
56
+ const snareVerb = new Tone.Reverb({ decay: 0.8, wet: 0.2 }).toDestination();
57
+ const snare = new Tone.NoiseSynth({
58
+ noise: { type: "white" },
59
+ envelope: { attack: 0.001, decay: 0.18, sustain: 0, release: 0.06 },
60
+ volume: -2
61
+ }).connect(snareVerb);
62
+
63
+ // Hi-hat — very short decay, high frequency
64
+ const hat = new Tone.MetalSynth({
65
+ frequency: 800,
66
+ envelope: { attack: 0.001, decay: 0.03, release: 0.008 },
67
+ harmonicity: 5.1,
68
+ modulationIndex: 32,
69
+ resonance: 5000,
70
+ octaves: 2,
71
+ volume: -8
72
+ }).toDestination();
73
+
74
+ // Orchestral strings — sparse, atmospheric
75
+ const stringsVerb = new Tone.Reverb({ decay: 4.0, wet: 0.6 }).toDestination();
76
+ const strings = new Tone.PolySynth(Tone.Synth, {
77
+ oscillator: { type: "sawtooth4" },
78
+ envelope: { attack: 0.4, decay: 1.0, sustain: 0.7, release: 1.5 },
79
+ volume: -12
80
+ }).connect(stringsVerb);
81
+ ```
82
+
83
+ ## Composition Structure
84
+
85
+ - **Bar 1:** 808 hit on beat 1, snare on beat 3 (half-time), hi-hat roll building
86
+ - **Bar 2:** 808 on beat 1 again (the sub is still decaying from bar 1), hi-hat variation
87
+ - **Bars 1-4:** Drum loop establishes half-time feel with rolls escalating
88
+ - **Bar 5-8:** Add strings — sparse pad chord, lots of reverb
89
+ - **Bar 9-16:** Full arrangement — 808 pattern + hi-hat rolls + strings harmony
90
+ - **Build:** Hi-hat velocity increases toward drop, 32nd note flurry
91
+ - **Drop:** 808 hits hard, hi-hats sparse, strings cut — maximum impact
92
+
93
+ Hi-hat roll anatomy: Start sparse (quarter notes), build to 8ths, then 16ths, then 32nds on the approach to the drop. This escalation is the genre's primary tension tool.
94
+
95
+ ## Example Variations
96
+
97
+ ### 1 — Classic 808 Double Hit
98
+ ```js
99
+ // 808 hits twice in a bar — beat 1 and the "and" of beat 3
100
+ const subPattern = ["C2", null, null, null, null, null, "C1", null];
101
+ // Second hit is higher (C1 vs C2) — common trap technique
102
+ ```
103
+
104
+ ### 2 — Hi-Hat Triplet Roll
105
+ ```js
106
+ // Triplet 16ths (12 per bar instead of 16) — more organic feel
107
+ new Tone.Sequence((time, vel) => {
108
+ if (vel) hat.triggerAttackRelease("32n", time, vel);
109
+ }, [0.9, 0.3, 0.6, 0.9, 0.3, 0.6, 0.9, 0.3, 0.6, 0.9, 0.3, 0.9], "8t").start(0);
110
+ ```
111
+
112
+ ### 3 — Sliding 808 Pitch (Tone.js portamento)
113
+ ```js
114
+ // Use portamento on MonoSynth for more dramatic pitch slide
115
+ const slideSub = new Tone.MonoSynth({
116
+ oscillator: { type: "sine" },
117
+ portamento: 0.4, // glide time in seconds
118
+ envelope: { attack: 0.001, decay: 1.2, sustain: 0, release: 0.5 },
119
+ filter: { type: "lowpass", frequency: 200 }
120
+ });
121
+ slideSub.triggerAttackRelease("C3", "2n", time);
122
+ // Then after portamento, note has already slid down
123
+ ```
@@ -0,0 +1,119 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>TuneFrames — Trap</title>
6
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
7
+ </head>
8
+ <body>
9
+ <!-- TuneFrames Metadata -->
10
+ <div id="tuneframes" style="display:none">{"bpm":140,"duration":"12s"}</div>
11
+
12
+ <script>
13
+ async function main() {
14
+ await Tone.start();
15
+ Tone.Transport.bpm.value = 140;
16
+
17
+ // --- MASTER BUS ---
18
+ const masterLimiter = new Tone.Limiter(-1).toDestination();
19
+ const masterGain = new Tone.Gain(0.85).connect(masterLimiter);
20
+
21
+ // --- 808 SUB — MembraneSynth with long pitchDecay for the signature glide ---
22
+ const subSaturate = new Tone.Distortion({ distortion: 0.12, wet: 0.25 }).connect(masterGain);
23
+ const subGain = new Tone.Gain(1.1).connect(subSaturate);
24
+ const sub = new Tone.MembraneSynth({
25
+ pitchDecay: 0.48, // the 808 "fall" — longer = more dramatic
26
+ octaves: 3, // pitch range of the glide
27
+ envelope: { attack: 0.001, decay: 1.1, sustain: 0, release: 0.5 }
28
+ }).connect(subGain);
29
+
30
+ // --- SNARE — punchy white noise, dry ---
31
+ const snareVerb = new Tone.Reverb({ decay: 0.7, wet: 0.18 }).connect(masterGain);
32
+ const snareGain = new Tone.Gain(0.7).connect(snareVerb);
33
+ const snare = new Tone.NoiseSynth({
34
+ noise: { type: "white" },
35
+ envelope: { attack: 0.001, decay: 0.16, sustain: 0, release: 0.05 }
36
+ }).connect(snareGain);
37
+
38
+ // --- HI-HAT — very tight, high frequency ---
39
+ const hatGain = new Tone.Gain(0.42).connect(masterGain);
40
+ const hat = new Tone.MetalSynth({
41
+ frequency: 820,
42
+ envelope: { attack: 0.001, decay: 0.028, release: 0.008 },
43
+ harmonicity: 5.1,
44
+ modulationIndex: 32,
45
+ resonance: 5200,
46
+ octaves: 2
47
+ }).connect(hatGain);
48
+
49
+ // --- STRINGS — atmospheric PolySynth, heavy reverb ---
50
+ const stringsVerb = new Tone.Reverb({ decay: 4.5, wet: 0.65 }).connect(masterGain);
51
+ const stringsGain = new Tone.Gain(0.5).connect(stringsVerb);
52
+ const strings = new Tone.PolySynth(Tone.Synth, {
53
+ oscillator: { type: "sawtooth4" },
54
+ envelope: { attack: 0.5, decay: 1.2, sustain: 0.65, release: 2.0 },
55
+ volume: -2
56
+ }).connect(stringsGain);
57
+
58
+ // --- 808 PATTERN — half-time feel, beat 1 of every bar ---
59
+ // Pattern: hit on beat 1, ghost on beat 3.5
60
+ const subPattern = new Tone.Sequence((time, note) => {
61
+ if (note === "C2") {
62
+ sub.triggerAttackRelease("C2", "2n", time);
63
+ } else if (note === "G1") {
64
+ sub.triggerAttackRelease("G1", "4n", time);
65
+ }
66
+ }, ["C2", null, null, null, null, null, "G1", null,
67
+ "C2", null, null, null, null, null, null, null], "16n");
68
+ subPattern.start(0);
69
+
70
+ // --- SNARE PATTERN — half-time: beat 3 of the bar (step 8 of 16) ---
71
+ const snareSeq = new Tone.Sequence((time, val) => {
72
+ if (val) snare.triggerAttackRelease("16n", time);
73
+ }, [null, null, null, null, null, null, null, null,
74
+ 1, null, null, null, null, null, null, null], "16n");
75
+ snareSeq.start(0);
76
+
77
+ // --- HI-HAT ROLLS — the trap signature ---
78
+ // 32nd-note grid: velocity variation creates rolls and accents
79
+ // Pattern evolves every bar: starts sparse, builds to dense rolls
80
+ let hatStep = 0;
81
+
82
+ // Bar 1-2 velocity map: sparse 16th pattern (every 2 32nds)
83
+ const hatBar1 = [
84
+ 0.8, 0, 0.4, 0, 0.8, 0, 0.4, 0,
85
+ 0.8, 0, 0.4, 0, 0.8, 0.3, 0.9, 0.3
86
+ ];
87
+ // Bar 3-4: 16th rolls with triplet accent flurry
88
+ const hatBar2 = [
89
+ 0.8, 0.3, 0.6, 0.2, 0.8, 0.3, 0.5, 0.2,
90
+ 0.7, 0.3, 0.6, 0.2, 0.9, 0.5, 0.8, 0.5
91
+ ];
92
+ // Bar 5-6: dense 32nd roll building to drop
93
+ const hatBar3 = [
94
+ 0.6, 0.3, 0.7, 0.3, 0.6, 0.4, 0.8, 0.3,
95
+ 0.6, 0.4, 0.7, 0.5, 0.8, 0.6, 0.9, 0.7
96
+ ];
97
+
98
+ // Merge all bars into one 96-step (6-bar) loop for 32nds at 140bpm
99
+ const fullHatPattern = [...hatBar1, ...hatBar1, ...hatBar2, ...hatBar2, ...hatBar3, ...hatBar3];
100
+
101
+ const hatSeq = new Tone.Sequence((time, vel) => {
102
+ if (vel > 0) hat.triggerAttackRelease("32n", time, vel);
103
+ }, fullHatPattern, "32n");
104
+ hatSeq.start(0);
105
+
106
+ // --- STRINGS — atmospheric minor chord, enters at bar 3 ---
107
+ // Cm: C3-Eb3-G3 + high B4 tension note
108
+ const stringNotes = ["C3", "Eb3", "G3", "Bb3"];
109
+ strings.triggerAttackRelease(stringNotes, "2m", "2m");
110
+ // Resolution chord at bar 5
111
+ strings.triggerAttackRelease(["Ab2", "C3", "Eb3", "G3"], "2m", "4m");
112
+ strings.triggerAttackRelease(["C3", "Eb3", "G3", "Bb3"], "2m", "6m");
113
+
114
+ }
115
+
116
+ main();
117
+ </script>
118
+ </body>
119
+ </html>
@@ -0,0 +1,115 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>TuneFrames — Render Retest</title>
6
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
7
+ </head>
8
+ <body>
9
+ <!-- TuneFrames Metadata -->
10
+ <div id="tuneframes" style="display:none">{"bpm":120,"duration":"10s"}</div>
11
+
12
+ <script>
13
+ async function main() {
14
+ await Tone.start();
15
+
16
+ // 120 BPM: quarter note = 0.5s, bar = 2.0s, 10s = 5 bars exactly.
17
+ // All note times are absolute seconds from 0. No Tone.now(), no Transport.start().
18
+ const beat = 0.5;
19
+ const bar = 2.0;
20
+
21
+ // --- MASTER BUS ---
22
+ const limiter = new Tone.Limiter(-1).toDestination();
23
+ const masterGain = new Tone.Gain(0.82).connect(limiter);
24
+
25
+ // --- REVERB connected to master ---
26
+ const reverb = new Tone.Reverb({ decay: 2.5, wet: 0.30 }).connect(masterGain);
27
+
28
+ // --- DELAY connected to master (parallel with reverb, not chained) ---
29
+ const delay = new Tone.FeedbackDelay(beat * 0.5, 0.25).connect(masterGain);
30
+
31
+ // --- KICK (MembraneSynth) ---
32
+ const kickGain = new Tone.Gain(0.88).connect(masterGain);
33
+ const kick = new Tone.MembraneSynth({
34
+ pitchDecay: 0.06,
35
+ octaves: 9,
36
+ envelope: { attack: 0.001, decay: 0.34, sustain: 0, release: 0.1 }
37
+ }).connect(kickGain);
38
+
39
+ // --- HI-HAT (MetalSynth) ---
40
+ // MetalSynth.triggerAttackRelease(duration, time, velocity) -- no pitch arg
41
+ const hatGain = new Tone.Gain(0.36).connect(masterGain);
42
+ const hat = new Tone.MetalSynth({
43
+ frequency: 440,
44
+ envelope: { attack: 0.001, decay: 0.05, release: 0.01 },
45
+ harmonicity: 5.1,
46
+ modulationIndex: 32,
47
+ resonance: 4000,
48
+ octaves: 1.5
49
+ }).connect(hatGain);
50
+
51
+ // --- CHORDS (PolySynth) through reverb ---
52
+ const padGain = new Tone.Gain(0.60).connect(reverb);
53
+ const pad = new Tone.PolySynth(Tone.Synth, {
54
+ oscillator: { type: "sine" },
55
+ envelope: { attack: 0.40, decay: 0.5, sustain: 0.60, release: 1.8 },
56
+ volume: -8
57
+ }).connect(padGain);
58
+
59
+ // --- MELODY (Synth) through delay + reverb ---
60
+ const melGain = new Tone.Gain(0.72).connect(delay);
61
+ melGain.connect(reverb);
62
+ const mel = new Tone.Synth({
63
+ oscillator: { type: "triangle" },
64
+ envelope: { attack: 0.012, decay: 0.20, sustain: 0.45, release: 0.65 },
65
+ volume: -5
66
+ }).connect(melGain);
67
+
68
+ // --- KICK: 4-on-the-floor, every 0.5s, 20 hits, last at 9.5s ---
69
+ for (let i = 0; i < 20; i++) {
70
+ kick.triggerAttackRelease("C1", "8n", i * beat);
71
+ }
72
+
73
+ // --- HI-HAT: 8th notes every 0.25s, on-beat louder, 40 hits, last at 9.75s ---
74
+ for (let i = 0; i < 40; i++) {
75
+ const vel = (i % 2 === 0) ? 0.60 : 0.35;
76
+ hat.triggerAttackRelease("16n", i * (beat * 0.5), vel);
77
+ }
78
+
79
+ // --- CHORDS: Dm -> Bb -> F -> C, one bar each, enter at bar 2 (2s) ---
80
+ // "1n" = whole note = 2.0s at 120 BPM = exactly one bar
81
+ const progression = [
82
+ { notes: ["D2","F2","A2"], time: bar }, // Dm @ 2s
83
+ { notes: ["Bb2","D3","F3"], time: bar * 2 }, // Bb @ 4s
84
+ { notes: ["F2","A2","C3"], time: bar * 3 }, // F @ 6s
85
+ { notes: ["C3","E3","G3"], time: bar * 4 }, // C @ 8s
86
+ ];
87
+ progression.forEach(({ notes, time }) => {
88
+ pad.triggerAttackRelease(notes, "1n", time);
89
+ });
90
+
91
+ // --- MELODY: D minor pentatonic, enters at bar 3 (4s), last note at 9.0s ---
92
+ const melody = [
93
+ { note: "F4", dur: "4n", t: 4.0 },
94
+ { note: "E4", dur: "8n", t: 4.5 },
95
+ { note: "D4", dur: "4n", t: 5.0 },
96
+ { note: "A4", dur: "4n", t: 5.5 },
97
+ { note: "G4", dur: "2n", t: 6.0 },
98
+ { note: "F4", dur: "4n", t: 7.0 },
99
+ { note: "E4", dur: "8n", t: 7.5 },
100
+ { note: "D4", dur: "4n", t: 8.0 },
101
+ { note: "F4", dur: "8n", t: 8.5 },
102
+ { note: "A4", dur: "4n", t: 9.0 },
103
+ ];
104
+ melody.forEach(({ note, dur, t }) => {
105
+ mel.triggerAttackRelease(note, dur, t);
106
+ });
107
+
108
+ // NOTE: do NOT call Tone.Transport.start() here.
109
+ // Tone.Offline manages the transport internally.
110
+ }
111
+
112
+ main();
113
+ </script>
114
+ </body>
115
+ </html>