1<!DOCTYPE html> 2<html lang="en-US"> 3<head profile="http://gmpg.org/xfn/11"> 4<meta http-equiv="X-UA-Compatible" content="IE=10" /> 5<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 6<title>JavaScript » desalasworks | Steven de Salas | Full Stack Developer, DevOps, Databases, JavaScript Developer, Interface Designer - AWS C# .NET Framework, Javascript</title> 7<meta name="robots" content="index,follow" /> 8<meta name="keywords" content="steven, de salas, salas, desalas, desalasworks, user, interface, development, websites, design, web, applications" /> 9<meta name="description" content="Steven de Salas is a freelance web application developer based out of Melbourne. Steven specialises in creating rich interfaces and low-latency backend storage / data feeds for web and mobile platforms featuring financial data" /> 10<meta name="viewport" content="width=device-width, initial-scale=1"> 11<meta name="google-site-verification" content="t_Q03qyW4Qql_p5ZxuXE4F7U7qhAt5Eb64O-CE8fdmA" /> 12<link rel="stylesheet" href="//desalasworks.com/wp-content/themes/desalasworks.v1.7/style.css?id=3" type="text/css" media="all" /> 13<link rel="pingback" href="//desalasworks.com/xmlrpc.php" /> 14<link rel="shortcut icon" href="//desalasworks.com/wp-content/themes/desalasworks.v1.7/favicon.ico" type="image/x-icon" />
15<script type="text/javascript" src="//desalasworks.com/wp-content/themes/desalasworks.v1.7/js/tinybox.js"></script>
vendor: 2 bytes, line 15
15
16<script type="text/javascript" src="//desalasworks.com/wp-content/themes/desalasworks.v1.7/js/common.js"></script>
16 17<style type="text/css"> 18img, div, a { behavior: url(//desalasworks.com/wp-content/themes/desalasworks.v1.7/js/iepngfix.htc) } 19</style> 20 21<meta name='robots' content='max-image-preview:large' /> 22<link rel="alternate" type="application/rss+xml" title="desalasworks » JavaScript Tag Feed" href="/skills/javascript/feed/" />
23<script type="text/javascript"> 24window._wpemojiSettings = {"baseUrl":"https:\/\/s.w.org\/images\/core\/emoji\/14.0.0\/72x72\/","ext":".png","svgUrl":"https:\/\/s.w.org\/images\/core\/emoji\/14.0.0\/svg\/","svgExt":".svg","source":{"concatemoji":"\/\/desalasworks.com\/wp-includes\/js\/wp-emoji-release.min.js?ver=6.1.14"}}; 25/*! This file is auto-generated */ 26!function(e,a,t){var n,r,o,i=a.createElement("canvas"),p=i.getContext&&i.getContext("2d");function s(e,t){var a=String.fromCharCode,e=(p.clearRect(0,0,i.width,i.height),p.fillText(a.apply(this,e),0,0),i.toDataURL());return p.clearRect(0,0,i.width,i.height),p.fillText(a.apply(this,t),0,0),e===i.toDataURL()}function c(e){var t=a.createElement("script");t.src=e,t.defer=t.type="text/javascript",a.getElementsByTagName("head")[0].appendChild(t)}for(o=Array("flag","emoji"),t.supports={everything:!0,everythingExceptFlag:!0},r=0;r<o.length;r++)t.supports[o[r]]=function(e){if(p&&p.fillText)switch(p.textBaseline="top",p.font="600 32px Arial",e){case"flag":return s([127987,65039,8205,9895,65039],[127987,65039,8203,9895,65039])?!1:!s([55356,56826,55356,56819],[55356,56826,8203,55356,56819])&&!s([55356,57332,56128,56423,56128,56418,56128,56421,56128,56430,56128,56423,56128,56447],[55356,57332,8203,56128,56423,8203,56128,56418,8203,56128,56421,8203,56128,56430,8203,56128,56423,8203,56128,56447]);case"emoji":return!s([129777,127995,8205,129778,127999],[129777,127995,8203,129778,127999])}return!1}(o[r]),t.supports.everything=t.supports.everything&&t.supports[o[r]],"flag"!==o[r]&&(t.supports.everythingExceptFlag=t.supports.everythingExceptFlag&&t.supports[o[r]]);t.supports.everythingExceptFlag=t.supports.everythingExceptFlag&&!t.supports.flag,t.DOMReady=!1,t.readyCallback=function(){t.DOMReady=!0},t.supports.everything||(n=function(){t.readyCallback()},a.addEventListener?(a.addEventListener("DOMContentLoaded",n,!1),e.addEventListener("load",n,!1)):(e.attachEvent("onload",n),a.attachEvent("onreadystatechange",function(){"complete"===a.readyState&&t.readyCallback()})),(e=t.source||{}).concatemoji?c(e.concatemoji):e.wpemoji&&e.twemoji&&(c(e.twemoji),c(e.wpemoji)))}(window,document,window._wpemojiSettings); 27</script>
27 28<style type="text/css"> 29img.wp-smiley, 30img.emoji { 31 display: inline !important; 32 border: none !important; 33 box-shadow: none !important; 34 height: 1em !important; 35 width: 1em !important; 36 margin: 0 0.07em !important; 37 vertical-align: -0.1em !important; 38 background: none !important; 39 padding: 0 !important; 40} 41</style> 42 <link rel='stylesheet' id='wp-block-library-css' href='//desalasworks.com/wp-includes/css/dist/block-library/style.min.css?ver=6.1.14' type='text/css' media='all' /> 43<link rel='stylesheet' id='classic-theme-styles-css' href='//desalasworks.com/wp-includes/css/classic-themes.min.css?ver=1' type='text/css' media='all' /> 44<style id='global-styles-inline-css' type='text/css'> 45body{--wp--preset--color--black: #000000;--wp--preset--color--cyan-bluish-gray: #abb8c3;--wp--preset--color--white: #ffffff;--wp--preset--color--pale-pink: #f78da7;--wp--preset--color--vivid-red: #cf2e2e;--wp--preset--color--luminous-vivid-orange: #ff6900;--wp--preset--color--luminous-vivid-amber: #fcb900;--wp--preset--color--light-green-cyan: #7bdcb5;--wp--preset--color--vivid-green-cyan: #00d084;--wp--preset--color--pale-cyan-blue: #8ed1fc;--wp--preset--color--vivid-cyan-blue: #0693e3;--wp--preset--color--vivid-purple: #9b51e0;--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple: linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%);--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan: linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%);--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange: linear-gradient(135deg,rgba(252,185,0,1) 0%,rgba(255,105,0,1) 100%);--wp--preset--gradient--luminous-vivid-orange-to-vivid-red: linear-gradient(135deg,rgba(255,105,0,1) 0%,rgb(207,46,46) 100%);--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray: linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%);
45--wp--preset--gradient--cool-to-warm-spectrum: linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%);--wp--preset--gradient--blush-light-purple: linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%);--wp--preset--gradient--blush-bordeaux: linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%);--wp--preset--gradient--luminous-dusk: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%);--wp--preset--gradient--pale-ocean: linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%);--wp--preset--gradient--electric-grass: linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%);--wp--preset--gradient--midnight: linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%);--wp--preset--duotone--dark-grayscale: url('#wp-duotone-dark-grayscale');--wp--preset--duotone--grayscale: url('#wp-duotone-grayscale');--wp--preset--duotone--purple-yellow: url('#wp-duotone-purple-yellow');--wp--preset--duotone--blue-red: url('#wp-duotone-blue-red');--wp--preset--duotone--midnight: url('#wp-duotone-midnight');--wp--preset--duotone--magenta-yellow: url('#wp-duotone-magenta-yellow');--wp--preset--duotone--purple-green: url('#wp-duotone-purple-green');--wp--preset--duotone--blue-orange: url('#wp-duotone-blue-orange');--wp--preset--font-size--small: 13px;--wp--preset--font-size--medium: 20px;--wp--preset--font-size--large: 36px;--wp--preset--font-size--x-large: 42px;--wp--preset--spacing--20: 0.44rem;--wp--preset--spacing--30: 0.67rem;--wp--preset--spacing--40: 1rem;--wp--preset--spacing--50: 1.5rem;--wp--preset--spacing--60: 2.25rem;--wp--preset--spacing--70: 3.38rem;--wp--preset--spacing--80: 5.06rem;}:where(.is-layout-flex){gap: 0.5em;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}body .is-layout-flex > *{margin: 0;}:where(.wp-block-columns.is-layout-flex){gap: 2em;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-color{color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-pale-pink-color{color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-color{color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-color{color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-color{color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-color{color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-color{color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-color{color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-color{color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-color{color: var(--wp--preset--color--vivid-purple) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-background-color{background-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-pale-pink-background-color{background-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-background-color{background-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-background-color{background-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-background-color{background-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-background-color{background-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-background-color{background-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-background-color{background-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-background-color{background-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-background-color{background-color: var(--wp--preset--color--vivid-purple) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-border-color{border-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-pale-pink-border-color{border-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-border-color{border-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-border-color{border-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-border-color{border-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-border-color{border-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-border-color{border-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-border-color{border-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-border-color{border-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-border-color{border-color: var(--wp--preset--color--vivid-purple) !important;}.has-vivid-cyan-blue-to-vivid-purple-gradient-background{background: var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple) !important;}.has-light-green-cyan-to-vivid-green-cyan-gradient-background{background: var(--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan) !important;}.has-luminous-vivid-amber-to-luminous-vivid-orange-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange) !important;}.has-luminous-vivid-orange-to-vivid-red-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-orange-to-vivid-red) !important;}
45.has-very-light-gray-to-cyan-bluish-gray-gradient-background{background: var(--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray) !important;}.has-cool-to-warm-spectrum-gradient-background{background: var(--wp--preset--gradient--cool-to-warm-spectrum) !important;}.has-blush-light-purple-gradient-background{background: var(--wp--preset--gradient--blush-light-purple) !important;}.has-blush-bordeaux-gradient-background{background: var(--wp--preset--gradient--blush-bordeaux) !important;}.has-luminous-dusk-gradient-background{background: var(--wp--preset--gradient--luminous-dusk) !important;}.has-pale-ocean-gradient-background{background: var(--wp--preset--gradient--pale-ocean) !important;}.has-electric-grass-gradient-background{background: var(--wp--preset--gradient--electric-grass) !important;}.has-midnight-gradient-background{background: var(--wp--preset--gradient--midnight) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-medium-font-size{font-size: var(--wp--preset--font-size--medium) !important;}.has-large-font-size{font-size: var(--wp--preset--font-size--large) !important;}.has-x-large-font-size{font-size: var(--wp--preset--font-size--x-large) !important;} 46.wp-block-navigation a:where(:not(.wp-element-button)){color: inherit;} 47:where(.wp-block-columns.is-layout-flex){gap: 2em;} 48.wp-block-pullquote{font-size: 1.5em;line-height: 1.6;} 49</style> 50<meta name="generator" content="WordPress 6.1.14" /> 51</head> 52<body class="archive tag tag-javascript tag-28"> 53<div class="header-wrap"> 54 <a id="responsive-nav" onmouseup="toggleClass('nav', 'show')">=</a> 55 <h1 class="logo"><a href="https://desalasworks.com/" title="desalasworks">desalasworks</a><span class="presents"> presents:</span></h1> 56 <h2 class="description">a portfolio of work by steven de salas</h2> 57 <ul id="nav"> 58 <li class="post_tag"><a href="https://desalasworks.com">Skills</a><ul> <li class="cat-item cat-item-28 current-cat"><a aria-current="page" href="/skills/javascript/">JavaScript</a> 59</li> 60 <li class="cat-item cat-item-19"><a href="/skills/sql-server/">SQL Server</a> 61</li> 62 <li class="cat-item cat-item-20"><a href="/skills/c/">C#</a> 63</li> 64 <li class="cat-item cat-item-29"><a href="/skills/performance-optimization/">Performance Optimization</a> 65</li> 66 <li class="cat-item cat-item-45"><a href="/skills/php/">PHP</a> 67</li> 68 <li class="cat-item cat-item-31"><a href="/skills/css3/">CSS3</a> 69</li> 70 <li class="cat-item cat-item-30"><a href="/skills/html5/">HTML5</a> 71</li> 72 <li class="cat-item cat-item-58"><a href="/skills/net-framework/">NET Framework</a> 73</li> 74 <li class="cat-item cat-item-52"><a href="/skills/android/">Android</a> 75</li> 76 <li class="cat-item cat-item-71"><a href="/skills/node-js/">Node.js</a> 77</li> 78 <li class="cat-item cat-item-48"><a href="/skills/iphone/">iPhone</a> 79</li> 80 <li class="cat-item cat-item-57"><a href="/skills/distributed-computing/">Distributed Computing</a> 81</li> 82</ul></li> <li class="categories"><a href="https://desalasworks.com">Categories</a><ul> <li class="cat-item cat-item-14"><a href="/category/code/">Code</a> 83</li> 84 <li class="cat-item cat-item-5"><a href="/category/websites/">Websites</a> 85</li> 86 <li class="cat-item cat-item-3"><a href="/category/web-applications/">Web Applications</a> 87</li> 88 <li class="cat-item cat-item-17"><a href="/category/ui/">UI</a> 89</li> 90 <li class="cat-item cat-item-15"><a href="/category/mobile/">Mobile</a> 91</li> 92 <li class="cat-item cat-item-10"><a href="/category/cms/">CMS</a> 93</li> 94 <li class="cat-item cat-item-83"><a href="/category/cloud/">Cloud</a> 95</li> 96 <li class="cat-item cat-item-6"><a href="/category/graphic-design/">Graphic Design</a> 97</li> 98 <li class="cat-item cat-item-87"><a href="/category/microservices/">Microservices</a> 99</li> 100 <li class="cat-item cat-item-62"><a href="/category/competitions/">Competitions</a> 101</li> 102 <li class="cat-item cat-item-117"><a href="/category/startups/">Startups</a> 103</li> 104 <li class="cat-item cat-item-77"><a href="/category/iot/">IoT</a> 105</li> 106</ul></li> <li><a href="#contact">Contact</a></li> 107 <li><a href="#more">More</a></li> 108 </ul> 109</div>
110<script type="text/javascript"> 111function toggleClass(id, cls) { 112 var dom = document.getElementById(id); 113 if (dom) { 114 var current = dom.className; 115 var pat = new RegExp("\\b" + cls + "\\b"); 116 if (current.match(pat)) dom.className = current.replace(pat, ''); 117 else dom.className = dom.className + " " + cls; 118 } 119} 120</script>
120 121<div class="body-wrap" style="background-color: #f5f5f5;"> 122<div class="body-main"> 123 <div id="container" class="clearfix"> 124 125 126 <h3 class="page-title"> 127 LetMePark </h3> 128 129 <div class="container-box"> 130 131 <div class="tags"> 132 <a href="/skills/api/" rel="tag">API</a> <a href="/skills/aws/" rel="tag">AWS</a> <a href="/skills/devops/" rel="tag">DevOps</a> <a href="/skills/docker/" rel="tag">Docker</a> <a href="/skills/ecs/" rel="tag">ECS</a> <a href="/skills/gitlab/" rel="tag">Gitlab</a> <a href="/skills/google-cloud-platform/" rel="tag">Google Cloud Platform</a> <a href="/skills/javascript/" rel="tag">JavaScript</a> <a href="/skills/lambda/" rel="tag">Lambda</a> <a href="/skills/leadership/" rel="tag">Leadership</a> <a href="/skills/microservices/" rel="tag">Microservices</a> <a href="/skills/mongodb/" rel="tag">MongoDB</a> <a href="/skills/mqtt/" rel="tag">MQTT</a> <a href="/skills/nginx/" rel="tag">Nginx</a> <a href="/skills/node-js/" rel="tag">Node.js</a> <a href="/skills/nuxt/" rel="tag">Nuxt</a> <a href="/skills/payment-processing/" rel="tag">Payment Processing</a> <a href="/skills/voice-assistants/" rel="tag">Voice Assistants</a> <a href="/skills/vue-js/" rel="tag">
132Vue.js</a> </div> 133 134 <div class="archives-page post-2271 post type-post status-publish format-standard hentry category-cloud category-microservices category-mobile category-startups category-voice tag-api tag-aws tag-devops tag-docker tag-ecs tag-gitlab tag-google-cloud-platform tag-javascript tag-lambda tag-leadership tag-microservices tag-mongodb tag-mqtt tag-nginx tag-node-js tag-nuxt tag-payment-processing tag-voice-assistants tag-vue-js"> 135 <!--h3 id="post-2271" class="post-title"><a href="/letmepark/" rel="bookmark" >LetMePark</a></h3--> 136 <div class="entry"> 137 <p><em>Building smart cities by connecting vehicles with their environment.</em></p> 138<p><img decoding="async" loading="lazy" class="alignnone size-full wp-image-2486" src="//desalasworks.com/wp-content/uploads/420x337-1.jpg" alt="" width="420" height="337" srcset="//desalasworks.com/wp-content/uploads/420x337-1.jpg 420w, //desalasworks.com/wp-content/uploads/420x337-1-300x241.jpg 300w" sizes="(max-width: 420px) 100vw, 420px" /></p> 139<h3>Founding Engineer</h3> 140<p>Steven had a pivotal role designing and building the core platform infrastructure for this startup in the urban mobility space. Maximizing IT resource use and speed-to-market with low-cost, stable, high-performance infrastructure.</p> 141<p>As the platform grew in complexity and demand, Steven became CTO in order to encompass the leadership of a growing technology team of engineers, designers and testers.</p> 142<p><img decoding="async" loading="lazy" class="alignnone size-full wp-image-2301" src="//desalasworks.com/wp-content/uploads/lmp-offering3.jpg" alt="" width="1300" height="837" srcset="//desalasworks.com/wp-content/uploads/lmp-offering3.jpg 1300w, //desalasworks.com/wp-content/uploads/lmp-offering3-300x193.jpg 300w, //desalasworks.com/wp-content/uploads/lmp-offering3-768x494.jpg 768w, //desalasworks.com/wp-content/uploads/lmp-offering3-600x386.jpg 600w" sizes="(max-width: 1300px) 100vw, 1300px" /></p> 143<h3>Backend Architecture & Integration</h3> 144<p><img decoding="async" loading="lazy" class="alignnone size-full wp-image-2308" src="//desalasworks.com/wp-content/uploads/cloud-computing-architecture.notext.jpg" alt="" width="750" height="302" srcset="//desalasworks.com/wp-content/uploads/cloud-computing-architecture.notext.jpg 750w, //desalasworks.com/wp-content/uploads/cloud-computing-architecture.notext-300x121.jpg 300w, //desalasworks.com/wp-content/uploads/cloud-computing-architecture.notext-600x242.jpg 600w" sizes="(max-width: 750px) 100vw, 750px" /></p> 145<p>Integration with multiple third-party parking provider services, allowing real-time price and availability checks, bookings and automated parking notifications.</p> 146<p>The skills used were: Google Cloud, Amazon Web Services, Build Automation, JavaScript, Node.js, API Development, MongoDB, DevOps, Docker, ECS, Gitlab, Security/Cryptography, Bash/Unix scripting, MQTT, Nginx, PCI Payment Processing (Paycomet TPV) and Test Automation.</p> 147<h3>Web & Mobile Hybrid App</h3> 148<p>Development and enhancements to a Vue.js (Nuxt) web app doubling up as a hybrid web-view app for iOS and Android platforms, allowing clients to carry their parking experience anywhere inside their pocket.</p> 149<h3><img decoding="async" loading="lazy" class="alignnone wp-image-2311" src="//desalasworks.com/wp-content/uploads/webapp2.png" alt="" width="450" height="420" srcset="//desalasworks.com/wp-content/uploads/webapp2.png 755w, //desalasworks.com/wp-content/uploads/webapp2-300x280.png 300w, //desalasworks.com/wp-content/uploads/webapp2-600x560.png 600w" sizes="(max-width: 450px) 100v
149w, 450px" /></h3> 150<h3>Voice Assistant Integration</h3> 151<p>Steven worked on integrating the Alexa Skill that allows users to search and book whilst driving their car.</p> 152<p>This included providing all necessary backend APIs and authentication/user flows as well as the technology platform capable of live multi-provider search and bookings, including live monitoring.</p> 153<p><img decoding="async" loading="lazy" class="alignnone size-full wp-image-2313" src="//desalasworks.com/wp-content/uploads/alexa-mobile-concept.jpg" alt="" width="800" height="533" srcset="//desalasworks.com/wp-content/uploads/alexa-mobile-concept.jpg 800w, //desalasworks.com/wp-content/uploads/alexa-mobile-concept-300x200.jpg 300w, //desalasworks.com/wp-content/uploads/alexa-mobile-concept-768x512.jpg 768w, //desalasworks.com/wp-content/uploads/alexa-mobile-concept-600x400.jpg 600w" sizes="(max-width: 800px) 100vw, 800px" /></p> 154<h3>Payment Processing</h3> 155<p>Steven also integrated the customer Credit Card capture and payment processing flows for both bookings and automated parkings.</p> 156<p><img decoding="async" loading="lazy" class="alignnone size-full wp-image-2318" src="//desalasworks.com/wp-content/uploads/payment.png" alt="" width="760" height="450" srcset="//desalasworks.com/wp-content/uploads/payment.png 760w, //desalasworks.com/wp-content/uploads/payment-300x178.png 300w, //desalasworks.com/wp-content/uploads/payment-600x355.png 600w" sizes="(max-width: 760px) 100v
156w, 760px" /></p> 157<h2>Feedback</h2> 158<p><a href="https://www.linkedin.com/in/ulianatorkunova?lipi=urn%3Ali%3Apage%3Ad_flagship3_profile_view_base_recommendations_details%3BShsjHE4CSfePxCQIWGhIvg%3D%3D"><img decoding="async" loading="lazy" class="alignnone size-full wp-image-2474" src="//desalasworks.com/wp-content/uploads/uliana.png" alt="" width="430" height="113" srcset="//desalasworks.com/wp-content/uploads/uliana.png 430w, //desalasworks.com/wp-content/uploads/uliana-300x79.png 300w" sizes="(max-width: 430px) 100vw, 430px" /></a></p> 159<blockquote><p>We were lucky to find Steven. He was able to work under time-pressure and with a constantly changing set of priorities to deliver much of the core IT infrastructure for our Mobility-based startup. Later growing in his role to encompass the leadership of a growing development team for our unique solution.</p> 160<p>Apart from being disciplined, motivated and highly knowledgeable, he barely had much of an ego, always willing to help junior developers and ‘wear many hats’ as needed, explaining technical problems and opportunities in plain English to make sure that the leadership was informed at every step of the choices we had available to us.</p></blockquote> 161 </div> 162 163 </div> 164 165 </div><!-- .container-box --> 166 167 168 <h3 class="page-title"> 169 Asynchronous Neural Networks in JavaScript </h3> 170 171 <div class="container-box"> 172 173 <div class="tags"> 174 <a href="/skills/javascript/" rel="tag">JavaScript</a> <a href="/skills/machine-learning/" rel="tag">Machine Learning</a> <a href="/skills/robotics/" rel="tag">Robotics</a> </div> 175 176 <div class="archives-page post-1865 dw-article type-dw-article status-publish hentry category-code category-iot tag-javascript tag-machine-learning tag-robotics"> 177 <!--h3 id="post-1865" class="post-title"><a href="/article/asynchronous-neural-networks-in-javascript/" rel="bookmark" >Asynchronous Neural Networks in JavaScript</a></h3--> 178 <div class="entry"> 179 <p><img decoding="async" loading="lazy" class="size-large wp-image-2166 aligncenter" src="//desalasworks.com/wp-content/uploads/IMG_20170608_174532234-600x338.jpg" alt="" width="600" height="338" srcset="//desalasworks.com/wp-content/uploads/IMG_20170608_174532234-600x338.jpg 600w, //desalasworks.com/wp-content/uploads/IMG_20170608_174532234-300x169.jpg 300w, //desalasworks.com/wp-content/uploads/IMG_20170608_174532234-768x432.jpg 768w" sizes="(max-width: 600px) 100vw, 600px" /></p> 180<p>For the past few months Iâve been trying to get <a href="http://github.com/sdesalas/robotics">some of my robots</a> to roam around the house with a mind of their own, learning as they go. It turns out that the task is harder than I thought.</p> 181<p>The main issue is paralell signal processing.</p> 182<p>To illustrate: a single robot will have usually 3-5 sensors that collect information on the world around it. I’m particularly interested in ambient light as i want it to be able to recharge its batteries with a mini solar panel if it needs to, but i also have some useful options for collision detection, orientation and WiFi triangulation.</p> 183<p><img decoding="async" loading="lazy" class="size-large wp-image-2164 aligncenter" src="//desalasworks.com/wp-content/uploads/basic-premise-600x351.png" alt="" width="600" height="351" srcset="//desalasworks.com/wp-content/uploads/basic-premise-600x351.png 600w, //desalasworks.com/wp-content/uploads/basic-premise-300x175.png 300w, //desalasworks.com/wp-content/uploads/basic-premise.png 686w" sizes="(max-width: 600px) 100vw, 600px" /></p> 184<h3>First attempt, enter the matrix</h3> 185<p>I originally attempted a weighted relational matrix of inputs to outputs. Like a chequers board where each square starts off with one piece that represents the strength of the relationship between an input and an output. To simulate learning, I would stack extra pieces on a square, for example to reinforce past choices, making it more likely to get picked in subsequent iterations. I called the engine <a href="https://github.com/sdesalas/fusspot/tree/7275a26f52e690dc8d54ce7c784e7f724be1901a">fusspot</a>, which I thought was a suitable name given that this algorithm could be trained but in the end, choices were always fuzzy and random.</p> 186<p><img decoding="async" src="https://raw.githubusercontent.com/sdesalas/fusspot/67619b6af2224606899c54497f540d2bde404417/learning.png" alt="chequers" /></p> 187<p>
187This worked, in a fashion. I could train the engine by modifying the strength of each relationship, making subsequent choices more likely to be biased by training, but it was a terrible solution when trying to implement it in practice.</p> 188<p>The main problem is that this solution does not take context into account, and it turns out context is everything â you canât rely on a single input channel (such as IR sensor) to determine the choices a robot can make (stop, turn, keep going), it takes a wholistic approach that integrates data from all the sensors at the same time.</p> 189<blockquote><p>Context is everything. You can’t rely on a single input channel, it takes a wholistic approach that integrates data from all the sensors <em>at the same time</em>.</p></blockquote> 190<p>I got to a point where trying to break down the problem further wasnât getting me anywhere. So I decided to look around and see what other people had done.</p> 191<h3>Video games and artificial gardening.</h3> 192<p>By this point I had done a bit of reading on <a href="https://en.wikipedia.org/wiki/Artificial_neural_network">Artificial Neural Networks</a> and decided that this was roughly where I was heading.</p> 193<p>This led me into the world of video games. Iâve played plenty of games myself and know that game developers are at the leading edge of AI.</p> 194<p><a href="https://en.wikipedia.org/wiki/Artificial_intelligence_(video_games)">The wikipedia article</a> inspired me to look a bit more into a game called <a href="https://aigamedev.com/open/review/creatures-ai/">Creatures</a> written by a guy called <a href="https://en.wikipedia.org/wiki/Steve_Grand_(roboticist)">Steve Grand</a>. Steve dropped off the face of the earth a few years ago through a self-inflicted <a href="https://www.kickstarter.com/projects/1508284443/grandroids-real-artificial-life-on-your-pc">kickstarter project</a> <a href="https://docs.google.com/document/d/1WDKL-2vihuaRLHjsZCxtr6qj0oCe8lV82inwmCVrjBs/pub">gone wrong</a>.</p> 195<p><img decoding="async" loading="lazy" class="size-large wp-image-2165 aligncenter" src="//desalasworks.com/wp-content/uploads/creatures.stevegrand-600x336.png" alt="" width="600" height="336" srcset="//desalasworks.com/wp-content/uploads/creatures.stevegrand-600x336.png 600w, //desalasworks.com/wp-content/uploads/creatures.stevegrand-300x168.png 300w, //desalasworks.com/wp-content/uploads/creatures.stevegrand.png 713w" sizes="(max-width: 600px) 100vw, 600px" /></p> 196<p>But before he did so, he wrote an excellent book called <a href="http://www.goodreads.com/book/show/304444.Creation">Creation, Life and how to make it</a>, where he details some of the techniques he used, as well a zen-like philosophical outlook on artificial life powerful enough to blow my s
196ocks off and turn upside down all my notions of what AI is and should be about:</p> 197<p>All life, both biological and artificial, are series of emergent patterns that persist longer than others, in other words, certain patterns of interaction between components (cells usually, though these are also made of patterns of interaction between cellular components and so on to the atomic level) happen to be more successful in their environment and thus persist through time.</p> 198<p>The beauty here is that life is a set of emergent patterns, rather than the components of which its made of, or the behaviour that arises from their interaction. The best humans can strive for is <em>creating the conditions</em> that would allow such patterns to emerge, for example by defining the underlying components and encouraging certain patterns of behaviour over others. Think of it as artificial gardening..</p> 199<blockquote><p>The beauty here is that life is a set of emergent patterns, rather than the components of which its made of, or the behaviour that arises from their interaction.</p></blockquote> 200<h3>Neural Networks, Rebooted.</h3> 201<p>So. I started modelling a neural network in JavaScript.</p> 202<p>Its not hard. Just an array of nodes, each containing another array of links to other nodes. With the added catch that each link to another node has a weight between 0 and 1, which determines if the onward connection should be fired during a chain-reaction event. My <a href="https://github.com/sdesalas/fusspot/blob/45418ede3548f03501102eb69c5ab3f5481c331c/NeuralNetwork.js">first commit</a> was 100 lines long. 60 if you remove comments and whitespacing.</p> 203<p>Since I was trying to accurately model the biological system in our own brains (rather than a mathematical abstraction), I noticed my own gut telling me to add a slight delay between firing neurons. It seems natural really, it generally <a href="https://en.wikipedia.org/wiki/Refractory_period_(physiology)">takes a whole millisecond</a> to fire off the next neuron inside our brains, so the signal is never going to travel all the way across the brain instantly.</p> 204<p><img decoding="async" loading="lazy" class="size-full wp-image-2171 aligncenter" src="//desalasworks.com/wp-content/uploads/Neuron.Class.png" alt="" width="432" height="247" srcset="//desalasworks.com/wp-content/uploads/Neuron.Class.png 432w, //desalasworks.com/wp-content/uploads/Neuron.Class-300x172.png 300w" sizes="(max-width: 432px) 100vw, 432px" /></p> 205<p>This solved my problem rather neatly. I had several input streams from each sensor that needed to be integrated, and I needed something that could be trained on a particular input, <em>while taking into account all the other inputs</em>, or even a changing set of inputs<em>. </em>Something that could behave more like a normal brain and create an intricately timed set of reactions to an input (think of the muscles involved in picking up your phone), or integrate an input with itself (by having a signal that loops around).</p> 206<blockquote><p>I noticed my own gut telling me to add a slight delay between firing neurons. It seems natural really … the signal its never going to travel all the way across the brain instantly.</p></blockquote> 207<p>I also researched the interleaving of signal patterns in real neurons and came up with a spiking model that combined multiple upstream signals before firing a signal downstream.</p> 208<p><img decoding="async" loading="lazy" class="alignnone size-full wp-image-2206" src="//desalasworks.com/wp-content/uploads/signal-buildup.gif" alt="" width="600" height="323" /></p> 209<h3>Chaos and the game of life</h3> 210<p>This was all confusing as hell so I needed to visualize my network in order to understand what it was doing.</p> 211<p>Thankfully JavaScript is a great language for that, there are tons of libraries out there for visualization and thanks to V8 and the NodeJS ecosystem you can run the same code on the browser as you would a command line executable. I used <a href="http://browserify.org/">browserify</a> to require my modules on a browser page and <a href="http://visjs.org/">viz.js</a> to perform visualizations using HTML5 <code><canvas/></code>.</p> 212<p>The result was intriguing. I noticed all sorts of patterns, travelling waves, and oscillations taking place when a neuron was fired. It was utter chaos but it was also mesmerizing.</p> 213<p><a href="https://sdesalas.github.io/fusspot/?shape=sausage&size=120"><img decoding="async" loading="lazy" class="alignnone size-full wp-image-2055" src="//des
213alasworks.com/wp-content/uploads/fusspot.gif" alt="" width="611" height="624" /></a></p> 214<blockquote><p>The result was intriguing. It was utter chaos, but it was also mesmerizing.</p></blockquote> 215<p>I felt I was on to something. After all <a href="http://musetude.com/brain-r-d/brain-frequencies/">electrical wave patterns</a> are an inherent part of the human brain. So why do prevailing models of neural networks ignore them?</p> 216<p>It turns out that I was building something called a <a href="https://en.wikipedia.org/wiki/Spiking_neural_network">Spiking Neural Network</a>, the so-called 3rd generation of neural networks that more closely emulate their biological cousins, and which encode information in patterns of signals.</p> 217<p><img decoding="async" loading="lazy" class="alignnone size-full wp-image-2177" src="//desalasworks.com/wp-content/uploads/3rd-gen-neurons.gif" alt="" width="600" height="384" /></p> 218<p>I tried different network shapes, a ball, a sausage and a dounught. All produced interesting variations. Jonathan, a colleague at work, said it reminded him of <a href="https://en.wikipedia.org/wiki/Conway's_Game_of_Life">Conwayâs Game of Life</a>. I had to agree with him.</p> 219<h3>Now in 3D</h3> 220<p>It took some extra modelling to come up with a good visualization that would let me understand what was going on, my original 2D library could only visualize around 300 neurons before falling over, using 3D I could increase that to a few thousands.</p> 221<p><a href="https://github.com/sdesalas/botbrains"><img decoding="async" src="https://raw.githubusercontent.com/sdesalas/botbrains/master/brain-3d.gif" /></a></p> 222<p>I called the newly born library <a href="https://github.com/sdesalas/botbrains">botbrains</a>, and used a combination of a <a href="https://www.npmjs.com/package/@sdesalas/3d-force-graph">3D Force Graph</a> in WebGL for rendering the network, <a href="https://en.wikipedia.org/wiki/WebSocket">WebSockets</a> for client/server interaction, and <a href="https://nodejs.org">node.js</a> for running the neural network on the back-end so it could integrate via USB Serial to the <a href="https://en.wikipedia.org/wiki/Arduino">Arduino</a> microcontroller running my motors and collecting data from sensors.</p> 223<p><strong>Machine Learning, for signal patterns.</strong></p> 224<p>Next challenge came from learning. I had to find a way to make the robot marginally smarter over time. After all its not very useful if your robot keeps bumping into the wall.</p> 225<p>Thankfully, plenty of people had already worked out the answer. Or rather two answers, really. You either use <a href="https://en.wikipedia.org/wiki/Reinforcement_learning">reinforcement</a> or <a href="https://www.youtube.com/watch?v=Ilg3gGewQ5U">back-propagation</a>. Both good approaches, but since my network was asynchronous, working out the <a href="https://en.wikipedia.org/wiki/Loss_function">loss-function</a> for back-propagation was a real headache (I had to backtrack in time taking into account competing signals) and besides I didn’t want to depend on layers as I wanted all the richness of signal patterns that different shapes of neural network could create.</p> 226<p>The biological answer here was reinforcement, aka “<a href="https://en.wikipedia.org/wiki/Long-term_potentiation">Long Term Potentiation</a>“. So I went with that. Later I found out that AlphaZero, the <a href="https://www.theguardian.com/technology/2017/dec/07/alphazero-google-deepmind-ai-beats-champion-program-teaching-itself-to-play-four-hours">current AI chess champion</a> used reinforcement too so that made me feel much better about my choice.</p> 227<p><img decoding="async" loading="lazy" class="alignnone size-full wp-image-2182" src="//desalasworks.com/wp-content/uploads/alphazero.jpg" alt="" width="794" height="556" srcset="//desalasworks.com/wp-content/uploads/alphazero.jpg 794w, //desalasworks.com/wp-content/uploads/alphazero-300x210.jpg 300w, //desalasworks.com/wp-content/uploads/alphazero-768x538.jpg 768w, //desalasworks.com/wp-content/uploads/alphazero-600x420.jpg 600w" sizes="(max-width: 794px) 100vw, 794px" /></p> 228<p>
228So, how to implement reinforcement learning?</p> 229<p>Well, its not so hard really, though it took several iterations. The basic premise was to reinforce synapses that fired recently whenever the robot does something good (like avoid an obstacle), and to weaken synapses that fired recently whenever something bad happens.</p> 230<p>Although pretty close, this wasn’t enough on its own, as the network would either overload or fail to connect altogether, so after a fair bit of testing I worked out that the most elegant model was to maintain the network weight constant by spreading out any positive or negative changes over the remaining unused synapses, so effectively during learning I would strengthen ‘successful’ connections while weakening all the other ones, reversing the process for negative reinforcement.</p> 231<p>I also added a tendency for synapse weights to decay towards their starting weight with each learning iteration, as well as strengthening unused synapses randomly whenever negative reinforcement is applied (ie, sometimes the solution to the problem can be found by using neural pathways that have never been used before).</p> 232<p><img decoding="async" loading="lazy" class="alignnone size-full wp-image-2179" src="//desalasworks.com/wp-content/uploads/learning.png" alt="" width="840" height="283" srcset="//desalasworks.com/wp-content/uploads/learning.png 840w, //desalasworks.com/wp-content/uploads/learning-300x101.png 300w, //desalasworks.com/wp-content/uploads/learning-768x259.png 768w, //desalasworks.com/wp-content/uploads/learning-600x202.png 600w" sizes="(max-width: 840px) 100vw, 840px" />Long-term memory was a problem, and still is to this day though its getting better.</p> 233<p>I added the ability for synapses to contain both ‘short-term’ and ‘long-term’ weights, both of which are influenced by the other. I pinched this idea from Steve Grand, which I found an incredibly simple and powerful solution to long-term memory.</p> 234<p><img decoding="async" loading="lazy" class="alignnone size-full wp-image-2180" src="//desalasworks.com/wp-content/uploads/long-term.png" alt="" width="615" height="337" srcset="//desalasworks.com/wp-content/uploads/long-term.png 615w, //des
234alasworks.com/wp-content/uploads/long-term-300x164.png 300w, //desalasworks.com/wp-content/uploads/long-term-600x329.png 600w" sizes="(max-width: 615px) 100vw, 615px" /></p> 235<p>Now all I had to do was to plug my robot into it.</p> 236<p> </p> 237 </div> 238 239 </div> 240 241 </div><!-- .container-box --> 242 243 244 <h3 class="page-title"> 245 DarklyLabs LaserWeb </h3> 246 247 <div class="container-box"> 248 249 <div class="tags"> 250 <a href="/skills/electron/" rel="tag">Electron</a> <a href="/skills/javascript/" rel="tag">JavaScript</a> <a href="/skills/react/" rel="tag">React</a> <a href="/skills/redux/" rel="tag">Redux</a> <a href="/skills/webgl/" rel="tag">WebGL</a> <a href="/skills/websockets/" rel="tag">WebSockets</a> </div> 251 252 <div class="archives-page post-2120 post type-post status-publish format-standard has-post-thumbnail hentry category-desktop-application category-iot category-ui tag-electron tag-javascript tag-react tag-redux tag-webgl tag-websockets"> 253 <!--h3 id="post-2120" class="post-title"><a href="/darklylabs-laserweb/" rel="bookmark" >DarklyLabs LaserWeb</a></h3--> 254 <div class="entry"> 255 <p>Steven was a one-man team building on top of open source “LaserWeb” software, automating and simplifying complex user flows to bring laser cutting to the beginner user.</p> 256<p><img decoding="async" loading="lazy" class="alignnone size-large wp-image-2121" src="https://desalasworks.com/wp-content/uploads/emblaser2-600x359.jpg" alt="" width="600" height="359" srcset="//desalasworks.com/wp-content/uploads/emblaser2-600x359.jpg 600w, //desalasworks.com/wp-content/uploads/emblaser2-300x180.jpg 300w, //desalasworks.com/wp-content/uploads/emblaser2-768x460.jpg 768w, //desalasworks.com/wp-content/uploads/emblaser2.jpg 800w" sizes="(max-width: 600px) 100vw, 600px" /></p> 257<p>This project involved full SDLC, building a simple and easy-to-use JavaScript wizard interface for the Emblaser2Â laser cutter & engraver, which went from design to delivery in 6 weeks.</p> 258<p>A challenging and very enjoyable project using NodeJS, JavaScript (ES6), Electron (Win/OSX Desktop app), Embedded, Computer Numerical Control (CNC/GCODE), WebWorkers, USB serialport, Github, CI / build automation (Travis, Appveyor), CSS, DOM Events, Webpack, React, Redux, Socket IO, WebGL 3D.</p> 259<p><img decoding="async" loading="lazy" class="alignnone wp-image-2122 size-full" src="https://desalasworks.com/wp-content/uploads/wizard.video_.gif" alt="" width="1004" height="646" /></p> 260<p> </p> 261 </div> 262 263 </div> 264 265 </div><!-- .container-box --> 266 267 268 <h3 class="page-title"> 269 SEEK Role Requirements </h3> 270 271 <div class="container-box"> 272 273 <div class="tags"> 274 <a href="/skills/angular-js/" rel="tag">Angular.js</a> <a href="/skills/aws/" rel="tag">AWS</a> <a href="/skills/c/" rel="tag">C#</a> <a href="/skills/javascript/" rel="tag">JavaScript</a> <a href="/skills/microservices/" rel="tag">Microservices</a> <a href="/skills/node-js/" rel="tag">Node.js</a> <a href="/skills/nosql/" rel="tag">NoSQL</a> </div> 275 276 <div class="archives-page post-2219 post type-post status-publish format-standard hentry category-cloud category-microservices category-web-applications tag-angular-js tag-aws tag-c tag-javascript tag-microservices tag-node-js tag-nosql"> 277 <!--h3 id="post-2219" class="post-title"><a href="/seek-role-requirements/" rel="bookmark" >SEEK Role Requirements</a></h3--> 278 <div class="entry"> 279 <p>Full Stack JavaScript contract role working on SEEK User Interfaces and AWS hosted APIâs.</p> 280<p>Steven joined the ‘Role Requirements’ team to help deliver great candidate matching. Millions of job applications are now getting scored and ranked for role suitability every year, resulting in significant time savings to users of the SEEK platform.</p> 281<p><img decoding="async" loading="lazy" class="alignnone wp-image-2222 size-full" src="https://desalasworks.com/wp-content/uploads/Seek.hirer_.platform.png" alt="" width="600" height="810" srcset="//desalasworks.com/w
281p-content/uploads/Seek.hirer_.platform.png 600w, //desalasworks.com/wp-content/uploads/Seek.hirer_.platform-222x300.png 222w, //desalasworks.com/wp-content/uploads/Seek.hirer_.platform-444x600.png 444w" sizes="(max-width: 600px) 100vw, 600px" /></p> 282<p>The majority of the work involved various enhancements to the back-end capability in the form of AWS-hosted micro-service APIs, and some user interface updates to accommodate improvements to job-seeker and hirer experiences.</p> 283<p><img decoding="async" loading="lazy" class="alignnone size-full wp-image-2220" src="https://desalasworks.com/wp-content/uploads/seek-api-stats.png" alt="" width="600" height="311" srcset="//desalasworks.com/wp-content/uploads/seek-api-stats.png 600w, //desalasworks.com/wp-content/uploads/seek-api-stats-300x156.png 300w" sizes="(max-width: 600px) 100vw, 600px" /></p> 284<p>Working in a Continuosly Deployed, Agile environment with Node.Js, Angular, Knockout, Jasmine, Karma, Gulp, TeamCity, LESS, Microservices / APIs, PACT, Swagger, Golang, DynamoDB, C#, SQL Server, Docker, Serverless, SNS/SQS, Lambdas, AWS DevOps.</p> 285 </div> 286 287 </div> 288 289 </div><!-- .container-box --> 290 291 292 <h3 class="page-title"> 293 Immutability in JavaScript, A Contrarian View </h3> 294 295 <div class="container-box"> 296 297 <div class="tags"> 298 <a href="/skills/javascript/" rel="tag">JavaScript</a> <a href="/skills/react/" rel="tag">React</a> <a href="/skills/redux/" rel="tag">Redux</a> </div> 299 300 <div class="archives-page post-1929 dw-article type-dw-article status-publish hentry category-code category-ui tag-javascript tag-react tag-redux"> 301 <!--h3 id="post-1929" class="post-title"><a href="/article/immutability-in-javascript-a-contrarian-view/" rel="bookmark" >Immutability in JavaScript, A Contrarian View</a></h3--> 302 <div class="entry"> 303 <p><strong>TL/DR:</strong> Immutability is more a fashion trend than a necessity in JavaScript. If you are using React it does provide a neat work-around to some <a href="https://github.com/facebook/react/issues/11527" rel="nofollow">confusing design choices</a> in state management. However in most other situations it wont add enough value over the complexity it introduces, serving more to <a href="https://dev.to/tra/expert-resum-driven-development" rel="nofollow">pad up a resume</a> than to fulfill an actual client need.</p> 304<p>I wrote this originally as a answer to a <a href="http://stackoverflow.com/questions/34385243/why-is-immutability-so-importantor-needed-in-javascript/43318963">stack overflow question</a> posted by someone who was as baffled by this JavaScript programming trend as I was.</p> 305<p>Here is my answer to this topic, it is contrarian and may also be contentious, but bear in mind its not the only answer, its just my own point of view.</p> 306<blockquote><p>Why is immutability so important(or needed) in javascript?</p></blockquote> 307<p>Well, I’m glad you asked!</p> 308<p>Some time ago a very talented guy called <a href="https://twitter.com/dan_abramov" rel="nofollow">Dan Abramov</a> wrote a javascript state management library called <a href="https://github.com/reactjs/redux" rel="nofollow">Redux</a> which uses pure functions and immutability. He also made some <a href="https://egghead.io/lessons/javascript-redux-the-single-immutable-state-tree" rel="nofollow">really cool videos</a> that made the idea really easy to understand (and sell).</p> 309<p>The timing was perfect. The novelty of <a href="https://angularjs.org/" rel="nofollow">Angular</a> was fading, and JavaScript world was ready to fixate on the latest thing that had the right degree of cool, and this library was not only innovative but slotted in perfectly with <a href="https://facebook.github.io/react/" rel="nofollow">React</a> which was being peddled by another <a href="http://facebook.com/" rel="nofollow">Silicon Valley powerhouse</a>.</p> 310<p>Sad as it may be, fashions rule in the world of JavaScript. Now Abramov is being hailed as a demigod and all us mere mortals have to subject ourselves to the <a href="https://medium.com/javascript-scene/the-dao-of-immutability-9f91a70c88cd" rel="nofollow">Dao of Immutability</a>… Wether it makes sense or not.</p> 311<blockquote><p>
311What is wrong in mutating objects?</p></blockquote> 312<p>Nothing!</p> 313<p>In fact programmers have been mutating objects for er… as long as there has been objects to mutate. <a href="https://en.wikipedia.org/wiki/Object-oriented_programming#History" rel="nofollow">50+ years</a> of application development in other words.</p> 314<p>And why complicate things? When you have object <code>cat</code> and it dies, do you really need a second <code>cat</code> to track the change? Most people would just say <code>cat.isDead = true</code> and be done with it.</p> 315<blockquote><p>Doesn’t (mutating objects) make things simple?</p></blockquote> 316<p>YES! .. Of course it does!</p> 317<p>Specially in JavaScript, which in practice is most useful used for rendering a view of some state that is maintained elsewhere (like in a database).</p> 318<blockquote><p>What if I have a new News object that has to be updated? … How do I achieve in this case? Delete the store & recreate it? Isn’t adding an object to the array a less expensive operation?</p></blockquote> 319<p>Well, you can go the traditional approach and update the <code>News</code> object, so your in-memory representation of that object changes (and the view displayed to the user, or so one would hope)…</p> 320<p>Or alternatively…</p> 321<p>You can try the sexy FP/Immutability approach and add your changes to the <code>News</code> object <em>to an array tracking every historical change</em> so you can then iterate through the array and figure out what the correct state representation should be (phew!).</p> 322<blockquote><p>I am trying to learn what’s right here. Please do enlighten me ð</p></blockquote> 323<p>Fashions come and go buddy. There are many ways to skin a cat.</p> 324<p>I am sorry that you have to bear the confusion of a constantly changing set of programming paradigms. But hey, WELCOME TO THE CLUB!!</p> 325<p>Now a couple of important points to remember with regards to Immutability, and you’ll get these thrown at you with the feverish intensity that only naivety can muster.</p> 326<p><strong>1) Immutability is awesome for avoiding <a href="https://stackoverflow.com/questions/34510/what-is-a-race-condition">race conditions in multi-threaded environments</a></strong>.</p> 327<p>Multi-threaded environments (like C++, Java and C#) are guilty of the practice of locking objects when more than one thread wants to change them. This is bad for performance, but better than the alternative of data corruption. And yet not as good as making everything immutable (Lord praise Haskell!).</p> 328<p>BUT ALAS! In JavaScript you always <a href="http://ryanmorr.com/understanding-scope-and-context-in-javascript/#execution-context" rel="nofollow">operate on a single thread</a>. Even web workers (each runs inside a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Web_Workers_API" rel="nofollow">separate context</a>). So since you can’t have a <em>thread related</em> race condition inside your execution context (all those lovely global variables and closures), the main point in favour of Immutability goes out the window.</p> 329<p>(Having said that, there <em>is</em> an advantage to using pure functions in web workers, which is that you’ll have no expectations about fiddling with objects on the main thread.)</p> 330<p><strong>2) Immutability can (somehow) avoid race conditions in the state of your app.</strong></p> 331<p>And here is the real crux of the matter, most (React) developers will tell you that Immutability and FP can somehow work this magic that allows the state of your application to become predictable.</p> 332<p>Of course this doesnât mean that you can avoid <a href="https://softwareengineering.stackexchange.com/questions/263726/how-to-prevent-race-conditions-in-a-web-application">race conditions in the database</a>, to pull that one off youâd have to coordinate <em>all users in all browsers</em>, and for that youâd need a back-end push technology like <a href="https://en.wikipedia.org/wiki/WebSocket" rel="nofollow">WebSockets</a> (more on this below) that will broadcast changes to everyone running the app.</p> 333<p>Nor does it mean that there is some inherent problem in JavaScript where your application state needs immutability in order to become predictable, any developer that has been coding front-end applications before React would tell you this.</p> 334<p>This rather confusing claim simply means that <em><strong>if you use React your application is prone to race conditions, but that immutability allows you to take that pain away</strong></em>. Why? Because React is special.. its been designed first and foremost as a <a href="https://reactjs.org/docs/optimizing-performance.html" rel="nofollow">highly optimised rendering library</a>
334 with state management <a href="https://reactjs.org/docs/react-component.html#setstate" rel="nofollow">subverted to that aim</a>, and thus component state is managed via an <a href="https://github.com/facebook/react/issues/11527" rel="nofollow">asynchronous chain of events</a> (aka “one-way data binding”) that optimize rendering but you have no control over and rely on you <a href="https://facebook.github.io/react/docs/state-and-lifecycle.html#using-state-correctly" rel="nofollow">remembering not to mutate state directly</a>…</p> 335<p>Given this context, its easy to see how the need for immutability has little to do with JavaScript and a lot to do with React: if have a bunch of inter-dependent changes in your spanking new application and no easy way to figure out what your state is currently at, <a href="https://reactjs.org/docs/faq-state.html#why-is-setstate-giving-me-the-wrong-value" rel="nofollow">you are going to get confused</a>, and thus <em>it makes perfect sense to use immutability to track every historical change</em>.</p> 336<p><strong>3) Race conditions are categorically bad.</strong></p> 337<p>Well, they might be if you are using React. But they are rare if you pick up a different framework.</p> 338<p>Besides, you normally have <strong>far bigger problems</strong> to deal with⦠Problems like dependency hell. Like a bloated code-base. Like your CSS not getting loaded. Like a slow build process or being stuck to a monolithic back-end that makes iterating almost impossible. Like inexperienced devs not understanding whats going on and making a mess of things.</p> 339<p>You know. Reality. But hey, who cares about that?</p> 340<p><strong>4) Immutability makes use of <a href="https://stackoverflow.com/questions/373419/whats-the-difference-between-passing-by-reference-vs-passing-by-value">Reference Types</a></strong> to reduce the performance impact of tracking every state change.</p> 341<p>Because seriously, if you are going to copy stuff every time your state changes, you better make sure you are smart about it.</p> 342<p><strong>5) Immutability allows you to UNDO stuff</strong>.</p> 343<p>Because er.. this is the number one feature your project manager is going to ask for, right?</p> 344<p><strong>6) Immutable state has lots of cool potential in combination with WebSockets</strong></p> 345<p>Last but not least, the accumulation of state deltas makes a pretty compelling case in combination with WebSockets, which allows for an easy consumption of <a href="https://teropa.info/blog/2015/09/10/full-stack-redux-tutorial.html" rel="nofollow">state as a flow of immutable events</a>…</p> 346<p>Once the penny drops on this concept (state being <em>a flow of events</em> — rather than a crude set of records representing the latest view), the immutable world becomes a magical place to inhabit. A land of <a href="https://martinfowler.com/eaaDev/EventSourcing.html" rel="nofollow">event-sourced</a> wonder and possibility that <em>transcends time itself</em>. And when done right this can definitely make real-time apps easi<em>er</em> to accomplish, you just broadcast the flow of events to everyone interested so they can <a href="https://martinfowler.com/eaaDev/ParallelModel.html" rel="nofollow">build their own representation</a> of the present and write back their own changes into the communal flow.<
346/p> 347<p>But at some point you wake up and realise that all that wonder and magic <a href="https://chriskiehl.com/article/event-sourcing-is-hard" rel="nofollow">do not come for free</a>. Unlike your eager colleagues, your stakeholders (yea, the people who pay you) care little about philosophy or fashion and a lot about the money they pay to build a product they can sell. And the bottom line is that its harder to code for immutability and easier to break it, plus there is little point having an immutable front-end if you don’t have a back-end to support it. When (and if!) you finally convince your stakeholders that you should publish and consume events via a <a href="https://en.wikipedia.org/wiki/Push_technology" rel="nofollow">push techology</a> like WebSockets, you find out what a <a href="https://samsaffron.com/archive/2015/12/29/websockets-caution-required" rel="nofollow">pain it is to scale in production</a>.</p> 348<p> </p> 349<hr /> 350<p>Now for some advice, should you choose to accept it.</p> 351<p>A choice to write JavaScript using FP/Immutability is also a choice to make your application code-base larger, more complex and harder to manage. I would strongly argue for limiting this approach to your Redux reducers, unless you know what you are doing… And IF you are going to go ahead and use immutability regardless, then apply <a href="https://martinfowler.com/eaaDev/EventSourcing.html" rel="nofollow">immutable state to your whole application stack</a>, and not just the client-side, as you’re missing the real value of it otherwise.</p> 352<p>Now, if you are fortunate enough to be able to make choices in your work, then try and use your wisdom (or not) and <a href="https://en.wikipedia.org/wiki/Golden_Rule" rel="nofollow">do what’s right by the person who is paying you</a>. You can base this on your experience, on your gut, or whats going on around you (admittedly if everyone is using React/Redux then there a valid argument that it will be easier to find a resource to continue your work).. Alternatively, you can try either <a href="https://dev.to/tra/expert-resum-driven-development" rel="nofollow">Resume Driven Development</a> or <a href="https://blog.daftcode.pl/hype-driven-development-3469fc2e9b22" rel="nofollow">Hype Driven Development</a> approaches. They might be more your sort of thing.</p> 353<p>In short, the thing to be said for immutability is that it <em>will</em> make you fashionable with your peers, at least until the next craze comes around, by which point you’ll be glad to move on.</p> 354<hr /> 355<p>And <a href="http://stackoverflow.com/questions/34385243/why-is-immutability-so-importantor-needed-in-javascript/43318963#link-post-43318963">the reply</a>!</p> 356<blockquote><p>Hello Steven, Yes. I had all these doubts when I considered immutable.js and redux. But, your answer is amazing! It adds lot of value and thanks for addressing every single point that I had doubts on. It’s so much more clear/better now even after working for months on immutable objects. â bozzmob</p></blockquote> 357 </div> 358 359 </div> 360 361 </div><!-- .container-box --> 362 363 364 <h3 class="page-title"> 365 How to Build a Cross-Platform Metal Detector App </h3> 366 367 <div class="container-box"> 368 369 <div class="tags"> 370 <a href="/skills/android/" rel="tag">Android</a> <a href="/skills/cordova/" rel="tag">Cordova</a> <a href="/skills/iphone/" rel="tag">iPhone</a> <a href="/skills/javascript/" rel="tag">JavaScript</a> </div> 371 372 <div class="archives-page post-1798 dw-article type-dw-article status-publish hentry category-code category-mobile category-plugins tag-android tag-cordova tag-iphone tag-javascript"> 373 <!--h3 id="post-1798" class="post-title"><a href="/article/how-to-build-a-cross-platform-metal-detector-app-ios-android/" rel="bookmark" >How to Build a Cross-Platform Metal Detector App</a></h3--> 374 <div class="entry"> 375 <p><img decoding="async" loading="lazy" class="alignnone size-full wp-image-1818" src="https://desalasworks.com/wp-content/uploads/coin-detector-xplatform-600px.jpg" alt="coin-detector-xplatform-600px" width="600" height="375" srcset="//desalasworks.com/w
375p-content/uploads/coin-detector-xplatform-600px.jpg 600w, //desalasworks.com/wp-content/uploads/coin-detector-xplatform-600px-300x188.jpg 300w" sizes="(max-width: 600px) 100vw, 600px" /></p> 376<p>Did you know that your smartphone has an in-built magnetometer to power its compass?</p> 377<p>The earth’s magnetic field is big but not particularly strong, and that means that the magnetometer in your phone is sensitive enough to detect small ferrous metal objects such as a coin, a large nail or a piece of cutlery which also generate small magnetic fields of their own. In other words YOU DONT HAVE TO GO AND BUY A METAL DETECTOR, you’ve ALREADY GOT ONE INSIDE YOUR PHONE! We just have to unlock its potential.</p> 378<h3>Do you want to try a bit of hacking?</h3> 379<p>In this post, I’m going to show you how you can build your own cross-platform metal detector app for BOTH  <strong>iPhone</strong> and <strong>Android</strong> phones, this app will interface with the magnetometer hardware in your phone and notify you of any ferrous metal or magnetic objects nearby.</p> 380<p><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAAU9AAAAJGM3NjAxMjEyLTJmZDUtNDhkYy1iMGFmLTgwMTZhZmNhOGQ5Nw.png" alt="" width="640" height="366" /></p> 381<p>This should take from 30 mins to 2 hrs of your time for either platform and depending on the speed of your internet connection, perhaps longer if you have bad luck and something doesn’t work straight away.</p> 382<h2>Introducing Cordova (or PhoneGap, for Adobe fans)</h2> 383<p><a href="http://cordova.apache.org/" target="_blank"><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAAU9AAAAJGE2MmIzYmRhLTE4NmYtNGY2MC1iYzZlLTNmYzEwOWEwNjA0YQ.jpg" alt="" width="640" height="286" /></a></p> 384<p>The <a href="http://cordova.apache.org/" target="_blank">Apache Cordova</a> project is an open source initiative aimed at bundling a HTML page (ie `index.html` – such as the one below) into a native app that will work across just about every smartphone device. This is great news because it means that with you <span class="underline">only need to code your app once</span> and it works on multiple devices!</p> 385<p><em># index.html</em></p> 386<p><em><html></em><br /> 387<em><head><title>My First App</title></head></em><br /> 388<em><body></em><br /> 389<em><button onclick=”alert(‘World’)”>Hello</button></em><br /> 390<em></body></em><br /> 391<em></html></em></p> 392<p><a href="http://phonegap.com/" target="_blank">PhoneGap</a> is the same thing, but with a snazzier name trademarked by Adobe (the Photoshop people) and with an <a href="http://build.phonegap.com/" target="_blank">online service </a>that gives you a wizard-like interface for building apps so you dont have to deal with obscure command line errors, but then you have to pay Adobe for the favour.</p> 393<h3><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAAXdAAAAJDE1M2I3YjgyLWY2YjgtNDg1OS05MDZkLTkyMjM4MDYxNDc2Yg.jpg" alt="" width="600" height="350" /></h3> 394<h3>So what does this ‘Cordova’ thingy do for me?</h3> 395<p>Using Cordova means that you don’t have to know how to setup a mobile app project because its all done for you! Cordova will basically <strong>generate all the folders and files</strong> you need to get started with a basic HTML ‘Hello World’ app for each platform.</p> 396<p>Cordova also has a powerful ‘plugin’ framework that allows access to the phone’s hardware, in this post we will be downloading 2 different plugins to unlock the <strong>Magnetometer</strong> and <strong>Vibration</strong> functionality inside your phone.</p> 397<p>To get started with a Cordova app you need to install NodeJS (<a href="http://nodejs.org" target="_blank">http://nodejs.org</a>) which is a powerful javascript command line tool that Cordova uses to do most of the donkey-work for setting up your project.</p> 398<h2><strong>The Code</strong></h2> 399<p>Go have a look at the following ‘<strong>index.html</strong>‘ file in Github, it contains all the code you need for a basic magnetometer app:</p> 400<p><a href="https://github.com/sdesalas/cordova-magnetometer-app/blob/master/www/index.html" target="_blank">https://github.com/sdesalas/cordova-magnetometer-app/blob/master/www/index.html</a></p> 401<p>Save it to your computer:</p> 402<p>1. Right-click on ‘<strong>Raw</strong>‘ button<br /> 4032. Choose ‘
403Save link as..’ on the context menu<br /> 4043. Put it somewhere sensible where you can find it</p> 405<h3>Hey, this is HTML right?</h3> 406<p>Correct, that means your browser can open it.</p> 407<p>Double click to open it on a browser. You should get something like this:</p> 408<p><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAAYGAAAAJGE3MWRhNDQ5LWM2YzEtNDYzMy04Y2U2LTY2ZTEzYzNmZDkyMw.png" alt="" width="381" height="564" /></p> 409<h3>Err.. Why?</h3> 410<p>Well, your mobile has a magnetometer in it <strong>your PC doesn’t</strong>. So all you’ll get out of it in a desktop browser is a ZERO reading.</p> 411<p>And thats ok, it just means everything is <span class="underline">working as expected</span>.</p> 412<p> </p> 413<h2><strong>The Android App</strong></h2> 414<p><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAAWLAAAAJDNiMThlMDlmLWQ4MjktNGRlMC04Yjc1LTYxNzU2MzcxNDYwYQ.png" alt="" width="640" height="331" /></p> 415<p>What You’ll need:</p> 416<p>1. A PC or Mac with <span class="underline">NodeJS</span>, Java (<span class="underline">JDK7</span>) and <span class="underline">Android Studio</span> installed.<br /> 4172. An Android (4.0+) device and USB cable for testing.<br /> 4183. Basic understanding of a command line interface (you can navigate between folders).</p> 419<p>CORPORATE FIREWALLS: Note that the steps below may not work behind a corporate firewall, you may need extra steps to set up internet access via a corporate proxy.</p> 420<p>You’ve already installed <a href="http://nodejs.org" target="_blank">NodeJS</a> right? If its a old version (older than 0.11) you might want to download the latest and re-install it.</p> 421<p>Ok, next you will need Java JDK 7 installed and Android Studio, these are needed to create a native app for Android. You can get them on the following links:</p> 422<p><strong>JDK7: </strong><a href="http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html" target="_blank">http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html</a> -> Remember x86=32bit, x64=64bit</p> 423<p><strong>Android Studio: </strong><a href="https://developer.android.com/sdk/index.html" target="_blank">https://developer.android.com/sdk/index.html</a></p> 424<p>BEWARE: after installing/running Android Studio, it will want to dowload the latest Android SDKs (the bits of functionality that enable your PC to talk to the phone hardware), this can take anywhere from 10 minutes and up to a couple of hours over a slow internet connection.</p> 425<h3>Installing Cordova</h3> 426<p>We are now going to install Cordova and create an Android project.</p> 427<p>Open up a command line window and type :</p> 428<p><em>> npm install -g cordova</em></p> 429<p><em><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAARKAAAAJDUzOWE3OGM1LTY1ZGMtNDkyZi1hMjM4LThiYzkzNzM1ZjU0Nw.png" alt="" width="640" height="278" /></em></p> 430<h3>Creating the Android Project</h3> 431<p>Next, navigate to your projects folder and create a new cordova project with android support:</p> 432<p><em>> cordova create magnetometer my.magnetometer magnetometer<br /> 433> cd magnetometer<br /> 434> cordova platform add android</em></p> 435<p><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAAWyAAAAJGU1ZTM5MDYzLTdiMWYtNDE1Yy05YzBmLWY2MDVmNDNiZGVhOA.png" alt="" width="640" height="562" />If all goes well, this should have created a bunch of files and folders (ie a project structure) within your ‘magnetometer’ project folder, you should be able to see them using a file explorer:</p> 436<p><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAATpAAAAJDM3ZjMyOTlhLTdiM2QtNDBjOC04ODdhLWExZWMxODAwMTA0NA.png" alt="" width="640" height="383" /></p> 437<h3>Copying our Metal Detector Code</h3> 438<p>The folder ‘<strong>www</strong>‘ contains our web code. Here is where we’ll be placing the index.html file that we <a href="https://github.com/sdesalas/cordova-magnetometer-app/blob/master/www/index.html" target="_blank">downloaded before</a>.</p> 439<p><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAASbAAAAJDFjYjMyZjEyLWZkZDItNDdjZS05Yzk2LWVlY2RkMmU3Y2JlOA.png" alt="" width="640" height="383" /></p> 440<h3>Building the App</h3> 441<p>Go back to the command line and run an extra couple of commands to ‘unlock’ the vibration and magnetometer functionality on your phone.</p> 442<p><em>> cordova plugin add cordova-plugin-vibration</em><br /> 443<em>> cordova plugin add cordova-plugin-magnetometer</em></p> 444<p><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAAR-AAAAJDAxOGFiMTVlLTYxMzYtNDY1Ny04MGQ3LTE2NzkwZjdmOTM3NA.png" alt="" width="640" height="221" /></p> 445<p>Next we are going to ‘build’
445 the app, so you can use it on your android phone. We need one more cordova command to ‘prepare’ the app:</p> 446<p><em>> cordova prepare</em></p> 447<p><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAAPXAAAAJGMxYTE1ODBkLWQyZDMtNDg2NC05YzVlLTcxYTNlNTdiZDgyZQ.png" alt="" width="640" height="233" /></p> 448<p>Then follow these instructions:</p> 449<p>1. Open Android Studio<br /> 4502. On the Welcome Prompt. Select ‘Import Project’<br /> 4513. Click on the ‘platforms\android’ folder inside your project.<br /> 4524. Press ‘OK’.<br /> 4535. Press ‘OK’ again if you get an extra prompt about Gradle settings.<br /> 4546. Wait for the progress bar to finish.</p> 455<p><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAAagAAAAJGEzMzYyNWMxLWJiYTItNDE1OC1iZTdiLTQwNzllMzFlMzY5Yw.png" alt="" width="640" height="486" /><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAAX_AAAAJGRiOTRkNDY1LTU0NTItNDk2Ny1hYjU1LTFmYjIxZWFmN2M0OA.png" alt="" width="440" height="529" />This will create an android project, next we will build the APK file that can be used to run the magnetometer app on your phone.</p> 456<p>Make sure you’ve waited until all the progress bars are finished before the next steps.</p> 457<p>1. Android Studio will need to get all required Java libraries that the project depends on, this usually takes some time. Did you really wait until all progress bars and loading messages were finished?<br /> 4582. Ok, click on the ‘Play’ button at the top menu as per screen below.<br /> 4593. This will trigger a build using Gradle. You can see the progress on the ‘Gradle Console’ window.<br /> 4604. When ready, a popup will appear asking if you want to run the emulator. <strong>DO NOT</strong> press OK here. It will grind your computer to a halt. Just press ‘Cancel’ on the popup.</p> 461<h3><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAAPPAAAAJGMxNDc0N2Q2LWMyNzQtNDZiZC1hMzlmLWZkMmRmNjAwMjRhYw.png" alt="" width="640" height="477" /></h3> 462<h3>Installing the App on your Android Phone</h3> 463<p>Next we are going to email ourselves the completed app.</p> 464<p>1. Navigate to your ‘magnetometer’ project folder using the file explorer.<br /> 4652. Click ‘platforms’ -> ‘android’ -> ‘build’ -> ‘outputs’ -> ‘apk’<br /> 4663. Find the file ‘<em>android-debug.apk</em>‘</p> 467<p><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAAWQAAAAJDcwNzg4ZjA4LWU4ZWMtNDFlYi1hZDVmLWJmNGZkMGI5ZGZiMw.png" alt="" width="640" height="315" /></p> 468<p>4. Next. Email it to yourself.<br /> 4695. Go to your Android phone.<br /> 4706. Open ‘Settings’ > ‘Security’<br /> 4717. Turn ON ‘Unknown Sources’ (you can turn it back OFF later)<br /> 4728. Open your email, click on the attached ‘APK’ file.<br /> 4739. You should then get an installation screen as per below.</p> 474<p><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAAVJAAAAJDE3MThjYjcxLTQwOWItNGE4ZC05MGM4LTE3MjcyY2JkODA4OQ.png" alt="" width="600" height="533" /></p> 475<p>10. Go to your Apps and look for ‘magnetometer’, then open it.<br /> 47611. READY! Start finding some metal objects!</p> 477<p><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAARIAAAAJGY2NmIzMTgxLTVmZWUtNDU2YS05YTVjLTliY2NiN2FlZThhOA.jpg" alt="" width="319" height="400" /></p> 478<h2><strong>The iOS App</strong></h2> 479<p><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAAXVAAAAJGFiYmRkNjdkLTc1MzMtNDViZS04ODZlLWQ3ZWRmOTAzMzc5YQ.png" alt="" width="640" height="389" /></p> 480<p>What You’ll need:</p> 481<p>– A newish Mac with NodeJS and XCode installed<br /> 482– An Apple Developer membership (US$99)<br /> 483– An iPhone (4.0+) and USB cable for testing.<br /> 484– Basic understanding of a command line interface (you can navigate between folders).</p> 485<h3>Installing Cordova</h3> 486<p>1. Make sure you have <a href="http://nodejs.org" target="_blank">NodeJS</a> installed and its at a reasonably new version (0.12+).<br /> 4872. If you need to update the version of node. Do it following <a href="http://davidwalsh.name/upgrade-nodejs" target="_blank">this article</a>.<br /> 4883. Open a terminal window (Look for the ‘Terminal’ app in Launchpad)<br /> 4894. Type the following command to install Cordova :</p> 490<p><em>$ sudo npm install -g cordova</em></p> 491<p><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAASwAAAAJGY4Yj
491MyNjYxLWJkMTktNDc4OC1hN2E0LTViMTYzMTc5NWFjZg.png" alt="" width="640" height="492" /></p> 492<p>5. You’ll be prompted to enter a password for your user account (on the mac).<br /> 4936. There will probably be some warnings when installing Cordova (“WARN”), you can safely ignore these. If you get any “ERROR”s they are a bit more of a problem and will need attention before continuing. Type the errors into Google for help.</p> 494<p>Creating the iOS Project</p> 495<p>Once Cordova has been installed, we are going to create the project structure for our magnetometer project. Firstly, navigate to your projects folder. The commands to use are �:</p> 496<p><em>$ cordova create magnetometer my.magnetometer magnetometer</em><br /> 497<em>$ cd magnetometer</em><br /> 498<em>$ cordova platform add ios</em><br /> 499<em>$ cordova plugin add cordova-plugin-vibration</em><br /> 500<em>$ cordova plugin add cordova-plugin-magnetometer</em></p> 501<p><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAAa3AAAAJDM3NDBiMTFmLWUwNTYtNGQwNS05YWU1LTg4Yjc0Y2VlMGM1NQ.png" alt="" width="640" height="529" /></p> 502<h3>Copying our Metal Detector App Code</h3> 503<p>This should have created your project structure as per following screenshot. Make sure you delete the contents of the ‘www’ folder and replace them with the ‘index.html’ file you <a href="https://github.com/sdesalas/cordova-magnetometer-app/blob/master/www/index.html" target="_blank">downloaded earlier</a>.</p> 504<p><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAAaiAAAAJDlkNDIyYzgyLTliMDItNDFhMC1hNjk4LWRjOGZkMDk3NzdiNQ.png" alt="" width="640" height="318" />You will then need to run the following command on your ‘magnetometer’ folder to ‘prepare’ the files (it will copy the ‘index.html’ to the XCode build folder).</p> 505<p><em>$ cordova prepare</em></p> 506<p><em><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAAQ9AAAAJDRmZmJiZThmLWQ2M2ItNGY5Ni1hMWE3LWQyOWMzNzIyOTFhNA.png" alt="" width="619" height="298" /></em></p> 507<h3>Building the iOS App</h3> 508<p>And now we can continue to XCode by finding the file ‘magnetometer.xcodeproj’ inside our project folder (look inside the sub-folder ‘platforms’ and ‘ios’). Just double-click on it and it will open (if you have XCode installed).</p> 509<p><em><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAAb2AAAAJGNkZDIzOTI1LWY5YjMtNDU5YS04NzlmLWFiZmE5M2MzY2JiMQ.png" alt="" width="640" height="395" /></em>Make sure you are signed in as an apple developer. Joining costs US$99. You cannot drop an app on your iOS device without one of these:</p> 510<p>1. Go to ‘XCode’ (on the top menu)<br /> 5112. Select ‘Preferences…’, then ‘Accounts’<br /> 5123. Click the plus (+) sign to add an account<br /> 5134. Enter your Apple Developer email and password</p> 514<p><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAAQ1AAAAJDAxMzdjNzUwLTU1MzgtNDA0Yi05MjI4LTUwZTI3MTBkMDk3Nw.png" alt="" width="640" height="492" /></p> 515<h3>Running the App on your iPhone</h3> 516<p>Then just go and run the app! This bit is even simpler than for Android!</p> 517<p>1. Connect your iPhone to your Mac using the USB cable.<br /> 5182. Select your iPhone as the device ‘target’ on the top menu<br /> 5193. Press the ‘Play’ button to run the build.</p> 520<p><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAAceAAAAJDEwNDVkM2Y3LTQ5MDktNDE5Yi04NTBlLWI1OTM1NzBiYjljYw.png" alt="" width="640" height="433" /></p> 521<p>And VOILA! Now start finding some metal with your iPhone!</p> 522<p><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAAa3AAAAJDNjOTZkZDAwLTIzZTctNDA1Ny05MzcxLWFjYmFmMzkzYjg1Yg.jpg" alt="" width="400" height="400" /></p> 523<h2><strong>And thats IT!</strong></h2> 524<p>That concludes my post for today, thanks for taking an interest. I hope you didn’t have to bang your head against the keyboard too much to get this working.</p> 525<p><img decoding="async" loading="lazy" class="center" src="https://media.licdn.com/mpr/mpr/AAEAAQAAAAAAAARHAAAAJGFiOTcxZTkyLWFiMzctNGZmNi04ZjgxLTljNGE3NmZkYmVjOA.jpg" alt="" width="570" height="381" /></p> 526<p>Please feel free to write your comments and experiences below.</p> 527<p>If you liked it then you are welcome to shout me a beer or help me out by sharing it with your friends and colleagues.</p> 528<p>BYE!!!</p> 529 </div> 530 531 </div> 532 533 </div><!-- .container-box --> 534 535 536 <h3 class="page-title"> 537 Tasmanian Convict Finder </h3> 538 539 <div class="container-box"> 540 541 <div class="tags"> 542 <a href="/skills/android/" rel="tag">Android</a> <a href="/skills/angular-js/" rel="tag">Angular.js</a> <a href="/skills/cordova/" rel="tag">Cordova</a> <a href="/skills/css3/" rel="tag">CSS3</a> <a href="/skills/html5/" rel="tag">HTML5</a> <a href="/skills/iphone/" rel="tag">iPhone</a> <a href="/skills/javascript/" rel="tag">JavaScript</a> <a href="/skills/node-js/" rel="tag">Node.js</a> </div> 543 544 <div class="archives-page post-1746 post type-post status-publish format-standard hentry category-mobile tag-android tag-angular-js tag-cordova tag-css3 tag-html5 tag-iphone tag-javascript tag-node-js"> 545 <!--h3 id="post-1746" class="post-title">
545<a href="/tasmanian-convict-finder/" rel="bookmark" >Tasmanian Convict Finder</a></h3--> 546 <div class="entry"> 547 <p>Browse and search through detailed records of 85,000 convicts transported to Tasmania and those convicted locally through the convict system (1803-1893).</p> 548<p><a href="https://play.google.com/store/apps/details?id=info.tasmanianconvictfinder" target="_blank"><img decoding="async" loading="lazy" class="alignnone size-large wp-image-1795" src="https://desalasworks.com/wp-content/uploads/tasmanian-convict-finder-600x400.jpg" alt="tasmanian-convict-finder" width="600" height="400" srcset="//desalasworks.com/wp-content/uploads/tasmanian-convict-finder.jpg 600w, //desalasworks.com/wp-content/uploads/tasmanian-convict-finder-300x200.jpg 300w" sizes="(max-width: 600px) 100vw, 600px" /></a></p> 549<p>This app uses publicly available data from <a href="https://data.gov.au/dataset/tasmanian-convicts-1803-1893" target="_blank">data.gov.au</a> provided by LINC and the Department of Education in Tasmania, as well as newspaper articles sourced from the National Library of Australia.</p> 550<p>It is cross-platform and available in both iPhone and Android version.</p> 551<p><a href="https://play.google.com/store/apps/details?id=info.tasmanianconvictfinder&hl=en" target="_blank"><img decoding="async" loading="lazy" class="alignnone size-medium wp-image-1808 noborder" src="https://desalasworks.com/wp-content/uploads/[email protected]" alt="google-play-en@2x" width="300" height="106" /></a><a href="https://itunes.apple.com/us/app/tasmanian-convict-finder/id1024005717?mt=8" target="_blank"><img decoding="async" loading="lazy" class="alignnone size-medium wp-image-1806 noborder" src="https://desalasworks.com/wp-content/uploads/apple-app-store-icon-300x103.png" alt="apple-app-store-icon" width="300" height="103" srcset="//desalasworks.com/wp-content/uploads/apple-app-store-icon-300x103.png 300w, //desalasworks.com/wp-content/uploads/apple-app-store-icon-600x207.png 600w, //desalasworks.com/wp-content/uploads/apple-app-store-icon.png 900w" sizes="(max-width: 300px) 100vw, 300px" /></a></p> 552<p> </p> 553<h2>Research your Ancestry</h2> 554<p>The app provides a useful reference for anyone seeking to find out more about the convict history of this beautiful island in Australia, and is a handy pocket guide for people researching their ancestry.</p> 555<p><img decoding="async" loading="lazy" class="alignnone size-large wp-image-1755" src="https://desalasworks.com/wp-content/uploads/tasmanian-convict-finder.workflow-600x356.png" alt="tasmanian-convict-finder.workflow" width="600" height="356" srcset="//desalasworks.com/wp-content/uploads/tasmanian-convict-finder.workflow.png 600w, //desalasworks.com/wp-content/uploads/tasmanian-convict-finder.workflow-300x178.png 300w" sizes="(max-width: 600px) 100vw, 600px" /></p> 556<p>Initially built as an entry into Govhack 2015, this app was completed though unfortunately not in time to join the prize lists.</p> 557 </div> 558 559 </div> 560 561 </div><!-- .container-box --> 562 563 564 <h3 class="page-title"> 565 TrifleJS </h3> 566 567 <div class="container-box"> 568 569 <div class="tags"> 570 <a href="/skills/c/" rel="tag">C#</a> <a href="/skills/distributed-computing/" rel="tag">Distributed Computing</a> <a href="/skills/github/" rel="tag">GitHub</a> <a href="/skills/javascript/" rel="tag">JavaScript</a> <a href="/skills/net-framework/" rel="tag">NET Framework</a> <a href="/skills/test-driven-development/" rel="tag">Test-Driven Development</a> </div> 571 572 <div class="archives-page post-1739 post type-post status-publish format-standard has-post-thumbnail hentry category-code category-libraries category-ui tag-c tag-distributed-computing tag-github tag-javascript tag-net-framework tag-test-driven-development"> 573 <!--h3 id="post-1739" class="post-title"><a href="/triflejs/" rel="bookmark" >TrifleJS</a></h3--> 574 <div class="entry"> 575 <p><a href="http://triflejs.org" target="_blank" rel="noopener"><img decoding="async" loading="lazy" class="alignnone wp-image-1741 size-full" src="https://desalasworks.com/w
575p-content/uploads/triflejs-org.png" alt="triflejs-org" width="600" height="1234" srcset="//desalasworks.com/wp-content/uploads/triflejs-org.png 600w, //desalasworks.com/wp-content/uploads/triflejs-org-146x300.png 146w, //desalasworks.com/wp-content/uploads/triflejs-org-292x600.png 292w" sizes="(max-width: 600px) 100vw, 600px" /></a></p> 576 </div> 577 578 </div> 579 580 </div><!-- .container-box --> 581 582 583 <h3 class="page-title"> 584 AutoTrader UK </h3> 585 586 <div class="container-box"> 587 588 <div class="tags"> 589 <a href="/skills/agile/" rel="tag">Agile</a> <a href="/skills/backbone-js/" rel="tag">Backbone.js</a> <a href="/skills/html5/" rel="tag">HTML5</a> <a href="/skills/jasmine/" rel="tag">Jasmine</a> <a href="/skills/javascript/" rel="tag">JavaScript</a> <a href="/skills/jquery/" rel="tag">jQuery</a> <a href="/skills/performance-optimization/" rel="tag">Performance Optimization</a> <a href="/skills/test-driven-development/" rel="tag">Test-Driven Development</a> </div> 590 591 <div class="archives-page post-1723 post type-post status-publish format-standard hentry category-code category-ui category-websites tag-agile tag-backbone-js tag-html5 tag-jasmine tag-javascript tag-jquery tag-performance-optimization tag-test-driven-development"> 592 <!--h3 id="post-1723" class="post-title"><a href="/autotrader-uk/" rel="bookmark" >AutoTrader UK</a></h3--> 593 <div class="entry"> 594 <p>This was a role working on Interface Development for Trader Media’s online marketplace with over 1 billion page impressions per month.</p> 595<p><img decoding="async" loading="lazy" class="alignnone size-large wp-image-1724" src="https://desalasworks.com/wp-content/uploads/autotrader_main-600x520.jpg" alt="autotrader_main" width="600" height="520" srcset="//desalasworks.com/wp-content/uploads/autotrader_main.jpg 600w, //desalasworks.com/wp-content/uploads/autotrader_main-300x260.jpg 300w" sizes="(max-width: 600px) 100vw, 600px" /></p> 596<p>I mainly focused on creating rich interfaces and user journeys with Event-driven and Object-Oriented JavaScript, DOM, HTML5, jQuery, AJAX, JSON and Backbone.js MVC framework continuously tested with Jasmine/Rhino.</p> 597<p><img decoding="async" loading="lazy" class="alignnone size-large wp-image-1726" src="https://desalasworks.com/wp-content/uploads/autotrader_gallery-600x369.jpg" alt="autotrader_gallery" width="600" height="369" srcset="//desalasworks.com/wp-content/uploads/autotrader_gallery.jpg 600w, //desalasworks.com/wp-content/uploads/autotrader_gallery-300x185.jpg 300w" sizes="(max-width: 600px) 100vw, 600px" /></p> 598<p>Our team used Agile development approach with Continuous Integration (CI), pair-programming and short development cycles based on Thoughtworks Extreme Programming (XP) model for Java/JUnit TDD projects.</p> 599<p><img decoding="async" loading="lazy" class="alignnone size-large wp-image-1725" src="https://desalasworks.com/wp-content/uploads/autotrader_agile_dave-600x360.jpg" alt="autotrader_agile_dave" width="600" height="360" srcset="//desalasworks.com/wp-content/uploads/autotrader_agile_dave.jpg 600w, //desalasworks.com/wp-content/uploads/autotrader_agile_dave-300x180.jpg 300w" sizes="(max-width: 600px) 100vw, 600px" /></p> 600 </div> 601 602 </div> 603 604 </div><!-- .container-box --> 605 606 607 <h3 class="page-title"> 608 MBTA mTicket </h3> 609 610 <div class="container-box"> 611 612 <div class="tags"> 613 <a href="/skills/agile/" rel="tag">Agile</a> <a href="/skills/android/" rel="tag">Android</a> <a href="/skills/cordova/" rel="tag">Cordova</a> <a href="/skills/css3/" rel="tag">CSS3</a> <a href="/skills/html5/" rel="tag">HTML5</a> <a href="/skills/iphone/" rel="tag">iPhone</a> <a href="/skills/javascript/" rel="tag">JavaScript</a> <a href="/skills/qunit/" rel="tag">QUnit</a> <a href="/skills/test-driven-development/" rel="tag">Test-Driven Development</a> </div> 614 615 <div class="archives-page post-1372 post type-post status-publish format-standard hentry category-mobile category-startups category-ui tag-agile tag-android tag-cordova tag-css3 tag-html5 tag-iphone tag-javascript tag-qunit tag-test-driven-development"> 616 <!--h3 id="post-1372" class="post-title"><a href="/mbta-mticket/" rel="bookmark" >MBTA mTicket</a></h3--> 617 <div class="entry"> 618 <p><em>
618A 6 month cross-platform (iOS and Android) mobile app project for Masabi, a specialist mTicketing company based near London Bridge. The MBTA mTicket application was launched in the Android and iPhone app markets in November 2012, and was a complete success grossing $1 million of sold tickets in the Boston Rail network for the first ten weeks of operation.</em></p> 619<div id="attachment_1526" style="width: 610px" class="wp-caption alignnone"><a title="MBTA mTicket in the Google Play Store" href="https://play.google.com/store/apps/details?id=com.mbta.mobileapp&hl=en" target="_blank" rel="noopener noreferrer"><img aria-describedby="caption-attachment-1526" decoding="async" loading="lazy" class="size-full wp-image-1526" title="MBTA mTicket in the Google Play store." src="//desalasworks.com/wp-content/uploads/mbta.mticket.1.jpg" alt="MBTA mTicket in the Google Play store." width="600" height="293" /></a><p id="caption-attachment-1526" class="wp-caption-text">MBTA mTicket in the Google Play store.</p></div> 620<h2>Developing the Front End User Experience</h2> 621<p>The project focused on building the User Interface for the application, this was done as a HTML5 /JavaScript UI due to the inherent advantages in being able to share code across different platforms. During development, we regularly tested against Android, iOS, BlackBerry, Symbian WebOS and Windows Mobile.</p> 622<p><iframe loading="lazy" src="https://www.youtube.com/embed/C1l5MxnHR3c?feature=player_detailpage" width="600" height="340" frameborder="0"></iframe></p> 623<h2>MBTA mTicket Payment Walkthrough</h2> 624<p>The process for purchasing tickets is straight forward, no complicated steps or black magic.
624</p> 625<p>To purchase a round trip ticket to/from Boston the steps are:</p> 626<p>1) Click ‘Buy Tickets’<br /> 6272) Choose a ‘From’ and ‘To’ Station from the list.<br /> 6283) Choose a ‘Round Trip’ ticket<br /> 6294) Choose an existing card (the app stores a payment key after the first time).<br /> 6305) Enter the Card CVV</p> 631<p><img decoding="async" loading="lazy" class="alignnone size-full wp-image-1647" src="//desalasworks.com/wp-content/uploads/mbta.workflow.new1_.png" alt="mbta.workflow.new1" width="600" height="353" srcset="//desalasworks.com/wp-content/uploads/mbta.workflow.new1_.png 600w, //desalasworks.com/wp-content/uploads/mbta.workflow.new1_-300x176.png 300w" sizes="(max-width: 600px) 100vw, 600px" /></p> 632<p>6) Review your purchase<br /> 6337) Press ‘Pay Now’<br /> 6348) DONE!</p> 635<p><img decoding="async" loading="lazy" class="alignnone size-full wp-image-1649" src="//desalasworks.com/wp-content/uploads/mbta.workflow.new2_.png" alt="mbta.workflow.new2" width="600" height="354" srcset="//desalasworks.com/wp-content/uploads/mbta.workflow.new2_.png 600w, //desalasworks.com/wp-content/uploads/mbta.workflow.new2_-300x177.png 300w" sizes="(max-width: 600px) 100vw, 600px" /></p> 636<p>Once the transaction goes through, the ticket is loaded into the My Tickets section. You now have two round trips to Boston waiting for you.</p> 637<p>These last few panels are a shot of your ticket. The rectangle at the top shows the current time, the time block moves back and forth, and the three blocks are multi-colored. The color scheme changes on a daily basis according to a scheme that the conductor knows. Once the ticket is used it changes into a grey scheme. The conductor when he approaches will generally glance at the screen, nod with a “thanks” and put a paper slip in the holder above your head. Done.</p> 638<p><img decoding="async" loading="lazy" class="alignnone size-full wp-image-1650" src="//desalasworks.com/wp-content/uploads/mbta.workflow.new3_.png" alt="mbta.workflow.new3" width="600" height="354" srcset="//desalasworks.com/wp-content/uploads/mbta.workflow.new3_.png 600w, //desalasworks.com/wp-content/uploads/mbta.workflow.new3_-300x177.png 300w" sizes="(max-width: 600px) 100vw, 600px" /></p> 639<h2>MBTA mTicket in the News</h2> 640<p>These are some of the online articles that were published before and after launch:</p> 641<p><img decoding="async" loading="lazy" class="alignnone size-full wp-image-1539" style="border: 0;" title="logo.engadget" src="//desalasworks.com/wp-content/uploads/logo.engadget.png" alt="" width="171" height="60" /></p> 642<p>Apr 23rd, 2012</p> 643<p><strong><a href="http://www.engadget.com/2012/04/23/mbta-masabi-smartphone-ticketing/" target="_blank" rel="noopener noreferrer">MBTA and Masabi team up for first smartphone rail ticketing system in the US, launching in Boston this fall</a></strong></p> 644<p>Jul 12th, 2012</p> 645<p><strong><a href="http://www.engadget.com/2012/07/12/new-york-mta-announces-smartphone-ticketing-trials/">New York MTA announces smartphone-based ticketing trials aboard Metro-North Railroad</a></strong></p> 646<hr /> 647<p><img decoding="async" loading="lazy" class="alignnone" style="border: 0;" src="https://s.wsj.net/img/logo_black_sm.png" alt="" width="194" height="18" /></p> 648<p>July 11, 2012</p> 649<p><strong><a href="http://blogs.wsj.com/metropolis/2012/07/11/new-smartphone-app-could-replace-railroad-tickets/?mod=google_news_blog" target="_blank" rel="noopener noreferrer">New Smartphone App Could Replace Railroad Tickets</a></strong></p> 650<hr /> 651<p><img decoding="async" loading="lazy" class="alignnone size-full wp-image-1544" style="border: 0;" title="logo.reuters" src="//desalasworks.com/wp-content/uploads/logo.reuters.png" alt="" width="200" height="56" /></p> 652<p>Nov 12, 2012</p> 653<p><strong><a href="http://www.reuters.com/article/2012/11/12/idUS110891+12-Nov-2012+BW20121112" target="_blank" rel="noopener noreferrer">MBTA & Masabi Launch US` First Smartphone Commuter Rail Ticketing System</a></strong></p> 654<p>Jul 11, 2012</p> 655<p><strong><a href="http://www.reuters.com/article/2012/07/11/usa-train-digital-idUSL2E8IB7Y920120711" target="_blank" rel="noopener noreferrer">Digital train tickets to replace paper for some NY commuters</a></strong></p> 656<hr /> 657<p><img decoding="async" loading="lazy" class="alignnone size-full wp-image-1545" style="border: 0;
657" title="logo.metro.us" src="//desalasworks.com/wp-content/uploads/logo.metro_.us_.png" alt="" width="170" height="91" /></p> 658<p>November 27, 2012</p> 659<p><strong><a href="http://www.metro.us/boston/boston/article/1156976--mticket-mbta-releases-mobile-ticketing-app-at-south-station" target="_blank" rel="noopener noreferrer">mTicket: MBTA releases mobile ticketing app at South Station</a></strong></p> 660<p>December 04, 2012</p> 661<p><strong><a href="http://www.metro.us/boston/boston/article/1157483--mticket-t-reports-225-000-in-mobile-app-ticket-sales-since-november-launch" target="_blank" rel="noopener noreferrer">mTicket: T reports $225,000 in mobile app ticket sales since November launch</a></strong></p> 662<p>January 15, 2013</p> 663<p><strong><a href="http://www.metro.us/boston/local/article/1159852--mticket-sees-1-million-in-commuter-line-sales" target="_blank" rel="noopener noreferrer">mTicket sees $1 million in commuter line sales</a></strong></p> 664<hr /> 665<p><img decoding="async" loading="lazy" class="alignnone size-full wp-image-1546" style="border: 0;" title="logo.bostino" src="//desalasworks.com/wp-content/uploads/logo.bostino.png" alt="" width="200" height="60" /></p> 666<p>November 21st, 2012</p> 667<p><strong><a href="http://bostinno.com/channels/what-bostonians-are-thankful-for/" target="_blank" rel="noopener noreferrer">What Bostonians Are Thankful For</a></strong></p> 668<p>November 27th, 2012</p> 669<p><strong><a href="http://bostinno.com/2012/11/27/mobile-ticketing-coming-to-mbta-commuter-boats-trains-riders-can-purchase-monthly-passes/" target="_blank" rel="noopener noreferrer">Mobile Ticketing Coming to MBTA Boats & More Commuter Trains, Riders Can Purchase Monthly Passes</a></strong></p> 670<hr /> 671<h2>Customer Respose</h2> 672<p>The MBTA mTicketing app went on to become a huge success shortly after launch. Customers reported a ‘seamless experience’ with ‘great usability’, ‘very convenient’, ‘neat & efficient’ etc.</p> 673<p>These are some of the responses the MBTA customers tweeted shortly after launch:</p> 674<blockquote class="twitter-tweet"><p>Just bought my first mobile <a href="https://twitter.com/search/%23mbcr">#mbcr</a> ticket with the new <a href="https://twitter.com/search/%23mbta">#mbta</a> mTicket app. Super easy, great interface. Will def. make life easier <img decoding="async" class="wp-smiley" src="https://www.masabi.com/wp-includes/images/smilies/icon_smile.gif" alt=":)" /> @<a href="https://twitter.com/mbtagm">mbtagm</a></p> 675<p>â Beth Vitale (@vitale_beth) <a href="https://twitter.com/vitale_beth/status/267982770431721473" data-datetime="2012-11-12T13:30:46+00:00">November 12, 2012</a></p></blockquote> 676<blockquote class="twitter-tweet"><p>I absolutely love the new <a href="https://twitter.com/search/%23mbta">#mbta</a> commuter rail app. Itâs so convenient and easy. <a href="https://twitter.com/search/%23greatjob">#greatjob</a></p> 677<p>â Colin Loiselle⢠(@colinloiselle) <a href="https://twitter.com/colinloiselle/status/268137471211081728" data-datetime="2012-11-12T23:45:29+00:00">November 12, 2012</a></p></blockquote> 678<blockquote class="twitter-tweet"><p>Whoever created this MBTA app is an actual angel from heaven</p> 679<p>â connor (@connorlately) <a href="https://twitter.com/connorlately/status/268449503508578304" data-datetime="2012-11-13T20:25:23+00:00">November 13, 2012</a></p></blockquote> 680<blockquote class="twitter-tweet"><p>.Just used the <a href="https://twitter.com/search/%23MBTA">#MBTA</a> mTicket app on my <a href="https://twitter.com/search/%23Android">#Android</a> phone. Pretty neat & efficient! <a href="https://twitter.com/search/%23ilovetechnology">#ilovetechnology</a> cc: @<a href="https://twitter.com/mbtagm">mbtagm</a></p> 681<p>â Meg McDevitt (@OhThatMeg) <a href="https://twitter.com/OhThatMeg/status/268846898830524416" data-datetime="2012-11-14T22:44:30+00:00">November 14, 2012</a></p></blockquote> 682<blockquote class="twitter-tweet"><p>MBTAâs making mobile e-tickets for the Commuter Rail now?! No more waiting in line and missing the train! WOO</p> 683<p>â Timmy C (@_LeTimmy) <a href="https://twitter.com/_LeTimmy/status/268493698109669376" data-datetime="2012-11-13T23:21:00+00:00">November 13, 2012</a></p></blockquote> 684<blockquote class="twitter-tweet"><p>Transforming <a href="https://twitter.com/search/%23transit">#transit</a> @<a href="https://twitter.com/masabi_com">masabi_com</a>RT @<a href="https://twitter.com/bostonglobe">bostonglobe</a>: The MBTA says its smartphone ticket app for commuters is a hit <a title="http://b.globe.com/RDCmot" href="http://t.co/6CSqOiT1">b.globe.com/RDCmot</a></p> 685<p>â Elizabeth Towler (@LizTowler) <a href="https://twitter.com/LizTowler/status/269035129404538880" data-datetime="2012-11-15T11:12:27+00:00">November 15, 2012</a></p></blockquote> 686<blockquote class="twitter-tweet" data-in-reply-to="270502171441774594"><p>@<a href="https://twitter.com/mbtagm">mbtagm</a> downloaded the <a href="https://twitter.com/search/%23MBTA">#MBTA</a> mTicket App today. real cutting edge!</p> 687<p>â eileensideways (@eileensideways) <a href="https://twitter.com/eileensideways/status/270512160864600064" data-datetime="2012-11-19T13:01:39+00:00">November 19, 2012</a></p></blockquote> 688<blockquote class="twitter-tweet"><p>This app just made my life so much easier. <a title="https://play.google.com/store/apps/details?id=com.mbta.mobileapp" href="https://t.co/sKqSdEyM">play.google.com/store/apps/detâ¦</a></p> 689<p>â Patrick Rodriguez (@LightAesthetic) <a href="https://twitter.com/LightAesthetic/status/270222307782438912" data-datetime="2012-11-18T17:49:53+00:00">November 18, 2012</a></p></blockquote> 690<blockquote class="twitter-tweet"><p>.@<a href="https://twitter.com/mbtagm">mbtagm</a> the new commuter rail ticketing app is amazing. I love it. <a href="https://twitter.com/search/%23mbta">#mbta</a> <a href="https://twitter.com/search/%23boston">#boston</a></p> 691<p>â cool dr money (@xieish) <a href="https://twitter.com/xieish/status/269963107290468352" data-datetime="2012-11-18T00:39:55+00:00">November 18, 2012</a></p></blockquote> 692<blockquote class="twitter-tweet"><p>@<a href="https://twitter.com/mbtanow">mbtanow</a> Love the new MBTA Ticketing App!</p> 693<p>â Owen (@OFenwick) <a href="https://twitter.com/OFenwick/status/269789521296642050" data-datetime="2012-11-17T13:10:08+00:00">November 17, 2012</a></p></blockquote> 694<blockquote class="twitter-tweet"><p>Mbta app is dope</p> 695<p>â John M. Billert (@_billert) <a href="https://twitter.com/_billert/status/269568254522044417" data-datetime="2012-11-16T22:30:54+00:00">November 16, 2012</a></p></blockquote> 696<blockquote class="twitter-tweet"><p>Been using <a href="https://twitter.com/search/%23MBTA">#MBTA</a> app this week on the commuter rail â so far, so good! Very convenient. @<a href="https://twitter.com/mbtagm">mbtagm</a></p> 697<p>â Ashley (@accordingtoalg) <a href="https://twitter.com/accordingtoalg/status/269427615453028352" data-datetime="2012-11-16T13:12:03+00:00">November 16, 2012</a></p></blockquote> 698<p>
698<script src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
698</p> 699 </div> 700 701 </div> 702 703 </div><!-- .container-box --> 704 705 706 <div class="pagination navigation clearfix"> 707 <div class="alignleft"><a href="/skills/javascript/page/2/" >« Older Entries</a></div> 708 <div class="alignright"></div> 709 </div> 710 711 712 </div><!-- #container --> 713 714<div id="sidebar"> 715 716<div class="sidebar-box"> 717 718 719<ul class="group"> 720 <div class="sb-title">Skills</div> 721 <li class="cat-item"><a href='/skills/agile/' title='Agile' class='agile'>Agile</a> (4)</li> 722 <li class="cat-item"><a href='/skills/ajax/' title='AJAX' class='ajax'>AJAX</a> (1)</li> 723 <li class="cat-item"><a href='/skills/android/' title='Android' class='android'>Android</a> (5)</li> 724 <li class="cat-item"><a href='/skills/angular-js/' title='Angular.js' class='angular-js'>Angular.js</a> (2)</li> 725 <li class="cat-item"><a href='/skills/api/' title='API' class='api'>API</a> (1)</li> 726 <li class="cat-item"><a href='/skills/asp/' title='ASP' class='asp'>ASP</a> (4)</li> 727 <li class="cat-item"><a href='/skills/autoscaling/' title='Autoscaling' class='autoscaling'>Autoscaling</a> (1)</li> 728 <li class="cat-item"><a href='/skills/aws/' title='AWS' class='aws'>AWS</a> (4)</li> 729 <li class="cat-item"><a href='/skills/backbone-js/' title='Backbone.js' class='backbone-js'>Backbone.js</a> (1)</li> 730 <li class="cat-item"><a href='/skills/c/' title='C#' class='c'>C#</a> (9)</li> 731 <li class="cat-item"><a href='/skills/cordova/' title='Cordova' class='cordova'>Cordova</a> (3)</li> 732 <li class="cat-item"><a href='/skills/css3/' title='CSS3' class='css3'>CSS3</a> (6)</li> 733 <li class="cat-item"><a href='/skills/data-feeds/' title='Data Feeds' class='data-feeds'>Data Feeds</a> (1)</li> 734 <li class="cat-item"><a href='/skills/devops/' title='DevOps' class='devops'>DevOps</a> (1)</li> 735 <li class="cat-item"><a href='/skills/distributed-computing/' title='Distributed Computing' class='distributed-computing'>Distributed Computing</a> (4)</li> 736 <li class="cat-item"><a href='/skills/docker/' title='Docker' class='docker'>Docker</a> (3)</li> 737 <li class="cat-item"><a href='/skills/dom/' title='DOM' class='dom'>DOM</a> (1)</li> 738 <li class="cat-item"><a href='/skills/ecs/' title='ECS' class='ecs'>ECS</a> (1)</li> 739 <li class="cat-item"><a href='/skills/electron/' title='Electron' class='electron'>Electron</a> (1)</li> 740 <li class="cat-item"><a href='/skills/extjs/' title='ExtJS' class='extjs'>ExtJS</a> (1)</li> 741 <li class="cat-item"><a href='/skills/facebook/' title='Facebook' class='facebook'>Facebook</a> (2)</li> 742 <li class="cat-item"><a href='/skills/flutter/' title='Flutter' class='flutter'>Flutter</a> (1)</li> 743 <li class="cat-item"><a href='/skills/github/' title='GitHub' class='github'>GitHub</a> (2)</li> 744 <li class="cat-item"><a href='/skills/gitlab/' title='Gitlab' class='gitlab'>Gitlab</a> (1)</li> 745 <li class="cat-item"><a href='/skills/google-cloud-platform/' title='Google Cloud Platform' class='google-cloud-platform'>Google Cloud Platform</a> (1)</li> 746 <li class="cat-item"><a href='/skills/grafana/' title='Grafana' class='grafana'>Grafana</a> (1)</li> 747 <li class="cat-item"><a href='/skills/heroku/' title='Heroku' class='heroku'>Heroku</a> (2)</li> 748 <li class="cat-item"><a href='/skills/html5/' title='HTML5' class='html5'>HTML5</a> (5)</li> 749 <li class="cat-item"><a href='/skills/investment-banking/' title='Investment Banking' class='investment-banking'>Investment Banking</a> (2)</li> 750 <li class="cat-item"><a href='/skills/ios/' title='iOS' class='ios'>iOS</a> (2)</li> 751 <li class="cat-item"><a href='/skills/iphone/' title='iPhone' class='iphone'>iPhone</a> (4)</li> 752 <li class="cat-item"><a href='/skills/jasmine/' title='Jasmine' class='jasmine'>Jasmine</a> (1)</li> 753 <li class="cat-item"><a href='/skills/javascript/' title='JavaScript' class='javascript'>JavaScript</a> (25)</li> 754 <li class="cat-item"><a href='/skills/jquery/' title='jQuery' class='jquery'>jQuery</a> (3)</li> 755 <li class="cat-item"><a href='/skills/json/' title='JSON' class='json'>JSON</a> (2)</li> 756 <li class="cat-item"><a href='/skills/k6/' title='K6' class='k6'>K6</a> (1)</li> 757 <li class="cat-item"><a href='/skills/kafka/' title='Kafka' class='kafka'>Kafka</a> (1)</li> 758 <li class="cat-item"><a href='/skills/kubernetes/' title='Kubernetes' class='kubernetes'>Kubernetes</a> (1)</li> 759 <li class="cat-item"><a href='/skills/lambda/' title='Lambda' class='lambda'>Lambda</a> (1)</li> 760 <li class="cat-item"><a href='/skills/leadership/' title='Leadership' class='leadership'>Leadership</a> (1)</li> 761 <li class="cat-item"><a href='/skills/load-testing/' title='Load testing' class='load-testing'>Load testing</a> (1)</li> 762 <li class="cat-item"><a href='/skills/machine-learning/' title='Machine Learning' class='machine-learning'>Machine Learning</a> (1)</li> 763 <li class="cat-item"><a href='/skills/microservices/' title='Microservices' class='microservices'>Microservices</a> (2)</li> 764 <li class="cat-item"><a href='/skills/mobile/' title='Mobile' class='mobile'>Mobile</a> (1)</li> 765 <li class="cat-item"><a href='/skills/mongodb/' title='MongoDB' class='mongodb'>MongoDB</a> (1)</li> 766 <li class="cat-item"><a href='/skills/mqtt/' title='MQTT' class='mqtt'>MQTT</a> (1)</li> 767 <li class="cat-item"><a href='/skills/mysql/' title='MySQL' class='mysql'>MySQL</a> (2)</li> 768 <li class="cat-item"><a href='/skills/net-framework/' title='NET Framework' class='net-framework'>NET Framework</a> (5)</li> 769 <li class="cat-item"><a href='/skills/nginx/' title='Nginx' class='nginx'>Nginx</a> (1)</li> 770 <li class="cat-item"><a href='/skills/node-js/' title='Node.js' class='node-js'>Node.js</a> (5)</li> 771 <li class="cat-item"><a href='/skills/nosql/' title='NoSQL' class='nosql'>NoSQL</a> (1)</li> 772 <li class="cat-item"><a href='/skills/nuxt/' title='Nuxt' class='nuxt'>Nuxt</a> (1)</li> 773 <li class="cat-item"><a href='/skills/object-orientation/' title='Object Orientation' class='object-orientation'>Object Orientation</a> (2)</li> 774 <li class="cat-item"><a href='/skills/orm/' title='ORM' class='orm'>ORM</a> (1)</li> 775 <li class="cat-item"><a href='/skills/payment-processing/' title='Payment Processing' class='payment-processing'>Payment Processing</a> (1)</li> 776 <li class="cat-item"><a href='/skills/performance-optimization/' title='Performance Optimization' class='performance-optimization'>Performance Optimization</a> (7)</li> 777 <li class="cat-item"><a href='/skills/photoshop/' title='Photoshop' class='photoshop'>Photoshop</a> (3)</li> 778 <li class="cat-item"><a href='/skills/php/' title='PHP' class='php'>PHP</a> (6)</li> 779 <li class="cat-item"><a href='/skills/qunit/' title='QUnit' class='qunit'>QUnit</a> (2)</li> 780 <li class="cat-item"><a href='/skills/rabbitmq/' title='RabbitMQ' class='rabbitmq'>RabbitMQ</a> (1)</li> 781 <li class="cat-item"><a href='/skills/react/' title='React' class='react'>React</a> (3)</li> 782 <li class="cat-item"><a href='/skills/react-native/' title='React Native' class='react-native'>React Native</a> (1)</li> 783 <li class="cat-item"><a href='/skills/redis/' title='Redis' class='redis'>Redis</a> (2)</li> 784 <li class="cat-item"><a href='/skills/redux/' title='Redux' class='redux'>Redux</a> (2)</li> 785 <li class="cat-item"><a href='/skills/robotics/' title='Robotics' class='robotics'>Robotics</a> (1)</li> 786 <li class="cat-item"><a href='/skills/sequelize/' title='Sequelize' class='sequelize'>Sequelize</a> (1)</li> 787 <li class="cat-item"><a href='/skills/soap/' title='SOAP' class='soap'>SOAP</a> (2)</li> 788 <li class="cat-item"><a href='/skills/sql-server/' title='SQL Server' class='sql-server'>SQL Server</a> (12)</li> 789 <li class="cat-item"><a href='/skills/stored-procedures/' title='Stored Procedures' class='stored-procedures'>Stored Procedures</a> (2)</li> 790 <li class="cat-item"><a href='/skills/test-driven-development/' title='Test-Driven Development' class='test-driven-development'>Test-Driven Development</a> (3)</li> 791 <li class="cat-item"><a href='/skills/trading-platform/' title='Trading Platform' class='trading-platform'>Trading Platform</a> (2)</li> 792 <li class="cat-item"><a href='/skills/typescript/' title='TypeScript' class='typescript'>TypeScript</a> (2)</li> 793 <li class="cat-item"><a href='/skills/voice-assistants/' title='Voice Assistants' class='voice-assistants'>Voice Assistants</a> (1)</li> 794 <li class="cat-item"><a href='/skills/vue-js/' title='Vue.js' class='vue-js'>Vue.js</a> (1)</li> 795 <li class="cat-item"><a href='/skills/web-services/' title='Web Services' class='web-services'>Web Services</a> (3)</li> 796 <li class="cat-item"><a href='/skills/webgl/' title='WebGL' class='webgl'>WebGL</a> (1)</li> 797 <li class="cat-item"><a href='/skills/websockets/' title='WebSockets' class='websockets'>WebSockets</a> (2)</li> 798 <li class="cat-item"><a href='/skills/wordpress/' title='Wordpress' class='wordpress'>Wordpress</a> (3)</li> 799 <li class="cat-item"><a href='/skills/xml/' title='XML' class='xml'>XML</a> (4)</li> 800 </ul> 801 802<ul class="group"> 803 </ul> 804 805 <ul class="viewing">
806 </ul> 807 808</div> <!-- .sidebar-box --> 809 810<div class="sidebar-heading">OTHER WORKS</div> 811 <div class="small-box pos-0 pos-even"> 812 <a class="thumbnail" href="/autotrader-uk/" title="Role working as an Interface Developer on Trader Media's online marketplace with over 1 billion page impressions per month." style=""> 813 <img src="//desalasworks.com/wp-content/themes/desalasworks.v1.7/img/spacer5x4.png" width="100%" /> 814 </a> 815 <p class="title"><a href="/autotrader-uk/" rel="bookmark" title="See AutoTrader UK">AutoTrader UK</a></p> 816 <p class="category"><a href="/category/code/" rel="category tag">Code</a>, <a href="/category/ui/" rel="category tag">UI</a>, <a href="/category/websites/" rel="category tag">Websites</a></p> 817 </div> 818 <div class="small-box pos-1 pos-odd"> 819 <a class="thumbnail" href="/markit-commission-manager/" title="Commission Manager is a trade reconciliation tool enabling brokers and funds in the worlds largest financial institutions to settle their commission differences." style="background-image: url(//desalasworks.com/wp-content/uploads/markit_comission_manager.jpg);"> 820 <img src="//desalasworks.com/wp-content/themes/desalasworks.v1.7/img/spacer5x4.png" width="100%" /> 821 </a> 822 <p class="title"><a href="/markit-commission-manager/" rel="bookmark" title="See Markit Commission Manager">Markit Commission Manager</a></p> 823 <p class="category"><a href="/category/web-applications/" rel="category tag">Web Applications</a></p> 824 </div> 825 <div class="small-box pos-2 pos-even"> 826 <a class="thumbnail" href="/article/asynchronous-neural-networks-in-javascript/" title="For the past few months Iâve been trying to get some of my robots to roam around the house with a mind of their own, learning as they go. It turns out that the task is harder than I thought. The main issue is paralell signal processing. To illustrate: a single robot will have usually […]" style="background-image: url(//desalasworks.com/wp-content/uploads/neuralspike.202x162.jpg);"> 827 <img src="//desalasworks.com/wp-content/themes/desalasworks.v1.7/img/spacer5x4.png" width="100%" /> 828 </a> 829 <p class="title"><a href="/article/asynchronous-neural-networks-in-javascript/" rel="bookmark" title="See Asynchronous Neural Networks in JavaScript">Asynchronous Neural Networks in JavaScript</a></p> 830 <p class="category"><a href="/category/code/" rel="category tag">Code</a>, <a href="/category/iot/" rel="category tag">IoT</a></p> 831 </div> 832 833</div> <!-- #sidebar --><div style="clear:both;"></div> 834 835</div> 836</div> 837<div class="footer-wrapper"> 838<div class="footer"> 839 <div class="footer-top">
840 <a name="contact"></a><a name="more"></a> 841 <div class="profile"> 842 <i class="profile-pic">Pic</i> 843 <h4>Steven de Salas</h4> 844 <h5>Full Stack Developer</h5> 845 </div> 846 </div> 847 <div class="footer-left"> 848 <h1>contact</h1> 849 <p>Steven loves creating RESTful APIs, back-end automations and interfaces that make everyday tasks simpler and more enjoyable. He is a full-stack developer, a Node.js and JavaScript specialist with many years of experience.</p> 850 <p>He recently had a pivotal role bootstrapping the core platform infrastructure for <a href="/letmepark"><u>LetMePark</u></a>, an urban mobility startup based in Madrid, Spain.</p> 851 <p>He has also lead the engineering effort in several projects with substantial impact both in UK and across Europe</p> 852 <!--p><div style="border:1px solid #fff;padding:5px 10px;font-weight: bold;font-size:14px;text-align:center;background-color:#b4520a;;color:#fff;-moz-border-radius:10px;border-radius:10px;"> 853 AVAILABLE IN: -32 MONTHS 854 </div></p--> 855 <!--p><div style="border:1px solid #fff;padding:5px 10px;font-weight: bold;font-size:14px;text-align:center;background-color:#008800;;color:#fff;-moz-border-radius:10px;border-radius:10px;"> 856 AVAILABLE IN -87 WEEKS!! 857 </div></p--> 858 <!--p><div style="border:1px solid #fff;padding:5px 10px;font-weight: bold;font-size:14px;text-align:center;background-color:#008800;;color:#fff;-moz-border-radius:10px;border-radius:10px;"> 859 AVAILABLE NOW 860 </div></p--> 861 <!--p><div style="border:1px solid #fff;padding:5px 10px;font-weight: bold;font-size:14px;text-align:center;background-color:#b4520a;;color:#fff;-moz-border-radius:10px;border-radius:10px;"> 862 AVAILABLE SOON! 863 </div></p--> 864 <!--p><div style="border:1px solid #fff;padding:5px 10px;font-weight: bold;font-size:14px;text-align:center;background-color:#b4520a;;color:#fff;-moz-border-radius:10px;border-radius:10px;"> 865 AVAILABLE HALF TIME 866 </div></p--> 867 <p><div style="border:1px solid #fff;padding:5px 10px;font-weight: bold;font-size:14px;text-align:center;background-color:#b4520a;;color:#fff;-moz-border-radius:10px;border-radius:10px;"> 868 UNAVAILABLE 869 </div></p> 870 <p>If you are after some help with your project feel free to get in touch on any of the following:</p> 871 <div class="block icon-linkedin"><a href="https://linkedin.com/in/sdesalas" target="_blank">linkedin.com/in/sdesalas</a></div> 872 <div class="block icon-telegram-sq"><a href="http://t.me/sdesalas" target="_blank">t.me/sdesalas</a></div> 873 <div class="block icon-whatsapp">+61 49<span style="display:none;">2 51</span>8 6
873<script type="text/javascript">document.write("65 3");</script>
87304</div> 874 <div class="block icon-email">ste<span style="display:none;">da</span>ven
874<script type="text/javascript">document.write("@de");</script>
874salasworks.com</div> 875 <div style="clear:both;"></div> 876 <h1>tools of the trade</h1> 877 <p>While each project has a different technology stack, these are some of the languages and applications that Steven uses more regularly.</p> 878 <div class="panel"> 879 <p>For back-end integration:</p> 880 <div class="block icon-express"><a href="/skills/express">Express</a></div> 881 <div class="block icon-nodejs"><a href="/skills/javascript">Node.js</a></div> 882 <div class="block icon-docker"><a href="/skills/docker">Docker</a></div> 883 <div class="block icon-bash"><a href="/skills/bash">Bash</a></div> 884 <div style="clear:both;"></div> 885 <p>For data storage:</p> 886 <div class="block icon-mongodb"><a href="/skills/mongodb">MongoDb</a></div> 887 <div class="block icon-db"><a href="/skills/sql-server">SQL (Postgres, MSSQL)</a></div> 888 <div class="block icon-redis"><a href="/skills/redis">Redis</a></div> 889 <div class="block icon-aws"><a href="/skills/aws">AWS (DynamoDB, S3)</a></div> 890 <div style="clear:both;"></div> 891 <p>For front-end work:</p> 892 <div class="block icon-vuejs"><a href="/skills/vue-js">Vue.js</a></div> 893 <div class="block icon-react"><a href="/skills/react">React</a></div> 894 <div class="block icon-svelte"><a href="/skills/svelte">Svelte</a></div> 895 <div class="block icon-feeds"><a href="/skills/javascript">JavaScript/TS</a></div> 896 <div class="block icon-css"><a href="/skills/css3">CSS</a></div> 897 <div class="block icon-html"><a href="skills/html5">HTML5</a></div> 898 <div style="clear:both;"></div> 899 </div> 900</form> 901 902 </div> 903 <div class="footer-right"> 904 <h1>more</h1> 905 <p class="association"><a href="https://aws.amazon.com/certification/" target="_blank"> 906 <img src="//desalasworks.com/wp-content/themes/desalasworks.v1.7/img/aws-logo.png" alt="StackOverflow" title="AW
906S Certified Developer" style="background:#fff;width:75px;"/> 907 </a> 908 <strong>AWS Certified Developer</strong><br/> 909 <em>Since 2017</em> 910 </p> 911 <p class="association"><a href="http://stackoverflow.com/users/448568/steven-de-salas" target="_blank"> 912 <img src="//desalasworks.com/wp-content/themes/desalasworks.v1.7/img/stackoverflow-logo.png" alt="StackOverflow" title="Stack Overflow Contributor" style="background:#fff;width:75px;"/> 913 </a> 914 <strong>StackOverflow Contributor</strong><br/> 915 <em>Since 2011</em> 916 </p> 917 <p class="association"><a href="https://github.com/sdesalas/" target="_blank"> 918 <img src="//desalasworks.com/wp-content/themes/desalasworks.v1.7/img/github-logo.png" alt="Github" title="Github Contributor" style="background:#fff;width:75px;"/> 919 </a> 920 <strong>Github Contributor</strong><br/> 921 <em>Since 2010</em> 922 </p> 923 <p class="association"><a href="http://en.wikipedia.org/wiki/Microsoft_Certified_Professional" target="_blank"> 924 <img src="//desalasworks.com/wp-content/themes/desalasworks.v1.7/img/mcpd-logo.gif" alt="Microsoft Certified Professional Developer" title="Microsoft Certified Professional Developer" /> 925 </a> 926 <strong>Microsoft Certified <br/>Professional Developer</strong><br/> 927 <em>Since 2010</em> 928 </p> 929 <p class="association"><a href="https://en.wikipedia.org/wiki/Association_of_Independent_Professionals_and_the_Self-Employed" target="_blank"> 930 <img src="//desalasworks.com/wp-content/themes/desalasworks.v1.7/img/pcg-logo.jpg" alt="Professional Contractors Group" title="Professional Contractors Group" /> 931 </a> 932 <strong>Professional Contractors Group</strong><br/> 933 <em>Member since 2004</em> 934 </p> 935 </div> 936</div> 937</div> 938<!--div style=" 939 clear: both; 940 color: white; 941 text-align: center; 942 font-size: 75%; 943 padding: 3em 0; 944">Desalasworks Ltd registered in England and Wales Number 07267267</div--> 945<div style="clear:both; padding: 3em;"></div>
946<script data-goatcounter="https://desalasworks.goatcounter.com/count" async src="//gc.zgo.at/count.js"></script>
946 947 948</body> 949</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.