├── README.md ├── media ├── intro.mp3 ├── intro.ogg ├── intro2.mp3 ├── intro2.ogg ├── instr2_1.mp3 ├── instr2_1.ogg ├── bounce_1_uh.mp3 ├── bounce_1_uh.ogg ├── bounce_2_uh.mp3 ├── bounce_2_uh.ogg ├── bridge1_1.mp3 ├── bridge1_1.ogg ├── bridge1_2.mp3 ├── bridge1_2.ogg ├── hipstep1_1.mp3 ├── hipstep1_1.ogg ├── hipstep1_2.mp3 ├── hipstep1_2.ogg ├── hipstep2_2.mp3 ├── hipstep2_2.ogg ├── instr2_2_1.mp3 ├── instr2_2_1.ogg ├── instr2_2_2.mp3 ├── instr2_2_2.ogg ├── putemup_1.mp3 ├── putemup_1.ogg ├── putemup_2.mp3 ├── putemup_2.ogg ├── tricka_1_uh.mp3 ├── tricka_1_uh.ogg ├── tricka_2_uh.mp3 ├── tricka_2_uh.ogg ├── bounce_1_yeah.mp3 ├── bounce_1_yeah.ogg ├── bounce_2_yeah.mp3 ├── bounce_2_yeah.ogg ├── tricka_1_cmon.mp3 ├── tricka_1_cmon.ogg ├── tricka_2_yeah.mp3 ├── tricka_2_yeah.ogg ├── youknowwhat_1.mp3 ├── youknowwhat_1.ogg ├── youknowwhat_2.mp3 ├── youknowwhat_2.ogg ├── youknowwhat_3.mp3 ├── youknowwhat_3.ogg ├── bounce_1_intro.mp3 ├── bounce_1_intro.ogg ├── bounce_2_outro.mp3 ├── bounce_2_outro.ogg ├── hipstep1_2_outro.mp3 ├── hipstep1_2_outro.ogg ├── hipstep2_1_intro.mp3 ├── hipstep2_1_intro.ogg ├── instr1_1_intro.mp3 ├── instr1_1_intro.ogg ├── instr1_1_plain.mp3 ├── instr1_1_plain.ogg ├── instr1_2_outro.mp3 ├── instr1_2_outro.ogg ├── instr1_2_plain.mp3 ├── instr1_2_plain.ogg ├── instr2_1_intro.mp3 ├── instr2_1_intro.ogg ├── instr2_1_scratch.mp3 ├── instr2_1_scratch.ogg ├── instr2_2_outro.mp3 ├── instr2_2_outro.ogg ├── putemup_1_intro.mp3 ├── putemup_1_intro.ogg ├── putemup_2_outro.mp3 ├── putemup_2_outro.ogg ├── tricka_1_intro.mp3 ├── tricka_1_intro.ogg ├── tricka_1_intro2.mp3 ├── tricka_1_intro2.ogg ├── tricka_1_plain.mp3 ├── tricka_1_plain.ogg ├── tricka_1_putemup.mp3 ├── tricka_1_putemup.ogg ├── tricka_2_plain.mp3 ├── tricka_2_plain.ogg ├── tricka_2_putemup.mp3 ├── tricka_2_putemup.ogg ├── tricka_2_tricka.mp3 ├── tricka_2_tricka.ogg ├── hipstep2_1_guitar.mp3 ├── hipstep2_1_guitar.ogg ├── youknowwhat_outro.mp3 ├── youknowwhat_outro.ogg ├── tricka_1_trickawhat.mp3 ├── tricka_1_trickawhat.ogg ├── hipstep2_2_guitar_outro.mp3 ├── hipstep2_2_guitar_outro.ogg ├── tricka_1_tricka_tricka.mp3 ├── tricka_1_tricka_tricka.ogg ├── tricka_2_outro_putemup.mp3 ├── tricka_2_outro_putemup.ogg ├── tricka_2_outro_tricka_cmon.mp3 ├── tricka_2_outro_tricka_cmon.ogg ├── tricka_2_outro_what_what.mp3 └── tricka_2_outro_what_what.ogg ├── Makefile ├── .gitignore ├── fetch.LICENSE ├── index.html ├── fetch.js ├── client.coffee.md ├── data.json ├── client.js └── Technology └── Technology.RPP /README.md: -------------------------------------------------------------------------------- 1 | client.coffee.md -------------------------------------------------------------------------------- /media/intro.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/intro.mp3 -------------------------------------------------------------------------------- /media/intro.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/intro.ogg -------------------------------------------------------------------------------- /media/intro2.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/intro2.mp3 -------------------------------------------------------------------------------- /media/intro2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/intro2.ogg -------------------------------------------------------------------------------- /media/instr2_1.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_1.mp3 -------------------------------------------------------------------------------- /media/instr2_1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_1.ogg -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build watch 2 | 3 | build: 4 | coffee -c . 5 | 6 | watch: 7 | coffee -cw . 8 | -------------------------------------------------------------------------------- /media/bounce_1_uh.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_1_uh.mp3 -------------------------------------------------------------------------------- /media/bounce_1_uh.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_1_uh.ogg -------------------------------------------------------------------------------- /media/bounce_2_uh.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_2_uh.mp3 -------------------------------------------------------------------------------- /media/bounce_2_uh.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_2_uh.ogg -------------------------------------------------------------------------------- /media/bridge1_1.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bridge1_1.mp3 -------------------------------------------------------------------------------- /media/bridge1_1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bridge1_1.ogg -------------------------------------------------------------------------------- /media/bridge1_2.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bridge1_2.mp3 -------------------------------------------------------------------------------- /media/bridge1_2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bridge1_2.ogg -------------------------------------------------------------------------------- /media/hipstep1_1.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep1_1.mp3 -------------------------------------------------------------------------------- /media/hipstep1_1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep1_1.ogg -------------------------------------------------------------------------------- /media/hipstep1_2.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep1_2.mp3 -------------------------------------------------------------------------------- /media/hipstep1_2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep1_2.ogg -------------------------------------------------------------------------------- /media/hipstep2_2.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep2_2.mp3 -------------------------------------------------------------------------------- /media/hipstep2_2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep2_2.ogg -------------------------------------------------------------------------------- /media/instr2_2_1.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_2_1.mp3 -------------------------------------------------------------------------------- /media/instr2_2_1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_2_1.ogg -------------------------------------------------------------------------------- /media/instr2_2_2.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_2_2.mp3 -------------------------------------------------------------------------------- /media/instr2_2_2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_2_2.ogg -------------------------------------------------------------------------------- /media/putemup_1.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/putemup_1.mp3 -------------------------------------------------------------------------------- /media/putemup_1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/putemup_1.ogg -------------------------------------------------------------------------------- /media/putemup_2.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/putemup_2.mp3 -------------------------------------------------------------------------------- /media/putemup_2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/putemup_2.ogg -------------------------------------------------------------------------------- /media/tricka_1_uh.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_uh.mp3 -------------------------------------------------------------------------------- /media/tricka_1_uh.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_uh.ogg -------------------------------------------------------------------------------- /media/tricka_2_uh.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_uh.mp3 -------------------------------------------------------------------------------- /media/tricka_2_uh.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_uh.ogg -------------------------------------------------------------------------------- /media/bounce_1_yeah.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_1_yeah.mp3 -------------------------------------------------------------------------------- /media/bounce_1_yeah.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_1_yeah.ogg -------------------------------------------------------------------------------- /media/bounce_2_yeah.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_2_yeah.mp3 -------------------------------------------------------------------------------- /media/bounce_2_yeah.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_2_yeah.ogg -------------------------------------------------------------------------------- /media/tricka_1_cmon.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_cmon.mp3 -------------------------------------------------------------------------------- /media/tricka_1_cmon.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_cmon.ogg -------------------------------------------------------------------------------- /media/tricka_2_yeah.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_yeah.mp3 -------------------------------------------------------------------------------- /media/tricka_2_yeah.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_yeah.ogg -------------------------------------------------------------------------------- /media/youknowwhat_1.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/youknowwhat_1.mp3 -------------------------------------------------------------------------------- /media/youknowwhat_1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/youknowwhat_1.ogg -------------------------------------------------------------------------------- /media/youknowwhat_2.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/youknowwhat_2.mp3 -------------------------------------------------------------------------------- /media/youknowwhat_2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/youknowwhat_2.ogg -------------------------------------------------------------------------------- /media/youknowwhat_3.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/youknowwhat_3.mp3 -------------------------------------------------------------------------------- /media/youknowwhat_3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/youknowwhat_3.ogg -------------------------------------------------------------------------------- /media/bounce_1_intro.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_1_intro.mp3 -------------------------------------------------------------------------------- /media/bounce_1_intro.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_1_intro.ogg -------------------------------------------------------------------------------- /media/bounce_2_outro.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_2_outro.mp3 -------------------------------------------------------------------------------- /media/bounce_2_outro.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/bounce_2_outro.ogg -------------------------------------------------------------------------------- /media/hipstep1_2_outro.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep1_2_outro.mp3 -------------------------------------------------------------------------------- /media/hipstep1_2_outro.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep1_2_outro.ogg -------------------------------------------------------------------------------- /media/hipstep2_1_intro.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep2_1_intro.mp3 -------------------------------------------------------------------------------- /media/hipstep2_1_intro.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep2_1_intro.ogg -------------------------------------------------------------------------------- /media/instr1_1_intro.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr1_1_intro.mp3 -------------------------------------------------------------------------------- /media/instr1_1_intro.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr1_1_intro.ogg -------------------------------------------------------------------------------- /media/instr1_1_plain.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr1_1_plain.mp3 -------------------------------------------------------------------------------- /media/instr1_1_plain.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr1_1_plain.ogg -------------------------------------------------------------------------------- /media/instr1_2_outro.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr1_2_outro.mp3 -------------------------------------------------------------------------------- /media/instr1_2_outro.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr1_2_outro.ogg -------------------------------------------------------------------------------- /media/instr1_2_plain.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr1_2_plain.mp3 -------------------------------------------------------------------------------- /media/instr1_2_plain.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr1_2_plain.ogg -------------------------------------------------------------------------------- /media/instr2_1_intro.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_1_intro.mp3 -------------------------------------------------------------------------------- /media/instr2_1_intro.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_1_intro.ogg -------------------------------------------------------------------------------- /media/instr2_1_scratch.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_1_scratch.mp3 -------------------------------------------------------------------------------- /media/instr2_1_scratch.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_1_scratch.ogg -------------------------------------------------------------------------------- /media/instr2_2_outro.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_2_outro.mp3 -------------------------------------------------------------------------------- /media/instr2_2_outro.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/instr2_2_outro.ogg -------------------------------------------------------------------------------- /media/putemup_1_intro.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/putemup_1_intro.mp3 -------------------------------------------------------------------------------- /media/putemup_1_intro.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/putemup_1_intro.ogg -------------------------------------------------------------------------------- /media/putemup_2_outro.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/putemup_2_outro.mp3 -------------------------------------------------------------------------------- /media/putemup_2_outro.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/putemup_2_outro.ogg -------------------------------------------------------------------------------- /media/tricka_1_intro.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_intro.mp3 -------------------------------------------------------------------------------- /media/tricka_1_intro.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_intro.ogg -------------------------------------------------------------------------------- /media/tricka_1_intro2.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_intro2.mp3 -------------------------------------------------------------------------------- /media/tricka_1_intro2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_intro2.ogg -------------------------------------------------------------------------------- /media/tricka_1_plain.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_plain.mp3 -------------------------------------------------------------------------------- /media/tricka_1_plain.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_plain.ogg -------------------------------------------------------------------------------- /media/tricka_1_putemup.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_putemup.mp3 -------------------------------------------------------------------------------- /media/tricka_1_putemup.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_putemup.ogg -------------------------------------------------------------------------------- /media/tricka_2_plain.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_plain.mp3 -------------------------------------------------------------------------------- /media/tricka_2_plain.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_plain.ogg -------------------------------------------------------------------------------- /media/tricka_2_putemup.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_putemup.mp3 -------------------------------------------------------------------------------- /media/tricka_2_putemup.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_putemup.ogg -------------------------------------------------------------------------------- /media/tricka_2_tricka.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_tricka.mp3 -------------------------------------------------------------------------------- /media/tricka_2_tricka.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_tricka.ogg -------------------------------------------------------------------------------- /media/hipstep2_1_guitar.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep2_1_guitar.mp3 -------------------------------------------------------------------------------- /media/hipstep2_1_guitar.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep2_1_guitar.ogg -------------------------------------------------------------------------------- /media/youknowwhat_outro.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/youknowwhat_outro.mp3 -------------------------------------------------------------------------------- /media/youknowwhat_outro.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/youknowwhat_outro.ogg -------------------------------------------------------------------------------- /media/tricka_1_trickawhat.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_trickawhat.mp3 -------------------------------------------------------------------------------- /media/tricka_1_trickawhat.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_trickawhat.ogg -------------------------------------------------------------------------------- /media/hipstep2_2_guitar_outro.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep2_2_guitar_outro.mp3 -------------------------------------------------------------------------------- /media/hipstep2_2_guitar_outro.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/hipstep2_2_guitar_outro.ogg -------------------------------------------------------------------------------- /media/tricka_1_tricka_tricka.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_tricka_tricka.mp3 -------------------------------------------------------------------------------- /media/tricka_1_tricka_tricka.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_1_tricka_tricka.ogg -------------------------------------------------------------------------------- /media/tricka_2_outro_putemup.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_outro_putemup.mp3 -------------------------------------------------------------------------------- /media/tricka_2_outro_putemup.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_outro_putemup.ogg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Technology/02 Tricka Technology.mp3 2 | Technology/02 Tricka Technology.mp3.reapeaks 3 | Technology/Technology.RPP-bak 4 | -------------------------------------------------------------------------------- /media/tricka_2_outro_tricka_cmon.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_outro_tricka_cmon.mp3 -------------------------------------------------------------------------------- /media/tricka_2_outro_tricka_cmon.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_outro_tricka_cmon.ogg -------------------------------------------------------------------------------- /media/tricka_2_outro_what_what.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_outro_what_what.mp3 -------------------------------------------------------------------------------- /media/tricka_2_outro_what_what.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgentle/markov-technology/HEAD/media/tricka_2_outro_what_what.ogg -------------------------------------------------------------------------------- /fetch.LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2016 GitHub, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Markov Technology 4 | 27 | 28 | 29 | 34 | 35 | 36 | 37 |
38 |
39 | Code on GitHub 40 |
41 |
42 | 🎵 A. Skillz & Krafty Kuts - Tricka Technology (iTunes Amazon) 43 |
44 |
45 | 46 | 47 | -------------------------------------------------------------------------------- /fetch.js: -------------------------------------------------------------------------------- 1 | (function(self) { 2 | 'use strict'; 3 | 4 | if (self.fetch) { 5 | return 6 | } 7 | 8 | var support = { 9 | searchParams: 'URLSearchParams' in self, 10 | iterable: 'Symbol' in self && 'iterator' in Symbol, 11 | blob: 'FileReader' in self && 'Blob' in self && (function() { 12 | try { 13 | new Blob() 14 | return true 15 | } catch(e) { 16 | return false 17 | } 18 | })(), 19 | formData: 'FormData' in self, 20 | arrayBuffer: 'ArrayBuffer' in self 21 | } 22 | 23 | function normalizeName(name) { 24 | if (typeof name !== 'string') { 25 | name = String(name) 26 | } 27 | if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) { 28 | throw new TypeError('Invalid character in header field name') 29 | } 30 | return name.toLowerCase() 31 | } 32 | 33 | function normalizeValue(value) { 34 | if (typeof value !== 'string') { 35 | value = String(value) 36 | } 37 | return value 38 | } 39 | 40 | // Build a destructive iterator for the value list 41 | function iteratorFor(items) { 42 | var iterator = { 43 | next: function() { 44 | var value = items.shift() 45 | return {done: value === undefined, value: value} 46 | } 47 | } 48 | 49 | if (support.iterable) { 50 | iterator[Symbol.iterator] = function() { 51 | return iterator 52 | } 53 | } 54 | 55 | return iterator 56 | } 57 | 58 | function Headers(headers) { 59 | this.map = {} 60 | 61 | if (headers instanceof Headers) { 62 | headers.forEach(function(value, name) { 63 | this.append(name, value) 64 | }, this) 65 | 66 | } else if (headers) { 67 | Object.getOwnPropertyNames(headers).forEach(function(name) { 68 | this.append(name, headers[name]) 69 | }, this) 70 | } 71 | } 72 | 73 | Headers.prototype.append = function(name, value) { 74 | name = normalizeName(name) 75 | value = normalizeValue(value) 76 | var list = this.map[name] 77 | if (!list) { 78 | list = [] 79 | this.map[name] = list 80 | } 81 | list.push(value) 82 | } 83 | 84 | Headers.prototype['delete'] = function(name) { 85 | delete this.map[normalizeName(name)] 86 | } 87 | 88 | Headers.prototype.get = function(name) { 89 | var values = this.map[normalizeName(name)] 90 | return values ? values[0] : null 91 | } 92 | 93 | Headers.prototype.getAll = function(name) { 94 | return this.map[normalizeName(name)] || [] 95 | } 96 | 97 | Headers.prototype.has = function(name) { 98 | return this.map.hasOwnProperty(normalizeName(name)) 99 | } 100 | 101 | Headers.prototype.set = function(name, value) { 102 | this.map[normalizeName(name)] = [normalizeValue(value)] 103 | } 104 | 105 | Headers.prototype.forEach = function(callback, thisArg) { 106 | Object.getOwnPropertyNames(this.map).forEach(function(name) { 107 | this.map[name].forEach(function(value) { 108 | callback.call(thisArg, value, name, this) 109 | }, this) 110 | }, this) 111 | } 112 | 113 | Headers.prototype.keys = function() { 114 | var items = [] 115 | this.forEach(function(value, name) { items.push(name) }) 116 | return iteratorFor(items) 117 | } 118 | 119 | Headers.prototype.values = function() { 120 | var items = [] 121 | this.forEach(function(value) { items.push(value) }) 122 | return iteratorFor(items) 123 | } 124 | 125 | Headers.prototype.entries = function() { 126 | var items = [] 127 | this.forEach(function(value, name) { items.push([name, value]) }) 128 | return iteratorFor(items) 129 | } 130 | 131 | if (support.iterable) { 132 | Headers.prototype[Symbol.iterator] = Headers.prototype.entries 133 | } 134 | 135 | function consumed(body) { 136 | if (body.bodyUsed) { 137 | return Promise.reject(new TypeError('Already read')) 138 | } 139 | body.bodyUsed = true 140 | } 141 | 142 | function fileReaderReady(reader) { 143 | return new Promise(function(resolve, reject) { 144 | reader.onload = function() { 145 | resolve(reader.result) 146 | } 147 | reader.onerror = function() { 148 | reject(reader.error) 149 | } 150 | }) 151 | } 152 | 153 | function readBlobAsArrayBuffer(blob) { 154 | var reader = new FileReader() 155 | reader.readAsArrayBuffer(blob) 156 | return fileReaderReady(reader) 157 | } 158 | 159 | function readBlobAsText(blob) { 160 | var reader = new FileReader() 161 | reader.readAsText(blob) 162 | return fileReaderReady(reader) 163 | } 164 | 165 | function Body() { 166 | this.bodyUsed = false 167 | 168 | this._initBody = function(body) { 169 | this._bodyInit = body 170 | if (typeof body === 'string') { 171 | this._bodyText = body 172 | } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { 173 | this._bodyBlob = body 174 | } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { 175 | this._bodyFormData = body 176 | } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { 177 | this._bodyText = body.toString() 178 | } else if (!body) { 179 | this._bodyText = '' 180 | } else if (support.arrayBuffer && ArrayBuffer.prototype.isPrototypeOf(body)) { 181 | // Only support ArrayBuffers for POST method. 182 | // Receiving ArrayBuffers happens via Blobs, instead. 183 | } else { 184 | throw new Error('unsupported BodyInit type') 185 | } 186 | 187 | if (!this.headers.get('content-type')) { 188 | if (typeof body === 'string') { 189 | this.headers.set('content-type', 'text/plain;charset=UTF-8') 190 | } else if (this._bodyBlob && this._bodyBlob.type) { 191 | this.headers.set('content-type', this._bodyBlob.type) 192 | } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { 193 | this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8') 194 | } 195 | } 196 | } 197 | 198 | if (support.blob) { 199 | this.blob = function() { 200 | var rejected = consumed(this) 201 | if (rejected) { 202 | return rejected 203 | } 204 | 205 | if (this._bodyBlob) { 206 | return Promise.resolve(this._bodyBlob) 207 | } else if (this._bodyFormData) { 208 | throw new Error('could not read FormData body as blob') 209 | } else { 210 | return Promise.resolve(new Blob([this._bodyText])) 211 | } 212 | } 213 | 214 | this.arrayBuffer = function() { 215 | return this.blob().then(readBlobAsArrayBuffer) 216 | } 217 | 218 | this.text = function() { 219 | var rejected = consumed(this) 220 | if (rejected) { 221 | return rejected 222 | } 223 | 224 | if (this._bodyBlob) { 225 | return readBlobAsText(this._bodyBlob) 226 | } else if (this._bodyFormData) { 227 | throw new Error('could not read FormData body as text') 228 | } else { 229 | return Promise.resolve(this._bodyText) 230 | } 231 | } 232 | } else { 233 | this.text = function() { 234 | var rejected = consumed(this) 235 | return rejected ? rejected : Promise.resolve(this._bodyText) 236 | } 237 | } 238 | 239 | if (support.formData) { 240 | this.formData = function() { 241 | return this.text().then(decode) 242 | } 243 | } 244 | 245 | this.json = function() { 246 | return this.text().then(JSON.parse) 247 | } 248 | 249 | return this 250 | } 251 | 252 | // HTTP methods whose capitalization should be normalized 253 | var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] 254 | 255 | function normalizeMethod(method) { 256 | var upcased = method.toUpperCase() 257 | return (methods.indexOf(upcased) > -1) ? upcased : method 258 | } 259 | 260 | function Request(input, options) { 261 | options = options || {} 262 | var body = options.body 263 | if (Request.prototype.isPrototypeOf(input)) { 264 | if (input.bodyUsed) { 265 | throw new TypeError('Already read') 266 | } 267 | this.url = input.url 268 | this.credentials = input.credentials 269 | if (!options.headers) { 270 | this.headers = new Headers(input.headers) 271 | } 272 | this.method = input.method 273 | this.mode = input.mode 274 | if (!body) { 275 | body = input._bodyInit 276 | input.bodyUsed = true 277 | } 278 | } else { 279 | this.url = input 280 | } 281 | 282 | this.credentials = options.credentials || this.credentials || 'omit' 283 | if (options.headers || !this.headers) { 284 | this.headers = new Headers(options.headers) 285 | } 286 | this.method = normalizeMethod(options.method || this.method || 'GET') 287 | this.mode = options.mode || this.mode || null 288 | this.referrer = null 289 | 290 | if ((this.method === 'GET' || this.method === 'HEAD') && body) { 291 | throw new TypeError('Body not allowed for GET or HEAD requests') 292 | } 293 | this._initBody(body) 294 | } 295 | 296 | Request.prototype.clone = function() { 297 | return new Request(this) 298 | } 299 | 300 | function decode(body) { 301 | var form = new FormData() 302 | body.trim().split('&').forEach(function(bytes) { 303 | if (bytes) { 304 | var split = bytes.split('=') 305 | var name = split.shift().replace(/\+/g, ' ') 306 | var value = split.join('=').replace(/\+/g, ' ') 307 | form.append(decodeURIComponent(name), decodeURIComponent(value)) 308 | } 309 | }) 310 | return form 311 | } 312 | 313 | function headers(xhr) { 314 | var head = new Headers() 315 | var pairs = (xhr.getAllResponseHeaders() || '').trim().split('\n') 316 | pairs.forEach(function(header) { 317 | var split = header.trim().split(':') 318 | var key = split.shift().trim() 319 | var value = split.join(':').trim() 320 | head.append(key, value) 321 | }) 322 | return head 323 | } 324 | 325 | Body.call(Request.prototype) 326 | 327 | function Response(bodyInit, options) { 328 | if (!options) { 329 | options = {} 330 | } 331 | 332 | this.type = 'default' 333 | this.status = options.status 334 | this.ok = this.status >= 200 && this.status < 300 335 | this.statusText = options.statusText 336 | this.headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers) 337 | this.url = options.url || '' 338 | this._initBody(bodyInit) 339 | } 340 | 341 | Body.call(Response.prototype) 342 | 343 | Response.prototype.clone = function() { 344 | return new Response(this._bodyInit, { 345 | status: this.status, 346 | statusText: this.statusText, 347 | headers: new Headers(this.headers), 348 | url: this.url 349 | }) 350 | } 351 | 352 | Response.error = function() { 353 | var response = new Response(null, {status: 0, statusText: ''}) 354 | response.type = 'error' 355 | return response 356 | } 357 | 358 | var redirectStatuses = [301, 302, 303, 307, 308] 359 | 360 | Response.redirect = function(url, status) { 361 | if (redirectStatuses.indexOf(status) === -1) { 362 | throw new RangeError('Invalid status code') 363 | } 364 | 365 | return new Response(null, {status: status, headers: {location: url}}) 366 | } 367 | 368 | self.Headers = Headers 369 | self.Request = Request 370 | self.Response = Response 371 | 372 | self.fetch = function(input, init) { 373 | return new Promise(function(resolve, reject) { 374 | var request 375 | if (Request.prototype.isPrototypeOf(input) && !init) { 376 | request = input 377 | } else { 378 | request = new Request(input, init) 379 | } 380 | 381 | var xhr = new XMLHttpRequest() 382 | 383 | function responseURL() { 384 | if ('responseURL' in xhr) { 385 | return xhr.responseURL 386 | } 387 | 388 | // Avoid security warnings on getResponseHeader when not allowed by CORS 389 | if (/^X-Request-URL:/m.test(xhr.getAllResponseHeaders())) { 390 | return xhr.getResponseHeader('X-Request-URL') 391 | } 392 | 393 | return 394 | } 395 | 396 | xhr.onload = function() { 397 | var options = { 398 | status: xhr.status, 399 | statusText: xhr.statusText, 400 | headers: headers(xhr), 401 | url: responseURL() 402 | } 403 | var body = 'response' in xhr ? xhr.response : xhr.responseText 404 | resolve(new Response(body, options)) 405 | } 406 | 407 | xhr.onerror = function() { 408 | reject(new TypeError('Network request failed')) 409 | } 410 | 411 | xhr.ontimeout = function() { 412 | reject(new TypeError('Network request failed')) 413 | } 414 | 415 | xhr.open(request.method, request.url, true) 416 | 417 | if (request.credentials === 'include') { 418 | xhr.withCredentials = true 419 | } 420 | 421 | if ('responseType' in xhr && support.blob) { 422 | xhr.responseType = 'blob' 423 | } 424 | 425 | request.headers.forEach(function(value, name) { 426 | xhr.setRequestHeader(name, value) 427 | }) 428 | 429 | xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit) 430 | }) 431 | } 432 | self.fetch.polyfill = true 433 | })(typeof self !== 'undefined' ? self : this); 434 | -------------------------------------------------------------------------------- /client.coffee.md: -------------------------------------------------------------------------------- 1 | Markov Technology 2 | ================= 3 | 4 | This is a demo showing the relationship between Markov chains and music. We 5 | use Tricka Technology by A. Skillz & Kraft Kuts because it has the large- and 6 | small-scale self-similarity that makes markov generation work best. 7 | 8 | 9 | $ = document.querySelector.bind(document) 10 | 11 | context = new (window.AudioContext || window.webkitAudioContext) 12 | console.log context.sampleRate 13 | 14 | 15 | Audio loading 16 | ------------- 17 | 18 | This loads audio data from the server, caching the audio buffer in memory for future plays. 19 | 20 | buffercache = {} 21 | maybeFetch = (src) -> 22 | if buffercache[src] 23 | Promise.resolve(buffercache[src]) 24 | else 25 | fetch(src) 26 | .then (response) -> response.arrayBuffer() 27 | .then (audioData) -> new Promise (accept) -> context.decodeAudioData audioData, accept 28 | .then (buffer) -> buffercache[src] = buffer; buffer 29 | 30 | play = (src, at) -> 31 | maybeFetch src 32 | .then (buffer) -> 33 | node = context.createBufferSource() 34 | node.buffer = buffer 35 | node.connect groupPulser.node 36 | if at then node.start(at, 0, buffer.duration) else node.start(context.currentTime, 0, buffer.duration) 37 | node 38 | 39 | 40 | Lag detector 41 | ------------ 42 | 43 | We do a few different graphical things that can stress out mobile devices and 44 | lesser browsers. In case it's too much, we can turn off some things using this 45 | lag detector. 46 | 47 | massiveLagCallbacks = [] 48 | onMassiveLag = (f) -> massiveLagCallbacks.push f 49 | watchFrameDrop = -> 50 | framesDropped = 0 51 | oldt = 0 52 | frameTime = 1/30 * 1000 53 | checkFrameDrop = -> requestAnimationFrame (t) -> 54 | if t - oldt > frameTime 55 | framesDropped += Math.min(Math.round((t - oldt) / frameTime), 10) 56 | console.log "framedrop", framesDropped, t - oldt 57 | else if framesDropped > 0 58 | framesDropped -= 0.25 59 | 60 | if framesDropped > 100 61 | f() for f in massiveLagCallbacks 62 | return 63 | 64 | oldt = t 65 | checkFrameDrop() 66 | checkFrameDrop() 67 | watchFrameDrop() 68 | 69 | 70 | Pulser 71 | ------ 72 | 73 | We make the current node pulse using a Web Audio Analyser Node hooked up to 74 | some JS to set the opacity of the node's pulse element. 75 | 76 | makePulser = (opts={}) -> 77 | node = context.createAnalyser() 78 | node.fftSize = opts.fftSize or 256 79 | ary = new Float32Array(node.fftSize) 80 | min = null 81 | max = null 82 | oldavg = null 83 | attachedEl = null 84 | drawing = false 85 | decay = opts.decay ? 0.0001 86 | attack = opts.attack ? 0.5 87 | smoothing = opts.smoothing ? 0.66 88 | oldt = null 89 | stopped = false 90 | draw = -> requestAnimationFrame (t) -> 91 | return if stopped 92 | 93 | if node.getFloatTimeDomainData 94 | node.getFloatTimeDomainData(ary) 95 | else 96 | ary[0] = 1 97 | 98 | avg = 0 99 | avg += Math.abs(val) for val in ary 100 | avg = avg * (1-smoothing) + oldavg * smoothing 101 | oldavg = avg 102 | 103 | if !oldavg 104 | min = avg 105 | max = avg 106 | oldavg = avg 107 | min = min * (1-attack) + avg * attack if avg < min 108 | max = max * (1-attack) + avg * attack if avg > max 109 | 110 | val = Math.round(Math.min((avg-min)/(max-min), 1)*1000)/1000 111 | 112 | newmax = max * (1-decay) + min * decay 113 | newmin = min * (1-decay) + max * decay 114 | max = newmax 115 | min = newmin 116 | 117 | attachedEl.style.opacity = if opts.invert then 1-val else val 118 | draw() 119 | 120 | attach = (el) -> 121 | attachedEl.style.opacity = 0 if attachedEl 122 | attachedEl = el 123 | draw() unless drawing 124 | drawing = true 125 | 126 | stop = -> stopped = true 127 | 128 | {node, attach, stop} 129 | 130 | pulser = makePulser() 131 | groupPulser = makePulser(fftsize: 2048, attack: 1, smoothing: 0.96, invert: true) 132 | pulser.node.connect context.destination 133 | groupPulser.node.connect pulser.node 134 | 135 | onMassiveLag -> groupPulser.stop() 136 | 137 | 138 | SVG 139 | --- 140 | 141 | This sets up our SVG document, some helpers for creating new SVG elements, and 142 | the top-level groups for different elements. 143 | 144 | svg = document.querySelector('svg') 145 | 146 | svgEl = (name, attribs={}) -> 147 | el = document.createElementNS 'http://www.w3.org/2000/svg', name 148 | el.setAttribute k, v for k, v of attribs 149 | el 150 | 151 | svgText = (text, attribs) -> 152 | el = svgEl 'text', attribs 153 | el.textContent = text 154 | el 155 | 156 | defs = svgEl 'defs' 157 | defs.innerHTML = """ 158 | 164 | 165 | 166 | """ 167 | svg.appendChild defs 168 | 169 | labelGroup = svgEl 'g', 170 | "font-size": 96 171 | "text-anchor": "middle" 172 | linkGroup = svgEl 'g', 173 | "opacity": 0.8 174 | nodeGroup = svgEl 'g' 175 | shadeGroup = svgEl 'g' 176 | svg.appendChild shadeGroup 177 | svg.appendChild labelGroup 178 | svg.appendChild linkGroup 179 | svg.appendChild nodeGroup 180 | 181 | labelGroup.style.fontFamily = '"Helvetica Neue", "Helvetica", Sans Serif' 182 | labelGroup.style.fontWeight = "600" 183 | 184 | Node group 185 | ---------- 186 | 187 | A node group is a group of Markov nodes, which is used to set the fill, stroke 188 | and pulse colours for nodes in the subgroup, as well as add the fuzzy coloured 189 | background shadow thing. 190 | 191 | groups = {} 192 | 193 | addGroup = (name, group) -> 194 | groups[name] = group 195 | 196 | group.el = el = svgEl 'g', 197 | fill: group.fill 198 | stroke: group.stroke 199 | 200 | group.pulseEl = pulseEl = svgEl 'g', 201 | opacity: 0 202 | stroke: group.stroke 203 | fill: group.pulse 204 | 205 | labelGroup.appendChild el 206 | labelGroup.appendChild pulseEl 207 | 208 | for t, i in group.label.text 209 | text = svgText t, 210 | x: group.label.x 211 | y: group.label.y + i * 96 212 | el.appendChild text 213 | pulseEl.appendChild text.cloneNode(true) 214 | 215 | 216 | if group.shade 217 | grad = svgEl 'radialGradient', id: "#{name}-gradient" 218 | grad.appendChild svgEl 'stop', offset: "0%", "stop-opacity": 1, "stop-color": group.shade.fill 219 | grad.appendChild svgEl 'stop', offset: "50%", "stop-opacity": 1, "stop-color": group.shade.fill 220 | grad.appendChild svgEl 'stop', offset: "100%", "stop-opacity": 0, "stop-color": group.shade.fill 221 | defs.appendChild grad 222 | 223 | shade = svgEl 'circle', 224 | cx: group.shade.x 225 | cy: group.shade.y 226 | r: group.shade.r * 1.5 227 | fill: "url(##{name}-gradient)" 228 | opacity: 0.8 229 | 230 | rate = 2.3529 231 | start = (Math.random() * -2 * rate).toFixed(4) 232 | shade.style.animation = "#{rate}s ease-in-out #{start}s infinite alternate pulse" 233 | 234 | onMassiveLag -> shade.style.animation = "" 235 | 236 | shadeGroup.appendChild shade 237 | 238 | 239 | Markov node 240 | ----------- 241 | 242 | Our audio is played by slowly traversing a weighted directed graph of Markov 243 | nodes. The nodes handle playing audio, and have an internal list of outgoing 244 | links that they randomly choose from. 245 | 246 | In order to achieve seamless playback, we have to schedule playing the next 247 | node ahead of when our node actually finishes. We use a combination of 248 | `setTimeout` and the Web Audio API's scheduler to do this. 249 | 250 | nodeProto = 251 | render: -> 252 | return if @el 253 | @el = el = svgEl 'g', 254 | opacity: 0.7 255 | x = @x 256 | y = @y 257 | 258 | circle = svgEl 'circle', 259 | cx: x 260 | cy: y 261 | r: 40 262 | fill: @group.fill 263 | stroke: @group.stroke 264 | 265 | @pulse = pulse = svgEl 'circle', 266 | cx: x 267 | cy: y 268 | r: 40 269 | fill: @group.pulse 270 | stroke: @group.stroke 271 | opacity: 0 272 | 273 | nameparts = @name.split '_' 274 | topname = nameparts.slice(0,2).join '_' 275 | botname = nameparts.slice(2).join '_' 276 | 277 | if @label 278 | label = svgText @label, 279 | x: x 280 | y: y + 6 281 | "text-anchor": "middle" 282 | "font-size": 16 283 | "font-family": "Helvetica Neue" 284 | "font-weight": "800" 285 | 286 | el.appendChild circle 287 | el.appendChild pulse 288 | el.appendChild label if label 289 | nodeGroup.appendChild el 290 | 291 | renderLink: (next) -> 292 | return unless @el and next.el 293 | @linkEls ||= {} 294 | if !@linkEl 295 | @linkEl = svgEl 'g', 296 | stroke: '#111' 297 | linkGroup.appendChild @linkEl 298 | 299 | m = (next.y - @y) / (next.x - @x) 300 | d = Math.sqrt((next.y - @y)**2 + (next.x - @x)**2) 301 | x1 = @x + (next.x - @x) * (40/d) 302 | y1 = @y + (next.y - @y) * (40/d) 303 | x2 = next.x - (next.x - @x) * (48/d) 304 | y2 = next.y - (next.y - @y) * (48/d) 305 | 306 | line = svgEl 'line', 307 | x1: x1, y1: y1 308 | x2: x2, y2: y2 309 | 'stroke-width': 2 310 | 'marker-end': 'url(#markerArrow)' 311 | @linkEl.appendChild line 312 | @linkEls[next.name] = line 313 | 314 | 315 | display: -> 316 | total = 0 317 | total += l.weight for l in @links 318 | "#{@name} ⇒ " + ("#{l.next?.name} (#{Math.round(l.weight/total*100)}%)" for l in @links).join ', ' 319 | 320 | activate: -> 321 | console.log @display() 322 | @el.setAttribute 'opacity', 0.8 323 | pulser.attach @pulse 324 | groupPulser.attach @group.pulseEl if @group.pulseEl 325 | 326 | deactivate: -> 327 | @el.setAttribute 'opacity', 0.7 328 | linkEl.setAttribute 'stroke', '#111' for k, linkEl of @linkEls if @linkEls 329 | 330 | preload: -> 331 | console.log "Loading", @src 332 | maybeFetch @src 333 | 334 | play: (at=context.currentTime+0.1, last) -> 335 | console.log "Playing", @src, "at", at 336 | 337 | setTimeout => 338 | @activate() 339 | last?.deactivate() 340 | , (at-context.currentTime)*1000 341 | 342 | play @src, at 343 | .then (audio) => 344 | scheduled = at + audio.buffer.duration 345 | time = (at - context.currentTime) * 1000 + 1000 # 500ms grace period 346 | next = @getNext() 347 | if next 348 | next.preload() 349 | setTimeout => 350 | @linkEls[next.name].setAttribute 'stroke', '#FF5252' 351 | next.play(scheduled, this) 352 | , time 353 | audio.onended = @ended.bind(this) 354 | 355 | ended: -> 356 | console.log "Finished", @src 357 | 358 | getNext: -> 359 | total = @links.reduce ((total, link) -> total + link.weight), 0 360 | rand = Math.random()*total 361 | cur = 0 362 | for link in @links 363 | cur += link.weight 364 | return link.next if rand <= cur 365 | null 366 | 367 | link: (nexts, weight=1) -> 368 | nexts = [nexts] unless Array.isArray(nexts) 369 | for next in nexts 370 | linkEl = @renderLink next 371 | @links.push {next, weight} 372 | this 373 | 374 | makeNode = (src, data) -> 375 | o = Object.create nodeProto 376 | o.src = src 377 | o.links = [] 378 | o.name = src.split('/').pop().split('.')[0] 379 | o[k] = data[k] for k in 'x y label'.split(' ') 380 | if data.group and groups[data.group] 381 | o.group = groups[data.group] 382 | else 383 | o.group = {fill: '#DDD', stroke: '#111', pulse: '#FFF'} 384 | o.render() 385 | o 386 | 387 | Setup 388 | ----- 389 | 390 | We create nodes for every media file we're using. The nodes are not preloaded, 391 | which probably means it will stutter if the network connection is too slow and 392 | the cache is cold. 393 | 394 | Firefox has issues with inaccurate mp3 buffer length, so we need to format 395 | sniff, use ogg, and fall back to mp3 if it's not available. 396 | 397 | canOgg = document.createElement('audio').canPlayType('audio/ogg') 398 | ext = if canOgg then 'ogg' else 'mp3' 399 | 400 | nodes = {} 401 | fetch('data.json') 402 | .then (result) -> result.json() 403 | .then (data) -> 404 | for groupName, group of data.groups 405 | addGroup groupName, group 406 | 407 | nodes[k] = makeNode "media/#{k}.#{ext}", v for k, v of data.nodes 408 | 409 | for k, v of data.nodes 410 | for weight, links of v.links 411 | nodes[k].link (nodes[l] for l in links), Number(weight) 412 | 413 | nodes.intro.preload() 414 | .then -> 415 | nodes.intro.play() 416 | -------------------------------------------------------------------------------- /data.json: -------------------------------------------------------------------------------- 1 | { 2 | "groups": { 3 | "bounce": { 4 | "fill": "#2196F3", 5 | "stroke": "#1976D2", 6 | "pulse": "#E3F2FD", 7 | "label": { 8 | "x": 200, 9 | "y": 640, 10 | "text": ["Bounce"] 11 | }, 12 | "shade": { 13 | "x": 200, 14 | "y": 640, 15 | "r": 400, 16 | "fill": "#BBDEFB" 17 | } 18 | }, 19 | "youknowwhat": { 20 | "fill": "#FFEB3B", 21 | "stroke": "#FDD835", 22 | "pulse": "#FFFDE7", 23 | "label": { 24 | "x": 225, 25 | "y": 150, 26 | "text": ["You", "Know", "What"] 27 | }, 28 | "shade": { 29 | "x": 225, 30 | "y": 150, 31 | "r": 400, 32 | "fill": "#FFF9C4" 33 | } 34 | }, 35 | "instr": { 36 | "fill": "#FF5722", 37 | "stroke": "#E64A19", 38 | "pulse": "#FBE9E7", 39 | "label": { 40 | "x": 1350, 41 | "y": 575, 42 | "text": ["Guitar"] 43 | }, 44 | "shade": { 45 | "x": 1350, 46 | "y": 575, 47 | "r": 400, 48 | "fill": "#FFCCBC" 49 | } 50 | }, 51 | "putemup": { 52 | "fill": "#E91E63", 53 | "stroke": "#C2185B", 54 | "pulse": "#FCE4EC", 55 | "label": { 56 | "x": 1075, 57 | "y": 115, 58 | "text": ["Put Em", "Up"] 59 | }, 60 | "shade": { 61 | "x": 1075, 62 | "y": 115, 63 | "r": 400, 64 | "fill": "#F8BBD0" 65 | } 66 | }, 67 | "hipstep": { 68 | "fill": "#673AB7", 69 | "stroke": "#4527A0", 70 | "pulse": "#EDE7F6", 71 | "label": { 72 | "x": 1400, 73 | "y": 115, 74 | "text": ["Hip", "Step"] 75 | }, 76 | "shade": { 77 | "x": 1400, 78 | "y": 115, 79 | "r": 250, 80 | "fill": "#D1C4E9" 81 | } 82 | }, 83 | "markov": { 84 | "fill": "#FF9800", 85 | "stroke": "#FF9800", 86 | "pulse": "#FFF3E0", 87 | "label": { 88 | "x": 800, 89 | "y": 475, 90 | "text": ["Markov", "Technology"] 91 | }, 92 | "shade": { 93 | "x": 800, 94 | "y": 475, 95 | "r": 400, 96 | "fill": "#FFE0B2" 97 | } 98 | } 99 | }, 100 | "nodes": { 101 | "intro": { 102 | "x": 750, "y": 50, 103 | "label": "Intro", 104 | "links": { 105 | "1": ["tricka_1_intro"] 106 | } 107 | }, 108 | "intro2": { 109 | "x": 850, "y": 50, 110 | "label": "Intro #2", 111 | "links": { 112 | "1": ["tricka_1_intro2"] 113 | } 114 | }, 115 | 116 | 117 | "tricka_1_intro": { 118 | "x": 750, "y": 200, 119 | "group": "markov", 120 | "label": "Intro", 121 | "links": { 122 | "1": ["tricka_2_plain"] 123 | } 124 | }, 125 | "tricka_1_intro2": { 126 | "x": 850, "y": 200, 127 | "group": "markov", 128 | "label": "Intro #2", 129 | "links": { 130 | "1": ["tricka_2_plain"] 131 | } 132 | }, 133 | 134 | 135 | "tricka_1_plain": { 136 | "x": 1100, "y": 425, 137 | "group": "markov", 138 | "label": "Plain", 139 | "links": { 140 | "1": [ 141 | "tricka_2_plain", "tricka_2_uh", "tricka_2_yeah", "tricka_2_tricka", 142 | "tricka_2_outro_tricka_cmon", "tricka_2_outro_what_what" 143 | ] 144 | } 145 | }, 146 | "tricka_1_cmon": { 147 | "x": 500, "y": 425, 148 | "group": "markov", 149 | "label": "Cmon", 150 | "links": { 151 | "1": [ 152 | "tricka_2_plain", "tricka_2_uh", "tricka_2_yeah", "tricka_2_tricka", 153 | "tricka_2_outro_tricka_cmon", "tricka_2_outro_what_what" 154 | ] 155 | } 156 | }, 157 | "tricka_1_uh": { 158 | "x": 1066.5, "y": 312.5, 159 | "group": "markov", 160 | "label": "Uh", 161 | "links": { 162 | "1": [ 163 | "tricka_2_plain", "tricka_2_uh", "tricka_2_yeah", "tricka_2_tricka", 164 | "tricka_2_outro_tricka_cmon", "tricka_2_outro_what_what" 165 | ] 166 | } 167 | }, 168 | "tricka_1_trickawhat": { 169 | "x": 533.5, "y": 312.5, 170 | "group": "markov", 171 | "label": "Trickawha", 172 | "links": { 173 | "1": [ 174 | "tricka_2_plain", "tricka_2_uh", "tricka_2_yeah", "tricka_2_tricka", 175 | "tricka_2_outro_tricka_cmon", "tricka_2_outro_what_what" 176 | ] 177 | } 178 | }, 179 | "tricka_1_tricka_tricka": { 180 | "x": 975, "y": 230, 181 | "group": "markov", 182 | "label": "Tricka", 183 | "links": { 184 | "1": [ 185 | "tricka_2_plain", "tricka_2_uh", "tricka_2_yeah", "tricka_2_tricka", 186 | "tricka_2_outro_tricka_cmon", "tricka_2_outro_what_what" 187 | ] 188 | } 189 | }, 190 | "tricka_1_putemup": { 191 | "x": 625, "y": 230, 192 | "label": "Putemup", 193 | "group": "markov", 194 | "links": { 195 | "1": ["tricka_2_putemup", "tricka_2_outro_putemup"] 196 | } 197 | }, 198 | 199 | 200 | "tricka_2_plain": { 201 | "x": 533.5, "y": 687.5, 202 | "group": "markov", 203 | "label": "Plain", 204 | "links": { 205 | "1": ["tricka_1_plain", "tricka_1_cmon", "tricka_1_uh", "tricka_1_trickawhat", "tricka_1_putemup"] 206 | } 207 | }, 208 | "tricka_2_uh": { 209 | "x": 1100, "y": 575, 210 | "group": "markov", 211 | "label": "Uh", 212 | "links": { 213 | "1": ["tricka_1_plain", "tricka_1_cmon", "tricka_1_uh", "tricka_1_trickawhat", "tricka_1_putemup"] 214 | } 215 | }, 216 | "tricka_2_yeah": { 217 | "x": 500, "y": 575, 218 | "group": "markov", 219 | "label": "Yeah", 220 | "links": { 221 | "1": ["tricka_1_plain", "tricka_1_cmon", "tricka_1_uh", "tricka_1_trickawhat", "tricka_1_putemup"] 222 | } 223 | }, 224 | "tricka_2_tricka": { 225 | "x": 1066.5, "y": 687.5, 226 | "group": "markov", 227 | "label": "Tricka", 228 | "links": { 229 | "1": ["tricka_1_plain", "tricka_1_cmon", "tricka_1_uh", "tricka_1_trickawhat", "tricka_1_putemup"] 230 | } 231 | }, 232 | "tricka_2_putemup": { 233 | "x": 625, "y": 770, 234 | "group": "markov", 235 | "label": "Putemup", 236 | "links": { 237 | "1": ["tricka_1_plain", "tricka_1_cmon", "tricka_1_uh", "tricka_1_trickawhat", "tricka_1_putemup"] 238 | } 239 | }, 240 | 241 | 242 | "tricka_2_outro_tricka_cmon": { 243 | "x": 750, "y": 800, 244 | "group": "markov", 245 | "label": "Outro #1", 246 | "links": { 247 | "1": ["instr1_1_intro", "instr2_1_intro", "bounce_1_intro"] 248 | } 249 | }, 250 | "tricka_2_outro_what_what": { 251 | "x": 850, "y": 800, 252 | "group": "markov", 253 | "label": "Outro #2", 254 | "links": { 255 | "1": ["instr1_1_intro", "instr2_1_intro", "bounce_1_intro"] 256 | } 257 | }, 258 | "tricka_2_outro_putemup": { 259 | "x": 975, "y": 770, 260 | "group": "markov", 261 | "label": "Outro #3", 262 | "links": { 263 | "1": ["instr1_1_intro", "instr2_1_intro", "bounce_1_intro"] 264 | } 265 | }, 266 | 267 | 268 | "instr1_1_intro": { 269 | "x": 1200, "y": 725, 270 | "group": "instr", 271 | "label": "Intro", 272 | "links": { 273 | "1": ["instr1_2_plain"] 274 | } 275 | }, 276 | "instr1_1_plain": { 277 | "x": 1200, "y": 475, 278 | "group": "instr", 279 | "label": "1-1", 280 | "links": { 281 | "1": ["instr1_2_plain", "instr1_2_outro"] 282 | } 283 | }, 284 | "instr1_2_plain": { 285 | "x": 1300, "y": 600, 286 | "group": "instr", 287 | "label": "1-2", 288 | "links": { 289 | "1": ["instr1_1_plain", "instr2_1"] 290 | } 291 | }, 292 | "instr1_2_outro": { 293 | "x": 1300, "y": 475, 294 | "group": "instr", 295 | "label": "Outro", 296 | "links": { 297 | "1": ["bridge1_1", "intro2"] 298 | } 299 | }, 300 | 301 | 302 | "instr2_1_intro": { 303 | "x": 1400, "y": 800, 304 | "group": "instr", 305 | "label": "Intro #2", 306 | "links": { 307 | "1": ["instr2_2_1", "instr2_2_2"] 308 | } 309 | }, 310 | "instr2_1": { 311 | "x": 1400, "y": 675, 312 | "group": "instr", 313 | "label": "2-1 #1", 314 | "links": { 315 | "1": ["instr2_2_1", "instr2_2_2"] 316 | } 317 | }, 318 | "instr2_1_scratch": { 319 | "x": 1400, "y": 550, 320 | "group": "instr", 321 | "label": "2-1 #2", 322 | "links": { 323 | "1": ["instr2_2_outro"] 324 | } 325 | }, 326 | "instr2_2_1": { 327 | "x": 1500, "y": 675, 328 | "group": "instr", 329 | "label": "2-2 #1", 330 | "links": { 331 | "1": ["instr2_1", "instr2_1_scratch"] 332 | } 333 | }, 334 | "instr2_2_2": { 335 | "x": 1500, "y": 550, 336 | "group": "instr", 337 | "label": "2-2 #2", 338 | "links": { 339 | "1": ["instr2_1", "instr2_1_scratch"] 340 | } 341 | }, 342 | "instr2_2_outro": { 343 | "x": 1500, "y": 425, 344 | "group": "instr", 345 | "label": "Outro #2", 346 | "links": { 347 | "1": ["hipstep1_1", "hipstep2_1_intro"] 348 | } 349 | }, 350 | 351 | 352 | "bridge1_1": { 353 | "x": 1300, "y": 350, 354 | "group": "instr", 355 | "label": "Bridge #1", 356 | "links": { 357 | "1": ["bridge1_2"] 358 | } 359 | }, 360 | "bridge1_2": { 361 | "group": "instr", 362 | "label": "Bridge #2", 363 | "x": 1400, "y": 350, 364 | "links": { 365 | "1": ["hipstep1_1"] 366 | } 367 | }, 368 | 369 | 370 | "hipstep1_1": { 371 | "x": 1400, "y": 200, 372 | "group": "hipstep", 373 | "label": "Plain 1", 374 | "links": { 375 | "1": ["hipstep1_2"] 376 | } 377 | }, 378 | "hipstep1_2": { 379 | "x": 1500, "y": 50, 380 | "group": "hipstep", 381 | "label": "Plain 2", 382 | "links": { 383 | "1": ["hipstep1_1"], 384 | "2": ["hipstep2_1_guitar"] 385 | } 386 | }, 387 | "hipstep2_1_intro": { 388 | "x": 1500, "y": 200, 389 | "group": "hipstep", 390 | "label": "Synth 1", 391 | "links": { 392 | "1": ["hipstep2_2"] 393 | } 394 | }, 395 | "hipstep2_1_guitar": { 396 | "x": 1400, "y": 50, 397 | "group": "hipstep", 398 | "label": "Guitar 1", 399 | "links": { 400 | "1": ["hipstep2_2"], 401 | "2": ["hipstep2_2_guitar_outro"] 402 | } 403 | }, 404 | "hipstep2_2": { 405 | "x": 1300, "y": 200, 406 | "group": "hipstep", 407 | "label": "Synth 2", 408 | "links": { 409 | "1": ["hipstep2_1_guitar"] 410 | } 411 | }, 412 | "hipstep2_2_guitar_outro": { 413 | "x": 1300, "y": 50, 414 | "group": "hipstep", 415 | "label": "Guitar 2", 416 | "links": { 417 | "1": ["putemup_1_intro"] 418 | } 419 | }, 420 | 421 | 422 | "putemup_1_intro": { 423 | "x": 1175, "y": 50, 424 | "group": "putemup", 425 | "label": "Intro", 426 | "links": { 427 | "1": ["putemup_2"] 428 | } 429 | }, 430 | "putemup_2": { 431 | "x": 1075, "y": 50, 432 | "group": "putemup", 433 | "label": "Part 2", 434 | "links": { 435 | "1": ["putemup_1"] 436 | } 437 | }, 438 | "putemup_1": { 439 | "x": 1075, "y": 175, 440 | "group": "putemup", 441 | "label": "Part 1", 442 | "links": { 443 | "1": ["putemup_2"], 444 | "2": ["putemup_2_outro"] 445 | } 446 | }, 447 | "putemup_2_outro": { 448 | "x": 975, "y": 50, 449 | "group": "putemup", 450 | "label": "Outro", 451 | "links": { 452 | "1": ["tricka_1_intro"] 453 | } 454 | }, 455 | 456 | 457 | "bounce_1_intro": { 458 | "x": 200, "y": 800, 459 | "group": "bounce", 460 | "label": "Intro", 461 | "links": { 462 | "1": ["bounce_2_yeah", "bounce_2_uh"] 463 | } 464 | }, 465 | "bounce_1_uh": { 466 | "x": 300, "y": 550, 467 | "group": "bounce", 468 | "label": "Uh #1", 469 | "links": { 470 | "1": ["bounce_2_yeah", "bounce_2_uh"], 471 | "2": ["bounce_2_outro"] 472 | } 473 | }, 474 | "bounce_1_yeah": { 475 | "x": 100, "y": 550, 476 | "group": "bounce", 477 | "label": "Yeah #1", 478 | "links": { 479 | "1": ["bounce_2_yeah", "bounce_2_uh"], 480 | "2": ["bounce_2_outro"] 481 | } 482 | }, 483 | "bounce_2_uh": { 484 | "x": 300, "y": 675, 485 | "group": "bounce", 486 | "label": "Uh #2", 487 | "links": { 488 | "1": ["bounce_1_uh"] 489 | } 490 | }, 491 | "bounce_2_yeah": { 492 | "x": 100, "y": 675, 493 | "group": "bounce", 494 | "label": "Yeah #2", 495 | "links": { 496 | "1": ["bounce_1_yeah"] 497 | } 498 | }, 499 | "bounce_2_outro": { 500 | "x": 200, "y": 450, 501 | "group": "bounce", 502 | "label": "Outro", 503 | "links": { 504 | "1": ["youknowwhat_1"] 505 | } 506 | }, 507 | 508 | 509 | "youknowwhat_1": { 510 | "x": 100, "y": 350, 511 | "group": "youknowwhat", 512 | "label": "#1", 513 | "links": { 514 | "1": ["youknowwhat_2", "youknowwhat_3"], 515 | "2": ["youknowwhat_outro"] 516 | } 517 | }, 518 | "youknowwhat_2": { 519 | "x": 225, "y": 225, 520 | "group": "youknowwhat", 521 | "label": "#2", 522 | "links": { 523 | "1": ["youknowwhat_1", "youknowwhat_3"], 524 | "2": ["youknowwhat_outro"] 525 | } 526 | }, 527 | "youknowwhat_3": { 528 | "x": 100, "y": 100, 529 | "group": "youknowwhat", 530 | "label": "#3", 531 | "links": { 532 | "1": ["youknowwhat_1", "youknowwhat_2"], 533 | "2": ["youknowwhat_outro"] 534 | } 535 | }, 536 | "youknowwhat_outro": { 537 | "x": 400, "y": 200, 538 | "group": "youknowwhat", 539 | "label": "Outro", 540 | "links": { 541 | "1": ["tricka_1_intro2"] 542 | } 543 | } 544 | } 545 | } -------------------------------------------------------------------------------- /client.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | (function() { 3 | var $, addGroup, buffercache, canOgg, context, defs, ext, groupPulser, groups, labelGroup, linkGroup, makeNode, makePulser, massiveLagCallbacks, maybeFetch, nodeGroup, nodeProto, nodes, onMassiveLag, play, pulser, shadeGroup, svg, svgEl, svgText, watchFrameDrop; 4 | 5 | $ = document.querySelector.bind(document); 6 | 7 | context = new (window.AudioContext || window.webkitAudioContext); 8 | 9 | console.log(context.sampleRate); 10 | 11 | buffercache = {}; 12 | 13 | maybeFetch = function(src) { 14 | if (buffercache[src]) { 15 | return Promise.resolve(buffercache[src]); 16 | } else { 17 | return fetch(src).then(function(response) { 18 | return response.arrayBuffer(); 19 | }).then(function(audioData) { 20 | return new Promise(function(accept) { 21 | return context.decodeAudioData(audioData, accept); 22 | }); 23 | }).then(function(buffer) { 24 | buffercache[src] = buffer; 25 | return buffer; 26 | }); 27 | } 28 | }; 29 | 30 | play = function(src, at) { 31 | return maybeFetch(src).then(function(buffer) { 32 | var node; 33 | node = context.createBufferSource(); 34 | node.buffer = buffer; 35 | node.connect(groupPulser.node); 36 | if (at) { 37 | node.start(at, 0, buffer.duration); 38 | } else { 39 | node.start(context.currentTime, 0, buffer.duration); 40 | } 41 | return node; 42 | }); 43 | }; 44 | 45 | massiveLagCallbacks = []; 46 | 47 | onMassiveLag = function(f) { 48 | return massiveLagCallbacks.push(f); 49 | }; 50 | 51 | watchFrameDrop = function() { 52 | var checkFrameDrop, frameTime, framesDropped, oldt; 53 | framesDropped = 0; 54 | oldt = 0; 55 | frameTime = 1 / 30 * 1000; 56 | checkFrameDrop = function() { 57 | return requestAnimationFrame(function(t) { 58 | var f, j, len; 59 | if (t - oldt > frameTime) { 60 | framesDropped += Math.min(Math.round((t - oldt) / frameTime), 10); 61 | console.log("framedrop", framesDropped, t - oldt); 62 | } else if (framesDropped > 0) { 63 | framesDropped -= 0.25; 64 | } 65 | if (framesDropped > 100) { 66 | for (j = 0, len = massiveLagCallbacks.length; j < len; j++) { 67 | f = massiveLagCallbacks[j]; 68 | f(); 69 | } 70 | return; 71 | } 72 | oldt = t; 73 | return checkFrameDrop(); 74 | }); 75 | }; 76 | return checkFrameDrop(); 77 | }; 78 | 79 | watchFrameDrop(); 80 | 81 | makePulser = function(opts) { 82 | var ary, attach, attachedEl, attack, decay, draw, drawing, max, min, node, oldavg, oldt, ref, ref1, ref2, smoothing, stop, stopped; 83 | if (opts == null) { 84 | opts = {}; 85 | } 86 | node = context.createAnalyser(); 87 | node.fftSize = opts.fftSize || 256; 88 | ary = new Float32Array(node.fftSize); 89 | min = null; 90 | max = null; 91 | oldavg = null; 92 | attachedEl = null; 93 | drawing = false; 94 | decay = (ref = opts.decay) != null ? ref : 0.0001; 95 | attack = (ref1 = opts.attack) != null ? ref1 : 0.5; 96 | smoothing = (ref2 = opts.smoothing) != null ? ref2 : 0.66; 97 | oldt = null; 98 | stopped = false; 99 | draw = function() { 100 | return requestAnimationFrame(function(t) { 101 | var avg, j, len, newmax, newmin, val; 102 | if (stopped) { 103 | return; 104 | } 105 | if (node.getFloatTimeDomainData) { 106 | node.getFloatTimeDomainData(ary); 107 | } else { 108 | ary[0] = 1; 109 | } 110 | avg = 0; 111 | for (j = 0, len = ary.length; j < len; j++) { 112 | val = ary[j]; 113 | avg += Math.abs(val); 114 | } 115 | avg = avg * (1 - smoothing) + oldavg * smoothing; 116 | oldavg = avg; 117 | if (!oldavg) { 118 | min = avg; 119 | max = avg; 120 | oldavg = avg; 121 | } 122 | if (avg < min) { 123 | min = min * (1 - attack) + avg * attack; 124 | } 125 | if (avg > max) { 126 | max = max * (1 - attack) + avg * attack; 127 | } 128 | val = Math.round(Math.min((avg - min) / (max - min), 1) * 1000) / 1000; 129 | newmax = max * (1 - decay) + min * decay; 130 | newmin = min * (1 - decay) + max * decay; 131 | max = newmax; 132 | min = newmin; 133 | attachedEl.style.opacity = opts.invert ? 1 - val : val; 134 | return draw(); 135 | }); 136 | }; 137 | attach = function(el) { 138 | if (attachedEl) { 139 | attachedEl.style.opacity = 0; 140 | } 141 | attachedEl = el; 142 | if (!drawing) { 143 | draw(); 144 | } 145 | return drawing = true; 146 | }; 147 | stop = function() { 148 | return stopped = true; 149 | }; 150 | return { 151 | node: node, 152 | attach: attach, 153 | stop: stop 154 | }; 155 | }; 156 | 157 | pulser = makePulser(); 158 | 159 | groupPulser = makePulser({ 160 | fftsize: 2048, 161 | attack: 1, 162 | smoothing: 0.96, 163 | invert: true 164 | }); 165 | 166 | pulser.node.connect(context.destination); 167 | 168 | groupPulser.node.connect(pulser.node); 169 | 170 | onMassiveLag(function() { 171 | return groupPulser.stop(); 172 | }); 173 | 174 | svg = document.querySelector('svg'); 175 | 176 | svgEl = function(name, attribs) { 177 | var el, k, v; 178 | if (attribs == null) { 179 | attribs = {}; 180 | } 181 | el = document.createElementNS('http://www.w3.org/2000/svg', name); 182 | for (k in attribs) { 183 | v = attribs[k]; 184 | el.setAttribute(k, v); 185 | } 186 | return el; 187 | }; 188 | 189 | svgText = function(text, attribs) { 190 | var el; 191 | el = svgEl('text', attribs); 192 | el.textContent = text; 193 | return el; 194 | }; 195 | 196 | defs = svgEl('defs'); 197 | 198 | defs.innerHTML = "\n \n"; 199 | 200 | svg.appendChild(defs); 201 | 202 | labelGroup = svgEl('g', { 203 | "font-size": 96, 204 | "text-anchor": "middle" 205 | }); 206 | 207 | linkGroup = svgEl('g', { 208 | "opacity": 0.8 209 | }); 210 | 211 | nodeGroup = svgEl('g'); 212 | 213 | shadeGroup = svgEl('g'); 214 | 215 | svg.appendChild(shadeGroup); 216 | 217 | svg.appendChild(labelGroup); 218 | 219 | svg.appendChild(linkGroup); 220 | 221 | svg.appendChild(nodeGroup); 222 | 223 | labelGroup.style.fontFamily = '"Helvetica Neue", "Helvetica", Sans Serif'; 224 | 225 | labelGroup.style.fontWeight = "600"; 226 | 227 | groups = {}; 228 | 229 | addGroup = function(name, group) { 230 | var el, grad, i, j, len, pulseEl, rate, ref, shade, start, t, text; 231 | groups[name] = group; 232 | group.el = el = svgEl('g', { 233 | fill: group.fill, 234 | stroke: group.stroke 235 | }); 236 | group.pulseEl = pulseEl = svgEl('g', { 237 | opacity: 0, 238 | stroke: group.stroke, 239 | fill: group.pulse 240 | }); 241 | labelGroup.appendChild(el); 242 | labelGroup.appendChild(pulseEl); 243 | ref = group.label.text; 244 | for (i = j = 0, len = ref.length; j < len; i = ++j) { 245 | t = ref[i]; 246 | text = svgText(t, { 247 | x: group.label.x, 248 | y: group.label.y + i * 96 249 | }); 250 | el.appendChild(text); 251 | pulseEl.appendChild(text.cloneNode(true)); 252 | } 253 | if (group.shade) { 254 | grad = svgEl('radialGradient', { 255 | id: name + "-gradient" 256 | }); 257 | grad.appendChild(svgEl('stop', { 258 | offset: "0%", 259 | "stop-opacity": 1, 260 | "stop-color": group.shade.fill 261 | })); 262 | grad.appendChild(svgEl('stop', { 263 | offset: "50%", 264 | "stop-opacity": 1, 265 | "stop-color": group.shade.fill 266 | })); 267 | grad.appendChild(svgEl('stop', { 268 | offset: "100%", 269 | "stop-opacity": 0, 270 | "stop-color": group.shade.fill 271 | })); 272 | defs.appendChild(grad); 273 | shade = svgEl('circle', { 274 | cx: group.shade.x, 275 | cy: group.shade.y, 276 | r: group.shade.r * 1.5, 277 | fill: "url(#" + name + "-gradient)", 278 | opacity: 0.8 279 | }); 280 | rate = 2.3529; 281 | start = (Math.random() * -2 * rate).toFixed(4); 282 | shade.style.animation = rate + "s ease-in-out " + start + "s infinite alternate pulse"; 283 | onMassiveLag(function() { 284 | return shade.style.animation = ""; 285 | }); 286 | return shadeGroup.appendChild(shade); 287 | } 288 | }; 289 | 290 | nodeProto = { 291 | render: function() { 292 | var botname, circle, el, label, nameparts, pulse, topname, x, y; 293 | if (this.el) { 294 | return; 295 | } 296 | this.el = el = svgEl('g', { 297 | opacity: 0.7 298 | }); 299 | x = this.x; 300 | y = this.y; 301 | circle = svgEl('circle', { 302 | cx: x, 303 | cy: y, 304 | r: 40, 305 | fill: this.group.fill, 306 | stroke: this.group.stroke 307 | }); 308 | this.pulse = pulse = svgEl('circle', { 309 | cx: x, 310 | cy: y, 311 | r: 40, 312 | fill: this.group.pulse, 313 | stroke: this.group.stroke, 314 | opacity: 0 315 | }); 316 | nameparts = this.name.split('_'); 317 | topname = nameparts.slice(0, 2).join('_'); 318 | botname = nameparts.slice(2).join('_'); 319 | if (this.label) { 320 | label = svgText(this.label, { 321 | x: x, 322 | y: y + 6, 323 | "text-anchor": "middle", 324 | "font-size": 16, 325 | "font-family": "Helvetica Neue", 326 | "font-weight": "800" 327 | }); 328 | } 329 | el.appendChild(circle); 330 | el.appendChild(pulse); 331 | if (label) { 332 | el.appendChild(label); 333 | } 334 | return nodeGroup.appendChild(el); 335 | }, 336 | renderLink: function(next) { 337 | var d, line, m, x1, x2, y1, y2; 338 | if (!(this.el && next.el)) { 339 | return; 340 | } 341 | this.linkEls || (this.linkEls = {}); 342 | if (!this.linkEl) { 343 | this.linkEl = svgEl('g', { 344 | stroke: '#111' 345 | }); 346 | linkGroup.appendChild(this.linkEl); 347 | } 348 | m = (next.y - this.y) / (next.x - this.x); 349 | d = Math.sqrt(Math.pow(next.y - this.y, 2) + Math.pow(next.x - this.x, 2)); 350 | x1 = this.x + (next.x - this.x) * (40 / d); 351 | y1 = this.y + (next.y - this.y) * (40 / d); 352 | x2 = next.x - (next.x - this.x) * (48 / d); 353 | y2 = next.y - (next.y - this.y) * (48 / d); 354 | line = svgEl('line', { 355 | x1: x1, 356 | y1: y1, 357 | x2: x2, 358 | y2: y2, 359 | 'stroke-width': 2, 360 | 'marker-end': 'url(#markerArrow)' 361 | }); 362 | this.linkEl.appendChild(line); 363 | return this.linkEls[next.name] = line; 364 | }, 365 | display: function() { 366 | var j, l, len, ref, total; 367 | total = 0; 368 | ref = this.links; 369 | for (j = 0, len = ref.length; j < len; j++) { 370 | l = ref[j]; 371 | total += l.weight; 372 | } 373 | return (this.name + " ⇒ ") + ((function() { 374 | var len1, n, ref1, ref2, results; 375 | ref1 = this.links; 376 | results = []; 377 | for (n = 0, len1 = ref1.length; n < len1; n++) { 378 | l = ref1[n]; 379 | results.push(((ref2 = l.next) != null ? ref2.name : void 0) + " (" + (Math.round(l.weight / total * 100)) + "%)"); 380 | } 381 | return results; 382 | }).call(this)).join(', '); 383 | }, 384 | activate: function() { 385 | console.log(this.display()); 386 | this.el.setAttribute('opacity', 0.8); 387 | pulser.attach(this.pulse); 388 | if (this.group.pulseEl) { 389 | return groupPulser.attach(this.group.pulseEl); 390 | } 391 | }, 392 | deactivate: function() { 393 | var k, linkEl, ref, results; 394 | this.el.setAttribute('opacity', 0.7); 395 | if (this.linkEls) { 396 | ref = this.linkEls; 397 | results = []; 398 | for (k in ref) { 399 | linkEl = ref[k]; 400 | results.push(linkEl.setAttribute('stroke', '#111')); 401 | } 402 | return results; 403 | } 404 | }, 405 | preload: function() { 406 | console.log("Loading", this.src); 407 | return maybeFetch(this.src); 408 | }, 409 | play: function(at, last) { 410 | if (at == null) { 411 | at = context.currentTime + 0.1; 412 | } 413 | console.log("Playing", this.src, "at", at); 414 | setTimeout((function(_this) { 415 | return function() { 416 | _this.activate(); 417 | return last != null ? last.deactivate() : void 0; 418 | }; 419 | })(this), (at - context.currentTime) * 1000); 420 | return play(this.src, at).then((function(_this) { 421 | return function(audio) { 422 | var next, scheduled, time; 423 | scheduled = at + audio.buffer.duration; 424 | time = (at - context.currentTime) * 1000 + 1000; 425 | next = _this.getNext(); 426 | if (next) { 427 | next.preload(); 428 | setTimeout(function() { 429 | _this.linkEls[next.name].setAttribute('stroke', '#FF5252'); 430 | return next.play(scheduled, _this); 431 | }, time); 432 | } 433 | return audio.onended = _this.ended.bind(_this); 434 | }; 435 | })(this)); 436 | }, 437 | ended: function() { 438 | return console.log("Finished", this.src); 439 | }, 440 | getNext: function() { 441 | var cur, j, len, link, rand, ref, total; 442 | total = this.links.reduce((function(total, link) { 443 | return total + link.weight; 444 | }), 0); 445 | rand = Math.random() * total; 446 | cur = 0; 447 | ref = this.links; 448 | for (j = 0, len = ref.length; j < len; j++) { 449 | link = ref[j]; 450 | cur += link.weight; 451 | if (rand <= cur) { 452 | return link.next; 453 | } 454 | } 455 | return null; 456 | }, 457 | link: function(nexts, weight) { 458 | var j, len, linkEl, next; 459 | if (weight == null) { 460 | weight = 1; 461 | } 462 | if (!Array.isArray(nexts)) { 463 | nexts = [nexts]; 464 | } 465 | for (j = 0, len = nexts.length; j < len; j++) { 466 | next = nexts[j]; 467 | linkEl = this.renderLink(next); 468 | this.links.push({ 469 | next: next, 470 | weight: weight 471 | }); 472 | } 473 | return this; 474 | } 475 | }; 476 | 477 | makeNode = function(src, data) { 478 | var j, k, len, o, ref; 479 | o = Object.create(nodeProto); 480 | o.src = src; 481 | o.links = []; 482 | o.name = src.split('/').pop().split('.')[0]; 483 | ref = 'x y label'.split(' '); 484 | for (j = 0, len = ref.length; j < len; j++) { 485 | k = ref[j]; 486 | o[k] = data[k]; 487 | } 488 | if (data.group && groups[data.group]) { 489 | o.group = groups[data.group]; 490 | } else { 491 | o.group = { 492 | fill: '#DDD', 493 | stroke: '#111', 494 | pulse: '#FFF' 495 | }; 496 | } 497 | o.render(); 498 | return o; 499 | }; 500 | 501 | canOgg = document.createElement('audio').canPlayType('audio/ogg'); 502 | 503 | ext = canOgg ? 'ogg' : 'mp3'; 504 | 505 | nodes = {}; 506 | 507 | fetch('data.json').then(function(result) { 508 | return result.json(); 509 | }).then(function(data) { 510 | var group, groupName, k, l, links, ref, ref1, ref2, ref3, v, weight; 511 | ref = data.groups; 512 | for (groupName in ref) { 513 | group = ref[groupName]; 514 | addGroup(groupName, group); 515 | } 516 | ref1 = data.nodes; 517 | for (k in ref1) { 518 | v = ref1[k]; 519 | nodes[k] = makeNode("media/" + k + "." + ext, v); 520 | } 521 | ref2 = data.nodes; 522 | for (k in ref2) { 523 | v = ref2[k]; 524 | ref3 = v.links; 525 | for (weight in ref3) { 526 | links = ref3[weight]; 527 | nodes[k].link((function() { 528 | var j, len, results; 529 | results = []; 530 | for (j = 0, len = links.length; j < len; j++) { 531 | l = links[j]; 532 | results.push(nodes[l]); 533 | } 534 | return results; 535 | })(), Number(weight)); 536 | } 537 | } 538 | return nodes.intro.preload(); 539 | }).then(function() { 540 | return nodes.intro.play(); 541 | }); 542 | 543 | }).call(this); 544 | -------------------------------------------------------------------------------- /Technology/Technology.RPP: -------------------------------------------------------------------------------- 1 | 27 | 29 | RENDER_FILE "/Users/samg/Code/markov-technology/media" 30 | RENDER_PATTERN $track 31 | RENDER_FMT 0 2 44100 32 | RENDER_1X 0 33 | RENDER_RANGE 4 0 0 34 | RENDER_RESAMPLE 3 0 1 35 | RENDER_ADDTOPROJ 0 36 | RENDER_STEMS 32 37 | RENDER_DITHER 0 38 | TIMELOCKMODE 1 39 | TEMPOENVLOCKMODE 1 40 | ITEMMIX 0 41 | DEFPITCHMODE 589824 42 | TAKELANE 1 43 | SAMPLERATE 44100 0 0 44 | 47 | LOCK 1 48 | 55 | GLOBAL_AUTO -1 56 | TEMPO 101.85 4 4 57 | PLAYRATE 1 0 0.25 4 58 | SELECTION 0 0 59 | SELECTION2 0 0 60 | MASTERAUTOMODE 0 61 | MASTERTRACKHEIGHT 0 62 | MASTERPEAKCOL 16576 63 | MASTERMUTESOLO 0 64 | MASTERTRACKVIEW 0 0.6667 0.5 0.5 -1 -1 -1 65 | MASTERHWOUT 0 0 1 0 0 0 0 -1 66 | MASTER_NCH 2 2 67 | MASTER_VOLUME 1 0 -1 -1 1 68 | MASTER_FX 1 69 | MASTER_SEL 0 70 | 77 | 84 | MARKER 1 4.71281296023565 "Chorus 1" 0 0 1 85 | MARKER 2 23.5640648011782 "Verse 1" 0 0 1 86 | MARKER 3 42.41531664212077 "Chorus 2" 0 0 1 87 | MARKER 4 61.26656848306333 "Instrumental 1" 0 0 1 88 | MARKER 5 80.11782032400589 Bridge 0 0 1 89 | MARKER 6 89.54344624447718 "Hip Step" 0 0 1 90 | MARKER 7 108.39469808541975 "Verse 2" 0 0 1 91 | MARKER 8 122.53313696612666 Bridge 0 0 1 92 | MARKER 9 127.2459499263623 "Chorus 3" 0 0 1 93 | MARKER 10 146.09720176730488 Bounce 0 0 1 94 | MARKER 11 164.94845360824743 "You know what" 0 0 1 95 | MARKER 12 183.79970544918999 "Chorus 4" 0 0 1 96 | MARKER 13 202.65095729013257 "Instrumental 2" 0 0 1 97 | MARKER 14 221.50220913107512 "Hip Step 2" 0 0 1 98 | MARKER 15 240.3534609720177 "Put em up" 0 0 1 99 | MARKER 16 259.20471281296022 Outro 0 0 1 100 | 102 | 146 | > 147 | > 148 | 192 | > 193 | > 194 | 238 | > 239 | > 240 | 284 | > 285 | > 286 | 330 | > 331 | > 332 | 376 | > 377 | > 378 | 422 | > 423 | > 424 | 468 | > 469 | > 470 | 514 | > 515 | > 516 | 560 | > 561 | > 562 | 606 | > 607 | > 608 | 652 | > 653 | > 654 | 698 | > 699 | > 700 | 744 | > 745 | > 746 | 790 | > 791 | > 792 | 836 | > 837 | > 838 | 882 | > 883 | > 884 | 928 | > 929 | > 930 | 974 | > 975 | > 976 | 1020 | > 1021 | > 1022 | 1066 | > 1067 | > 1068 | 1112 | > 1113 | > 1114 | 1158 | > 1159 | > 1160 | 1204 | > 1205 | > 1206 | 1250 | > 1251 | > 1252 | 1296 | > 1297 | > 1298 | 1342 | > 1343 | > 1344 | 1388 | > 1389 | > 1390 | 1434 | > 1435 | > 1436 | 1480 | > 1481 | > 1482 | 1526 | > 1527 | > 1528 | 1572 | > 1573 | > 1574 | 1618 | > 1619 | > 1620 | 1664 | > 1665 | > 1666 | 1710 | > 1711 | > 1712 | 1756 | > 1757 | > 1758 | 1802 | > 1803 | > 1804 | 1848 | > 1849 | > 1850 | 1894 | > 1895 | > 1896 | 1940 | > 1941 | > 1942 | 1986 | > 1987 | > 1988 | 2032 | > 2033 | > 2034 | 2078 | > 2079 | > 2080 | 2124 | > 2125 | > 2126 | 2170 | > 2171 | > 2172 | 2216 | > 2217 | > 2218 | 2262 | > 2263 | > 2264 | 2308 | > 2309 | > 2310 | 2354 | > 2355 | > 2356 | 2400 | > 2401 | > 2402 | 2446 | > 2447 | > 2448 | 2492 | > 2493 | > 2494 | 2538 | > 2539 | > 2540 | 2584 | > 2585 | > 2586 | 2630 | > 2631 | > 2632 | 2676 | > 2677 | > 2678 | 2722 | > 2723 | > 2724 | 2768 | > 2769 | > 2770 | 2814 | > 2815 | > 2816 | 2860 | > 2861 | > 2862 | 2906 | > 2907 | > 2908 | 2952 | > 2953 | > 2954 | 2998 | > 2999 | > 3000 | 3044 | > 3045 | > 3046 | 3090 | > 3091 | > 3092 | 3136 | > 3137 | > 3138 | 3182 | > 3183 | > 3184 | 3228 | > 3229 | > 3230 | 3274 | > 3275 | > 3276 | 3320 | > 3321 | > 3322 | 3366 | > 3367 | > 3368 | 3412 | > 3413 | > 3414 | 3458 | > 3459 | > 3460 | 3504 | > 3505 | > 3506 | 3550 | > 3551 | > 3552 | 3596 | > 3597 | > 3598 | 3642 | > 3643 | > 3644 | 3688 | > 3689 | > 3690 | 3734 | > 3735 | > 3736 | 3780 | > 3781 | > 3782 | > 3783 | --------------------------------------------------------------------------------