├── README.md ├── src ├── app │ ├── app.js │ └── component.service.js ├── styles.css ├── index.js └── custom.css ├── .gitignore ├── assets ├── favicon.ico ├── airy-icon.png ├── flow-icon.png └── share-image.jpg ├── tailwind.config.js ├── package.json └── index.html /README.md: -------------------------------------------------------------------------------- 1 | # derek-cv 2 | Static web CV 3 | -------------------------------------------------------------------------------- /src/app/app.js: -------------------------------------------------------------------------------- 1 | export const run = () => { 2 | // todo 3 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | npm-debug.log 4 | .env 5 | .DS_Store -------------------------------------------------------------------------------- /assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derekcsm/derek-cv/master/assets/favicon.ico -------------------------------------------------------------------------------- /assets/airy-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derekcsm/derek-cv/master/assets/airy-icon.png -------------------------------------------------------------------------------- /assets/flow-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derekcsm/derek-cv/master/assets/flow-icon.png -------------------------------------------------------------------------------- /assets/share-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derekcsm/derek-cv/master/assets/share-image.jpg -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss/base"; 2 | @import "tailwindcss/components"; 3 | @import "tailwindcss/utilities"; -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import {run} from "./app/app"; 2 | import { ComponentService } from "./app/component.service"; 3 | const componentService = new ComponentService(); 4 | run(componentService); -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | theme: { 3 | container: { 4 | center: true, 5 | padding: '2rem' 6 | } 7 | }, 8 | variants: {}, 9 | plugins: [] 10 | } 11 | -------------------------------------------------------------------------------- /src/custom.css: -------------------------------------------------------------------------------- 1 | .key1-bg { 2 | background-color: #1FA774; 3 | } 4 | .key2-bg { 5 | background-color: #34013F; 6 | } 7 | .key3-bg { 8 | background-color: #FF9408; 9 | } 10 | .key4-bg { 11 | background-color: #CB416B; 12 | } 13 | 14 | .keyboard-bg { 15 | background-color: #BDF6FE; 16 | } 17 | 18 | .page-bg { 19 | background-color: #FFFFE4; 20 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "derek-cv", 3 | "version": "1.0.0", 4 | "description": "Resume Website", 5 | "private": true, 6 | "main": "index.js", 7 | "dependencies": { 8 | "rebound": "^0.1.0", 9 | "tailwindcss": "^1.2.0", 10 | "tone": "^13.8.25" 11 | }, 12 | "devDependencies": { 13 | "webpack": "^4.42.1", 14 | "webpack-cli": "^3.3.11" 15 | }, 16 | "scripts": { 17 | "wp": "webpack", 18 | "build.css": "tailwind build src/styles.css -o dist/styles.css" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/derekcsm/derek-cv.git" 23 | }, 24 | "author": "derekcsm", 25 | "license": "ISC", 26 | "bugs": { 27 | "url": "https://github.com/derekcsm/derek-cv/issues" 28 | }, 29 | "homepage": "https://github.com/derekcsm/derek-cv#readme" 30 | } 31 | -------------------------------------------------------------------------------- /src/app/component.service.js: -------------------------------------------------------------------------------- 1 | import * as Tone from "tone"; 2 | import * as rebound from "rebound"; 3 | 4 | var springSystem = new rebound.SpringSystem(); 5 | var spring1 = springSystem.createSpring(69, 9); 6 | var spring2 = springSystem.createSpring(69, 9); 7 | var spring3 = springSystem.createSpring(69, 9); 8 | var spring4 = springSystem.createSpring(69, 9); 9 | 10 | var sound1Layout; 11 | var sound2Layout; 12 | var sound3Layout; 13 | var sound4Layout; 14 | 15 | window.downEvt = window.ontouchstart !== undefined ? 'touchstart' : 'mousedown'; 16 | window.upEvt = window.ontouchend !== undefined ? 'touchend' : 'mouseup'; 17 | 18 | export class ComponentService { 19 | 20 | constructor() { 21 | 22 | document.documentElement.addEventListener( 23 | "mousedown", function () { 24 | if (Tone.context.state !== 'running') { 25 | Tone.context.resume(); 26 | } 27 | }); 28 | 29 | var phaser = new Tone.Phaser({ 30 | "frequency": 20, 31 | "octaves": 2, 32 | "Q": 10, 33 | "baseFrequency": 1600 34 | }).toMaster(); 35 | 36 | var poly = new Tone.PolySynth(6, Tone.FMSynth, { 37 | "volume": -6, 38 | "harmonicity": 4, 39 | "modulationIndex": 8, 40 | "oscillator": { 41 | "type": "sine" 42 | }, 43 | "envelope": { 44 | "attack": 0.001, 45 | "decay": 2, 46 | "sustain": 0.4, 47 | "release": 1.8 48 | }, 49 | "modulation": { 50 | "type": "square" 51 | }, 52 | "modulationEnvelope": { 53 | "attack": 0.002, 54 | "decay": 0.2, 55 | "sustain": 0, 56 | "release": 0.3 57 | } 58 | }); 59 | 60 | poly.connect(phaser); 61 | poly.toMaster() 62 | 63 | window.onload = function () { 64 | sound1Layout = document.getElementById("sound1"); 65 | sound2Layout = document.getElementById("sound2"); 66 | sound3Layout = document.getElementById("sound3"); 67 | sound4Layout = document.getElementById("sound4"); 68 | 69 | document.body.addEventListener(upEvt, () => { 70 | cancelAllSounds(); 71 | }); 72 | 73 | document.addEventListener("keydown", (key) => { 74 | if (key.repeat) { 75 | return; 76 | } 77 | 78 | if (key.Handled) { 79 | return; 80 | } 81 | 82 | console.log("keydown: " + key.code); 83 | 84 | switch (key.code) { 85 | case "Digit1": 86 | playSound1(true); 87 | break; 88 | case "Digit2": 89 | playSound2(true); 90 | break; 91 | case "Digit3": 92 | playSound3(true); 93 | break; 94 | case "Digit4": 95 | playSound4(true); 96 | break; 97 | } 98 | 99 | key.Handled = true; 100 | }); 101 | 102 | document.addEventListener("keyup", (key) => { 103 | console.log("keyup: " + key.code); 104 | 105 | key.stopImmediatePropagation(); 106 | 107 | switch (key.code) { 108 | case "Digit1": 109 | playSound1(false); 110 | break; 111 | case "Digit2": 112 | playSound2(false); 113 | break; 114 | case "Digit3": 115 | playSound3(false); 116 | break; 117 | case "Digit4": 118 | playSound4(false); 119 | break; 120 | } 121 | }); 122 | 123 | connectEventListeners(); 124 | 125 | connectAnimationListeners(); 126 | } 127 | 128 | let sound1Playing = false; 129 | function playSound1(clickDown) { 130 | if (clickDown && sound1Playing) { 131 | return; 132 | } 133 | 134 | if (clickDown) { 135 | sound1Playing = true; 136 | poly.triggerAttack(["B3"]); 137 | spring1.setEndValue(1); 138 | } else { 139 | sound1Playing = false; 140 | poly.triggerRelease(["B3"]); 141 | spring1.setEndValue(0); 142 | } 143 | } 144 | 145 | let sound2Playing = false; 146 | function playSound2(clickDown) { 147 | if (clickDown && sound2Playing) { 148 | return; 149 | } 150 | 151 | if (clickDown) { 152 | sound2Playing = true; 153 | poly.triggerAttack(["D3"]); 154 | spring2.setEndValue(1); 155 | } else { 156 | sound2Playing = false; 157 | poly.triggerRelease(["D3"]); 158 | spring2.setEndValue(0); 159 | } 160 | } 161 | 162 | let sound3Playing = false; 163 | function playSound3(clickDown) { 164 | if (clickDown && sound3Playing) { 165 | return; 166 | } 167 | 168 | if (clickDown) { 169 | sound3Playing = true; 170 | poly.triggerAttack(["F#3"]); 171 | spring3.setEndValue(1); 172 | } else { 173 | sound3Playing = false; 174 | poly.triggerRelease(["F#3"]); 175 | spring3.setEndValue(0); 176 | } 177 | } 178 | 179 | let sound4Playing = false; 180 | function playSound4(clickDown) { 181 | if (clickDown && sound4Playing) { 182 | return; 183 | } 184 | 185 | if (clickDown) { 186 | sound4Playing = true; 187 | poly.triggerAttack(["A3"]); 188 | spring4.setEndValue(1); 189 | } else { 190 | sound4Playing = false; 191 | poly.triggerRelease(["A3"]); 192 | spring4.setEndValue(0); 193 | } 194 | } 195 | 196 | function cancelAllSounds() { 197 | poly.releaseAll(); 198 | 199 | sound1Playing = false; 200 | sound2Playing = false; 201 | sound3Playing = false; 202 | sound4Playing = false; 203 | 204 | spring1.setEndValue(0); 205 | spring2.setEndValue(0); 206 | spring3.setEndValue(0); 207 | spring4.setEndValue(0); 208 | } 209 | 210 | /* 211 | Listeners for click & touch events, as well as animation listeners 212 | and logic below. 213 | */ 214 | 215 | function connectEventListeners() { 216 | 217 | sound1Layout.addEventListener(downEvt, () => { 218 | playSound1(true); 219 | }); 220 | 221 | sound2Layout.addEventListener(downEvt, () => { 222 | playSound2(true); 223 | }); 224 | 225 | sound3Layout.addEventListener(downEvt, () => { 226 | playSound3(true); 227 | }); 228 | 229 | sound4Layout.addEventListener(downEvt, () => { 230 | playSound4(true); 231 | }); 232 | } 233 | 234 | function connectAnimationListeners() { 235 | spring1.addListener({ 236 | onSpringUpdate: function (spring) { 237 | var val = spring.getCurrentValue(); 238 | val = rebound.MathUtil 239 | .mapValueInRange(val, 0, 1, 1, 0.4); 240 | scale(sound1Layout, val); 241 | } 242 | }); 243 | 244 | spring2.addListener({ 245 | onSpringUpdate: function (spring) { 246 | var val = spring.getCurrentValue(); 247 | val = rebound.MathUtil 248 | .mapValueInRange(val, 0, 1, 1, 0.4); 249 | scale(sound2Layout, val); 250 | } 251 | }); 252 | 253 | spring3.addListener({ 254 | onSpringUpdate: function (spring) { 255 | var val = spring.getCurrentValue(); 256 | val = rebound.MathUtil 257 | .mapValueInRange(val, 0, 1, 1, 0.4); 258 | scale(sound3Layout, val); 259 | } 260 | }); 261 | 262 | spring4.addListener({ 263 | onSpringUpdate: function (spring) { 264 | var val = spring.getCurrentValue(); 265 | val = rebound.MathUtil 266 | .mapValueInRange(val, 0, 1, 1, 0.4); 267 | scale(sound4Layout, val); 268 | } 269 | }); 270 | } 271 | 272 | function scale(el, val) { 273 | el.style.mozTransform = 274 | el.style.msTransform = 275 | el.style.webkitTransform = 276 | el.style.transform = 'scale3d(' + 277 | val + ', ' + val + ', 1)'; 278 | } 279 | } 280 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 |contact@derekcsm.xyz
44 |Hey there, thanks for visiting my resume. I hope you have fun with the synthesizer at the 59 | top! To play it like a piano Press “1, 2, 3, 4” on your keyboard, or just click on the tiles. (Try pressing all 60 | 4 at once to make a Bm7 chord!) 61 |
62 |Now onto the professional stuff. I’ve been an Android developer for the last 7+ years. 63 | During high school I taught myself programming to create a bus schedule app for the city of Nanaimo. Around that 64 | time I found a job at a small design shop called Impact Studio (currently 460 Communications). At Impact I used my time to focus on 66 | Android 67 | development, with the goal being to finish my bus schedule app. Soon after, I joined Metalab on a temporary 69 | contract and proved myself useful. Metalab 70 | then took me on full time and I moved to Victoria, BC for a new 71 | beginning.
72 |Throughout my career I have been directly responsible for the success of many projects 73 | from 74 | start to finish. Android apps have always been my main focus, but I am also well versed in UI/UX design, and web 75 | development. At work I have been a proven mentor to co-workers, and junior developers. Further, I have 76 | personally worked with co-op students, helping them learn new technologies and techniques to jump-start their 77 | careers. 78 |
79 |Since beginning Android development I've been consistent with my quality of work, always 80 | taking the time to build things to a high quality standard. I’ve consistently delivered features, updates, and 81 | products on time, under my own time management. Given a clear goal with a timeline to work towards I can manage 82 | myself or a small team to meet (and when necessary exceed) the given task. 83 |
84 |Right now I’m looking to find a full-time Android development position in Vancouver, or 85 | a 86 | full-time remote position. 87 |
88 | 89 | 90 | 91 | 92 |During my time at Airy my responsibility was to develop and maintain our Android client. 107 | I 108 | joined very early on when the team was only 5 people in May 2016. The company originally focused on creating an 109 | app for consumers to direct message any business like they would their friends. This approach required us 110 | releasing 2 Android apps, one for consumers and one for businesses—we built both using the same core code base, 111 | utilizing build variants.
112 |Since then, the product shifted to find a fit in the consumer-to-business messaging 113 | market. We ditched the consumer app, and today Airy is a platform that conglomerates 3rd party messaging 114 | services 115 | into one shared inbox. We were one of the first to create a CRM for a business to manage conversations with 116 | their 117 | customers over their existing channels. Airy innovated on many fronts, from complex messaging automations, to 118 | being the first to provide an in-app template builder for Facebook Messenger templates.
119 |Our Airy Messenger app allows businesses to communicate in real time with their 120 | customers. 121 | Our feature set focused on making messaging easy for businesses, and convenient for consumers. We had rich media 122 | messages, and complex template support that works natively in Facebook Messenger, among lots more features 123 | around 124 | CRM like tagging, and filtering.
125 |The Android app was complex, not complicated. I was very strict about what to build new, 126 | and what to make more robust and reusable. I'm proud of how understandable the codebase is; it's easy to 127 | maintain & keeps consistent standards. There is also not much bloat through regular refactoring efforts and our 128 | 1st party 129 | solutions for problems, otherwise solved by oft bloated dependencies. All in all our app was responsive, 130 | dependable, and small (around 8mb), which was my goal from the start.
131 |I would be happy to talk more about specific libraries and techniques we used at Airy. 132 | To 133 | write it all here would just be too long.
134 | 135 | 136 | 137 |I originally joined when Flow was a project within Metalab, I worked for Metalab to 149 | convert their mobile web version of Flow to a native Android app. After some time Flow got too big 150 | and split off into its own company and I went with it to continue my work there (we were in the same office so 151 | the split wasn’t so dramatic).
152 |At Flow I was in charge of our Tasks and Chat apps for Android. Both 153 | respectable full 154 | applications with their own unique challenges—technologies, and feature sets. For the most part I was the only 155 | Android developer, however I worked with other senior Android developers along the way for months at a time. I 156 | worked to find and hire 2 senior Android engineers during my time at Flow (both went on to Metalab). While 157 | working for Flow my role shifted to team lead, we would exchange ideas and techniques, review code, and I would 158 | manage 159 | deadlines. I also mentored a co-op student (who later joined full time) one-on-one for some months helping them 160 | to ship production code, and gain confidence in our code base.
161 |Our apps were quite interesting to work on. Having two (sometimes 4 for 162 | debug/production) apps installed on a single device is a fun challenge on Android. Doing things like that forces 163 | you to learn the platform even deeper than normal, digging in to AccountManager to share auth for example. The 164 | technical challenges and pace of work forced me to cut out unnecessary tasks and keep a sharp focus on my time. 165 |
166 | 167 | 168 | 169 |