Avery Felts 5ed84192d5 Implement lofi hip hop generator with Tone.js
- Set up Next.js project with shadcn/ui and Tailwind CSS
- Created audio engine with MembraneSynth drums, FMSynth chords, and ambient noise layers
- Implemented 16-step drum sequencer with boom bap patterns
- Added jazz chord progressions (ii-V-I, minor key, neo soul)
- Built React hook for audio state management
- Created UI components: transport controls, volume sliders, layer mixer, beat visualizer
- Applied lofi-themed dark color scheme with oklch colors

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 17:29:28 -07:00

146 lines
5.4 KiB
TypeScript

import { DrumPattern, ChordProgression } from '@/types/audio';
// Classic boom bap patterns
export const drumPatterns: DrumPattern[] = [
{
// Classic boom bap
kick: [true, false, false, false, false, false, false, false, true, false, true, false, false, false, false, false],
snare: [false, false, false, false, true, false, false, false, false, false, false, false, true, false, false, false],
hihat: [true, false, true, false, true, false, true, false, true, false, true, false, true, false, true, false],
openhat: [false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, true],
},
{
// Laid back groove
kick: [true, false, false, false, false, false, true, false, true, false, false, false, false, false, false, false],
snare: [false, false, false, false, true, false, false, false, false, false, false, false, true, false, false, true],
hihat: [true, true, true, false, true, true, true, false, true, true, true, false, true, true, true, false],
openhat: [false, false, false, true, false, false, false, true, false, false, false, true, false, false, false, false],
},
{
// Minimal chill
kick: [true, false, false, false, false, false, false, false, true, false, false, false, false, false, true, false],
snare: [false, false, false, false, true, false, false, false, false, false, false, false, true, false, false, false],
hihat: [true, false, true, true, true, false, true, true, true, false, true, true, true, false, true, true],
openhat: [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
},
{
// Jazzy swing
kick: [true, false, false, true, false, false, true, false, false, false, true, false, false, false, false, false],
snare: [false, false, false, false, true, false, false, false, false, true, false, false, true, false, false, true],
hihat: [true, false, true, false, true, false, true, false, true, false, true, false, true, false, true, false],
openhat: [false, false, false, false, false, true, false, false, false, false, false, false, false, true, false, false],
},
{
// Deep pocket
kick: [true, false, false, false, false, false, false, true, false, false, true, false, false, false, false, false],
snare: [false, false, false, false, true, false, false, false, false, false, false, false, true, false, false, false],
hihat: [true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true],
openhat: [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
},
];
// Jazz chord progressions in lofi style
export const chordProgressions: ChordProgression[] = [
{
name: 'Classic ii-V-I',
chords: [
['D3', 'F3', 'A3', 'C4'], // Dm7
['G2', 'B2', 'D3', 'F3'], // G7
['C3', 'E3', 'G3', 'B3'], // Cmaj7
['C3', 'E3', 'G3', 'B3'], // Cmaj7
],
durations: ['2n', '2n', '2n', '2n'],
},
{
name: 'Minor Key Chill',
chords: [
['A2', 'C3', 'E3', 'G3'], // Am7
['D3', 'F3', 'A3', 'C4'], // Dm7
['E2', 'G#2', 'B2', 'D3'], // E7
['A2', 'C3', 'E3', 'G3'], // Am7
],
durations: ['2n', '2n', '2n', '2n'],
},
{
name: 'Neo Soul',
chords: [
['F3', 'A3', 'C4', 'E4'], // Fmaj7
['E3', 'G3', 'B3', 'D4'], // Em7
['D3', 'F3', 'A3', 'C4'], // Dm7
['G2', 'B2', 'D3', 'F3'], // G7
],
durations: ['2n', '2n', '2n', '2n'],
},
{
name: 'Dreamy',
chords: [
['C3', 'E3', 'G3', 'B3'], // Cmaj7
['A2', 'C3', 'E3', 'G3'], // Am7
['F3', 'A3', 'C4', 'E4'], // Fmaj7
['G2', 'B2', 'D3', 'F3'], // G7
],
durations: ['2n', '2n', '2n', '2n'],
},
{
name: 'Melancholy',
chords: [
['D3', 'F3', 'A3', 'C4'], // Dm7
['G2', 'Bb2', 'D3', 'F3'], // Gm7
['C3', 'Eb3', 'G3', 'Bb3'],// Cm7
['F2', 'A2', 'C3', 'Eb3'], // F7
],
durations: ['2n', '2n', '2n', '2n'],
},
];
export function getRandomPattern(): DrumPattern {
return drumPatterns[Math.floor(Math.random() * drumPatterns.length)];
}
export function getRandomProgression(): ChordProgression {
return chordProgressions[Math.floor(Math.random() * chordProgressions.length)];
}
export function generateRandomPattern(): DrumPattern {
const pattern: DrumPattern = {
kick: new Array(16).fill(false),
snare: new Array(16).fill(false),
hihat: new Array(16).fill(false),
openhat: new Array(16).fill(false),
};
// Kick on 1 and somewhere in second half
pattern.kick[0] = true;
pattern.kick[8 + Math.floor(Math.random() * 4)] = true;
if (Math.random() > 0.5) {
pattern.kick[6 + Math.floor(Math.random() * 2)] = true;
}
// Snare on 2 and 4
pattern.snare[4] = true;
pattern.snare[12] = true;
// Ghost notes
if (Math.random() > 0.6) {
pattern.snare[Math.floor(Math.random() * 16)] = true;
}
// Hi-hats with variation
for (let i = 0; i < 16; i++) {
if (i % 2 === 0) {
pattern.hihat[i] = Math.random() > 0.1;
} else {
pattern.hihat[i] = Math.random() > 0.5;
}
}
// Open hi-hat occasionally
if (Math.random() > 0.3) {
pattern.openhat[7] = true;
}
if (Math.random() > 0.5) {
pattern.openhat[15] = true;
}
return pattern;
}