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,164 @@
1
+ <div id="tuneframes" style="display:none">{"bpm":82,"duration":"12s"}</div>
2
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
3
+ <script>
4
+ // TuneFrames — Downtempo / Trip-Hop
5
+ // Portishead/Massive Attack: syncopated breakbeat, vinyl crackle,
6
+ // sub bass movement, AMSynth eerie melody, BitCrusher grit
7
+
8
+ async function main() {
9
+ await Tone.start();
10
+
11
+ const beat = 60 / 82; // one quarter note in seconds ≈ 0.732s
12
+ const eighth = beat / 2; // ≈ 0.366s
13
+ const sixteenth = beat / 4; // ≈ 0.183s
14
+
15
+ // ── Effects chain ──────────────────────────────────────────────────────────
16
+ const reverb = new Tone.Reverb({ decay: 5.5, preDelay: 0.03, wet: 0.65 });
17
+ await reverb.generate();
18
+ reverb.toDestination();
19
+
20
+ const filter = new Tone.Filter({ frequency: 2200, type: 'lowpass', rolloff: -24 });
21
+ filter.connect(reverb);
22
+
23
+ const bitCrusher = new Tone.BitCrusher(7);
24
+ bitCrusher.connect(reverb);
25
+
26
+ // ── Vinyl crackle — pink noise, always running ─────────────────────────────
27
+ // Constant sustain with no attack creates the "record playing" texture
28
+ const crackle = new Tone.NoiseSynth({
29
+ noise: { type: 'pink' },
30
+ envelope: { attack: 0.1, decay: 0.1, sustain: 1.0, release: 0.5 },
31
+ volume: -30,
32
+ }).toDestination();
33
+ // Start immediately in offline context
34
+ crackle.triggerAttack(0);
35
+
36
+ // ── Sub bass — pure sine, root movement ───────────────────────────────────
37
+ const bass = new Tone.MonoSynth({
38
+ oscillator: { type: 'sine' },
39
+ filter: { Q: 3, frequency: 110, type: 'lowpass', rolloff: -24 },
40
+ filterEnvelope: {
41
+ attack: 0.05, decay: 0.6, sustain: 0.3, release: 0.8,
42
+ baseFrequency: 60, octaves: 1.2,
43
+ },
44
+ envelope: { attack: 0.02, decay: 0.25, sustain: 0.65, release: 0.5 },
45
+ volume: -3,
46
+ }).toDestination();
47
+
48
+ // ── Eerie melody — AMSynth for trembling character ────────────────────────
49
+ const lead = new Tone.AMSynth({
50
+ harmonicity: 2.8,
51
+ oscillator: { type: 'sawtooth' },
52
+ envelope: { attack: 0.18, decay: 0.7, sustain: 0.35, release: 2.0 },
53
+ modulation: { type: 'sine' },
54
+ modulationEnvelope: {
55
+ attack: 0.4, decay: 0.0, sustain: 1.0, release: 0.8,
56
+ },
57
+ volume: -14,
58
+ });
59
+ lead.connect(filter);
60
+
61
+ // ── Kick — heavy, syncopated (NOT four-on-the-floor) ─────────────────────
62
+ const kick = new Tone.MembraneSynth({
63
+ pitchDecay: 0.1, octaves: 12,
64
+ envelope: { attack: 0.001, decay: 0.62, sustain: 0, release: 0.15 },
65
+ volume: -1,
66
+ }).toDestination();
67
+
68
+ // ── Snare — pink noise, sits late on 2 and 4 (swung feel) ────────────────
69
+ const snare = new Tone.NoiseSynth({
70
+ noise: { type: 'pink' },
71
+ envelope: { attack: 0.001, decay: 0.2, sustain: 0, release: 0.14 },
72
+ volume: -8,
73
+ });
74
+ snare.connect(bitCrusher);
75
+
76
+ // ── Hi-hat — sparse, off-beat 16th accents ────────────────────────────────
77
+ const hihat = new Tone.NoiseSynth({
78
+ noise: { type: 'white' },
79
+ envelope: { attack: 0.001, decay: 0.05, sustain: 0, release: 0.02 },
80
+ volume: -22,
81
+ }).toDestination();
82
+
83
+ // ── Schedule: bass enters at 0, beat at 2s, melody at 5s ─────────────────
84
+
85
+ // Bass line: D2 → A2 → F2 → G2 (minor feel, moves every 2 beats)
86
+ // Crackle plays from 0, bass from ~0, drums from 2s, melody from 5s
87
+ const bassNotes = [
88
+ { note: 'D2', t: 0 },
89
+ { note: 'A1', t: beat * 2 },
90
+ { note: 'F1', t: beat * 4 },
91
+ { note: 'G1', t: beat * 6 },
92
+ { note: 'D2', t: beat * 8 },
93
+ { note: 'A1', t: beat * 10 },
94
+ { note: 'F1', t: beat * 12 },
95
+ { note: 'G1', t: beat * 14 },
96
+ ];
97
+ bassNotes.forEach(({ note, t }) => {
98
+ if (t < 12) bass.triggerAttackRelease(note, '4n', t);
99
+ });
100
+
101
+ // Breakbeat kick pattern (syncopated — not on every beat)
102
+ // Pattern over 2 bars: beats 1, the-and-of-2, 3, the-and-of-4
103
+ // In 8ths at 82 BPM: positions 0, 3, 4, 7 out of 8
104
+ const kickPattern = [0, 3, 4, 7]; // 8th-note positions
105
+ for (let bar = 0; bar < 4; bar++) {
106
+ kickPattern.forEach((pos) => {
107
+ const t = 2.0 + bar * eighth * 8 + pos * eighth;
108
+ if (t < 12) kick.triggerAttackRelease('C1', '8n', t);
109
+ });
110
+ }
111
+
112
+ // Snare: beats 2 and 4 but slightly swung (pushed ~30ms late)
113
+ const snareSwing = 0.03;
114
+ const snarePattern = [1, 3, 5, 7]; // 8th positions = beats 2, 4 etc
115
+ for (let bar = 0; bar < 4; bar++) {
116
+ snarePattern.filter((_, i) => i % 2 === 0).forEach((pos) => {
117
+ const t = 2.0 + bar * eighth * 8 + pos * eighth + snareSwing;
118
+ if (t < 12) snare.triggerAttackRelease('8n', t);
119
+ });
120
+ }
121
+
122
+ // Sparse hi-hat: 16th-note accents on the off-offbeats
123
+ const hihatPositions = [1, 5, 9, 13]; // 16th positions
124
+ for (let bar = 0; bar < 4; bar++) {
125
+ hihatPositions.forEach((pos) => {
126
+ const t = 2.0 + bar * sixteenth * 16 + pos * sixteenth;
127
+ if (t < 12) hihat.triggerAttackRelease('16n', t);
128
+ });
129
+ }
130
+
131
+ // Eerie melody: enters at 5s, D minor descending line
132
+ // D4 → C4 → Bb3 → A3 → G3 → A3 (minor descent, last note back up)
133
+ const melodyNotes = [
134
+ { note: 'D4', dur: '4n', t: 5.0 },
135
+ { note: 'C4', dur: '4n', t: 5.0 + beat },
136
+ { note: 'Bb3', dur: '4n', t: 5.0 + beat * 2 },
137
+ { note: 'A3', dur: '2n', t: 5.0 + beat * 3 },
138
+ { note: 'G3', dur: '4n', t: 5.0 + beat * 5 },
139
+ { note: 'A3', dur: '2n', t: 5.0 + beat * 6 },
140
+ { note: 'D4', dur: '4n', t: 5.0 + beat * 8 },
141
+ { note: 'C4', dur: '4n', t: 5.0 + beat * 9 },
142
+ ];
143
+ melodyNotes.forEach(({ note, dur, t }) => {
144
+ if (t < 12) lead.triggerAttackRelease(note, dur, t);
145
+ });
146
+ }
147
+
148
+ window.renderComposition = async function(wavPath) {
149
+ let attempts = 0;
150
+ while (typeof Tone === 'undefined' && attempts < 50) {
151
+ await new Promise(r => setTimeout(r, 100));
152
+ attempts++;
153
+ }
154
+ if (typeof Tone === 'undefined') throw new Error('Tone not loaded');
155
+
156
+ const meta = JSON.parse(document.getElementById('tuneframes').textContent);
157
+ const durationSec = parseFloat(meta.duration) + 1.5;
158
+
159
+ const buffer = await Tone.Offline(async () => { await main(); }, durationSec, 1, 44100);
160
+ const wav = audioBufferToWav(buffer);
161
+ window.writeFile(wavPath, Array.from(new Uint8Array(wav)));
162
+ return wav.byteLength;
163
+ };
164
+ </script>
@@ -0,0 +1,139 @@
1
+ ---
2
+ name: audio-folk
3
+ description: Folk / Acoustic — fingerpicked guitar simulation (PolySynth pluck, alternating bass + chord tones), simple melodic line, bass root on downbeats, optional brushed snare. C/G/D major. Warm, wooden, human. Simon & Garfunkel simplicity.
4
+ ---
5
+
6
+ # TuneFrames — Folk / Acoustic
7
+
8
+ ## Genre Profile
9
+ - BPM range: 80–110 (comfortable walking pace)
10
+ - Key characteristics: Fingerpicking simulation via PolySynth with very fast attack, short decay, near-zero sustain (pluck envelope); thumb alternates root/5th bass notes while fingers pluck upper chord tones in between; simple diatonic melody; warm reverb, no chorus; near-dry bass; optional soft brushed snare
11
+ - Typical instruments: PolySynth/Synth (acoustic guitar pluck), Synth (bass), optional NoiseSynth (brushed snare)
12
+ - Mood: Intimate, warm, storytelling, human, wooden, unadorned
13
+
14
+ ## Core Pattern
15
+
16
+ ```js
17
+ // Folk fingerpicking — 92 BPM, G major
18
+ // Travis picking: thumb alternates G2/D2 (root/5th), fingers pluck upper chord tones
19
+ // Chord progression: G – C – D – Em (4-bar loop)
20
+
21
+ Tone.Transport.bpm.value = 92;
22
+
23
+ // Acoustic pluck: triangle oscillator, very short decay, no sustain
24
+ const guitarVerb = new Tone.Reverb({ decay: 1.5, wet: 0.2 }).toDestination();
25
+ const guitar = new Tone.PolySynth(Tone.Synth, {
26
+ oscillator: { type: "triangle" },
27
+ envelope: { attack: 0.003, decay: 0.22, sustain: 0.0, release: 0.5 },
28
+ volume: -6
29
+ }).connect(guitarVerb);
30
+
31
+ // Travis picking pattern for G major (8th notes, 1 bar):
32
+ // G2 (thumb-root), B3 (finger), D2 (thumb-5th), G3 (finger),
33
+ // G2 (thumb-root), D3 (finger), D2 (thumb-5th), B3 (finger)
34
+ const gBar = [
35
+ ["0:0:0","G2"], ["0:0:2","B3"], ["0:1:0","D2"], ["0:1:2","G3"],
36
+ ["0:2:0","G2"], ["0:2:2","D3"], ["0:3:0","D2"], ["0:3:2","B3"],
37
+ ];
38
+
39
+ const cBar = [
40
+ ["1:0:0","C2"], ["1:0:2","E3"], ["1:1:0","G2"], ["1:1:2","C3"],
41
+ ["1:2:0","C2"], ["1:2:2","G3"], ["1:3:0","G2"], ["1:3:2","E3"],
42
+ ];
43
+
44
+ const dBar = [
45
+ ["2:0:0","D2"], ["2:0:2","F#3"], ["2:1:0","A2"], ["2:1:2","D3"],
46
+ ["2:2:0","D2"], ["2:2:2","A3"], ["2:3:0","A2"], ["2:3:2","F#3"],
47
+ ];
48
+
49
+ const emBar = [
50
+ ["3:0:0","E2"], ["3:0:2","G3"], ["3:1:0","B2"], ["3:1:2","E3"],
51
+ ["3:2:0","E2"], ["3:2:2","B3"], ["3:3:0","B2"], ["3:3:2","G3"],
52
+ ];
53
+
54
+ const arpPart = new Tone.Part((time, note) => {
55
+ guitar.triggerAttackRelease(note, "8n", time);
56
+ }, [...gBar, ...cBar, ...dBar, ...emBar]);
57
+ arpPart.loopEnd = "4m";
58
+ arpPart.loop = true;
59
+ arpPart.start(0);
60
+
61
+ Tone.Transport.start();
62
+ ```
63
+
64
+ ## Instrument Configuration
65
+
66
+ ```js
67
+ // Acoustic guitar pluck — triangle oscillator, zero sustain is critical
68
+ const guitarVerb = new Tone.Reverb({ decay: 1.4, wet: 0.18 }).toDestination();
69
+ const guitar = new Tone.PolySynth(Tone.Synth, {
70
+ oscillator: { type: "triangle" },
71
+ envelope: { attack: 0.003, decay: 0.22, sustain: 0.0, release: 0.5 },
72
+ volume: -6
73
+ }).connect(guitarVerb);
74
+ // Note: sustain: 0.0 is what makes it "pluck" instead of "pad"
75
+ // Increase decay (0.3–0.5) for a slower, more resonant string feel
76
+
77
+ // Bass — sine oscillator, warm, simple root notes
78
+ const bass = new Tone.Synth({
79
+ oscillator: { type: "sine" },
80
+ envelope: { attack: 0.04, decay: 0.15, sustain: 0.6, release: 0.4 },
81
+ volume: -4
82
+ }).toDestination();
83
+
84
+ // Optional: brushed snare (very soft white noise, quiet)
85
+ const brushVerb = new Tone.Reverb({ decay: 0.6, wet: 0.3 }).toDestination();
86
+ const brushSnare = new Tone.NoiseSynth({
87
+ noise: { type: "white" },
88
+ envelope: { attack: 0.01, decay: 0.08, sustain: 0, release: 0.04 },
89
+ volume: -22 // very quiet — just a whisper of rhythm
90
+ }).connect(brushVerb);
91
+
92
+ // Optional: simple melody line (same pluck synth, higher octave)
93
+ const melody = new Tone.Synth({
94
+ oscillator: { type: "triangle" },
95
+ envelope: { attack: 0.005, decay: 0.3, sustain: 0.2, release: 0.6 },
96
+ volume: -8
97
+ }).connect(guitarVerb); // shares reverb with guitar
98
+ ```
99
+
100
+ ## Composition Structure
101
+
102
+ - **Bars 1–2:** Guitar fingerpicking alone — just the pattern, completely bare, let it breathe
103
+ - **Bars 3–4:** Add bass root notes on beat 1 of each bar — minimal, subtle
104
+ - **Bars 5–8:** Melody enters over the fingerpicking — simple 8th-note lines, diatonic
105
+ - **Bars 9–16:** Optional brushed snare very quietly on beats 2 and 4
106
+ - **Variation:** Pick one chord per 2 bars instead of 1 for a slower, more hymn-like feel
107
+ - **Outro:** Slow down BPM (use Transport.bpm ramp), fingerpicking thins to just root notes
108
+
109
+ Harmonic vocabulary: Stay strictly diatonic. G major scale only (G A B C D E F#). Common progressions: G–C–D–G (I–IV–V–I), G–Em–C–D (I–vi–IV–V), G–D–Em–C (the "axis" — works everywhere).
110
+
111
+ ## Example Variations
112
+
113
+ ### 1 — Capo feel (higher key)
114
+ ```js
115
+ // Transpose all notes up a perfect 4th (G → C, like a capo at fret 5)
116
+ // G major → C major: C D E F G A B
117
+ const gBar_capo = [
118
+ ["0:0:0","C3"], ["0:0:2","E4"], ["0:1:0","G2"], ["0:1:2","C4"],
119
+ ["0:2:0","C3"], ["0:2:2","G3"], ["0:3:0","G2"], ["0:3:2","E4"],
120
+ ];
121
+ ```
122
+
123
+ ### 2 — Arpeggio (no alternating bass, just chord tones ascending)
124
+ ```js
125
+ // Simpler pattern: just arpeggiate each chord upward
126
+ const gArp = ["G2","B2","D3","G3","D3","B2","G2","B2"];
127
+ let step = 0;
128
+ new Tone.Sequence((time) => {
129
+ guitar.triggerAttackRelease(gArp[step++ % gArp.length], "8n", time);
130
+ }, new Array(8).fill(1), "8n").start(0);
131
+ ```
132
+
133
+ ### 3 — Slower "hymn" tempo with whole-note chord pads
134
+ ```js
135
+ // One chord per 2 bars at 72 BPM — more contemplative
136
+ Tone.Transport.bpm.value = 72;
137
+ pad.triggerAttackRelease(["G3","B3","D4"], "2m", "0m");
138
+ pad.triggerAttackRelease(["C3","E3","G3"], "2m", "2m");
139
+ ```
@@ -0,0 +1,132 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>TuneFrames — Folk / Acoustic</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":92,"duration":"12s"}</div>
11
+
12
+ <script>
13
+ async function main() {
14
+ await Tone.start();
15
+ Tone.Transport.bpm.value = 92;
16
+
17
+ // --- MASTER BUS ---
18
+ const masterLimiter = new Tone.Limiter(-1).toDestination();
19
+ const masterGain = new Tone.Gain(0.88).connect(masterLimiter);
20
+
21
+ // --- GUITAR — triangle pluck, very short decay, near-zero sustain ---
22
+ const guitarVerb = new Tone.Reverb({ decay: 1.4, wet: 0.18 }).connect(masterGain);
23
+ await guitarVerb.ready;
24
+ const guitarGain = new Tone.Gain(0.65).connect(guitarVerb);
25
+ const guitar = new Tone.PolySynth(Tone.Synth, {
26
+ oscillator: { type: "triangle" },
27
+ envelope: { attack: 0.003, decay: 0.22, sustain: 0.0, release: 0.5 },
28
+ volume: 0
29
+ }).connect(guitarGain);
30
+
31
+ // --- MELODY — slightly warmer pluck, higher register ---
32
+ const melodyGain = new Tone.Gain(0.45).connect(guitarVerb); // shares reverb
33
+ const melody = new Tone.Synth({
34
+ oscillator: { type: "triangle" },
35
+ envelope: { attack: 0.005, decay: 0.3, sustain: 0.15, release: 0.6 },
36
+ volume: 0
37
+ }).connect(melodyGain);
38
+
39
+ // --- BASS — warm sine, root notes only ---
40
+ const bassGain = new Tone.Gain(0.55).connect(masterGain);
41
+ const bass = new Tone.Synth({
42
+ oscillator: { type: "sine" },
43
+ envelope: { attack: 0.04, decay: 0.15, sustain: 0.6, release: 0.4 },
44
+ volume: 0
45
+ }).connect(bassGain);
46
+
47
+ // --- BRUSHED SNARE — barely there, just rhythm shape ---
48
+ const brushVerb = new Tone.Reverb({ decay: 0.6, wet: 0.3 }).connect(masterGain);
49
+ await brushVerb.ready;
50
+ const brushGain = new Tone.Gain(0.12).connect(brushVerb);
51
+ const brush = new Tone.NoiseSynth({
52
+ noise: { type: "white" },
53
+ envelope: { attack: 0.01, decay: 0.08, sustain: 0, release: 0.04 }
54
+ }).connect(brushGain);
55
+
56
+ // --- TRAVIS PICKING — 4-bar G–C–D–Em loop ---
57
+ // Each bar: thumb on root/5th (low notes), finger on upper chord tones
58
+ // 8th note grid: bass note, chord tone, bass note, chord tone...
59
+ const pickingPattern = [
60
+ // Bar 0: G major — G2(thumb), B3(finger), D2(thumb-5th), G3(finger)...
61
+ ["0:0:0","G2"], ["0:0:2","B3"], ["0:1:0","D2"], ["0:1:2","G3"],
62
+ ["0:2:0","G2"], ["0:2:2","D3"], ["0:3:0","D2"], ["0:3:2","B3"],
63
+ // Bar 1: C major — C2(thumb), E3(finger), G2(thumb-5th), C3(finger)...
64
+ ["1:0:0","C2"], ["1:0:2","E3"], ["1:1:0","G2"], ["1:1:2","C3"],
65
+ ["1:2:0","C2"], ["1:2:2","G3"], ["1:3:0","G2"], ["1:3:2","E3"],
66
+ // Bar 2: D major — D2(thumb), F#3(finger), A2(thumb-5th), D3(finger)...
67
+ ["2:0:0","D2"], ["2:0:2","F#3"], ["2:1:0","A2"], ["2:1:2","D3"],
68
+ ["2:2:0","D2"], ["2:2:2","A3"], ["2:3:0","A2"], ["2:3:2","F#3"],
69
+ // Bar 3: E minor — E2(thumb), G3(finger), B2(thumb-5th), E3(finger)...
70
+ ["3:0:0","E2"], ["3:0:2","G3"], ["3:1:0","B2"], ["3:1:2","E3"],
71
+ ["3:2:0","E2"], ["3:2:2","B3"], ["3:3:0","B2"], ["3:3:2","G3"],
72
+ ];
73
+ const pickPart = new Tone.Part((time, note) => {
74
+ guitar.triggerAttackRelease(note, "8n", time);
75
+ }, pickingPattern);
76
+ pickPart.loopEnd = "4m";
77
+ pickPart.loop = true;
78
+ pickPart.start(0);
79
+
80
+ // --- BASS — root on beat 1 of each bar, enters bar 2 ---
81
+ const bassPart = new Tone.Part((time, note) => {
82
+ bass.triggerAttackRelease(note, "2n", time);
83
+ }, [
84
+ ["0:0:0","G2"],
85
+ ["1:0:0","C2"],
86
+ ["2:0:0","D2"],
87
+ ["3:0:0","E2"],
88
+ ]);
89
+ bassPart.loopEnd = "4m";
90
+ bassPart.loop = true;
91
+ bassPart.start("1m"); // bass enters bar 2
92
+
93
+ // --- MELODY — simple diatonic line over the chord progression, enters bar 3 ---
94
+ const melodyNotes = [
95
+ // Over G: D4 – B3 – G3 – A3
96
+ { time: "0:0:0", note: "D4", dur: "4n" },
97
+ { time: "0:1:0", note: "B3", dur: "4n" },
98
+ { time: "0:2:0", note: "G3", dur: "4n" },
99
+ { time: "0:3:0", note: "A3", dur: "4n" },
100
+ // Over C: G3 – E3 – C4 – D4
101
+ { time: "1:0:0", note: "G3", dur: "4n" },
102
+ { time: "1:1:0", note: "E3", dur: "4n" },
103
+ { time: "1:2:0", note: "C4", dur: "2n" },
104
+ // Over D: D4 – F#3 – A3 – G3
105
+ { time: "2:0:0", note: "D4", dur: "4n" },
106
+ { time: "2:1:0", note: "F#3", dur: "4n" },
107
+ { time: "2:2:0", note: "A3", dur: "4n" },
108
+ { time: "2:3:0", note: "G3", dur: "4n" },
109
+ // Over Em: E3 – G3 – B3 – G3
110
+ { time: "3:0:0", note: "E3", dur: "4n" },
111
+ { time: "3:1:0", note: "G3", dur: "4n" },
112
+ { time: "3:2:0", note: "B3", dur: "2n" },
113
+ ];
114
+ const melodyPart = new Tone.Part((time, val) => {
115
+ melody.triggerAttackRelease(val.note, val.dur, time);
116
+ }, melodyNotes.map(({ time, note, dur }) => [time, { note, dur }]));
117
+ melodyPart.loopEnd = "4m";
118
+ melodyPart.loop = true;
119
+ melodyPart.start("2m"); // melody enters bar 3
120
+
121
+ // --- BRUSHED SNARE — beats 2 and 4, very quiet, enters bar 3 ---
122
+ const brushSeq = new Tone.Sequence((time, val) => {
123
+ if (val) brush.triggerAttackRelease("8n", time);
124
+ }, [null,null,null,null,1,null,null,null,null,null,null,null,1,null,null,null], "16n");
125
+ brushSeq.start("2m");
126
+
127
+ }
128
+
129
+ main();
130
+ </script>
131
+ </body>
132
+ </html>
@@ -0,0 +1,149 @@
1
+ ---
2
+ name: audio-funk
3
+ description: Funk — slap bass, brass stabs, 16th-note hi-hats, syncopated groove that snaps
4
+ ---
5
+
6
+ # TuneFrames — Funk
7
+
8
+ ## Genre Profile
9
+ - BPM range: 95–115 (the pocket lives around 100–108)
10
+ - Key characteristics: Syncopated 16th-note grid, everything lands on or anticipates the beat, brass stabs on upbeats, rhythm guitar "chank" on the "and" of 2 and 4, bass owns the low end
11
+ - Typical instruments: Slap bass (Synth with fast attack + filter), brass stabs (PolySynth sawtooth, sharp envelope), rhythm guitar (PolySynth pluck), kick (MembraneSynth), tight snare (NoiseSynth), 16th hi-hats (MetalSynth)
12
+ - Mood: Infectious, locked-in, body-moving, celebratory — James Brown, Parliament, Daft Punk, Vulfpeck
13
+
14
+ ## Core Pattern
15
+
16
+ ```js
17
+ // Funk core: 104 BPM, E minor groove
18
+ async function main() {
19
+ await Tone.start();
20
+ Tone.Transport.bpm.value = 104;
21
+
22
+ const s16 = Tone.Time('16n').toSeconds(); // one 16th note
23
+ const q = Tone.Time('4n').toSeconds(); // one beat
24
+
25
+ const dest = new Tone.Gain(1).toDestination();
26
+ const comp = new Tone.Compressor(-18, 4).connect(dest);
27
+
28
+ // ── Slap bass ────────────────────────────────────────────────────────
29
+ const bass = new Tone.Synth({
30
+ oscillator: { type: 'square' },
31
+ envelope: { attack: 0.001, decay: 0.08, sustain: 0.0, release: 0.05 }
32
+ }).connect(comp);
33
+ bass.volume.value = -6;
34
+
35
+ // Classic one-bar funk bass: E on 1, ghost note 16th early, pop on e2-and
36
+ const bassLine = [
37
+ { note: 'E2', time: 0 },
38
+ { note: 'E2', time: s16 * 2 },
39
+ { note: 'G2', time: s16 * 3 },
40
+ { note: 'E2', time: s16 * 4 },
41
+ { note: 'A2', time: s16 * 6 },
42
+ { note: 'E2', time: s16 * 8 },
43
+ { note: 'D2', time: s16 * 10 },
44
+ { note: 'E2', time: s16 * 12 },
45
+ { note: 'G2', time: s16 * 14 },
46
+ ];
47
+ bassLine.forEach(({ note, time }) => bass.triggerAttackRelease(note, '16n', time));
48
+
49
+ // ── Brass stabs (upbeat hits — "and" of 2 and 4) ──────────────────────
50
+ const brass = new Tone.PolySynth(Tone.Synth, {
51
+ oscillator: { type: 'sawtooth' },
52
+ envelope: { attack: 0.005, decay: 0.12, sustain: 0.0, release: 0.08 }
53
+ }).connect(comp);
54
+ brass.volume.value = -12;
55
+
56
+ // Stab on the "and" of beat 2 (s16*9) and "and" of beat 4 (s16*13)
57
+ brass.triggerAttackRelease(['E4','G4','B4'], '16n', s16 * 9);
58
+ brass.triggerAttackRelease(['E4','G4','B4'], '16n', s16 * 13);
59
+
60
+ // ── Tight kick ────────────────────────────────────────────────────────
61
+ const kick = new Tone.MembraneSynth({
62
+ pitchDecay: 0.04, octaves: 6,
63
+ envelope: { attack: 0.001, decay: 0.25, sustain: 0, release: 0.1 }
64
+ }).connect(dest);
65
+ kick.volume.value = -10;
66
+ kick.triggerAttackRelease('C1', '8n', 0); // beat 1
67
+ kick.triggerAttackRelease('C1', '8n', s16 * 10); // beat 2-and-a (syncopated)
68
+
69
+ // ── Snare on 3 ────────────────────────────────────────────────────────
70
+ const snare = new Tone.NoiseSynth({
71
+ noise: { type: 'white' },
72
+ envelope: { attack: 0.001, decay: 0.1, sustain: 0, release: 0.05 }
73
+ }).connect(new Tone.Filter(3500, 'bandpass').connect(dest));
74
+ snare.volume.value = -14;
75
+ snare.triggerAttackRelease('8n', s16 * 8); // beat 3
76
+
77
+ // ── 16th hi-hats ─────────────────────────────────────────────────────
78
+ const hat = new Tone.MetalSynth({
79
+ frequency: 600, harmonicity: 5.1, modulationIndex: 32,
80
+ resonance: 4000, octaves: 1.5,
81
+ envelope: { attack: 0.001, decay: 0.03, release: 0.01 }
82
+ }).connect(dest);
83
+ hat.volume.value = -20;
84
+ for (let i = 0; i < 16; i++) hat.triggerAttackRelease('32n', i * s16);
85
+ }
86
+ ```
87
+
88
+ ## Instrument Configuration
89
+
90
+ ```js
91
+ // Slap bass — square wave, nearly no sustain (percussive pluck feel)
92
+ const bass = new Tone.Synth({
93
+ oscillator: { type: 'square' },
94
+ envelope: { attack: 0.001, decay: 0.08, sustain: 0.0, release: 0.05 }
95
+ });
96
+
97
+ // Rhythm guitar "chank" — bright pluck, bandpass filtered, chord stab
98
+ const guitar = new Tone.PolySynth(Tone.Synth, {
99
+ oscillator: { type: 'triangle' },
100
+ envelope: { attack: 0.003, decay: 0.15, sustain: 0.0, release: 0.08 }
101
+ });
102
+ const guitarFilter = new Tone.Filter(2500, 'bandpass');
103
+ guitar.connect(guitarFilter);
104
+
105
+ // Brass stabs — sawtooth, instant attack, very short decay (punchy, no tail)
106
+ const brass = new Tone.PolySynth(Tone.Synth, {
107
+ oscillator: { type: 'sawtooth' },
108
+ envelope: { attack: 0.005, decay: 0.12, sustain: 0.0, release: 0.08 }
109
+ });
110
+
111
+ // Bus compressor glues everything together (the "pumping" funk feel)
112
+ const comp = new Tone.Compressor({ threshold: -18, ratio: 4, attack: 0.003, release: 0.1 });
113
+ comp.toDestination();
114
+ // Route everything through comp
115
+ [bass, brass, guitar].forEach(s => s.connect(comp));
116
+ ```
117
+
118
+ ## Composition Structure
119
+
120
+ 1. **Intro (0–2s):** Bass alone on the groove, just root and fifth — establish the pocket
121
+ 2. **Drums in (2–4s):** Kick + snare + 16th hats lock with the bass
122
+ 3. **Full band (4–8s):** Brass stabs enter on upbeats, guitar chank fills the midrange
123
+ 4. **Breakdown (8–10s):** Strip to bass + kick only — tension before the re-hit
124
+ 5. **Re-hit (10–12s):** Everything back, maybe add a filter sweep on the bass up
125
+
126
+ ## Example Variations
127
+
128
+ ### 1 — Open hi-hat accent on beat 4
129
+ ```js
130
+ const openHat = new Tone.MetalSynth({ envelope: { decay: 0.25 } }).connect(dest);
131
+ openHat.volume.value = -18;
132
+ openHat.triggerAttackRelease('8n', q * 3); // beat 4 open
133
+ ```
134
+
135
+ ### 2 — Wah filter on brass (auto-wah effect)
136
+ ```js
137
+ const autoWah = new Tone.AutoFilter({ frequency: 4, baseFrequency: 200, octaves: 4, wet: 0.8 });
138
+ autoWah.connect(dest);
139
+ autoWah.start(0);
140
+ brass.connect(autoWah);
141
+ ```
142
+
143
+ ### 3 — Daft Punk-style filtered house funk
144
+ ```js
145
+ // Add LP filter sweep on the bass from 300 Hz to 2000 Hz over 4 bars
146
+ const bassFilter = new Tone.Filter(300, 'lowpass').connect(comp);
147
+ bass.connect(bassFilter);
148
+ bassFilter.frequency.rampTo(2000, bar * 4);
149
+ ```