Commit fdc99693d597db195828a60363373b6278be377f
updated for jsconf.asia 2016
Matt McKegg committed on 11/21/2016, 8:38:53 PMFiles changed
.gitignore | added |
README.md | added |
build.js | added |
heart.png | added |
images/jsconf.asia.png | added |
images/just-press-play.jpg | added |
lib/markdown.js | added |
logo.png | added |
npm-debug.log | added |
output.html | added |
package.json | added |
slides.html.ejs | added |
README.md | ||
---|---|---|
@@ -1,0 +1,91 @@ | ||
1 … | +# I make **music** with **computers**. | |
2 … | + | |
3 … | +💥~🤖~💥 | |
4 … | + | |
5 … | +# I also make **_software_** to make **music** with **computers**. | |
6 … | + | |
7 … | +https://www.youtube.com/watch?v=01_udUeZ2GU | |
8 … | + | |
9 … | +# _Why would you do that?_ | |
10 … | + | |
11 … | +# Playing computer music **live** is ... uh... _complicated_. | |
12 … | + | |
13 … | +> For most bands and musicians this is not a problem. If you are a guitarist, it makes no difference whether you are playing on stage or recording in a studio. In both cases, you are playing the guitar. In most cases with music, the album is just a recording of what the musicians would do anyway. The great thing about this is the musician can intuitively pick up on the feeling in the room and change the way the are playing to accommodate this. They might not even notice they're doing it. There is this amazing feeling that comes from a band jamming with each other, and vibing off the audience. | |
14 … | + | |
15 … | +> But this is not the case with most computer music. I was creating music on a timeline. | |
16 … | + | |
17 … | +### **Painted**, not played. | |
18 … | + | |
19 … | +[![](http://www.arsov.net/SoundBytes/Images/2014-03/Ableton-main.jpg)](https://www.youtube.com/watch?v=9SH4g4FHZHw) | |
20 … | + | |
21 … | +> This leaves only a few options. | |
22 … | + | |
23 … | +## _Option 1_: Recruit some musicians and **play it for real**! | |
24 … | + | |
25 … | +> This is actually something that would be a lot of fun, but it is not representative of my musical process. I'd be doing completely different things in the studio to what I'm doing on stage. It would be treating the album like a score, things would get very complicated very quick with the amount of sounds I use. | |
26 … | + | |
27 … | +[![](http://blogs.kcrw.com/musicnews/wp-content/uploads/2013/04/full-band-21-sm.jpg)](http://www.npr.org/event/music/185510193/james-blake-live-in-concert) | |
28 … | + | |
29 … | +## _Option 2_: **DJ it!** (_just press play..._) | |
30 … | + | |
31 … | +> The performance would be more like an exhibition, much like how a painter would present their work. But I don't find this very fun. I want to perform, share and be in the moment with the audience, able to easily adapt to the room vibe. Not just a boring and predictable exhibit. | |
32 … | + | |
33 … | +[![](images/just-press-play.jpg)](https://youtu.be/j6WY8L0wLTM?t=33) | |
34 … | + | |
35 … | +## _Option 3_: Play **everything** yourself! | |
36 … | + | |
37 … | +[![](http://www.freeweekly.com/wp-content/uploads/2014/10/HenryInvisibles.jpg)](https://www.youtube.com/watch?v=GIzq8-gSz-0) | |
38 … | + | |
39 … | +> https://www.youtube.com/watch?v=iHSMnWpyIeM | |
40 … | + | |
41 … | +> This was the option I wanted to go with. But my songs are already sequenced. How to go about destructuring and playing? | |
42 … | + | |
43 … | +> I had this crazy idea that I'd take my existing songs, and learn how to play all the individual parts and then build them up one loop at a time. | |
44 … | + | |
45 … | +### **Recording anxiety** | |
46 … | + | |
47 … | +[![](https://c1.staticflickr.com/1/58/168150955_40c7db0172_z.jpg?zz=1)](https://www.youtube.com/watch?v=q0HVo6M3w64) | |
48 … | + | |
49 … | +> I suffer from acute recording anxiety. This was always a problem. I’d be jamming away a cool riff, and it sounds awesome, then I hit record and my figures turn to jelly, and it comes out all wrong. I looked long and hard for a solution, but couldn't find anything reliable. It was time to build my own solution. | |
50 … | + | |
51 … | +## **Time travel** with Node.js | |
52 … | + | |
53 … | +> I created a Node.js script that allowed me more control over input, storing all MIDI in a buffer, and then allowing me to go back in time and create loops after I played them, along with some realtime loop transform controls. | |
54 … | + | |
55 … | +> In the process of trying to learn my songs, and failing kind of miserably, I managed to develop a lot of the core looping concepts that still exist in Loop Drop today. | |
56 … | + | |
57 … | +## _Previously on JSConf.asia_... | |
58 … | + | |
59 … | +[![](images/jsconf.asia.png)](https://www.youtube.com/watch?v=NL0nb8A8FDM) | |
60 … | + | |
61 … | +``youtu.be/NL0nb8A8FDM`` | |
62 … | + | |
63 … | +## **Loop Drop** — loopjs.com | |
64 … | + | |
65 … | +[![](logo.png)](http://loopjs.com) | |
66 … | + | |
67 … | +>> [Play some tunes](https://www.youtube.com/watch?v=5zutWpFG8pk) and give a quick overview of how it works. | |
68 … | + | |
69 … | +>> Sample the audience and make a song! | |
70 … | + | |
71 … | +## Loop Drop is **JavaScript**! | |
72 … | + | |
73 … | +> Loop Drop is a desktop application that you can install on your computer. But it is actually 100% JavaScript! This is achieved using Electron (made by Github), which combines Node.js with Chromium giving you everything you'd ever want! | |
74 … | + | |
75 … | +Electron - **Chromium + Node.js** | |
76 … | + | |
77 … | +http://electron.atom.io | |
78 … | + | |
79 … | +# Contributing | |
80 … | + | |
81 … | +- github.com/mmckegg/loop-drop-app | |
82 … | +- patreon.com/MattMcKegg | |
83 … | + | |
84 … | +![](heart.png) | |
85 … | + | |
86 … | +# DESTROY WITH SCIENCE | |
87 … | + | |
88 … | +Loop Drop R&D **super secret\*** laboratory! | |
89 … | + | |
90 … | +- soundcloud.com/destroy-with-science | |
91 … | +- destroywithscience.bandcamp.com/album/droptivist |
build.js | ||
---|---|---|
@@ -1,0 +1,11 @@ | ||
1 … | +var fs = require('fs') | |
2 … | +var ejs = require('ejs') | |
3 … | +var markdown = require('./lib/markdown') | |
4 … | +var additionalStyles = fs.readFileSync(require.resolve('highlight.js/styles/monokai.css'), 'utf8') | |
5 … | +var raw = fs.readFileSync('README.md', 'utf8') | |
6 … | +var template = ejs.compile(fs.readFileSync('slides.html.ejs', 'utf8')) | |
7 … | + | |
8 … | +fs.writeFileSync('output.html', template({ | |
9 … | + body: markdown.render(raw), | |
10 … | + additionalStyles: additionalStyles | |
11 … | +})) |
heart.png |
---|
images/jsconf.asia.png |
---|
images/just-press-play.jpg |
---|
lib/markdown.js | ||
---|---|---|
@@ -1,0 +1,21 @@ | ||
1 … | +var hljs = require('highlight.js') | |
2 … | +var iterator = require('markdown-it-for-inline') | |
3 … | + | |
4 … | +module.exports = require('markdown-it')({ | |
5 … | + linkify: true, | |
6 … | + highlight: function (str, lang) { | |
7 … | + if (lang && hljs.getLanguage(lang)) { | |
8 … | + try { | |
9 … | + return hljs.highlight(lang, str).value | |
10 … | + } catch (__) {} | |
11 … | + } | |
12 … | + | |
13 … | + try { | |
14 … | + return hljs.highlightAuto(str).value | |
15 … | + } catch (__) {} | |
16 … | + | |
17 … | + return '' | |
18 … | + } | |
19 … | +}).use(iterator, 'url_new_win', 'link_open', function (tokens, idx) { | |
20 … | + tokens[idx].attrPush([ 'target', '_blank' ]) | |
21 … | +}) |
logo.png |
---|
npm-debug.log | ||
---|---|---|
@@ -1,0 +1,24 @@ | ||
1 … | +0 info it worked if it ends with ok | |
2 … | +1 verbose cli [ '/usr/local/bin/node', '/usr/local/bin/npm', 'start' ] | |
3 … | +2 info using npm@3.10.3 | |
4 … | +3 info using node@v6.4.0 | |
5 … | +4 verbose stack Error: missing script: start | |
6 … | +4 verbose stack at run (/usr/local/lib/node_modules/npm/lib/run-script.js:151:19) | |
7 … | +4 verbose stack at /usr/local/lib/node_modules/npm/lib/run-script.js:61:5 | |
8 … | +4 verbose stack at /usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:356:5 | |
9 … | +4 verbose stack at checkBinReferences_ (/usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:320:45) | |
10 … | +4 verbose stack at final (/usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:354:3) | |
11 … | +4 verbose stack at then (/usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:124:5) | |
12 … | +4 verbose stack at /usr/local/lib/node_modules/npm/node_modules/read-package-json/read-json.js:311:12 | |
13 … | +4 verbose stack at /usr/local/lib/node_modules/npm/node_modules/graceful-fs/graceful-fs.js:78:16 | |
14 … | +4 verbose stack at tryToString (fs.js:455:3) | |
15 … | +4 verbose stack at FSReqWrap.readFileAfterClose [as oncomplete] (fs.js:442:12) | |
16 … | +5 verbose cwd /Users/matt/Code/jsconfasia-talk-2016-livejs | |
17 … | +6 error Darwin 15.6.0 | |
18 … | +7 error argv "/usr/local/bin/node" "/usr/local/bin/npm" "start" | |
19 … | +8 error node v6.4.0 | |
20 … | +9 error npm v3.10.3 | |
21 … | +10 error missing script: start | |
22 … | +11 error If you need help, you may report this error at: | |
23 … | +11 error <https://github.com/npm/npm/issues> | |
24 … | +12 verbose exit [ 1, true ] |
output.html | ||
---|---|---|
@@ -1,0 +1,470 @@ | ||
1 … | + | |
2 … | +<html lang='en'> | |
3 … | +<head> | |
4 … | + <meta charset="utf-8" /> | |
5 … | + <title>Slides</title> | |
6 … | + <style> | |
7 … | + body, html { | |
8 … | + height: 100%; | |
9 … | + background: #222; | |
10 … | + margin: 0; | |
11 … | + font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif; | |
12 … | + overflow: hidden; | |
13 … | + color: #CCC; | |
14 … | + font-size: 40px; | |
15 … | + letter-spacing: 1px; | |
16 … | + } | |
17 … | + | |
18 … | + h1, h2, h3 { | |
19 … | + font-weight: normal; | |
20 … | + text-align: center; | |
21 … | + color: white; | |
22 … | + margin: 20px 0; | |
23 … | + max-width: 80%; | |
24 … | + } | |
25 … | + | |
26 … | + p { | |
27 … | + max-width: 80%; | |
28 … | + text-align: center; | |
29 … | + margin-bottom: 20px | |
30 … | + } | |
31 … | + | |
32 … | + p + p { | |
33 … | + margin-top: 20px | |
34 … | + } | |
35 … | + | |
36 … | + a { | |
37 … | + color: #ff8bef; | |
38 … | + text-decoration: none; | |
39 … | + font-weight: normal; | |
40 … | + } | |
41 … | + | |
42 … | + ul { | |
43 … | + max-width: 70%; | |
44 … | + } | |
45 … | + | |
46 … | + strong { | |
47 … | + color: white; | |
48 … | + } | |
49 … | + | |
50 … | + h1 strong { | |
51 … | + font-weight: normal; | |
52 … | + color: #6AFF00; | |
53 … | + } | |
54 … | + | |
55 … | + h2 strong { | |
56 … | + color: #FFB500; | |
57 … | + } | |
58 … | + | |
59 … | + h1 { | |
60 … | + font-weight: normal; | |
61 … | + font-size: 300%; | |
62 … | + } | |
63 … | + | |
64 … | + h2 { | |
65 … | + font-weight: normal; | |
66 … | + font-size: 200%; | |
67 … | + } | |
68 … | + | |
69 … | + img { | |
70 … | + max-height: 65vh; | |
71 … | + margin: -10px 0; | |
72 … | + } | |
73 … | + | |
74 … | + .coffeescript .javascript, .javascript .xml, .tex .hljs-formula, .xml .javascript, .xml .vbscript, .xml .css, .xml .hljs-cdata { | |
75 … | + opacity: 0.9 !important; | |
76 … | + } | |
77 … | + | |
78 … | + .Viewer { | |
79 … | + display: flex; | |
80 … | + flex-direction: column; | |
81 … | + align-items: center; | |
82 … | + height: 100%; | |
83 … | + justify-content: center; | |
84 … | + } | |
85 … | + | |
86 … | + .Viewer > * { | |
87 … | + display: none | |
88 … | + } | |
89 … | + | |
90 … | + .Viewer > .-active { | |
91 … | + display: block | |
92 … | + } | |
93 … | + | |
94 … | + .Viewer > blockquote { | |
95 … | + display: none !important | |
96 … | + } | |
97 … | + | |
98 … | + pre { | |
99 … | + min-width: 60%; | |
100 … | + max-height: 80%; | |
101 … | + overflow: auto; | |
102 … | + padding: 10px; | |
103 … | + background: #111; | |
104 … | + border: 1px solid #333; | |
105 … | + color: #DDD; | |
106 … | + } | |
107 … | + | |
108 … | + pre > code { | |
109 … | + display: block; | |
110 … | + font: 20px/normal 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace; | |
111 … | + padding: 10px; | |
112 … | + padding-bottom: 100px; | |
113 … | + } | |
114 … | + | |
115 … | + p code, ul code { | |
116 … | + padding: 0 10px; | |
117 … | + background: #111; | |
118 … | + color: #EEE; | |
119 … | + border: 1px solid #333; | |
120 … | + font: 80% 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace; | |
121 … | + } | |
122 … | + | |
123 … | + .Overlay { | |
124 … | + position: absolute; | |
125 … | + top: 20px; | |
126 … | + right: 20px; | |
127 … | + font-size: 90%; | |
128 … | + opacity: 0.8; | |
129 … | + } | |
130 … | + | |
131 … | + ::selection { | |
132 … | + background: #AAA; | |
133 … | + } | |
134 … | + | |
135 … | + /* | |
136 … | +Monokai style - ported by Luigi Maselli - http://grigio.org | |
137 … | +*/ | |
138 … | + | |
139 … | +.hljs { | |
140 … | + display: block; | |
141 … | + overflow-x: auto; | |
142 … | + padding: 0.5em; | |
143 … | + background: #272822; | |
144 … | + -webkit-text-size-adjust: none; | |
145 … | +} | |
146 … | + | |
147 … | +.hljs-tag, | |
148 … | +.hljs-tag .hljs-title, | |
149 … | +.hljs-keyword, | |
150 … | +.hljs-literal, | |
151 … | +.hljs-strong, | |
152 … | +.hljs-change, | |
153 … | +.hljs-winutils, | |
154 … | +.hljs-flow, | |
155 … | +.nginx .hljs-title, | |
156 … | +.tex .hljs-special { | |
157 … | + color: #f92672; | |
158 … | +} | |
159 … | + | |
160 … | +.hljs { | |
161 … | + color: #ddd; | |
162 … | +} | |
163 … | + | |
164 … | +.hljs .hljs-constant, | |
165 … | +.asciidoc .hljs-code, | |
166 … | +.markdown .hljs-code { | |
167 … | + color: #66d9ef; | |
168 … | +} | |
169 … | + | |
170 … | +.hljs-code, | |
171 … | +.hljs-class .hljs-title, | |
172 … | +.hljs-header { | |
173 … | + color: white; | |
174 … | +} | |
175 … | + | |
176 … | +.hljs-link_label, | |
177 … | +.hljs-attribute, | |
178 … | +.hljs-symbol, | |
179 … | +.hljs-symbol .hljs-string, | |
180 … | +.hljs-value, | |
181 … | +.hljs-regexp { | |
182 … | + color: #bf79db; | |
183 … | +} | |
184 … | + | |
185 … | +.hljs-link_url, | |
186 … | +.hljs-tag .hljs-value, | |
187 … | +.hljs-string, | |
188 … | +.hljs-bullet, | |
189 … | +.hljs-subst, | |
190 … | +.hljs-title, | |
191 … | +.hljs-emphasis, | |
192 … | +.hljs-type, | |
193 … | +.hljs-preprocessor, | |
194 … | +.hljs-pragma, | |
195 … | +.ruby .hljs-class .hljs-parent, | |
196 … | +.hljs-built_in, | |
197 … | +.django .hljs-template_tag, | |
198 … | +.django .hljs-variable, | |
199 … | +.smalltalk .hljs-class, | |
200 … | +.django .hljs-filter .hljs-argument, | |
201 … | +.smalltalk .hljs-localvars, | |
202 … | +.smalltalk .hljs-array, | |
203 … | +.hljs-attr_selector, | |
204 … | +.hljs-pseudo, | |
205 … | +.hljs-addition, | |
206 … | +.hljs-stream, | |
207 … | +.hljs-envvar, | |
208 … | +.apache .hljs-tag, | |
209 … | +.apache .hljs-cbracket, | |
210 … | +.tex .hljs-command, | |
211 … | +.hljs-prompt, | |
212 … | +.hljs-name { | |
213 … | + color: #a6e22e; | |
214 … | +} | |
215 … | + | |
216 … | +.hljs-comment, | |
217 … | +.hljs-annotation, | |
218 … | +.smartquote, | |
219 … | +.hljs-blockquote, | |
220 … | +.hljs-horizontal_rule, | |
221 … | +.hljs-decorator, | |
222 … | +.hljs-pi, | |
223 … | +.hljs-doctype, | |
224 … | +.hljs-deletion, | |
225 … | +.hljs-shebang, | |
226 … | +.apache .hljs-sqbracket, | |
227 … | +.tex .hljs-formula { | |
228 … | + color: #75715e; | |
229 … | +} | |
230 … | + | |
231 … | +.hljs-keyword, | |
232 … | +.hljs-literal, | |
233 … | +.css .hljs-id, | |
234 … | +.hljs-doctag, | |
235 … | +.hljs-title, | |
236 … | +.hljs-header, | |
237 … | +.hljs-type, | |
238 … | +.vbscript .hljs-built_in, | |
239 … | +.rsl .hljs-built_in, | |
240 … | +.smalltalk .hljs-class, | |
241 … | +.diff .hljs-header, | |
242 … | +.hljs-chunk, | |
243 … | +.hljs-winutils, | |
244 … | +.bash .hljs-variable, | |
245 … | +.apache .hljs-tag, | |
246 … | +.tex .hljs-special, | |
247 … | +.hljs-request, | |
248 … | +.hljs-status { | |
249 … | + font-weight: bold; | |
250 … | +} | |
251 … | + | |
252 … | +.coffeescript .javascript, | |
253 … | +.javascript .xml, | |
254 … | +.tex .hljs-formula, | |
255 … | +.xml .javascript, | |
256 … | +.xml .vbscript, | |
257 … | +.xml .css, | |
258 … | +.xml .hljs-cdata { | |
259 … | + opacity: 0.5; | |
260 … | +} | |
261 … | + | |
262 … | + | |
263 … | + </style> | |
264 … | +</head> | |
265 … | +<body> | |
266 … | + <div class='Viewer'> | |
267 … | + <h1>I make <strong>music</strong> with <strong>computers</strong>.</h1> | |
268 … | +<p>💥~🤖~💥</p> | |
269 … | +<h1>I also make <strong><em>software</em></strong> to make <strong>music</strong> with <strong>computers</strong>.</h1> | |
270 … | +<p><a href="https://www.youtube.com/watch?v=01_udUeZ2GU" target="_blank">https://www.youtube.com/watch?v=01_udUeZ2GU</a></p> | |
271 … | +<h1><em>Why would you do that?</em></h1> | |
272 … | +<h1>Playing computer music <strong>live</strong> is ... uh... <em>complicated</em>.</h1> | |
273 … | +<blockquote> | |
274 … | +<p>For most bands and musicians this is not a problem. If you are a guitarist, it makes no difference whether you are playing on stage or recording in a studio. In both cases, you are playing the guitar. In most cases with music, the album is just a recording of what the musicians would do anyway. The great thing about this is the musician can intuitively pick up on the feeling in the room and change the way the are playing to accommodate this. They might not even notice they're doing it. There is this amazing feeling that comes from a band jamming with each other, and vibing off the audience.</p> | |
275 … | +</blockquote> | |
276 … | +<blockquote> | |
277 … | +<p>But this is not the case with most computer music. I was creating music on a timeline.</p> | |
278 … | +</blockquote> | |
279 … | +<h3><strong>Painted</strong>, not played.</h3> | |
280 … | +<p><a href="https://www.youtube.com/watch?v=9SH4g4FHZHw" target="_blank"><img src="http://www.arsov.net/SoundBytes/Images/2014-03/Ableton-main.jpg" alt=""></a></p> | |
281 … | +<blockquote> | |
282 … | +<p>This leaves only a few options.</p> | |
283 … | +</blockquote> | |
284 … | +<h2><em>Option 1</em>: Recruit some musicians and <strong>play it for real</strong>!</h2> | |
285 … | +<blockquote> | |
286 … | +<p>This is actually something that would be a lot of fun, but it is not representative of my musical process. I'd be doing completely different things in the studio to what I'm doing on stage. It would be treating the album like a score, things would get very complicated very quick with the amount of sounds I use.</p> | |
287 … | +</blockquote> | |
288 … | +<p><a href="http://www.npr.org/event/music/185510193/james-blake-live-in-concert" target="_blank"><img src="http://blogs.kcrw.com/musicnews/wp-content/uploads/2013/04/full-band-21-sm.jpg" alt=""></a></p> | |
289 … | +<h2><em>Option 2</em>: <strong>DJ it!</strong> (<em>just press play...</em>)</h2> | |
290 … | +<blockquote> | |
291 … | +<p>The performance would be more like an exhibition, much like how a painter would present their work. But I don't find this very fun. I want to perform, share and be in the moment with the audience, able to easily adapt to the room vibe. Not just a boring and predictable exhibit.</p> | |
292 … | +</blockquote> | |
293 … | +<p><a href="https://youtu.be/j6WY8L0wLTM?t=33" target="_blank"><img src="images/just-press-play.jpg" alt=""></a></p> | |
294 … | +<h2><em>Option 3</em>: Play <strong>everything</strong> yourself!</h2> | |
295 … | +<p><a href="https://www.youtube.com/watch?v=GIzq8-gSz-0" target="_blank"><img src="http://www.freeweekly.com/wp-content/uploads/2014/10/HenryInvisibles.jpg" alt=""></a></p> | |
296 … | +<blockquote> | |
297 … | +<p><a href="https://www.youtube.com/watch?v=iHSMnWpyIeM" target="_blank">https://www.youtube.com/watch?v=iHSMnWpyIeM</a></p> | |
298 … | +</blockquote> | |
299 … | +<blockquote> | |
300 … | +<p>This was the option I wanted to go with. But my songs are already sequenced. How to go about destructuring and playing?</p> | |
301 … | +</blockquote> | |
302 … | +<blockquote> | |
303 … | +<p>I had this crazy idea that I'd take my existing songs, and learn how to play all the individual parts and then build them up one loop at a time.</p> | |
304 … | +</blockquote> | |
305 … | +<h3><strong>Recording anxiety</strong></h3> | |
306 … | +<p><a href="https://www.youtube.com/watch?v=q0HVo6M3w64" target="_blank"><img src="https://c1.staticflickr.com/1/58/168150955_40c7db0172_z.jpg?zz=1" alt=""></a></p> | |
307 … | +<blockquote> | |
308 … | +<p>I suffer from acute recording anxiety. This was always a problem. I’d be jamming away a cool riff, and it sounds awesome, then I hit record and my figures turn to jelly, and it comes out all wrong. I looked long and hard for a solution, but couldn't find anything reliable. It was time to build my own solution.</p> | |
309 … | +</blockquote> | |
310 … | +<h2><strong>Time travel</strong> with Node.js</h2> | |
311 … | +<blockquote> | |
312 … | +<p>I created a Node.js script that allowed me more control over input, storing all MIDI in a buffer, and then allowing me to go back in time and create loops after I played them, along with some realtime loop transform controls.</p> | |
313 … | +</blockquote> | |
314 … | +<blockquote> | |
315 … | +<p>In the process of trying to learn my songs, and failing kind of miserably, I managed to develop a lot of the core looping concepts that still exist in Loop Drop today.</p> | |
316 … | +</blockquote> | |
317 … | +<h2><em>Previously on <a href="http://JSConf.asia" target="_blank">JSConf.asia</a></em>...</h2> | |
318 … | +<p><a href="https://www.youtube.com/watch?v=NL0nb8A8FDM" target="_blank"><img src="images/jsconf.asia.png" alt=""></a></p> | |
319 … | +<p><code>youtu.be/NL0nb8A8FDM</code></p> | |
320 … | +<h2><strong>Loop Drop</strong> — <a href="http://loopjs.com" target="_blank">loopjs.com</a></h2> | |
321 … | +<p><a href="http://loopjs.com" target="_blank"><img src="logo.png" alt=""></a></p> | |
322 … | +<blockquote> | |
323 … | +<blockquote> | |
324 … | +<p><a href="https://www.youtube.com/watch?v=5zutWpFG8pk" target="_blank">Play some tunes</a> and give a quick overview of how it works.</p> | |
325 … | +</blockquote> | |
326 … | +</blockquote> | |
327 … | +<blockquote> | |
328 … | +<blockquote> | |
329 … | +<p>Sample the audience and make a song!</p> | |
330 … | +</blockquote> | |
331 … | +</blockquote> | |
332 … | +<h2>Loop Drop is <strong>JavaScript</strong>!</h2> | |
333 … | +<blockquote> | |
334 … | +<p>Loop Drop is a desktop application that you can install on your computer. But it is actually 100% JavaScript! This is achieved using Electron (made by Github), which combines Node.js with Chromium giving you everything you'd ever want!</p> | |
335 … | +</blockquote> | |
336 … | +<p>Electron - <strong>Chromium + Node.js</strong></p> | |
337 … | +<p><a href="http://electron.atom.io" target="_blank">http://electron.atom.io</a></p> | |
338 … | +<h1>Contributing</h1> | |
339 … | +<ul> | |
340 … | +<li><a href="http://github.com/mmckegg/loop-drop-app" target="_blank">github.com/mmckegg/loop-drop-app</a></li> | |
341 … | +<li><a href="http://patreon.com/MattMcKegg" target="_blank">patreon.com/MattMcKegg</a></li> | |
342 … | +</ul> | |
343 … | +<p><img src="heart.png" alt=""></p> | |
344 … | +<h1>DESTROY WITH SCIENCE</h1> | |
345 … | +<p>Loop Drop R&D <strong>super secret*</strong> laboratory!</p> | |
346 … | +<ul> | |
347 … | +<li><a href="http://soundcloud.com/destroy-with-science" target="_blank">soundcloud.com/destroy-with-science</a></li> | |
348 … | +<li><a href="http://destroywithscience.bandcamp.com/album/droptivist" target="_blank">destroywithscience.bandcamp.com/album/droptivist</a></li> | |
349 … | +</ul> | |
350 … | + | |
351 … | + </div> | |
352 … | + <div class='Overlay'> | |
353 … | + <a href='https://twitter.com/MattMcKegg'>@MattMcKegg</a> | <strong>github.com/mmckegg</strong> | |
354 … | + </div> | |
355 … | + <script> | |
356 … | + var viewer = document.querySelector('.Viewer') | |
357 … | + var firstElement = viewer.firstElementChild | |
358 … | + firstElement.classList.add('-active') | |
359 … | + more(firstElement) | |
360 … | + | |
361 … | + Array.from(document.querySelectorAll('p')).forEach(function (element) { | |
362 … | + if (element.childNodes.length === 1 && element.childNodes[0].nodeName === 'IMG') { | |
363 … | + element.parentNode.replaceChild(element.childNodes[0], element) | |
364 … | + } | |
365 … | + }) | |
366 … | + | |
367 … | + function copyAll (element) { | |
368 … | + var range = document.createRange(); | |
369 … | + range.selectNodeContents(element); | |
370 … | + var sel = window.getSelection(); | |
371 … | + sel.removeAllRanges(); | |
372 … | + sel.addRange(range) | |
373 … | + document.execCommand('copy') | |
374 … | + sel.removeAllRanges(); | |
375 … | + } | |
376 … | + | |
377 … | + function next () { | |
378 … | + var active = viewer.querySelectorAll('.-active') | |
379 … | + var nextElement = getNext(last(active)) | |
380 … | + if (nextElement) { | |
381 … | + nextElement.classList.add('-active') | |
382 … | + each(active, function (el) { | |
383 … | + el.classList.remove('-active') | |
384 … | + }) | |
385 … | + more(nextElement) | |
386 … | + } | |
387 … | + } | |
388 … | + | |
389 … | + function prev () { | |
390 … | + var active = viewer.querySelectorAll('.-active') | |
391 … | + var prevElement = getPrev(first(active)) | |
392 … | + if (prevElement) { | |
393 … | + prevElement.classList.add('-active') | |
394 … | + each(active, function (el) { | |
395 … | + el.classList.remove('-active') | |
396 … | + }) | |
397 … | + more(prevElement) | |
398 … | + } | |
399 … | + } | |
400 … | + | |
401 … | + function getNext (current) { | |
402 … | + if (current) { | |
403 … | + var nextElement = current.nextElementSibling | |
404 … | + while (nextElement && !isHeading(nextElement)) { | |
405 … | + nextElement = nextElement.nextElementSibling | |
406 … | + } | |
407 … | + return nextElement | |
408 … | + } | |
409 … | + | |
410 … | + } | |
411 … | + | |
412 … | + function getPrev (current) { | |
413 … | + if (current) { | |
414 … | + var prevElement = current.previousElementSibling | |
415 … | + while (prevElement && !isHeading(prevElement)) { | |
416 … | + prevElement = prevElement.previousElementSibling | |
417 … | + } | |
418 … | + return prevElement | |
419 … | + } | |
420 … | + } | |
421 … | + | |
422 … | + function more (current) { | |
423 … | + if (current) { | |
424 … | + var nextElement = current.nextElementSibling | |
425 … | + while (nextElement && !isHeading(nextElement)) { | |
426 … | + nextElement.classList.add('-active') | |
427 … | + if (nextElement.tagName === 'PRE') { | |
428 … | + nextElement.tabIndex = 0 | |
429 … | + nextElement.focus() | |
430 … | + copyAll(nextElement) | |
431 … | + } | |
432 … | + nextElement = nextElement.nextElementSibling | |
433 … | + } | |
434 … | + } | |
435 … | + } | |
436 … | + | |
437 … | + function each (elements, fn) { | |
438 … | + for (var i = 0; i < elements.length; i++) { | |
439 … | + fn(elements[i]) | |
440 … | + } | |
441 … | + } | |
442 … | + | |
443 … | + function first (elements) { | |
444 … | + if (elements.length) { | |
445 … | + return elements[0] | |
446 … | + } | |
447 … | + } | |
448 … | + | |
449 … | + function isHeading (element) { | |
450 … | + return !!element.tagName.match(/^H[0-9]$/) | |
451 … | + } | |
452 … | + | |
453 … | + function last (elements) { | |
454 … | + if (elements.length) { | |
455 … | + return elements[elements.length-1] | |
456 … | + } | |
457 … | + } | |
458 … | + | |
459 … | + document.onkeydown = function (e) { | |
460 … | + if (e.keyCode === 39) { // right arrow | |
461 … | + next() | |
462 … | + } else if (e.keyCode === 37) { // left arrow | |
463 … | + prev() | |
464 … | + } else if (e.keyCode === 40) { // left arrow | |
465 … | + more() | |
466 … | + } | |
467 … | + } | |
468 … | + </script> | |
469 … | +</body> | |
470 … | +</html> |
package.json | ||
---|---|---|
@@ -1,0 +1,25 @@ | ||
1 … | +{ | |
2 … | + "name": "web-audio-conf-talk-2016", | |
3 … | + "version": "0.0.0", | |
4 … | + "private": true, | |
5 … | + "main": "build.js", | |
6 … | + "scripts": { | |
7 … | + "test": "echo \"Error: no test specified\" && exit 1" | |
8 … | + }, | |
9 … | + "repository": { | |
10 … | + "type": "git", | |
11 … | + "url": "https://github.com/mmckegg/jsconfasia-talk-2015" | |
12 … | + }, | |
13 … | + "author": "Matt McKegg", | |
14 … | + "license": "MIT", | |
15 … | + "bugs": { | |
16 … | + "url": "https://github.com/mmckegg/jsconfasia-talk-2015/issues" | |
17 … | + }, | |
18 … | + "homepage": "https://github.com/mmckegg/jsconfasia-talk-2015", | |
19 … | + "dependencies": { | |
20 … | + "ejs": "~2.3.3", | |
21 … | + "highlight.js": "~8.7.0", | |
22 … | + "markdown-it": "~4.4.0", | |
23 … | + "markdown-it-for-inline": "~0.1.1" | |
24 … | + } | |
25 … | +} |
slides.html.ejs | ||
---|---|---|
@@ -1,0 +1,261 @@ | ||
1 … | +<!DOCTYPE html> | |
2 … | +<html lang='en'> | |
3 … | +<head> | |
4 … | + <meta charset="utf-8" /> | |
5 … | + <title>Slides</title> | |
6 … | + <style> | |
7 … | + body, html { | |
8 … | + height: 100%; | |
9 … | + background: #222; | |
10 … | + margin: 0; | |
11 … | + font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif; | |
12 … | + overflow: hidden; | |
13 … | + color: #CCC; | |
14 … | + font-size: 40px; | |
15 … | + letter-spacing: 1px; | |
16 … | + } | |
17 … | + | |
18 … | + h1, h2, h3 { | |
19 … | + font-weight: normal; | |
20 … | + text-align: center; | |
21 … | + color: white; | |
22 … | + margin: 20px 0; | |
23 … | + max-width: 80%; | |
24 … | + } | |
25 … | + | |
26 … | + p { | |
27 … | + max-width: 80%; | |
28 … | + text-align: center; | |
29 … | + margin-bottom: 20px | |
30 … | + } | |
31 … | + | |
32 … | + p + p { | |
33 … | + margin-top: 20px | |
34 … | + } | |
35 … | + | |
36 … | + a { | |
37 … | + color: #ff8bef; | |
38 … | + text-decoration: none; | |
39 … | + font-weight: normal; | |
40 … | + } | |
41 … | + | |
42 … | + ul { | |
43 … | + max-width: 70%; | |
44 … | + } | |
45 … | + | |
46 … | + strong { | |
47 … | + color: white; | |
48 … | + } | |
49 … | + | |
50 … | + h1 strong { | |
51 … | + font-weight: normal; | |
52 … | + color: #6AFF00; | |
53 … | + } | |
54 … | + | |
55 … | + h2 strong { | |
56 … | + color: #FFB500; | |
57 … | + } | |
58 … | + | |
59 … | + h1 { | |
60 … | + font-weight: normal; | |
61 … | + font-size: 300%; | |
62 … | + } | |
63 … | + | |
64 … | + h2 { | |
65 … | + font-weight: normal; | |
66 … | + font-size: 200%; | |
67 … | + } | |
68 … | + | |
69 … | + img { | |
70 … | + max-height: 65vh; | |
71 … | + margin: -10px 0; | |
72 … | + } | |
73 … | + | |
74 … | + .coffeescript .javascript, .javascript .xml, .tex .hljs-formula, .xml .javascript, .xml .vbscript, .xml .css, .xml .hljs-cdata { | |
75 … | + opacity: 0.9 !important; | |
76 … | + } | |
77 … | + | |
78 … | + .Viewer { | |
79 … | + display: flex; | |
80 … | + flex-direction: column; | |
81 … | + align-items: center; | |
82 … | + height: 100%; | |
83 … | + justify-content: center; | |
84 … | + } | |
85 … | + | |
86 … | + .Viewer > * { | |
87 … | + display: none | |
88 … | + } | |
89 … | + | |
90 … | + .Viewer > .-active { | |
91 … | + display: block | |
92 … | + } | |
93 … | + | |
94 … | + .Viewer > blockquote { | |
95 … | + display: none !important | |
96 … | + } | |
97 … | + | |
98 … | + pre { | |
99 … | + min-width: 60%; | |
100 … | + max-height: 80%; | |
101 … | + overflow: auto; | |
102 … | + padding: 10px; | |
103 … | + background: #111; | |
104 … | + border: 1px solid #333; | |
105 … | + color: #DDD; | |
106 … | + } | |
107 … | + | |
108 … | + pre > code { | |
109 … | + display: block; | |
110 … | + font: 20px/normal 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace; | |
111 … | + padding: 10px; | |
112 … | + padding-bottom: 100px; | |
113 … | + } | |
114 … | + | |
115 … | + p code, ul code { | |
116 … | + padding: 0 10px; | |
117 … | + background: #111; | |
118 … | + color: #EEE; | |
119 … | + border: 1px solid #333; | |
120 … | + font: 80% 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace; | |
121 … | + } | |
122 … | + | |
123 … | + .Overlay { | |
124 … | + position: absolute; | |
125 … | + top: 20px; | |
126 … | + right: 20px; | |
127 … | + font-size: 90%; | |
128 … | + opacity: 0.8; | |
129 … | + } | |
130 … | + | |
131 … | + ::selection { | |
132 … | + background: #AAA; | |
133 … | + } | |
134 … | + | |
135 … | + <%= additionalStyles %> | |
136 … | + | |
137 … | + </style> | |
138 … | +</head> | |
139 … | +<body> | |
140 … | + <div class='Viewer'> | |
141 … | + <%- body %> | |
142 … | + </div> | |
143 … | + <div class='Overlay'> | |
144 … | + <a href='https://twitter.com/MattMcKegg'>@MattMcKegg</a> | <strong>github.com/mmckegg</strong> | |
145 … | + </div> | |
146 … | + <script> | |
147 … | + var viewer = document.querySelector('.Viewer') | |
148 … | + var firstElement = viewer.firstElementChild | |
149 … | + firstElement.classList.add('-active') | |
150 … | + more(firstElement) | |
151 … | + | |
152 … | + Array.from(document.querySelectorAll('p')).forEach(function (element) { | |
153 … | + if (element.childNodes.length === 1 && element.childNodes[0].nodeName === 'IMG') { | |
154 … | + element.parentNode.replaceChild(element.childNodes[0], element) | |
155 … | + } | |
156 … | + }) | |
157 … | + | |
158 … | + function copyAll (element) { | |
159 … | + var range = document.createRange(); | |
160 … | + range.selectNodeContents(element); | |
161 … | + var sel = window.getSelection(); | |
162 … | + sel.removeAllRanges(); | |
163 … | + sel.addRange(range) | |
164 … | + document.execCommand('copy') | |
165 … | + sel.removeAllRanges(); | |
166 … | + } | |
167 … | + | |
168 … | + function next () { | |
169 … | + var active = viewer.querySelectorAll('.-active') | |
170 … | + var nextElement = getNext(last(active)) | |
171 … | + if (nextElement) { | |
172 … | + nextElement.classList.add('-active') | |
173 … | + each(active, function (el) { | |
174 … | + el.classList.remove('-active') | |
175 … | + }) | |
176 … | + more(nextElement) | |
177 … | + } | |
178 … | + } | |
179 … | + | |
180 … | + function prev () { | |
181 … | + var active = viewer.querySelectorAll('.-active') | |
182 … | + var prevElement = getPrev(first(active)) | |
183 … | + if (prevElement) { | |
184 … | + prevElement.classList.add('-active') | |
185 … | + each(active, function (el) { | |
186 … | + el.classList.remove('-active') | |
187 … | + }) | |
188 … | + more(prevElement) | |
189 … | + } | |
190 … | + } | |
191 … | + | |
192 … | + function getNext (current) { | |
193 … | + if (current) { | |
194 … | + var nextElement = current.nextElementSibling | |
195 … | + while (nextElement && !isHeading(nextElement)) { | |
196 … | + nextElement = nextElement.nextElementSibling | |
197 … | + } | |
198 … | + return nextElement | |
199 … | + } | |
200 … | + | |
201 … | + } | |
202 … | + | |
203 … | + function getPrev (current) { | |
204 … | + if (current) { | |
205 … | + var prevElement = current.previousElementSibling | |
206 … | + while (prevElement && !isHeading(prevElement)) { | |
207 … | + prevElement = prevElement.previousElementSibling | |
208 … | + } | |
209 … | + return prevElement | |
210 … | + } | |
211 … | + } | |
212 … | + | |
213 … | + function more (current) { | |
214 … | + if (current) { | |
215 … | + var nextElement = current.nextElementSibling | |
216 … | + while (nextElement && !isHeading(nextElement)) { | |
217 … | + nextElement.classList.add('-active') | |
218 … | + if (nextElement.tagName === 'PRE') { | |
219 … | + nextElement.tabIndex = 0 | |
220 … | + nextElement.focus() | |
221 … | + copyAll(nextElement) | |
222 … | + } | |
223 … | + nextElement = nextElement.nextElementSibling | |
224 … | + } | |
225 … | + } | |
226 … | + } | |
227 … | + | |
228 … | + function each (elements, fn) { | |
229 … | + for (var i = 0; i < elements.length; i++) { | |
230 … | + fn(elements[i]) | |
231 … | + } | |
232 … | + } | |
233 … | + | |
234 … | + function first (elements) { | |
235 … | + if (elements.length) { | |
236 … | + return elements[0] | |
237 … | + } | |
238 … | + } | |
239 … | + | |
240 … | + function isHeading (element) { | |
241 … | + return !!element.tagName.match(/^H[0-9]$/) | |
242 … | + } | |
243 … | + | |
244 … | + function last (elements) { | |
245 … | + if (elements.length) { | |
246 … | + return elements[elements.length-1] | |
247 … | + } | |
248 … | + } | |
249 … | + | |
250 … | + document.onkeydown = function (e) { | |
251 … | + if (e.keyCode === 39) { // right arrow | |
252 … | + next() | |
253 … | + } else if (e.keyCode === 37) { // left arrow | |
254 … | + prev() | |
255 … | + } else if (e.keyCode === 40) { // left arrow | |
256 … | + more() | |
257 … | + } | |
258 … | + } | |
259 … | + </script> | |
260 … | +</body> | |
261 … | +</html> |
Built with git-ssb-web