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.
- package/LICENSE +166 -166
- package/README.md +158 -55
- package/examples/example-ai-dj-chill.html +167 -0
- package/examples/example-ai-dj-dark.html +167 -0
- package/examples/example-ai-dj-energetic.html +167 -0
- package/examples/example-ai-dj-happy.html +167 -0
- package/examples/example-ai-dj.html +167 -0
- package/examples/example-ambient.html +63 -63
- package/examples/example-ambient.mp3 +0 -0
- package/examples/example-bass.html +47 -47
- package/examples/example-lofi.html +45 -45
- package/examples/example-lofi.mp3 +0 -0
- package/examples/example-minimal.html +20 -20
- package/examples/example-orchestral.html +67 -67
- package/examples/example-orchestral.mp3 +0 -0
- package/examples/example-piano.html +52 -52
- package/examples/example-piano.mp3 +0 -0
- package/examples/example-techno.html +69 -69
- package/examples/example-techno.mp3 +0 -0
- package/package.json +42 -37
- package/registry/presets/bass-electric.html +67 -0
- package/registry/presets/bass-saw.html +22 -0
- package/registry/presets/chord-progression.html +22 -0
- package/registry/presets/drums-808.html +85 -0
- package/registry/presets/drums-lofi.html +35 -0
- package/registry/presets/lead-piano.html +26 -0
- package/registry/presets/piano-salamander.html +69 -0
- package/registry/presets/reverb-warm.html +11 -0
- package/registry/samples.json +226 -0
- package/skills/audio-ambient/SKILL.md +141 -0
- package/skills/audio-ambient/example.html +113 -0
- package/skills/audio-boss-battle/SKILL.md +157 -0
- package/skills/audio-boss-battle/example.html +185 -0
- package/skills/audio-boss-battle/example.mp3 +0 -0
- package/skills/audio-chillwave/SKILL.md +142 -0
- package/skills/audio-chillwave/example.html +144 -0
- package/skills/audio-cinematic/SKILL.md +147 -0
- package/skills/audio-cinematic/example.html +123 -0
- package/skills/audio-classical/SKILL.md +138 -0
- package/skills/audio-classical/example.html +145 -0
- package/skills/audio-dnb/SKILL.md +142 -0
- package/skills/audio-dnb/example.html +124 -0
- package/skills/audio-downtempo/SKILL.md +152 -0
- package/skills/audio-downtempo/example.html +164 -0
- package/skills/audio-folk/SKILL.md +139 -0
- package/skills/audio-folk/example.html +132 -0
- package/skills/audio-funk/SKILL.md +149 -0
- package/skills/audio-funk/example.html +144 -0
- package/skills/audio-future-bass/SKILL.md +163 -0
- package/skills/audio-future-bass/example.html +164 -0
- package/skills/audio-hip-hop/SKILL.md +133 -0
- package/skills/audio-hip-hop/example.html +129 -0
- package/skills/audio-house/SKILL.md +147 -0
- package/skills/audio-house/example.html +128 -0
- package/skills/audio-indie-pop/SKILL.md +150 -0
- package/skills/audio-indie-pop/example.html +121 -0
- package/skills/audio-jazz/SKILL.md +141 -0
- package/skills/audio-jazz/example.html +146 -0
- package/skills/audio-lofi/SKILL.md +140 -0
- package/skills/audio-lofi/example.html +135 -0
- package/skills/audio-minimal/SKILL.md +155 -0
- package/skills/audio-minimal/example.html +118 -0
- package/skills/audio-orchestral/SKILL.md +156 -0
- package/skills/audio-orchestral/example.html +140 -0
- package/skills/audio-r-and-b/SKILL.md +134 -0
- package/skills/audio-r-and-b/example.html +154 -0
- package/skills/audio-r-and-b/example.mp3 +0 -0
- package/skills/audio-techno/SKILL.md +140 -0
- package/skills/audio-techno/example.html +125 -0
- package/skills/audio-trap/SKILL.md +123 -0
- package/skills/audio-trap/example.html +119 -0
- package/skills/render-retest/example.html +115 -0
- package/skills/tuneframes/SKILL.md +221 -0
- package/skills/tuneframes-cli/SKILL.md +46 -0
- package/skills/verify/example.html +104 -0
- package/src/cli.js +260 -151
- package/src/render.js +134 -7
- package/examples/example-bass.mp3 +0 -0
- package/examples/example-demo-beat.wav +0 -0
- package/examples/example-orchestral.wav +0 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>TuneFrames — Indie Pop</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":128,"duration":"12s"}</div>
|
|
11
|
+
|
|
12
|
+
<script>
|
|
13
|
+
async function main() {
|
|
14
|
+
await Tone.start();
|
|
15
|
+
Tone.Transport.bpm.value = 128;
|
|
16
|
+
|
|
17
|
+
// --- MASTER BUS ---
|
|
18
|
+
const masterLimiter = new Tone.Limiter(-1).toDestination();
|
|
19
|
+
const masterGain = new Tone.Gain(0.85).connect(masterLimiter);
|
|
20
|
+
|
|
21
|
+
// --- GUITAR — jangly pluck through chorus + reverb ---
|
|
22
|
+
const guitarVerb = new Tone.Reverb({ decay: 2.5, wet: 0.35 }).connect(masterGain);
|
|
23
|
+
const guitarChorus = new Tone.Chorus({ frequency: 3.5, delayTime: 2, depth: 0.6, wet: 0.5 }).connect(guitarVerb);
|
|
24
|
+
guitarChorus.start();
|
|
25
|
+
const guitarGain = new Tone.Gain(0.55).connect(guitarChorus);
|
|
26
|
+
const guitar = new Tone.PolySynth(Tone.Synth, {
|
|
27
|
+
oscillator: { type: "triangle" },
|
|
28
|
+
envelope: { attack: 0.005, decay: 0.28, sustain: 0.06, release: 0.7 },
|
|
29
|
+
volume: 0
|
|
30
|
+
}).connect(guitarGain);
|
|
31
|
+
|
|
32
|
+
// --- BASS — warm sine, root movement ---
|
|
33
|
+
const bassVerb = new Tone.Reverb({ decay: 0.6, wet: 0.1 }).connect(masterGain);
|
|
34
|
+
const bassGain = new Tone.Gain(0.65).connect(bassVerb);
|
|
35
|
+
const bass = new Tone.Synth({
|
|
36
|
+
oscillator: { type: "sine" },
|
|
37
|
+
envelope: { attack: 0.05, decay: 0.2, sustain: 0.7, release: 0.4 },
|
|
38
|
+
volume: 0
|
|
39
|
+
}).connect(bassGain);
|
|
40
|
+
|
|
41
|
+
// --- KICK — punchy but not hard ---
|
|
42
|
+
const kickDist = new Tone.Distortion({ distortion: 0.1, wet: 0.3 }).connect(masterGain);
|
|
43
|
+
const kickGain = new Tone.Gain(0.75).connect(kickDist);
|
|
44
|
+
const kick = new Tone.MembraneSynth({
|
|
45
|
+
pitchDecay: 0.06,
|
|
46
|
+
octaves: 5,
|
|
47
|
+
envelope: { attack: 0.001, decay: 0.3, sustain: 0, release: 0.1 }
|
|
48
|
+
}).connect(kickGain);
|
|
49
|
+
|
|
50
|
+
// --- SNARE — white noise with moderate reverb ---
|
|
51
|
+
const snareVerb = new Tone.Reverb({ decay: 0.9, wet: 0.38 }).connect(masterGain);
|
|
52
|
+
const snareGain = new Tone.Gain(0.45).connect(snareVerb);
|
|
53
|
+
const snare = new Tone.NoiseSynth({
|
|
54
|
+
noise: { type: "white" },
|
|
55
|
+
envelope: { attack: 0.001, decay: 0.15, sustain: 0, release: 0.05 }
|
|
56
|
+
}).connect(snareGain);
|
|
57
|
+
|
|
58
|
+
// --- HI-HAT — highpass filtered, airy ---
|
|
59
|
+
const hatFilter = new Tone.Filter(7000, "highpass").connect(masterGain);
|
|
60
|
+
const hatGain = new Tone.Gain(0.22).connect(hatFilter);
|
|
61
|
+
const hat = new Tone.NoiseSynth({
|
|
62
|
+
noise: { type: "white" },
|
|
63
|
+
envelope: { attack: 0.001, decay: 0.05, sustain: 0, release: 0.01 }
|
|
64
|
+
}).connect(hatGain);
|
|
65
|
+
|
|
66
|
+
// --- ARPEGGIO — 4-bar G–C–Em–D chord cycle, 8th note grid ---
|
|
67
|
+
// 8 notes per bar × 4 bars = 32-note grid, loops seamlessly
|
|
68
|
+
const arpGrid = [
|
|
69
|
+
// Bar 0: G major
|
|
70
|
+
"G3","B3","D4","G4","D4","B3","G3","B3",
|
|
71
|
+
// Bar 1: C major
|
|
72
|
+
"C4","E4","G4","C5","G4","E4","C4","E4",
|
|
73
|
+
// Bar 2: E minor
|
|
74
|
+
"E3","G3","B3","E4","B3","G3","E3","G3",
|
|
75
|
+
// Bar 3: D major
|
|
76
|
+
"D3","F#3","A3","D4","A3","F#3","D3","F#3",
|
|
77
|
+
];
|
|
78
|
+
let arpStep = 0;
|
|
79
|
+
const arpSeq = new Tone.Sequence((time) => {
|
|
80
|
+
guitar.triggerAttackRelease(arpGrid[arpStep % arpGrid.length], "8n", time);
|
|
81
|
+
arpStep++;
|
|
82
|
+
}, new Array(8).fill(1), "8n");
|
|
83
|
+
arpSeq.start(0);
|
|
84
|
+
|
|
85
|
+
// --- BASS — root + 5th pattern per chord, enters bar 3 ---
|
|
86
|
+
const bassPart = new Tone.Part((time, note) => {
|
|
87
|
+
bass.triggerAttackRelease(note, "4n", time);
|
|
88
|
+
}, [
|
|
89
|
+
["0:0:0","G2"], ["0:2:0","D2"],
|
|
90
|
+
["1:0:0","C2"], ["1:2:0","G2"],
|
|
91
|
+
["2:0:0","E2"], ["2:2:0","B2"],
|
|
92
|
+
["3:0:0","D2"], ["3:2:0","A2"],
|
|
93
|
+
]);
|
|
94
|
+
bassPart.loopEnd = "4m";
|
|
95
|
+
bassPart.loop = true;
|
|
96
|
+
bassPart.start("2m");
|
|
97
|
+
|
|
98
|
+
// --- KICK — beats 1 and 3, enters bar 3 ---
|
|
99
|
+
const kickSeq = new Tone.Sequence((time, val) => {
|
|
100
|
+
if (val) kick.triggerAttackRelease("C1", "8n", time);
|
|
101
|
+
}, ["C1",null,null,null,null,null,null,null,"C1",null,null,null,null,null,null,null], "16n");
|
|
102
|
+
kickSeq.start("2m");
|
|
103
|
+
|
|
104
|
+
// --- SNARE — beats 2 and 4, enters bar 3 ---
|
|
105
|
+
const snareSeq = new Tone.Sequence((time, val) => {
|
|
106
|
+
if (val) snare.triggerAttackRelease("8n", time);
|
|
107
|
+
}, [null,null,null,null,1,null,null,null,null,null,null,null,1,null,null,null], "16n");
|
|
108
|
+
snareSeq.start("2m");
|
|
109
|
+
|
|
110
|
+
// --- HI-HAT — 8th notes throughout ---
|
|
111
|
+
const hatSeq = new Tone.Sequence((time, val) => {
|
|
112
|
+
if (val) hat.triggerAttackRelease("16n", time, 0.5);
|
|
113
|
+
}, [1,null,1,null,1,null,1,null,1,null,1,null,1,null,1,null], "16n");
|
|
114
|
+
hatSeq.start(0);
|
|
115
|
+
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
main();
|
|
119
|
+
</script>
|
|
120
|
+
</body>
|
|
121
|
+
</html>
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: audio-jazz
|
|
3
|
+
description: Jazz — ii-V-I changes, extended chords, walking bass, swing feel, bebop melody
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TuneFrames — Jazz
|
|
7
|
+
|
|
8
|
+
## Genre Profile
|
|
9
|
+
- BPM range: 100–180 (ballad 60–90, bebop 180–240)
|
|
10
|
+
- Key characteristics: ii-V-I progressions, extended/altered chords (maj7, min7, dom7, dim7), swing 8th notes, call-and-response phrasing
|
|
11
|
+
- Typical instruments: Piano (PolySynth), walking upright bass (Synth sine), ride cymbal (MetalSynth), brush snare (NoiseSynth), optional vibraphone
|
|
12
|
+
- Mood: Sophisticated, improvisatory, warm, intellectually alive
|
|
13
|
+
|
|
14
|
+
## Core Pattern
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
// Jazz core: 120 BPM swing, Dm7–G7–Cmaj7 (ii-V-I in C major)
|
|
18
|
+
async function main() {
|
|
19
|
+
await Tone.start();
|
|
20
|
+
Tone.Transport.bpm.value = 120;
|
|
21
|
+
|
|
22
|
+
const reverb = new Tone.Reverb({ decay: 1.8, wet: 0.3 }).toDestination();
|
|
23
|
+
|
|
24
|
+
// ── Piano voicings (shell voicings: root + 3rd + 7th) ─────────────────
|
|
25
|
+
const piano = new Tone.PolySynth(Tone.Synth, {
|
|
26
|
+
oscillator: { type: 'triangle' },
|
|
27
|
+
envelope: { attack: 0.01, decay: 0.4, sustain: 0.5, release: 1.2 }
|
|
28
|
+
}).connect(reverb);
|
|
29
|
+
piano.volume.value = -10;
|
|
30
|
+
|
|
31
|
+
const bar = Tone.Time('1n').toSeconds(); // 1 bar in seconds
|
|
32
|
+
|
|
33
|
+
// ii–V–I–I in C (shell voicings)
|
|
34
|
+
const changes = [
|
|
35
|
+
['D3','F3','C4'], // Dm7 (ii)
|
|
36
|
+
['G3','B3','F4'], // G7 (V)
|
|
37
|
+
['C3','E3','B3'], // Cmaj7 (I)
|
|
38
|
+
['C3','E3','G3','B3'], // Cmaj7 full (I hold)
|
|
39
|
+
];
|
|
40
|
+
changes.forEach((ch, i) => piano.triggerAttackRelease(ch, '1n', i * bar));
|
|
41
|
+
|
|
42
|
+
// ── Walking bass (quarter notes, chromatic approach notes) ────────────
|
|
43
|
+
const bass = new Tone.Synth({
|
|
44
|
+
oscillator: { type: 'sine' },
|
|
45
|
+
envelope: { attack: 0.01, decay: 0.1, sustain: 0.6, release: 0.2 }
|
|
46
|
+
}).connect(reverb);
|
|
47
|
+
bass.volume.value = -8;
|
|
48
|
+
|
|
49
|
+
// Classic walking line: D–F–A–C / G–B–D–F / C–E–G–B / C–E–G–C
|
|
50
|
+
const walk = ['D2','F2','A2','C3','G2','B2','D3','F3','C2','E2','G2','B2','C2','E2','G2','C3'];
|
|
51
|
+
const q = Tone.Time('4n').toSeconds();
|
|
52
|
+
walk.forEach((n, i) => bass.triggerAttackRelease(n, '4n', i * q));
|
|
53
|
+
|
|
54
|
+
// ── Ride cymbal (swing pattern: 1–2–ah–3–4–ah) ────────────────────────
|
|
55
|
+
const ride = new Tone.MetalSynth({
|
|
56
|
+
frequency: 250, harmonicity: 5.1, modulationIndex: 16,
|
|
57
|
+
resonance: 4000, octaves: 1.5,
|
|
58
|
+
envelope: { attack: 0.001, decay: 0.3, release: 0.1 }
|
|
59
|
+
}).connect(reverb);
|
|
60
|
+
ride.volume.value = -22;
|
|
61
|
+
|
|
62
|
+
// Swing ride: beats 1, 2 triplet-late, 3, 4 triplet-late
|
|
63
|
+
const swingOffset = q * 0.67; // triplet-swing "ah"
|
|
64
|
+
for (let i = 0; i < 4; i++) {
|
|
65
|
+
ride.triggerAttackRelease('8n', i * bar);
|
|
66
|
+
ride.triggerAttackRelease('8n', i * bar + swingOffset);
|
|
67
|
+
ride.triggerAttackRelease('8n', i * bar + q * 2);
|
|
68
|
+
ride.triggerAttackRelease('8n', i * bar + q * 2 + swingOffset);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Instrument Configuration
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
// Jazz piano — triangle wave (mellow, not harsh), moderate decay
|
|
77
|
+
const piano = new Tone.PolySynth(Tone.Synth, {
|
|
78
|
+
oscillator: { type: 'triangle' },
|
|
79
|
+
envelope: { attack: 0.01, decay: 0.4, sustain: 0.5, release: 1.2 }
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Upright bass feel — pure sine, tight decay, no sustain tail
|
|
83
|
+
const bass = new Tone.Synth({
|
|
84
|
+
oscillator: { type: 'sine' },
|
|
85
|
+
envelope: { attack: 0.01, decay: 0.08, sustain: 0.55, release: 0.15 }
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Ride cymbal — low frequency MetalSynth, long decay
|
|
89
|
+
const ride = new Tone.MetalSynth({
|
|
90
|
+
frequency: 250, harmonicity: 5.1, modulationIndex: 16,
|
|
91
|
+
resonance: 4000, octaves: 1.5,
|
|
92
|
+
envelope: { attack: 0.001, decay: 0.3, release: 0.1 }
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Brush snare — bandpass-filtered white noise
|
|
96
|
+
const snare = new Tone.NoiseSynth({
|
|
97
|
+
noise: { type: 'white' },
|
|
98
|
+
envelope: { attack: 0.005, decay: 0.1, sustain: 0, release: 0.05 }
|
|
99
|
+
});
|
|
100
|
+
const snareFilter = new Tone.Filter(2500, 'bandpass');
|
|
101
|
+
snare.connect(snareFilter);
|
|
102
|
+
|
|
103
|
+
// Room reverb — short, warm
|
|
104
|
+
const reverb = new Tone.Reverb({ decay: 1.8, wet: 0.3 }).toDestination();
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Composition Structure
|
|
108
|
+
|
|
109
|
+
1. **Head in (0–4s):** Piano states the changes, bass walks, ride establishes swing
|
|
110
|
+
2. **Melody chorus (4–8s):** Bebop-style single-note melody over the changes (arpeggiated +chromatic passing tones)
|
|
111
|
+
3. **Reharmonization (8–12s):** Tritone sub on the V chord (Db7 instead of G7), piano plays richer voicings
|
|
112
|
+
4. **Tag / turnaround:** Quick ii-V back to the top, ritardando on the final bar
|
|
113
|
+
|
|
114
|
+
## Example Variations
|
|
115
|
+
|
|
116
|
+
### 1 — Tritone substitution on V (G7 → Db7)
|
|
117
|
+
```js
|
|
118
|
+
// Replace G7 shell with Db7 shell (tritone sub)
|
|
119
|
+
const changes = [
|
|
120
|
+
['D3','F3','C4'], // Dm7
|
|
121
|
+
['Db3','F3','B3'], // Db7 (tritone sub for G7)
|
|
122
|
+
['C3','E3','B3'], // Cmaj7
|
|
123
|
+
];
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### 2 — Ballad tempo (70 BPM) with rubato melody
|
|
127
|
+
```js
|
|
128
|
+
Tone.Transport.bpm.value = 70;
|
|
129
|
+
// Hold each chord for 2 bars, melody uses longer note values ('2n', '1n')
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 3 — Vibraphone color (add shimmer on the I chord)
|
|
133
|
+
```js
|
|
134
|
+
const vibe = new Tone.PolySynth(Tone.Synth, {
|
|
135
|
+
oscillator: { type: 'sine' },
|
|
136
|
+
envelope: { attack: 0.005, decay: 1.5, sustain: 0.2, release: 2.0 }
|
|
137
|
+
});
|
|
138
|
+
const vibeVib = new Tone.Vibrato({ frequency: 5, depth: 0.05 }).connect(reverb);
|
|
139
|
+
vibe.connect(vibeVib);
|
|
140
|
+
vibe.triggerAttackRelease(['C4','E4','G4','B4'], '2n', barStart);
|
|
141
|
+
```
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>TuneFrames — Jazz</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":132,"duration":"12s"}</div>
|
|
9
|
+
<script>
|
|
10
|
+
async function main() {
|
|
11
|
+
await Tone.start();
|
|
12
|
+
Tone.Transport.bpm.value = 132;
|
|
13
|
+
|
|
14
|
+
// ── Effects ─────────────────────────────────────────────────────────
|
|
15
|
+
const reverb = new Tone.Reverb({ decay: 1.6, preDelay: 0.01, wet: 0.28 }).toDestination();
|
|
16
|
+
await reverb.ready;
|
|
17
|
+
|
|
18
|
+
// ── Piano — shell voicings (3rd + 7th on top) ───────────────────────
|
|
19
|
+
const piano = new Tone.PolySynth(Tone.Synth, {
|
|
20
|
+
oscillator: { type: 'triangle' },
|
|
21
|
+
envelope: { attack: 0.008, decay: 0.35, sustain: 0.5, release: 1.4 }
|
|
22
|
+
}).connect(reverb);
|
|
23
|
+
piano.volume.value = -10;
|
|
24
|
+
|
|
25
|
+
const bar = Tone.Time('1n').toSeconds();
|
|
26
|
+
const q = Tone.Time('4n').toSeconds();
|
|
27
|
+
|
|
28
|
+
// ii–V–I–vi turnaround in C major (2 full passes = 8 bars)
|
|
29
|
+
// Bar layout: Dm7 | G7 | Cmaj7 | Am7 | repeat
|
|
30
|
+
const changes = [
|
|
31
|
+
{ chord: ['D3','F3','C4'], dur: '1n' }, // Dm7
|
|
32
|
+
{ chord: ['G2','B2','F3'], dur: '1n' }, // G7
|
|
33
|
+
{ chord: ['C3','E3','B3'], dur: '1n' }, // Cmaj7
|
|
34
|
+
{ chord: ['A2','C3','G3','E4'], dur: '1n' }, // Am7
|
|
35
|
+
{ chord: ['D3','F3','C4'], dur: '1n' }, // Dm7 (repeat)
|
|
36
|
+
{ chord: ['Db3','F3','B3'], dur: '1n' }, // Db7 (tritone sub)
|
|
37
|
+
{ chord: ['C3','E3','B3','G4'], dur: '2n' }, // Cmaj7
|
|
38
|
+
{ chord: ['G2','B2','F3'], dur: '2n' }, // G7 (turnaround)
|
|
39
|
+
];
|
|
40
|
+
let t = 0;
|
|
41
|
+
changes.forEach(({ chord, dur }) => {
|
|
42
|
+
piano.triggerAttackRelease(chord, dur, t);
|
|
43
|
+
t += Tone.Time(dur).toSeconds();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// ── Walking bass ────────────────────────────────────────────────────
|
|
47
|
+
const bass = new Tone.Synth({
|
|
48
|
+
oscillator: { type: 'sine' },
|
|
49
|
+
envelope: { attack: 0.008, decay: 0.09, sustain: 0.55, release: 0.15 }
|
|
50
|
+
}).connect(reverb);
|
|
51
|
+
bass.volume.value = -6;
|
|
52
|
+
|
|
53
|
+
// 8-bar walking line (one note per beat = 32 quarter notes)
|
|
54
|
+
const walkLine = [
|
|
55
|
+
// Bar 1 Dm7
|
|
56
|
+
'D2','F2','A2','C3',
|
|
57
|
+
// Bar 2 G7
|
|
58
|
+
'G2','B2','D3','F3',
|
|
59
|
+
// Bar 3 Cmaj7
|
|
60
|
+
'C3','E2','G2','B2',
|
|
61
|
+
// Bar 4 Am7
|
|
62
|
+
'A2','C3','E3','G2',
|
|
63
|
+
// Bar 5 Dm7 (chromatic approach)
|
|
64
|
+
'D2','Eb2','E2','F2',
|
|
65
|
+
// Bar 6 Db7 (tritone)
|
|
66
|
+
'Db3','Ab2','F2','Eb2',
|
|
67
|
+
// Bar 7 Cmaj7
|
|
68
|
+
'C2','G2','E2','B2',
|
|
69
|
+
// Bar 8 G7
|
|
70
|
+
'G2','Ab2','A2','B2',
|
|
71
|
+
];
|
|
72
|
+
walkLine.forEach((n, i) => bass.triggerAttackRelease(n, '4n', i * q));
|
|
73
|
+
|
|
74
|
+
// ── Ride cymbal (jazz swing pattern) ────────────────────────────────
|
|
75
|
+
const ride = new Tone.MetalSynth({
|
|
76
|
+
frequency: 240, harmonicity: 5.1, modulationIndex: 16,
|
|
77
|
+
resonance: 4000, octaves: 1.5,
|
|
78
|
+
envelope: { attack: 0.001, decay: 0.28, release: 0.08 }
|
|
79
|
+
}).connect(reverb);
|
|
80
|
+
ride.volume.value = -22;
|
|
81
|
+
|
|
82
|
+
// Swing triplet feel: beat 1, beat 2-and (triplet late), beat 3, beat 4-and
|
|
83
|
+
const swingLate = q * 0.67;
|
|
84
|
+
for (let b = 0; b < 8; b++) {
|
|
85
|
+
const base = b * bar;
|
|
86
|
+
ride.triggerAttackRelease('8n', base);
|
|
87
|
+
ride.triggerAttackRelease('8n', base + swingLate);
|
|
88
|
+
ride.triggerAttackRelease('8n', base + q * 2);
|
|
89
|
+
ride.triggerAttackRelease('8n', base + q * 2 + swingLate);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ── Brush snare on 2 and 4 ──────────────────────────────────────────
|
|
93
|
+
const snareBp = new Tone.Filter(2800, 'bandpass').connect(reverb);
|
|
94
|
+
const snare = new Tone.NoiseSynth({
|
|
95
|
+
noise: { type: 'white' },
|
|
96
|
+
envelope: { attack: 0.004, decay: 0.09, sustain: 0, release: 0.04 }
|
|
97
|
+
}).connect(snareBp);
|
|
98
|
+
snare.volume.value = -26;
|
|
99
|
+
|
|
100
|
+
for (let b = 0; b < 8; b++) {
|
|
101
|
+
snare.triggerAttackRelease('8n', b * bar + q); // beat 2
|
|
102
|
+
snare.triggerAttackRelease('8n', b * bar + q * 3); // beat 4
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ── Bebop melody (enters bar 3) ─────────────────────────────────────
|
|
106
|
+
const melody = new Tone.Synth({
|
|
107
|
+
oscillator: { type: 'triangle' },
|
|
108
|
+
envelope: { attack: 0.005, decay: 0.15, sustain: 0.4, release: 0.5 }
|
|
109
|
+
}).connect(reverb);
|
|
110
|
+
melody.volume.value = -12;
|
|
111
|
+
|
|
112
|
+
// Classic bebop phrase: arpeggiated chords + chromatic passing tones
|
|
113
|
+
const phrase = [
|
|
114
|
+
// Over Cmaj7 (bar 3)
|
|
115
|
+
{ note: 'G4', time: bar * 2, dur: '8n' },
|
|
116
|
+
{ note: 'E4', time: bar * 2 + q * 0.5, dur: '8n' },
|
|
117
|
+
{ note: 'C5', time: bar * 2 + q, dur: '8n' },
|
|
118
|
+
{ note: 'B4', time: bar * 2 + q * 1.5, dur: '8n' },
|
|
119
|
+
{ note: 'A4', time: bar * 2 + q * 2, dur: '8n' },
|
|
120
|
+
{ note: 'G4', time: bar * 2 + q * 2.5, dur: '8n' },
|
|
121
|
+
{ note: 'F4', time: bar * 2 + q * 3, dur: '4n' },
|
|
122
|
+
// Over Am7 (bar 4)
|
|
123
|
+
{ note: 'E4', time: bar * 3 + q * 0.5, dur: '8n' },
|
|
124
|
+
{ note: 'C5', time: bar * 3 + q, dur: '8n' },
|
|
125
|
+
{ note: 'B4', time: bar * 3 + q * 1.5, dur: '8n' },
|
|
126
|
+
{ note: 'A4', time: bar * 3 + q * 2, dur: '4n' },
|
|
127
|
+
{ note: 'G4', time: bar * 3 + q * 3, dur: '4n' },
|
|
128
|
+
// Over Dm7 again (bar 5)
|
|
129
|
+
{ note: 'D5', time: bar * 4, dur: '8n' },
|
|
130
|
+
{ note: 'C5', time: bar * 4 + q * 0.5, dur: '8n' },
|
|
131
|
+
{ note: 'A4', time: bar * 4 + q, dur: '8n' },
|
|
132
|
+
{ note: 'F4', time: bar * 4 + q * 1.5, dur: '8n' },
|
|
133
|
+
{ note: 'E4', time: bar * 4 + q * 2, dur: '4n' },
|
|
134
|
+
{ note: 'Eb4', time: bar * 4 + q * 3, dur: '8n' }, // chromatic
|
|
135
|
+
{ note: 'D4', time: bar * 4 + q * 3.5, dur: '8n' },
|
|
136
|
+
// Over Db7 (bar 6) — altered sound
|
|
137
|
+
{ note: 'Ab4', time: bar * 5, dur: '8n' },
|
|
138
|
+
{ note: 'F4', time: bar * 5 + q * 0.5, dur: '8n' },
|
|
139
|
+
{ note: 'Db4', time: bar * 5 + q, dur: '4n' },
|
|
140
|
+
{ note: 'C4', time: bar * 5 + q * 2, dur: '2n' }, // resolve to C
|
|
141
|
+
];
|
|
142
|
+
phrase.forEach(({ note, time, dur }) => melody.triggerAttackRelease(note, dur, time));
|
|
143
|
+
}
|
|
144
|
+
</script>
|
|
145
|
+
</body>
|
|
146
|
+
</html>
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: audio-lofi
|
|
3
|
+
description: Lo-fi Hip Hop — dusty jazzy chords, soft keys, muffled drums, chill study vibe
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TuneFrames — Lo-fi Hip Hop
|
|
7
|
+
|
|
8
|
+
## Genre Profile
|
|
9
|
+
- BPM range: 70–90
|
|
10
|
+
- Key characteristics: Jazz-adjacent chord progressions (Am–F–C–G or ii–V–I extensions), swung/lazy 8th-note feel, heavy reverb + slight LP filter on everything, vinyl warmth
|
|
11
|
+
- Typical instruments: Rhodes/electric piano (PolySynth triangle/sine), soft bass (Synth), MembraneSynth kick filtered low, closed hi-hat (MetalSynth), pad layer
|
|
12
|
+
- Mood: Nostalgic, cozy, introspective, study-focus
|
|
13
|
+
|
|
14
|
+
## Core Pattern
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
// Lo-fi core: 80 BPM, Am–F–C–G, 4 chords × 2 beats each
|
|
18
|
+
async function main() {
|
|
19
|
+
await Tone.start();
|
|
20
|
+
Tone.Transport.bpm.value = 80;
|
|
21
|
+
|
|
22
|
+
// ── Effects chain (everything runs warm and muffled) ──────────────────
|
|
23
|
+
const reverb = new Tone.Reverb({ decay: 3.5, wet: 0.55 }).toDestination();
|
|
24
|
+
const lpFilter = new Tone.Filter(1800, 'lowpass').connect(reverb);
|
|
25
|
+
// Optional: slight tape distortion
|
|
26
|
+
const warm = new Tone.Distortion(0.04).connect(lpFilter);
|
|
27
|
+
|
|
28
|
+
// ── Rhodes-style chords ───────────────────────────────────────────────
|
|
29
|
+
const keys = new Tone.PolySynth(Tone.Synth, {
|
|
30
|
+
oscillator: { type: 'triangle' },
|
|
31
|
+
envelope: { attack: 0.04, decay: 0.3, sustain: 0.7, release: 1.8 }
|
|
32
|
+
}).connect(warm);
|
|
33
|
+
keys.volume.value = -10;
|
|
34
|
+
|
|
35
|
+
// ii–V–I–VI in A minor
|
|
36
|
+
const chords = [
|
|
37
|
+
['A3','C4','E4'], // Am
|
|
38
|
+
['F3','A3','C4'], // F maj
|
|
39
|
+
['C3','E3','G3'], // C maj
|
|
40
|
+
['G3','B3','D4'], // G maj
|
|
41
|
+
];
|
|
42
|
+
const step = Tone.Time('2n').toSeconds(); // 2 beats each chord
|
|
43
|
+
chords.forEach((ch, i) => keys.triggerAttackRelease(ch, '2n', i * step));
|
|
44
|
+
|
|
45
|
+
// ── Walking bass ──────────────────────────────────────────────────────
|
|
46
|
+
const bass = new Tone.Synth({
|
|
47
|
+
oscillator: { type: 'sine' },
|
|
48
|
+
envelope: { attack: 0.02, decay: 0.1, sustain: 0.5, release: 0.4 }
|
|
49
|
+
}).connect(lpFilter);
|
|
50
|
+
bass.volume.value = -8;
|
|
51
|
+
|
|
52
|
+
const bassNotes = ['A2', 'F2', 'C2', 'G2'];
|
|
53
|
+
bassNotes.forEach((n, i) => bass.triggerAttackRelease(n, '4n', i * step));
|
|
54
|
+
|
|
55
|
+
// ── Muffled kick (MembraneSynth + deep LP) ────────────────────────────
|
|
56
|
+
const kickFilter = new Tone.Filter(200, 'lowpass').connect(reverb);
|
|
57
|
+
const kick = new Tone.MembraneSynth({
|
|
58
|
+
pitchDecay: 0.05, octaves: 5,
|
|
59
|
+
envelope: { attack: 0.001, decay: 0.4, sustain: 0, release: 0.4 }
|
|
60
|
+
}).connect(kickFilter);
|
|
61
|
+
kick.volume.value = -14;
|
|
62
|
+
|
|
63
|
+
// Kick on beats 1 and 3 (seconds at 80 BPM: beat = 0.75s)
|
|
64
|
+
const beat = 60 / 80;
|
|
65
|
+
[0, beat * 2, beat * 4, beat * 6].forEach(t => kick.triggerAttackRelease('C1', '8n', t));
|
|
66
|
+
|
|
67
|
+
// ── Soft hi-hat ───────────────────────────────────────────────────────
|
|
68
|
+
const hat = new Tone.MetalSynth({
|
|
69
|
+
frequency: 400, envelope: { attack: 0.001, decay: 0.08, release: 0.01 },
|
|
70
|
+
harmonicity: 5.1, modulationIndex: 32, resonance: 4000, octaves: 1.5
|
|
71
|
+
}).connect(reverb);
|
|
72
|
+
hat.volume.value = -24;
|
|
73
|
+
|
|
74
|
+
for (let i = 0; i < 8; i++) {
|
|
75
|
+
hat.triggerAttackRelease('8n', i * beat * 0.5);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Instrument Configuration
|
|
81
|
+
|
|
82
|
+
```js
|
|
83
|
+
// Rhodes feel — triangle oscillator, slow attack, long release
|
|
84
|
+
const keys = new Tone.PolySynth(Tone.Synth, {
|
|
85
|
+
oscillator: { type: 'triangle' },
|
|
86
|
+
envelope: { attack: 0.04, decay: 0.3, sustain: 0.7, release: 1.8 }
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Tape warmth — very light distortion before the LP filter
|
|
90
|
+
const tape = new Tone.Distortion(0.04);
|
|
91
|
+
const lp = new Tone.Filter(1800, 'lowpass'); // cuts harsh highs
|
|
92
|
+
|
|
93
|
+
// Lush room reverb
|
|
94
|
+
const reverb = new Tone.Reverb({ decay: 3.5, preDelay: 0.01, wet: 0.55 });
|
|
95
|
+
|
|
96
|
+
// Chain: keys → tape → lp → reverb → destination
|
|
97
|
+
keys.connect(tape); tape.connect(lp); lp.connect(reverb); reverb.toDestination();
|
|
98
|
+
|
|
99
|
+
// Muffled kick — very tight low-pass (200 Hz)
|
|
100
|
+
const kickLp = new Tone.Filter(200, 'lowpass').toDestination();
|
|
101
|
+
const kick = new Tone.MembraneSynth({ pitchDecay: 0.05, octaves: 5 }).connect(kickLp);
|
|
102
|
+
|
|
103
|
+
// Airy hat — kept very quiet, short decay
|
|
104
|
+
const hat = new Tone.MetalSynth({ frequency: 400, envelope: { decay: 0.08 } });
|
|
105
|
+
hat.volume.value = -24;
|
|
106
|
+
hat.connect(reverb);
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Composition Structure
|
|
110
|
+
|
|
111
|
+
1. **Intro (0–2s):** Keys enter alone with chord 1, bass follows on beat 2
|
|
112
|
+
2. **Main loop (2–8s):** Full Am–F–C–G cycle, kick on 1+3, hat 8ths, bass walks roots
|
|
113
|
+
3. **Variation (8–10s):** Add a simple melody fragment (C5–E5–G5–A5) over the progression
|
|
114
|
+
4. **Outro:** Let chords ring out with long release, hat fades (volume ramp)
|
|
115
|
+
|
|
116
|
+
## Example Variations
|
|
117
|
+
|
|
118
|
+
### 1 — Jazzier chord voicings (7ths)
|
|
119
|
+
```js
|
|
120
|
+
const chords = [
|
|
121
|
+
['A3','C4','E4','G4'], // Am7
|
|
122
|
+
['F3','A3','C4','E4'], // Fmaj7
|
|
123
|
+
['C3','E3','G3','B3'], // Cmaj7
|
|
124
|
+
['G3','B3','D4','F4'], // G7
|
|
125
|
+
];
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 2 — Slower, dreamier (70 BPM + delay)
|
|
129
|
+
```js
|
|
130
|
+
Tone.Transport.bpm.value = 70;
|
|
131
|
+
const delay = new Tone.FeedbackDelay('8n.', 0.3).connect(reverb);
|
|
132
|
+
keys.connect(delay); // dotted-8th echo
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### 3 — Add a simple vinyl-crackle effect (noise burst)
|
|
136
|
+
```js
|
|
137
|
+
const noise = new Tone.Noise('pink').connect(new Tone.Filter(600, 'lowpass').toDestination());
|
|
138
|
+
noise.volume.value = -40;
|
|
139
|
+
noise.start(0).stop('+10');
|
|
140
|
+
```
|