Last active
February 10, 2026 23:30
-
-
Save amiika/ebf911fe7ee85eb8f13bfc1de6dbcb81 to your computer and use it in GitHub Desktop.
Funcbeat pattern function library
This file contains hidden or 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
| // monkeypatchers pattern library inspired by tidalcycles | |
| const r = t => 0; // Rest | |
| const pitch = (n) => 55 * pow(2, n / 12); | |
| const repeat = (val, n) => Array(n).fill(val); | |
| const speed = (s, pat) => t => { | |
| const rate = typeof s === 'function' ? s(t) : s; | |
| return pat(t * rate); | |
| }; | |
| const seq = (...args) => t => { | |
| const pats = args.flat(); | |
| const n = pats.length; | |
| const i = floor(t * n); | |
| return (pats[i % n] || r)(floor(t) + (t * n % 1)); | |
| } | |
| const cat = (...args) => t => { | |
| const list = args.flat(); | |
| const len = list.length; | |
| const i = floor(t); | |
| const val = list[i % len]; | |
| return (typeof val === 'function') ? val(floor(t / len) + (t % 1)) : val; | |
| } | |
| const stack = (...args) => t => { | |
| const pats = args.flat(); | |
| let sum = 0; | |
| for(let i=0; i<pats.length; i++) sum += pats[i](t); | |
| return sum; | |
| } | |
| const list = (...vals) => t => { | |
| const n = vals.length; | |
| const i = floor(t * n); | |
| return vals[i % n]; | |
| } | |
| const chord = (inst, ...vals) => stack(...vals.map(n => inst(n))); | |
| const gain = (vol, pat) => t => pat(t) * vol; | |
| const clip = (x, n) => x > n ? n : x < -n ? -n : x; | |
| const clamp = (x, a, b) => min(b, max(a, x)); | |
| // DSP | |
| const saw = (f, t) => 1 - 2 * (t % (1 / f)) * f; | |
| const sqr = (f, t) => sin(t * f * PI * 2) > 0 ? 1 : -1; | |
| const tri = (f, t) => abs(1 - (2 * t * f) % 2) * 2 - 1; | |
| const noise = () => random() * 2 - 1; | |
| const acid = (pattern) => { | |
| let phase = 0; | |
| let note = 0; | |
| let cut = 0; | |
| return t => { | |
| const target = pattern(t); | |
| const gate = (target !== null && target !== undefined) ? 1 : 0; | |
| if (gate) note += (target - note) * 0.0015; | |
| const f = pitch(note); | |
| phase += f / 44100; | |
| if (phase >= 1) phase -= 1; | |
| const wave = 2 * (phase - 0.5); | |
| const subT = (t * 16) % 1; | |
| const env = exp(-6 * subT); | |
| const cutoff = 0.02 + (0.45 * env); | |
| cut += (wave - cut) * cutoff; | |
| return tanh(cut * 5) * 0.7 * gate; | |
| } | |
| } | |
| const pad = (n) => t => { | |
| const f = pitch(n) * 150; | |
| return ( | |
| sin(t * f) * 0.5 + | |
| sin(t * f * 2.01) * 0.25 + | |
| sin(t * f * 1.5) * 0.1 | |
| ) | |
| } | |
| // DRUMS | |
| const perc = (wave, decay, o) => { | |
| const env = max(0, 0.889 - (o * decay) / ((o * decay) + 1)); | |
| return wave * env * env; | |
| } | |
| const bd = t => { | |
| const localT = t % 1; | |
| const wave = tanh(sin(cbrt(localT) * 160) * 8); | |
| return wave * exp(-4 * localT); | |
| } | |
| const hh = t => noise() * exp(-60 * (t % 1)) | |
| const sn = t => { | |
| const lt = t % 1; | |
| const raw = tri(200, lt) + noise(); | |
| return clip(raw * 5, 0.5) * exp(-15 * lt); | |
| } | |
| const cowbell = t => { | |
| const lt = t % 1; | |
| const wave = sqr(666, lt) + sqr(999,lt); | |
| return perc(wave, 7, t%2) * 0.5; | |
| } | |
| const oh = t => { | |
| const localT = t % 1; | |
| const n = noise() + ((t * 32768) & 1 ? -0.25 : 0.25); | |
| return n * exp(-20 * localT); | |
| } | |
| // Composition | |
| const line1 = list(0, 0, 12, 0, 0, 0, 10, 0); | |
| const line2 = list(0, 0, 12, 12, 15, 12, 10, 0); | |
| const line3 = list(12, 12, 0, 12, 0, 10, 0, 10); | |
| const synth303 = acid( | |
| cat( | |
| repeat(line1, 3), | |
| line2, | |
| repeat(line3, 2) | |
| ) | |
| ) | |
| const cm7 = chord(pad, 0, 3, 7, 10); | |
| const fm7 = chord(pad, 5, 8, 12, 15); | |
| const bb7 = chord(pad, 10, 14, 17, 20); | |
| const ebM = chord(pad, 3, 7, 10, 14); | |
| const abM = chord(pad, 8, 12, 15, 19); | |
| const g7 = chord(pad, 7, 11, 14, 17); | |
| const chords = cat( | |
| cm7, | |
| fm7, | |
| seq(bb7, ebM), | |
| seq(abM, g7) | |
| ) | |
| const padGate = list(0.25,0.5,0.5,0.25,0.25); | |
| const kicks = seq(bd,bd,bd,bd); | |
| const hits = stack( | |
| seq(r, hh, r, r), | |
| seq(r, cat(r,sn), sn, r), | |
| cat( | |
| repeat(seq(r, r, oh, cowbell), 3), | |
| seq(cowbell, cowbell, cowbell, cowbell) | |
| ), | |
| t => seq(hh, r, hh, r)(t * cat(2, 4, 8)(t)) | |
| ); | |
| const BPM = 135; | |
| return t => { | |
| const m = t * (BPM / 60) / 4; | |
| const acidFade = clamp((t - 1) / 4, 0, 1); | |
| const hitsGate = (t > 6) ? 1 : 0; | |
| const kickGate = (t > 8) ? 1 : 0; | |
| const drill = cat(repeat(1,32),2,2,4,4,8,8,12,14,repeat(0,6))(t); | |
| return stack( | |
| t => kicks(t * drill) * kickGate, | |
| t => hits(t) * hitsGate, | |
| t => synth303(t) * acidFade * 0.5, | |
| t => chords(t) * padGate(t * cat(6,8)(t)) * 0.15 | |
| )(m) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment