Created
February 29, 2024 14:30
-
-
Save lynn/5854634c6465dd0e33bb255c3760d9fd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://wavepot.com/ | |
const tau = 2*Math.PI; | |
const bpm = 132; | |
const bps = bpm / 60; | |
const spb = 1 / bps; | |
function pos(t) { | |
const beat = Math.floor(t / spb); | |
const frac = (t / spb) % 1.0; | |
return [beat, frac]; | |
} | |
function saw(t, n) { | |
let mix = 0; | |
for (let i = 1; i <= n; i++) { | |
mix += 1/i * Math.sin(t*i); | |
} | |
return mix; | |
} | |
function realsaw(t) { | |
return 1-t%1*2; | |
} | |
function hz(st) { | |
st -= 1; | |
return 220 * Math.pow(2, st/12); | |
} | |
function bell(t, p) { | |
return Math.pow(Math.sin(tau * t * p), 3); | |
} | |
function unison(f) { | |
function u(t, p) { | |
let mix = 0; | |
for (let k =0; k < 4; k ++) mix += f(t, p * (0.99+k*0.006)); | |
return mix / 4; | |
} | |
return u; | |
} | |
function melody(t) { | |
const [beat, frac] = pos(t*2); | |
const p = hz([0, 12, 11, 2, 4, 7, 2, 0][beat % 8] + 12); | |
const vol = Math.pow(1-frac, 3); | |
return unison(bell)(t, p) * vol; | |
} | |
function sidechain(t) { | |
const frac = pos(t)[1]; | |
return Math.pow(frac, 1.2); | |
} | |
function chord(t) { | |
const [beat, frac] = pos(t); | |
const transpose = 0; | |
const vol = sidechain(t); | |
let mix = 0; | |
const notes = [ | |
[0, 2, 4, 7], | |
[0, 2, 4, 7], | |
[0, 2, 4, 7], | |
[0, 2, 4, 7], | |
[-5, 1, 5, 10], | |
[-5, 1, 5, 10.5], // whoa! | |
[-6, 1, 5, 12], | |
[5, frac * 6 % 1 < 0.5 ? 24 : 12], | |
[-7, 0, 4, 7], | |
[-7, 0, 4, 7], | |
[-7, 0, 4, 7], | |
[-7, 0, 4, 7], | |
[-7, 0, 4, 7], | |
[-7, 2, 4, 7], | |
[-3, 2, 4, 5], | |
[-2, 5], | |
][beat % 16]; | |
for (let i = 0; i < notes.length; i++) { | |
const st = notes[i]; | |
const p = hz(st + transpose); | |
mix += saw(tau * t * p, 3) * vol; | |
} | |
return mix; | |
} | |
function bass(t) { | |
const [beat,frac] = pos(t*4); | |
const i = beat % 16; | |
const transpose = 0; | |
const bassline = [-7,-7,-7,null, null, null, 0, 5, null, 0, 5 + Math.floor(frac*3)/3*2, 7, null, null, -12, null]; | |
if (bassline[i] === null) return 0; | |
const p = hz(bassline[i] - 12 + transpose); | |
const vol = 1; | |
return (realsaw(t * p)*0.5 + Math.sin(tau*t*p)) * vol; | |
} | |
function delay(f, t) { | |
let mix = 0; | |
for (let i = 0; i < 7; i ++) { | |
mix += Math.pow(0.6, i) * f(t + 64*spb - i * 0.2*spb); | |
} | |
return mix; | |
} | |
function shape(t) { | |
return Math.tanh(2*t); | |
} | |
function kick(t) { | |
const [beat,frac] = pos(t); | |
const vol = beat % 2 === 0 ? 1 : 0.8; | |
return shape(Math.sin(1 / ((frac+0.21) / 30))) * (1-frac) * vol; | |
} | |
function hat(t) { | |
const [beat,frac] = pos(t*4); | |
return Math.random(t) * Math.pow(1-frac, 4) * (beat%4/4); | |
} | |
function clap(t) { | |
const [beat,frac] = pos(t); | |
if (beat % 2 === 0) return 0; | |
return (frac * 20 % 1 < 0.3 ? Math.pow(Math.sin(0.4 / ((frac+0.2) / 90)), 3) : Math.random() * 0.6) * Math.pow(1-frac, 8); | |
} | |
function drums(t) { | |
return kick(t) + 0.2 * hat(t) + 0.8 * clap(t); | |
} | |
function dsp(t) { | |
return 0.13 * drums(t) + 0.1 * delay(melody, t) + 0.03 * chord(t) + 0.03 * bass(t); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment