import keys from './lib/keys' import color from './lib/color' import kalimba from './lib/kalimba' import life from './lib/life' import organ from './lib/organ' import { browser, requestAudioContext, choice } from './lib/util' let instrument = kalimba const root = 440 const s = 50 const w = window.innerWidth const h = window.innerHeight const ws = Math.ceil(w/s), hs = Math.ceil(h/s) const add_on = 0 const mul_on = 1.0 const add_off = 0.1 const mul_off = 0.9 let dragging = false let erasing = false let lastFreq = 0 let notes = [] requestAudioContext( () => { for (var i = 0; i < ws; i++) { notes[i] = [] for (var j = 0; j < hs; j++) { notes[i][j] = add(i, j) } } life.init(notes, assign) }) function play(freq) { freq.playing = true instrument.play(freq.frequency) freq.div.classList.add('playing') life.assign_item(freq, true) } function pause(freq) { freq.playing = false instrument.pause(freq.frequency) freq.div.classList.remove('playing') life.assign_item(freq, false) } function assign(freq, state) { if (state) { play(freq) } else { pause(freq) } } function toggle(freq) { assign(freq, freq.playing = !freq.playing) } const gliderShape = [ [0,0,0,0,0], [0,1,1,1,0], [0,0,0,1,0], [0,0,1,0,0], [0,0,0,0,0], ] const gliderShapeFlip = gliderShape.map(a => a.slice(0).reverse()) const gliderShapes = [ gliderShape, gliderShapeFlip, gliderShape.slice(0).reverse(), gliderShapeFlip.slice(0).reverse(), ] function glider() { const x = Math.floor(Math.random() * ws) const y = Math.floor(Math.random() * hs) console.log("glider at", x, y) const shape = choice(gliderShapes) weave(x,y,shape) } function weave (x,y,shape) { const xmag = shape.length const ymag = shape[0].length let i, j, px, py for (i = 0; i < xmag; i++) { for (j = 0; j < ymag; j++) { px = (x+i) % ws py = (y+j) % hs assign(notes[px][py], shape[i][j]) } } } function forEach(f){ let i, j, note, s; for (i = 0; i < ws; i++) { for (j = 0; j < hs; j++) { note = notes[i][j] s = f(i,j,note.playing) assign(note, s) } } } function clear(){ forEach((x,y,state) => { return false }) } function white(){ forEach((x,y,state) => { return false }) } function stripex(odd){ odd = !! odd forEach((x,y,state) => { return x % 2 ? odd : !odd }) } function stripey(odd){ odd = !! odd forEach((x,y,state) => { return y % 2 ? odd : !odd }) } function noise(){ forEach((x,y,state) => { return Math.random() < 0.5 }) } function add (i, j) { const a = i + 1 const b = j + 1 const div = document.createElement('div') const frequency = root * a/b let add = 0 let frac = Math.log2(a/b) % 1 div.style.left = (i * s) + 'px' div.style.top = (j * s) + 'px' div.innerHTML = `
${a}<\/div>
\/
${b}<\/div>` const freq = { frequency, div, i, j, playing: false } if (frac < 0) { frac += 1 } if (a < b) { add = -Math.log(b/a) / 3.5 } else { add = Math.log(a/b) / 6 } if ( frac === 0) { div.style.fontWeight = '900' div.style.left = (i * s) + 'px' div.style.top = (j * s) + 'px' } div.style.backgroundColor = color(frac, add_off + add, mul_off) if (browser.isDesktop) { div.addEventListener('mousedown', function(){ div.style.backgroundColor = color(frac, add + add_on, mul_on) toggle( freq ) erasing = !freq.playing }) div.addEventListener('mouseenter', function(){ div.style.backgroundColor = color(frac, add + add_on, mul_on) if (dragging) { if (erasing) { pause( freq ) } else { toggle( freq ) } } }) div.addEventListener('mouseleave', function(){ div.style.backgroundColor = color(frac, add + add_off, mul_off) }) } else { div.addEventListener('touchstart', function(e){ e.preventDefault() toggle( freq ) erasing = !freq.playing lastFreq = freq }) } document.body.appendChild(div) return freq } if (browser.isDesktop) { document.addEventListener('mousedown', () => { dragging = true }) document.addEventListener('mouseup', () => { dragging = false }) } else { document.addEventListener('touchstart', (e) => { e.preventDefault(); dragging = true }) document.addEventListener('touchmove', (e) => { e.preventDefault() const x = Math.floor( e.touches[0].pageX / s ) const y = Math.floor( e.touches[0].pageY / s ) if (! (x in notes) || ! (y in notes[x])) return const freq = notes[x][y] if (freq !== lastFreq) { if (dragging) { if (erasing) { pause( freq ) } else { toggle( freq ) } } lastFreq = freq } }) document.addEventListener('touchend', () => { dragging = false }) } function swap_instrument(){ instrument = (instrument === kalimba) ? organ : kalimba } let life_bpm = 50 window.addEventListener("keydown", keydown, true) function keydown(e){ // console.log(e.keyCode) if (e.altKey || e.ctrlKey || e.metaKey) return switch (e.keyCode){ case 32: // space life.toggle() break case 38: // up life_bpm += e.shiftKey ? 1 : 5 life.setTempo(life_bpm) break case 40: // down life_bpm -= e.shiftKey ? 1 : 5 life_bpm = Math.max(1, life_bpm) life.setTempo(life_bpm) break case 71: // g glider() break case 83: // s swap_instrument() break case 67: // c clear() break case 87: // w white() break case 78: // n noise() break case 69: // e stripex(Math.random() < 0.5) break case 82: // r stripey(Math.random() < 0.5) break } } keys.listen(function(index){ // const freq = scales.current().index(index) // document.body.style.backgroundColor = color( index / scales.current().scale.length ) // instrument.toggle(freq) }) let hash = window.location.hash || window.location.search if (hash.match('sin') || hash.match('organ')) { instrument = organ } if (hash.match('glider')) { instrument = organ clear() glider() life.setTempo(life_bpm = 120 * 8) life.toggle() }