<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Andrew Bales on Medium]]></title>
        <description><![CDATA[Stories by Andrew Bales on Medium]]></description>
        <link>https://medium.com/@agbales?source=rss-c8642f7f989a------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*Qth7c2XVC3FhmTlTXBf3nw.jpeg</url>
            <title>Stories by Andrew Bales on Medium</title>
            <link>https://medium.com/@agbales?source=rss-c8642f7f989a------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 16 May 2026 17:23:29 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@agbales/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[How to see your React state & props in the browser]]></title>
            <link>https://medium.com/free-code-camp/how-to-see-your-react-state-props-in-the-browser-774098a50fcc?source=rss-c8642f7f989a------2</link>
            <guid isPermaLink="false">https://medium.com/p/774098a50fcc</guid>
            <category><![CDATA[firefox]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[coding]]></category>
            <category><![CDATA[reactjs]]></category>
            <category><![CDATA[chrome]]></category>
            <dc:creator><![CDATA[Andrew Bales]]></dc:creator>
            <pubDate>Mon, 15 Apr 2019 21:40:41 GMT</pubDate>
            <atom:updated>2019-04-15T21:40:41.822Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*QeEbGcmtoQ3T_zUdpG8TZw.jpeg" /></figure><p>If you’re building a web app with React, you may want to see the state or props of components in real-time. Here’s a quick solution for Chrome &amp; FireFox!</p><h3>React Developer Tools</h3><p>Install the React Developer Tools extension for <a href="https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en">Chrome</a> or <a href="https://addons.mozilla.org/en-US/firefox/addon/react-devtools/">FireFox</a>. It allows you to inspect React component hierarchies within the developer tools — the same way you would peek at the DOM elements, console, or network.</p><h3>Inspecting React Components</h3><ol><li>Open your app and inspect the page with developer tools (Command+Option+I).</li><li>Select the React Developer Tools</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/813/1*d-0Whw8xiwsZC3Sriac0-g.png" /></figure><p>3. Pick a component in the tree to see its state and current props.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/893/1*mHwnzemjHotJHk4nBMzQSg.gif" /></figure><p>You can also select a React element directly from the page by hovering over it with the selection tool:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/48/1*TOWQfbNuzjCivOcRCRZVgg.png" /><figcaption>Selection tool menu icon in developer tools</figcaption></figure><h3>Modifying the State</h3><p>If you want to update your state in the browser— you can! Modify it by clicking and editing state attributes in the React tab. This will re-render the DOM, passing the state down through the props.</p><p>Happy coding! 🚀</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=774098a50fcc" width="1" height="1" alt=""><hr><p><a href="https://medium.com/free-code-camp/how-to-see-your-react-state-props-in-the-browser-774098a50fcc">How to see your React state &amp; props in the browser</a> was originally published in <a href="https://medium.com/free-code-camp">We’ve moved to freeCodeCamp.org/news</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to scrape with Ruby and Nokogiri and map the data]]></title>
            <link>https://medium.com/free-code-camp/how-to-scrape-with-ruby-and-nokogiri-and-map-the-data-bd9febb5e18a?source=rss-c8642f7f989a------2</link>
            <guid isPermaLink="false">https://medium.com/p/bd9febb5e18a</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[google-maps]]></category>
            <category><![CDATA[nokogiri]]></category>
            <category><![CDATA[ruby]]></category>
            <category><![CDATA[technology]]></category>
            <dc:creator><![CDATA[Andrew Bales]]></dc:creator>
            <pubDate>Thu, 24 May 2018 22:52:19 GMT</pubDate>
            <atom:updated>2018-06-01T04:38:32.294Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kUyC5E-rXXkL4DcR8L91rA.jpeg" /><figcaption>Ruby, JSON, &amp; Nokogiri</figcaption></figure><p>Sometimes you want to grab data from a website for your own project. So what do you use? Ruby, Nokogiri, and JSON to the rescue!</p><p>Recently, I was working on a project to map <a href="https://bridgereports.com/">data about bridges</a>. Using Nokogiri, I was able to capture a city’s bridge data from a table. I then used links within that same table to scrape associated pages. Finally, I converted the scraped data to JSON and used it to populate a Google Map.</p><p>This article walks you through the tools I used and how the code works!</p><p>See the full code on my <a href="https://github.com/agbales/wichita-bridges">GitHub</a> repo.</p><p>Live map demo <a href="https://agbales.github.io/wichita-bridges/">here</a>.</p><h3>The Project</h3><p>My goal was to take a table from a bridge data <a href="https://bridgereports.com/">website</a> and turn it into a Google map with geolocated pins that would produce informational popups for each bridge.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*z4v8QX5kZHFifiOpH7Mqpw.jpeg" /><figcaption>The Idea: HTML Table to Map</figcaption></figure><p>To make this happen, I’d need to:</p><ol><li>Scrape data from the original website.</li><li>Convert that data into a <a href="https://www.w3schools.com/js/js_json_objects.asp">JSON object</a>.</li><li>Apply that data to make a new, interactive map.</li></ol><p>Your project will vary, surely — how many people are trying to map antique bridges? — but I hope this process will prove useful for your context.</p><h3>Nokogiri</h3><p>Ruby has an amazing web scraping gem called <a href="https://github.com/sparklemotion/nokogiri">Nokogiri</a>. Among other features, it allows you to search HTML documents by CSS selectors. That means if we know the ids, classes, or even types of elements where the data is stored in the DOM, we’re able to pluck it out.</p><h4>The scraper</h4><p>If you’re following along with the <a href="https://github.com/agbales/wichita-bridges">GibHub repo</a>, you can find my scraper in bridges_scraper.rb</p><pre>require &#39;open-uri&#39;<br>require &#39;nokogiri&#39;<br>require &#39;json&#39;</pre><p>Open-uri lets us open the HTML like a file and pass it to Nokogiri for the heavy lifting.</p><p>In the code below, I’m passing the DOM information from the URL with the bridge data over to Nokogiri. I then find the table element holding the data, search for its rows, and iterate through them.</p><pre>url = &#39;<a href="https://bridgereports.com/city/wichita-kansas/&#39;">https://bridgereports.com/city/wichita-kansas/&#39;</a><br>html = open(url)</pre><pre>doc = Nokogiri::HTML(html)<br>bridges = []<br>table = doc.at(&#39;table&#39;)</pre><pre>table.search(&#39;tr&#39;).each do |tr|<br>  bridges.push(<br>    carries: cells[1].text,<br>    crosses: cells[2].text,<br>    location: cells[3].text,<br>    design: cells[4].text,<br>    status: cells[5].text,<br>    year_build: cells[6].text.to_i,<br>    year_recon: cells[7].text,<br>    span_length: cells[8].text.to_f,<br>    total_length: cells[9].text.to_f,<br>    condition: cells[10].text,<br>    suff_rating: cells[11].text.to_f,<br>    id: cells[12].text.to_i<br>  )<br>end</pre><pre>json = JSON.pretty_generate(bridges)<br>File.open(&quot;data.json&quot;, &#39;w&#39;) { |file| file.write(json) }</pre><p>Nokogiri has lots of methods (here’s a <a href="https://github.com/sparklemotion/nokogiri/wiki/Cheat-sheet">cheat sheet</a> and a starter <a href="https://readysteadycode.com/howto-parse-html-with-ruby-and-nokogiri">guide</a>!). We’re using just a few.</p><p>The table is found with <strong>.at(‘table’)</strong>, which returns the first occurrence of a table element in the DOM. This works just fine for this relatively simple page.</p><p>With the table in hand, <strong>.search(‘tr’) </strong>provides an array of the row elements that we iterate over with <strong>.each</strong>. In each row, the data is cleaned up and pushed into a single entry for the bridges array.</p><p>After all the rows are collected, the data is converted into JSON and saved in a new file called “data.json”.</p><h3>Combining data from multiple pages</h3><p>In this case, I needed information from other associated pages. Specifically, I needed the latitude and longitude of each bridge, which was not featured on the table. However, I found that the link in the first cell of each row led to a page that <em>did</em> provide those details.</p><p>I needed to write code that did a few things:</p><ul><li>Gathered links from the first cell in the table.</li><li>Created a new Nokogiri object from the HTML on that page.</li><li>Pluck out the latitude and longitude.</li><li>Sleep the program until that process completes.</li></ul><pre>cells = tr.search(&#39;th, td&#39;)<br>  links = {}<br>  cells[0].css(&#39;a&#39;).each do |a|<br>    links[a.text] = a[&#39;href&#39;]<br>  end<br>  <br>  got_coords = false<br>  <br>  if links[&#39;NBI report&#39;]<br>    nbi = links[&#39;NBI report&#39;]<br>    report = &quot;<a href="https://bridgereports.com">https://bridgereports.com</a>&quot; + nbi<br>    report_html = open(report)<br>    sleep 1 until report_html<br>    r = Nokogiri::HTML(report_html)<br>    <br>    lat = r.css(&#39;span.latitude&#39;).text.strip.to_f<br>    long = r.css(&#39;span.longitude&#39;).text.strip.to_f</pre><pre>    got_coords = true<br>  else<br>    got_coords = true<br>  end<br>  <br>  sleep 1 until got_coords == true</pre><pre>  bridges.push(    <br>    links: links,    <br>    latitude: lat,    <br>    longitude: long,    <br>    carries: cells[1].text,    <br>    ..., # all other previous key/value pairs<br>  )<br>end</pre><p>A few additional things are worth pointing out here:</p><ul><li>I’m using the “got_coords” as a simple binary. This is set to <strong>false</strong> by default and is toggled when the data is captured OR simply not available.</li><li>The latitude and longitude are located in spans with corresponding classes. That makes securing the data simple: <strong>.css(‘span.latitude’) </strong>This is followed by<strong> .text, .strip </strong>and <strong>.to_f </strong>which 1) gets the text from the span, 2) strips any excess whitespace, and 3) converts the string to a float number.</li></ul><h3><strong>JSON → Google Map</strong></h3><p>The newly formed JSON object has to be modified a touch to fit the Google Maps API. I did this with JavaScript inside <strong>map.js</strong></p><p>The JSON data is accessible within <strong>map.js</strong> because it has been moved to the JS folder, assigned to a variable called “bridge_data”, and included in a &lt;script&gt; tag in index.html.</p><p>All right! We’ll now convert the JSON file (assigned to the variable bridge_data) to a new array that’s usable by Google Maps.</p><pre>const locations = bridge_data.map(function(b) {<br>  var mapEntry = [];<br>  var info = &quot;&lt;b&gt;Built In: &lt;/b&gt;&quot; + b.year_build + &quot;&lt;br&gt;&quot; +<br>             &quot;&lt;b&gt;Span Length: &lt;/b&gt;&quot; + b.span_length + &quot; ft&lt;br&gt;&quot; +<br>             &quot;&lt;b&gt;Total Length: &lt;/b&gt;&quot; + b.total_length + &quot; ft&lt;br&gt;&quot; +<br>             &quot;&lt;b&gt;Condition: &lt;/b&gt;&quot; + b.condition + &quot;&lt;br&gt;&quot; +<br>             &quot;&lt;b&gt;Design: &lt;/b&gt;&quot; + b.design + &quot;&lt;br&gt;&quot;;<br>  mapEntry.push(<br>    info,<br>    b.latitude,<br>    b.longitude,<br>    b.id<br>  )<br>  return mapEntry;<br>});</pre><p>I’m using <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map">.map</a> to create a new dimensional array called “locations”. Each entry has info, which will appear in our Google Maps popup if the user clicks on that pin on the map. We also include the latitude, longitude, and unique bridge ID.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*26MFP40WnJgWslvDPjUyOg.png" /><figcaption>Bridge map from JSON data</figcaption></figure><p>The result is a Google Map that plots the array of locations with info-rich popups for each bridge!</p><p>Did this help you? Give it a few claps and follow!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bd9febb5e18a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/free-code-camp/how-to-scrape-with-ruby-and-nokogiri-and-map-the-data-bd9febb5e18a">How to scrape with Ruby and Nokogiri and map the data</a> was originally published in <a href="https://medium.com/free-code-camp">We’ve moved to freeCodeCamp.org/news</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How you can build a terminal game with CSV and Ruby]]></title>
            <link>https://medium.com/free-code-camp/how-you-can-build-a-terminal-game-with-csv-and-ruby-a269f17b88b0?source=rss-c8642f7f989a------2</link>
            <guid isPermaLink="false">https://medium.com/p/a269f17b88b0</guid>
            <category><![CDATA[ruby]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[games]]></category>
            <category><![CDATA[technology]]></category>
            <dc:creator><![CDATA[Andrew Bales]]></dc:creator>
            <pubDate>Thu, 24 May 2018 17:57:39 GMT</pubDate>
            <atom:updated>2018-05-24T17:57:39.274Z</atom:updated>
            <content:encoded><![CDATA[<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F268122405%3Fapp_id%3D122963&amp;dntp=1&amp;url=https%3A%2F%2Fvimeo.com%2F268122405&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F698795561_1280.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1920" height="1080" frameborder="0" scrolling="no"><a href="https://medium.com/media/8fb2fe3a080755b3489d7ba1362d22a3/href">https://medium.com/media/8fb2fe3a080755b3489d7ba1362d22a3/href</a></iframe><p>In this article, you’ll learn how to build a terminal game with a CSV and a few Ruby gems! See a demo in the video above, and find the code on <a href="https://github.com/agbales/solar-system">GitHub</a>.</p><p>This project comes from a lecture I gave at <a href="https://www.adadevelopersacademy.org">Ada Developers Academy</a> in Seattle. The topic was the Ruby CSV library, and I wanted a fun way to illustrate its methods and potential.</p><p>In class, we discussed how most people use programs like Excel to create and edit CSVs. They make updates by clicking cells and changing values. But the question for us became: what can we do with a CSV if we approach it as <strong>programmers</strong>? Using a programming language like Ruby, how can you open, read, and manipulate those values? Can those orderly rows and columns become a database for an application?</p><p>This article covers those questions in three sections:</p><ol><li>Comma-separated values</li><li>Ruby’s CSV library: creating, opening, appending, using headers</li><li>Building the game</li></ol><h3>Comma-Separated Values</h3><p>CSV stands for comma-separated values, and that’s exactly what it sounds like. If you’ve ever opened one of these files in a program like Excel, you’ve seen these values rendered in a spreadsheet.</p><p>However, if you were to open that same file in a text editor like Atom or Sublime, you’d find a series of — you guessed it — comma-separated values. As you see below, Excel uses those raw values to render a user-friendly table.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wrExasjrLVB2AOvKDG_TAQ.jpeg" /></figure><h3>Quick Setup</h3><p>It’s easiest to follow along if you download this <a href="https://github.com/agbales/solar-system">GitHub repository</a>. Once you’ve done that, navigate to that folder in your terminal. This repo includes <strong>all</strong><em> </em>of the examples below, so please know that you’ll need to comment out sections that you don’t want to run.</p><p>Also, you’ll want to install <a href="https://github.com/awesome-print/awesome_print">Awesome Print</a>, which beautifies terminal output:</p><pre>gem install awesome_print</pre><h3>Ruby’s CSV Library</h3><p>Ruby comes with a <a href="http://ruby-doc.org/stdlib-2.0.0/libdoc/csv/rdoc/CSV.html">CSV library</a> that allows us to open, read, and manipulate CSV files.</p><h4><strong>Creating a CSV</strong></h4><p>Let’s start by making our own CSV. In the Github repo, you’ll find planets.rb. This file begins by setting the variable planets equal to a two-dimensional array (an array of arrays).</p><p>In each, we have planet attributes: id, name, mass, and distance. We’ve assigned the attribute names to the headers variable as another array.</p><pre>require &#39;csv&#39;<br>require &#39;awesome_print&#39;</pre><pre>planets = [<br>  [1, &quot;Mercury&quot;, 0.055, 0.4],<br>  [2, &quot;Venus&quot;, 0.815, 0.7],<br>  [3, &quot;Earth&quot;, 1.0, 1.0],<br>  [4, &quot;Mars&quot;, 0.107, 1.5]<br>]<br><br>headers = [&quot;id&quot;, &quot;name&quot;, &quot;mass&quot;, &quot;distance&quot;]<br><br>CSV.open(&quot;planet_data.csv&quot;, &quot;w&quot;) do |file|<br>  file &lt;&lt; headers<br>  planets.each do |planet|<br>    file &lt;&lt; planet<br>  end<br>end</pre><p>Above, CSV.open accepts up to three arguments:</p><pre>CSV.open(file name, mode, options)</pre><p>We have given it a file name (planet_data.csv). Because we have also given the mode of “w” (Write-only), it creates a new file for us even if it didn’t exist already. No options were passed in this time.</p><p>The following block does a few things:</p><ol><li>It adds the headers array to the file we’ve created. This creates a single row with four columns — each with a string entry of the property name.</li><li>We use planets.each to iterate through the planet array (filled with info about its id, name, and so on) and append each entry as an individual row.</li></ol><p>If you run this bit of code, you’ll find the following CSV has been created:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4-WNK1cd7_0AQ_0yKVeNWg.png" /></figure><h4>Modes</h4><p>Above, we used “w” as our mode to write a new file. You have a number of other options available to you, depending on the task at hand. The biggest factors to consider are if you want to read and/or write, and where in the CSV you’d like to start your work.</p><p>For instance, if you are using the file to populate your website with listings, “r” (read-only) would be an appropriate mode. If you want to add new planets to your CSV, the “a” mode (append read-write) would begin at the end of the file and immediately let you append those rows.</p><p>Here’s a complete list of modes:</p><pre>“r”  Read-only, starts at beginning of file (default mode).<br>“r+” Read-write, starts at beginning of file.<br>“w”  Write-only, truncates existing file to zero length.<br>“w+” Read-write, truncates existing file to zero length.<br>“a”  Append write-only, starts at end of file if file exists.<br>“a+” Append read-write, starts at end of file if file exists.<br>“b”  Binary file mode.<br>“t”  Text file mode.</pre><h4><strong>Appending</strong></h4><p>We can append a new planet to planet_data.csv like this:</p><pre>CSV.open(&quot;planet_data.csv&quot;, &quot;a&quot;) do |file|<br>  file &lt;&lt; [5, &quot;Jupiter&quot;, 1234, 3321]<br>end</pre><p>In the mode list above, “a” is “write-only” and “starts at end of file.” So Jupiter’s information will be inserted at the end of the existing CSV.</p><h4>Iterating</h4><p>Because .open with the “r” mode will return an array of arrays, we can use .each to iterate over the rows. The code below will print every row of the CSV in the terminal.</p><pre>CSV.open(&quot;planet_data.csv&quot;, &quot;r&quot;).each do |row|<br>  ap row<br>end</pre><p>You can take this a step further to create interpolated sentences!</p><pre>CSV.open(&quot;planet_data.csv&quot;, &quot;r&quot;).each do |row|<br>  ap &quot;#{row[1]} has a mass of #{row[2]} and distance of #{row[3]}.&quot;<br>end</pre><p>This is great, but it could be a bit better. We’re having to use indices (1, 2, 3) to access the data. This is prone to errors and generally no fun. Next, we’ll see how to fix this by passing in options.</p><h4>Using Headers</h4><p>When you add in the option for headers to be true, you’ll get back a new CSV::Table object.</p><pre>csv_with_headers = CSV.open(&quot;planet_data.csv&quot;, &quot;r&quot;, headers: true, header_converters: :symbol)<br><br>csv_with_headers.each do |row|<br>  ap row<br>end<br></pre><p>Reading with headers and converting those headers to symbols, we will get back a unique object: an array of hashes. That means it’s possible to iterate through each row as we did before, but then we can also use the symbols in the hash to isolate key data.</p><p>If we return to the sentence example, it becomes:</p><pre>CSV.open(&quot;planet_data.csv&quot;, &quot;r&quot;, headers: true, header_converters: :symbol).each do |row|<br>  ap &quot;#{row[:name]} has a mass of #{row[:mass]} and distance of #{row[:distance]}.&quot;<br>end</pre><p>That’s much more readable than the number indices we used before!</p><p>When headers are set to true, the library gives us the CSV::Table object, which also gives us access to some handy methods. Below, .read is synonymous with .open in the “r” mode:</p><pre>csv = CSV.read(&quot;planet_data.csv&quot;, headers: true, header_converters: :symbol)<br>ap csv               # &lt;CSV::Table mode:col_or_row row_count:6&gt;<br>ap csv.headers       # Returns an array of headers<br>ap csv.by_col[:id]   # Array of id column data<br>ap csv.by_col[:name] # Array of name column data<br>ap csv.by_row[0]     # Entire row at 0 (or any position)<br>ap csv[:name][3]     # Name of the 3rd entry =&gt; &quot;Mars&quot;<br>ap csv[3][:name]     # 3rd row&#39;s name =&gt; &quot;Mars&quot;</pre><h3>Building a Solar System Game!</h3><p>We know how to open and use data in a CSV file with Ruby, so let’s put those methods to work to make a solar system game.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*CsSq6749MEFgk7jBOHroEg.gif" /><figcaption>Via <a href="https://imahimesama.tumblr.com/post/164924479335">SUPERNOVA</a></figcaption></figure><h4><strong>Setup</strong></h4><p>You’ll need to install Catpix and Launchy. Catpix allows illustrations in the terminal and Launchy lets us to control a browser window. In the terminal:</p><pre>gem install catpix <br>gem install launchy </pre><h4><strong>CSV as a Database</strong></h4><p>You may want to open “Solar System.csv” in Excel to visually get a sense of the attributes for each entry. Once you’re comfortable with the data, we’ll use Ruby to read the CSV file and assign it to a global variable ($solar_system_data). This will serve as our database.</p><p>As the game opens, we welcome the user TO THE SOLAR SYSTEM! and create that database like so:</p><pre>require &#39;catpix&#39;<br>require &#39;launchy&#39;<br><br>$solar_system_data = CSV.read(&quot;Solar System.csv&quot;, headers: true, header_converters: :symbol)</pre><pre>ap &quot;WELCOME TO THE SOLAR SYSTEM!&quot;</pre><p>The game really starts up when we call the method explore_planet. That method contains this code:</p><pre>ap $solar_system_data.by_col[:name]<br>prompt = &quot;Where would you like to start? 0 - #{$solar_system_data.length}&quot;</pre><pre>ap prompt<br>input = gets.chomp</pre><pre>until $selected_planet &amp;&amp; /\d/.match(input)<br>  ap prompt<br>  input = gets.chomp<br>  $selected_planet = $solar_system_data[input.to_i]<br>end</pre><pre>ap $selected_planet</pre><p>Above, the terminal prints all of the names from the “name” column. It then asks the user select an entry between the first (0-index) to the last (our data length). This is a good point to pause to consider the following:</p><p><strong>Question:</strong> If we’ve used headers in order to get a hash, how can solar_system_data.length == 14?</p><p><strong>Answer:</strong> This CSV::Table may <strong>look</strong> like a hash, but it’s actually an array of hashes. Therefore, it has a length and we can iterate through each hash. To select the correct record, we just need to convert the input from a string to an integer (.to_i)</p><p>You’ll also see that we used an until statement. This validates the selection — requesting a response until the user gives us a valid number. Once a proper selection is made, the terminal prints out the planet info.</p><p>The user can then pick if they want to LEARN about or SEE the planet:</p><pre>prompt = &quot;Do you want to LEARN or SEE?&quot;<br>ap prompt</pre><pre>while input = gets.chomp<br>  case input.downcase<br>  when &quot;learn&quot;<br>    Launchy.open($selected_planet[:uri])<br>    return<br>  when &quot;see&quot;<br>    Catpix::print_image $selected_planet[:image]<br>    return<br>  else<br>    ap prompt<br>  end<br>end</pre><p>Similar to before, a while statement is used to make sure we get a valid entry. This time, it either uses Launchy to open the associated URI for the planet or prints the image in the terminal with Catpix.</p><p>The game has one more piece of functionality. This is held in the select_attribute method. We use the CSV methods we’ve just covered to return specific attributes for <strong>every</strong> planet in our database.</p><pre>ap &quot;Which attribute do want to see for each planet (ex: number_of_moons)?&quot;</pre><pre>ap $solar_system_data.headers.to_s<br>attribute = gets.chomp</pre><pre>ap &quot;Here are the #{attribute} findings:&quot;</pre><pre>$solar_system_data.each do |row|<br>  ap &quot;#{row[:name]} --&gt; #{attribute}: #{row[attribute.to_sym]}&quot;<br>end</pre><p>First, we print out all of the headers as strings. This gives the user a list of attributes to pick from. With the user response, we can list out the planet name along with the attribute requested and its value.</p><p>Finally, they can SELECT another attribute or start over and EXPLORE individual planets:</p><pre>prompt = &quot;SELECT another attribute or EXPLORE another planet?&quot;<br>ap prompt</pre><pre>while input = gets.chomp<br>  case input.downcase<br>  when &quot;select&quot;<br>    select_attribute()<br>  when &quot;explore&quot;<br>    explore_planet()<br>  else<br>    ap prompt<br>  end<br>end</pre><p>I hope this helps clarify CSV methods and gets you excited to make your own games.</p><p>If you expand on this one or design something new, leave a comment. I’d love to see what you come up with!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a269f17b88b0" width="1" height="1" alt=""><hr><p><a href="https://medium.com/free-code-camp/how-you-can-build-a-terminal-game-with-csv-and-ruby-a269f17b88b0">How you can build a terminal game with CSV and Ruby</a> was originally published in <a href="https://medium.com/free-code-camp">We’ve moved to freeCodeCamp.org/news</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Making 8-Bit Art with JavaScript]]></title>
            <link>https://medium.com/@agbales/how-to-make-8-bit-art-with-javascript-eea808a70ba2?source=rss-c8642f7f989a------2</link>
            <guid isPermaLink="false">https://medium.com/p/eea808a70ba2</guid>
            <category><![CDATA[technology]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[digital-art]]></category>
            <category><![CDATA[retro]]></category>
            <category><![CDATA[image-processing]]></category>
            <dc:creator><![CDATA[Andrew Bales]]></dc:creator>
            <pubDate>Tue, 05 Dec 2017 06:15:57 GMT</pubDate>
            <atom:updated>2017-12-11T19:35:45.933Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SVsGg0jAvTIsovJxNakg7Q.jpeg" /><figcaption>Fresh to 8-bit Prince of Bel-Air</figcaption></figure><p>Want to make 8-bit style art from your photos? Me too. So I put together a project that draws from a wonderful JavaScript module called <a href="https://github.com/rogeriopvl/8bit/blob/master/README.md">8bit</a> from <a href="http://www.medium.com/@rogeriopvl">Rogério Vicente</a> (<a href="http://twitter.com/rogeriopvl">@rogeriopvl</a>)!</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodepen.io%2Fagbales%2Fembed%2Fpreview%2FjyXbbE%3Fheight%3D600%26slug-hash%3DjyXbbE%26default-tabs%3Djs%2Cresult%26host%3Dhttps%3A%2F%2Fcodepen.io%26embed-version%3D2&amp;url=https%3A%2F%2Fcodepen.io%2Fagbales%2Fpen%2FjyXbbE&amp;image=https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fi.cdpn.io%2F387928.jyXbbE.small.c15a63cf-a26b-4632-ba61-8ea57943b0e3.png&amp;key=d04bfffea46d4aeda930ec88cc64b87c&amp;type=text%2Fhtml&amp;schema=codepen" width="800" height="600" frameborder="0" scrolling="no"><a href="https://medium.com/media/4f9a1d8469eed7a805f453521d4074f0/href">https://medium.com/media/4f9a1d8469eed7a805f453521d4074f0/href</a></iframe><p>This pen allows you to submit any image URL to create new 8-bit renderings. It pixelates/abstracts the images to varying degrees with instant updates. It’s a fun project, but also a valuable tool if you’d like to find the best 8bit settings for any given project without re-coding image URLs or values by hand.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xVM-E74YShYld9IUOfESFg.jpeg" /></figure><h4>HTML</h4><p>The HTML uses a few inputs to do the manipulation and a canvas element to display the result. The first input allows users to specify their image. This is followed by a button that ‘uploads’ the user URL.</p><p>The subsequent “range” input is set to a default of 1.5, but allows for a enhancement and degradation from 0 (min) — 5(max). Finally the div#pixelationDisplay will be updated to mirror the current increment of pixelation.</p><pre>&lt;div id=&quot;controls&quot;&gt;<br>  &lt;div class=&quot;form-group&quot;&gt;<br>    &lt;label for=&quot;url&quot;&gt;Image URL&lt;/label&gt;<br>    &lt;input type=&quot;url&quot; class=&quot;form-control&quot; id=&quot;urlInput&quot;&gt;<br>    &lt;button id=&quot;submitImage&quot;&gt;Upload&lt;/button&gt;<br>  &lt;/div&gt;<br>  &lt;div class=&quot;form-group&quot;&gt;<br>    &lt;label for=&quot;pixelation&quot;&gt;Pixelation&lt;/label&gt;<br>    &lt;input id=&quot;pixelation&quot; type=&quot;range&quot; min=&quot;0&quot; max=&quot;5&quot; step=&#39;.1&#39; value=&quot;1.5&quot;/&gt;<br>    &lt;div id=&quot;pixelationDisplay&quot;&gt;1.5&lt;/div&gt;<br>  &lt;/div&gt;<br>&lt;/div&gt;</pre><pre>&lt;canvas id=&quot;canvas&quot;&gt;&lt;/div&gt;</pre><h4>JavaScript</h4><p>Three event listeners are in place to trigger the updates:</p><ol><li>When the DOM content loads, updateImage() is called. It will use the default image and default pixelation value of 1.5.</li><li>If the #submitImage button is clicked, updateImage() is called.</li><li>If the #pixelation range is modified, updateImage() is called.</li></ol><pre>document.addEventListener(&quot;DOMContentLoaded&quot;, function() { updateImage() });</pre><pre>document.getElementById(&#39;submitImage&#39;).addEventListener(&quot;click&quot;, updateImage);</pre><pre>document.getElementById(&#39;pixelation&#39;).addEventListener(&quot;input&quot;, updateImage);</pre><blockquote>Note: The <a href="https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded">DOMContentLoaded</a> event is fired when the initial HTML document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading.</blockquote><p>In the updateImage() function, two things occur:</p><ol><li><strong>Pixelation Value: </strong>The new sider value will is assigned to the #pixelationDisplay. The user sees this updated value.</li><li><strong>Image Update:</strong> A new 8bit image will be generated. It uses the current #urlInput field URL and value on the pixelation slider and replace the previous image on the canvas.</li></ol><pre>function updateImage() {<br>  // pixelation value<br>  var val = document.getElementById(&#39;pixelation&#39;).value;   <br>  document.getElementById(&#39;pixelationDisplay&#39;).innerHTML = val; </pre><pre>  // Image update<br>  var img = new Image();<br>  var defaultImage = // default image URL<br>  img.onload = function() { eightBit(document.getElementById(&#39;canvas&#39;), img, val) };<br>  var url = document.getElementById(&quot;urlInput&quot;).value;<br>  img.src = url || defaultImage;<br>}</pre><p>Like this project? Follow me on <a href="https://codepen.io/agbales/">CodePen</a>!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=eea808a70ba2" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Build a Best Sellers List with New York Times and Google Books API]]></title>
            <link>https://medium.com/free-code-camp/build-a-best-sellers-list-with-new-york-times-google-books-api-46201c30aec7?source=rss-c8642f7f989a------2</link>
            <guid isPermaLink="false">https://medium.com/p/46201c30aec7</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[digital-humanities]]></category>
            <category><![CDATA[api]]></category>
            <category><![CDATA[technology]]></category>
            <category><![CDATA[google]]></category>
            <dc:creator><![CDATA[Andrew Bales]]></dc:creator>
            <pubDate>Mon, 04 Dec 2017 03:13:27 GMT</pubDate>
            <atom:updated>2017-12-24T16:31:49.985Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tuzy15MrU82LA_GnVo8kKA.gif" /></figure><p>A single API may not always have all of the data you need. In this article, we’ll walk through the steps to combine two APIs by using unique identifiers from the New York Times API to grab book covers from the Google Books API.</p><p>You can find the full project on <a href="https://github.com/agbales/best-sellers">GitHub</a> and view a demo on <a href="https://codepen.io/agbales/full/LNWPYW/">CodePen</a>.</p><p>Here are the steps we’ll cover:</p><ol><li>Fetch best selling books data from the New York Times API.</li><li>Append listings to the DOM.</li><li>Query the Google Books API with ISBN numbers to add cover images to the listings.</li></ol><p>At the end of the tutorial, you’ll have a best sellers list! Here’s a peek:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bVvh3q1aNXWnfkjXNp_vwQ.png" /></figure><h3>Wait, but why?</h3><p>I first began working on this project a little over a year ago. I was learning about APIs and requesting keys to practice accessing and displaying data.</p><p>While exploring the <em>New York Times</em> API, I found that it was possible to get a list of best selling books. For each book on the list, the API provides a current rank and number of weeks on the list. It also offers info like a synopsis and an Amazon link.</p><p>I was able to populate textual info, but the list lacked the natural visual component of book covers. At the time, I didn’t see a clear road forward, so I put the project on the shelf.</p><p><strong>This is an instance where having access to an API is helpful, but incomplete.</strong></p><p>This week, I returned with the goal of adding books covers. I found that Google Books API will return thumbnails for books when provided an ISBN, a unique identifying number. As luck would have it, the New York Times API provides that ISBN.</p><p>We’re in business!</p><h3>Getting Started</h3><p>First, we want to generate a list of the top selling fiction books with a bit of info about each. It would be nice to display information about how long the book has been on the list. We also need to see the cover and provide a link for users to learn more about the book or buy a copy.</p><p>The New York Times<em> </em>API provides all of that information except for the book cover. Grab a free <a href="https://developer.nytimes.com">NYT API key</a> to get started.</p><p>We’ll use the <a href="https://developers.google.com/web/updates/2015/03/introduction-to-fetch">Fetch API</a> to get the best seller data for hardcover works of fiction:</p><pre>fetch(&#39;<a href="https://api.nytimes.com/svc/books/v3/lists.json?list-name=hardcover-fiction&amp;api-key=6ad84e249d054efeaefe1abb8f89df5b&#39;">https://api.nytimes.com/svc/books/v3/lists.json?list-name=hardcover-fiction&amp;api-key=&#39;</a> + apiKey, {<br>    method: &#39;get&#39;,<br>  })<br>  .then(response =&gt; { return response.json(); })<br>  .then(json =&gt; { console.log(json); });</pre><p>If you inspect the browser, you’ll see a JSON object logged in the console. If you haven’t used an API before, it will be helpful to spend a moment looking through this object. Burrowing into the data to access exactly what you’re looking for may take a while to get used to.</p><p>The response returns 15 objects within “results”. Each result is one book. For clarity, this example uses .forEach() to drill down into the API response nytimesBestSellers looping over each book.</p><pre>nytimesBestSellers.results.forEach(function(book) {<br>  var isbn = book.isbns[1].isbn10;<br>  var bookInfo = book.book_details[0];<br>  var lastWeekRank = book.rank_last_week || ‘n/a’;<br>  var weeksOnList = book.weeks_on_list || ‘New this week’;</pre><pre>  // ...</pre><pre>});</pre><p>As we loop over each individual book, the variables are set to the data for their specific listing, which we’ll use when making the entry.</p><p>In the code listing above,</p><ul><li>the ISBN number is located within the book’s isbns array</li><li>we select the 10-digit version of the ISBN number at book_details[0]</li><li>the last week ranking is at rank_last_week and defaults to ‘n/a’</li><li>the number of weeks it has been on the best sellers list, is at weeks_on_list and defaults to “New this week” for books that appear on the list for the first time</li></ul><p>Next, we can make an HTML object to append to the best-seller-titles list. Be sure your project includes <a href="https://jquery.com/">jQuery</a>. On CodePen, you can go to settings and add it in the JavaScript panel.</p><pre>var listing = <br>  &#39;&lt;div id=&quot;&#39; + book.rank + &#39;&quot; class=&quot;entry&quot;&gt;&#39; + <br>    &#39;&lt;p&gt;&#39; + <br>      &#39;&lt;img src=&quot;&quot; class=&quot;book-cover&quot; id=&quot;cover-&#39; + book.rank + &#39;&quot;&gt;&#39; + <br>    &#39;&lt;/p&gt;&#39; + <br>    &#39;&lt;h2&gt;&lt;a href=&quot;&#39; + book.amazon_product_url + &#39;&quot; target=&quot;_blank&quot;&gt;&#39; + bookInfo.title + &#39;&lt;/a&gt;&lt;/h2&gt;&#39; +<br>    &#39;&lt;h4&gt;By &#39; + bookInfo.author + &#39;&lt;/h4&gt;&#39; +<br>    &#39;&lt;h4 class=&quot;publisher&quot;&gt;&#39; + bookInfo.publisher + &#39;&lt;/h4&gt;&#39; +<br>    &#39;&lt;p&gt;&#39; + bookInfo.description + &#39;&lt;/p&gt;&#39; + <br>    &#39;&lt;div class=&quot;stats&quot;&gt;&#39; +<br>      &#39;&lt;hr&gt;&#39; + <br>      &#39;&lt;p&gt;Last Week: &#39; + lastWeekRank + &#39;&lt;/p&gt;&#39; + <br>      &#39;&lt;p&gt;Weeks on list: &#39; + weeksOnList + &#39;&lt;/p&gt;&#39; +<br>    &#39;&lt;/div&gt;&#39; +<br>  &#39;&lt;/div&gt;&#39;;</pre><pre>$(&#39;#best-seller-titles&#39;).append(listing);</pre><p>Notice that the image is left blank. On <a href="https://codepen.io/agbales/pen/LNWPYW">CodePen</a>, I’ve added a placeholder image to fill in any undefined responses from Google.</p><p>Finally, we’ll can update the book rank number and pass along the rank and ISBN number to updateCover().</p><pre>$(&#39;#&#39; + book.rank).attr(&#39;nyt-rank&#39;, book.rank);</pre><pre>updateCover(book.rank, isbn);</pre><p>We can now write updateCover(), which will handle retrieving the thumbnail from the Google Books API.</p><h3>Google Books API</h3><p>We’ve gathered the textual parts of the listing, but to add a book cover, one of the easiest ways I came across was to call upon the Google Books API. Be sure to grab an API Key from the <a href="https://developers.google.com/books/">Google Books API</a>.</p><p>Using the 10-digit ISBN number, we can get a thumbnail book cover image by again using fetch(). As before, we have to drill down into the object to find the single link referencing the thumbnail image we’re looking for:</p><pre>function updateCover(id, isbn) {<br>  fetch(&#39;<a href="https://www.googleapis.com/books/v1/volumes?q=isbn:&#39;">https://www.googleapis.com/books/v1/volumes?q=isbn:&#39;</a> + isbn + &quot;&amp;key=&quot; + apiKey, {<br>    method: &#39;get&#39;<br>  })<br>  .then(response =&gt; { return response.json(); })<br>  .then(data =&gt; {<br>    var img = data.items[0].volumeInfo.imageLinks.thumbnail;<br>    img = img.replace(/^http:\/\//i, &#39;<a href="https://&#39;">https://&#39;</a>);<br>    $(&#39;#cover-&#39; + id).attr(&#39;src&#39;, img);<br>  })  <br>  .catch(error=&gt; {   <br>    console.log(error);<br>  });<br>}</pre><p>After the image is secured, replace() swaps any HTTP links to secure HTTPS versions. We then update the book cover by selecting the proper cover ID and updating its image source.</p><h3>Style</h3><p>I’ve added additional styles with SASS. If you’re more comfortable with CSS or SCSS, use the drop down button in that window to compile the code.</p><p>The last bit of JavaScript you’ll see controls the logo scaling. This code is triggered when the window scrolls. As the window scrolls down, the logo condenses from a height of 80px down to 35px.</p><pre>$(window).scroll(function (event) {<br>  var scroll = $(window).scrollTop();<br>  if (scroll &gt; 50) {<br>    $(‘#masthead’).css({‘height’:’50&#39;, ‘padding’ : ‘8’});<br>    $(‘#nyt-logo’).css({‘height’:’35&#39;});<br>  } else {<br>    $(‘#masthead’).css({‘height’:’100&#39;, ‘padding’:’10&#39;});<br>    $(‘#nyt-logo’).css({‘height’:’80&#39;});<br>  }<br>});</pre><h3>Final Thoughts</h3><p>It was exciting to return to a project and build on its features. While I may have approached this problem differently if I’d begun from scratch, this example shows a way to take a typical API call and add upon that work.</p><p>In fact, one reason I particularly wanted to share this project was remembering how frustrating it could get for me when I first started working with APIs. I’d get overwhelmed with the documentation, not sure which features or syntax were leading me in the right direction. I often wished for a clear example or walk-through of something a touch beyond the Hello World.</p><p>APIs each provide a specific service, and sometimes it’s necessary to combine them. This is just one way of bringing together multiple services, but I hope it’s a bit of inspiration for those exploring ways to combine APIs to create richer content.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=46201c30aec7" width="1" height="1" alt=""><hr><p><a href="https://medium.com/free-code-camp/build-a-best-sellers-list-with-new-york-times-google-books-api-46201c30aec7">Build a Best Sellers List with New York Times and Google Books API</a> was originally published in <a href="https://medium.com/free-code-camp">We’ve moved to freeCodeCamp.org/news</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to Build a Collaborative MIDI App with Express.js & Socket.io]]></title>
            <link>https://medium.com/hackernoon/how-to-build-a-collaborative-midi-app-with-express-js-socket-io-273663b63201?source=rss-c8642f7f989a------2</link>
            <guid isPermaLink="false">https://medium.com/p/273663b63201</guid>
            <category><![CDATA[tech]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[tutorial]]></category>
            <category><![CDATA[midi]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Andrew Bales]]></dc:creator>
            <pubDate>Mon, 10 Jul 2017 23:56:56 GMT</pubDate>
            <atom:updated>2019-04-25T05:53:40.142Z</atom:updated>
            <content:encoded><![CDATA[<p>In this tutorial I’ll show you how to build an app that allows you to use your midi keyboard to make music in real-time with friends! Here’s what we’re building:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F224262761&amp;url=https%3A%2F%2Fvimeo.com%2F224262761&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F643278001_1280.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1920" height="1080" frameborder="0" scrolling="no"><a href="https://medium.com/media/76f8c4c78c9f12b8cca45fdc384c2b01/href">https://medium.com/media/76f8c4c78c9f12b8cca45fdc384c2b01/href</a></iframe><blockquote>Try out a live demo: <a href="https://midi-collaboration.herokuapp.com/">https://midi-collaboration.herokuapp.com/</a></blockquote><blockquote>Check out the code: <a href="https://github.com/agbales/web-midi-collaboration">https://github.com/agbales/web-midi-collaboration</a></blockquote><p>As you can see, playing a note lights up a key in pink and displays the input data in the list. If another user joins the session, their input will light up blue and their data will appear in the list as blue entries.</p><p>We’ll break the process into 5 parts:</p><ol><li>Creating an App with Express.js</li><li>Connecting a Midi Controller</li><li>Adding Socket.io for Collaboration</li><li>Styling</li><li>Deploying to Heroku</li></ol><h3><strong>Step 1: Creating an Express.js App</strong></h3><p>We’ll get started by making a directory and initializing the project with these three terminal commands:</p><pre>mkdir midi-collaboration<br>cd midi-collaboration<br>npm init</pre><p>The NPM utility will ask for a bit of information to set up the package.json file. Feel free to provide that info or just hit enter to leave these fields blank for now.</p><p>Next, add Express:</p><pre>npm install express --save</pre><p>In package.json, you will now see Express included as a dependency.</p><p>In the root folder, make server.js with:</p><pre>touch server.js</pre><p>In server.js, add the following:</p><pre>var express = require(&#39;express&#39;);<br>var app = express();<br>const port = process.env.PORT || 8080;<br>var server = app.listen(port);</pre><pre>app.use(express.static(&#39;public&#39;));</pre><p>This creates an instance of Express and sets the port that defaults to 8080. app.use instructs express to serve up files from the ‘public’ folder. If you ran the server now, you’d get an error. That’s because we don’t yet have a public folder!</p><p><strong>Public folder</strong></p><p>Let’s make that public folder:</p><pre>mkdir public<br>cd public</pre><p>Inside, we’ll make index.html, index.js, and a CSS folder containing style.css:</p><pre>touch index.html<br>touch index.js<br>mkdir css<br>cd css<br>touch style.css</pre><p>The ‘public’ folder should look like this:</p><pre>public<br>  --&gt; index.html<br>  --&gt; index.js<br>  css<br>     --&gt; style.css</pre><p>Index.html should link to style.css and index.js. So let’s add the following:</p><pre>&lt;!-- /public/index.html --&gt;</pre><pre>&lt;html&gt;<br>  &lt;head&gt;<br>    &lt;title&gt;Midi Collaboration&lt;/title&gt;<br>    &lt;link rel=&quot;stylesheet&quot; href=&quot;css/style.css&quot;&gt;<br>  &lt;/head&gt;<br>  &lt;body&gt;<br>    &lt;div&gt;<br>      &lt;h1&gt;MIDI Collaboration&lt;/h1&gt;<br>      &lt;ul id=&quot;midi-data&quot;&gt;<br>      &lt;/ul&gt;  <br>    &lt;/div&gt;<br>  &lt;/body&gt;<br>  &lt;script src=&quot;index.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;<br>&lt;/html&gt;</pre><p>This makes a simple header followed by an empty unordered list — this is where we’ll log our midi data. After the body, it references index.js. For now, let’s add a simple console.log to index.js so that we’re sure it’s working properly:</p><pre>// public/index.js</pre><pre>console.log(&#39;index.js is connected!&#39;);</pre><p><strong>Update package.json</strong></p><p>Finally, we want to update package.json so that we can run our server with the terminal command ‘npm start’. Add the following line to “scripts”:</p><pre>&quot;start&quot;: &quot;node server.js&quot;</pre><p>Your package.json should look like this:</p><pre>{<br>  &quot;name&quot;: &quot;remaking-midi-maker&quot;,<br>  &quot;version&quot;: &quot;1.0.0&quot;,<br>  &quot;description&quot;: &quot;&quot;,<br>  &quot;main&quot;: &quot;index.js&quot;,<br>  &quot;scripts&quot;: {<br>    &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;&amp; exit 1&quot;,<br>    &quot;start&quot;: &quot;node server.js&quot;<br>  },<br>  &quot;author&quot;: &quot;&quot;,<br>  &quot;license&quot;: &quot;ISC&quot;,<br>  &quot;dependencies&quot;: {<br>    &quot;express&quot;: &quot;^4.15.3&quot;<br>  }<br>}</pre><p>Make sure you’re in the midi-collaboration folder and start up your app:</p><pre>npm start</pre><p>Great! It’s now hosted at <a href="http://localhost:3000/">http://localhost:8080/</a></p><h3><strong>Step 2: Connecting your Midi Controller</strong></h3><p>We’ll use the Web Midi API to connect a USB midi controller to the app. If you’re seeing ‘index.js is connected!’ in your console, let’s replace the console.log with:</p><pre>// public/javascript/index.js</pre><pre>var context = new AudioContext();<br>var oscillators = {};<br>var midi, data;</pre><pre>if (navigator.requestMIDIAccess) {<br>  navigator.requestMIDIAccess({<br>    sysex: false<br>  }).then(onMIDISuccess, onMIDIFailure);<br>} else {<br>  console.warn(&quot;No MIDI support in your browser&quot;);<br>}</pre><pre>function onMIDISuccess(midiData) {<br>  console.log(midiData);<br>  midi = midiData;<br>  var allInputs = midi.inputs.values();<br>  for (var input = allInputs.next(); input &amp;&amp; !input.done; input = allInputs.next()) {<br>    input.value.onmidimessage = onMIDImessage;<br>  }<br>}</pre><pre>function onMIDIFailure() {<br>  console.warn(&quot;Not finding a MIDI controller&quot;);<br>}</pre><p>If your midi controller is hooked up, you should see the console.log for the MIDIAccess object:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*r2QVSOv5UbV8pYOslEHNWw.jpeg" /></figure><p>In onMIDISuccess(), a for loop listens for midi messages. Right now it should be causing an error in the console. Why? Because we haven’t defined what to do when it receives a midi message.</p><p>Let’s create the onMIDImessage function referenced in the loop:</p><pre>function onMIDImessage(messageData) {<br>  var newItem = document.createElement(&#39;li&#39;);<br>  newItem.appendChild(document.createTextNode(messageData.data));<br>  newItem.className = &#39;user-midi&#39;;<br>  document.getElementById(&#39;midi-data&#39;).prepend(newItem);<br>}</pre><p>This function creates a new &lt;li&gt; element. It appends a text node with our midi data. It adds a css class of user-midi (this will be important later). Finally, it adds that new list item to the unordered list with the id “midi-data”.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*HY1lNt7nh_WpnWh34cyP2w.gif" /><figcaption>Midi keyup and keydown events</figcaption></figure><p>Pretty cool, eh?</p><p>But what do these numbers mean? Also: where’s the sound?</p><p><strong>MIDI Protocol</strong></p><p>MIDI Protocol is a rabbit hole all its own, but for our purposes you can understand the numbers with a simple chart:</p><blockquote>On / Off → 144 / 128</blockquote><blockquote>Pitch → 0–127</blockquote><blockquote>Velocity → 0–127</blockquote><p>When working with this data, we’ll treat the first number like an on/off switch. 144 = on, 128 = off. The second number is the range of pitches. The final velocity input could also be understood as <em>volume</em> in our usage here.</p><p>If you’d like a more in-depth look at midi, here’s <a href="https://www.cs.cmu.edu/~music/cmsip/readings/MIDI%20tutorial%20for%20programmers.html">a good place to start</a>.</p><p><strong>Sound</strong></p><p>The MIDI data is not the sound, but a set of directions: ON/OFF, PITCH, VELOCITY. We’ll need to make our own synth that can turn this information into musical tones.</p><p>First, let’s convert those value sets from an array into a ‘note’ object that we can pass to our sound player. In onMIDImessage function, add:</p><pre>var d = messageData.data; // Example: [144, 60, 100]<br>var note = {<br>  on: d[0], <br>  pitch: d[1],<br>  velocity: d[2]<br>}<br>play(note);</pre><p>Above, the variable ‘d’ is assigned the incoming data: an array of three numbers. Those three values are accessed by their index and assigned as values for the object properties. The note object is then passed to the play function. Let’s write that function:</p><pre>function play(note){<br>    switch(note.on) {<br>      case 144:<br>        noteOn(frequency(note.pitch), note.velocity);<br>        break;<br>      case 128:<br>        noteOff(frequency(note.pitch), note.velocity);<br>        break;<br>    }</pre><pre>    function frequency(note) {<br>      return Math.pow(2, ((note - 69) / 12)) * 440;<br>    }</pre><pre>    function noteOn(frequency, velocity) {<br>      var osc = oscillators[frequency] = context.createOscillator();<br>          osc.type = &#39;sawtooth&#39;;<br>          osc.frequency.value = frequency;<br>          osc.connect(context.destination);<br>          osc.start(context.currentTime);<br>    }</pre><pre>    function noteOff(frequency, velocity) {<br>        oscillators[frequency].stop(context.currentTime);<br>        oscillators[frequency].disconnect();<br>    }<br>  }</pre><p>This function checks to see if the note is on (144) or off (128) and triggers the appropriate command. Both noteOn and noteOff reference two global variables that we established at the top of index.js to handle the starting and stopping of sound.</p><p>The frequency function is used to convert the midi note number into a hertz frequency. If you’re curious, you can <a href="https://en.wikipedia.org/wiki/Piano_key_frequencies">read more about it here</a>.</p><p>Your app should now play like a synth. Huzzah!</p><h3>Step 3: Adding Socket.io for Collaboration</h3><p>Now it’s time for the fun stuff: syncing multiple users in a session! Socket.io will help us do that by enabling ‘real-time bidirectional event-based communication.’ That means we can send and receive midi messages as they’re triggered on any browser as they occur. Add Socket.io to the project:</p><pre>npm install socket.io --save</pre><p>You’ll also need to add this inside the head of index.html:</p><pre>&lt;!-- public/index.html --&gt;</pre><pre>&lt;script src=&quot;<a href="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js">https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js</a>&quot;&gt;&lt;/script&gt;</pre><p><em>Note: I’m using version 2.0.3 — be sure your versions of Socket.io match your package.json and html script. You might want this link to the </em><a href="https://cdnjs.com/libraries/socket.io"><em>CDN</em></a><em>.</em></p><p>For Socket.io to connect users we need to assure that:</p><blockquote>1) Users can both send and receive notes</blockquote><blockquote>2) The server listens for user input and broadcasts it to other users</blockquote><p><strong>User send/receive</strong></p><p>First, let’s make sure the user emits a signal every time they play a note. This can be accomplished by opening index.js and adding a single line to the function onMIDImessage. Be sure to add this <em>after </em>the note object has been defined.</p><pre>// public/index.js (inside onMIDImessage)</pre><pre>socket.emit(&#39;midi&#39;, note);</pre><p>We also want to play any external notes that come in from other users. Inside index.js, add the following:</p><pre>// public/index.js</pre><pre>var socket = io();<br>socket.on(&#39;externalMidi&#39;, gotExternalMidiMessage);</pre><pre>function gotExternalMidiMessage(data) {<br>  var newItem = document.createElement(&#39;li&#39;);<br>  newItem.appendChild(document.createTextNode(&#39;Note: &#39; + data.pitch + &#39; Velocity: &#39; + data.velocity));<br>  newItem.className = &quot;external-midi&quot;;<br>  document.getElementById(&#39;midi-data&#39;).prepend(newItem);</pre><pre>  playNote(data);<br>}</pre><p>When we receive an ‘externalMidi’ message from the server, it triggers gotExternalMidiMessage. This function should look familiar — in fact, we could refactor this later, but for now we’ll repeat code for clarity. It displays the external note in the view in a manner that’s almost identical to how we treat midi input from our own keyboard. However, we’ve given the &lt;li&gt; a class name ‘external-midi’. This will be important in a moment when we add styles to differentiate between our midi input and that of outside users.</p><p>Finally, the note is passed to the player to trigger a sound.</p><p><strong>Server</strong></p><p>Now let’s make a bridge between users. We want to handle any incoming signals and pass them to any other sessions.</p><p>In server.js, require Socket.io and add the function newConnection. This will be triggered when the server gains a new connection.</p><pre>// server.js</pre><pre>var socket = require(&#39;socket.io&#39;);<br>var io = socket(server);</pre><pre>io.sockets.on(&#39;connection&#39;, newConnection);</pre><pre>function newConnection(socket) {<br>  console.log(&#39;new connection from:&#39; + socket.id);</pre><pre>  socket.on(&#39;midi&#39;, midiMsg);<br>  function midiMsg(data) {<br>    socket.broadcast.emit(&#39;externalMidi&#39;, data);<br>  }<br>}</pre><p>The newConnection console.log will appear in the terminal every time a new connection is made. We also have socket.on listening for messages from any user and then triggering midiMsg, which broadcasts that data to every user <em>except</em> the original user.</p><p>With that, we’re all set!</p><h3>Step 3: Styling</h3><p>If you’re just interested in seeing your notes differentiated from other players, you can take a shortcut here and simply add these classes to your style.css:</p><pre>// public/css/sytle.css</pre><pre>.user-midi {<br>  color: green;<br>}</pre><pre>.external-midi {<br>  color: blue;<br>}</pre><p>That’s it! Now you will see your own input in green and any external signal in blue. Feel free to skip to the next step and deploy your app to Heroku!</p><p>If you’d like to create a keyboard interface, let’s keep rolling:</p><p><strong>Build a keyboard</strong></p><p>This keyboard we’ll make builds off of @baileyparker’s <a href="https://codepen.io/baileyparker/pen/pgPxYZ">CodePen project</a>. We’ll make some design and functionality changes to fit our usage. To start, open up this pen in a separate window:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodepen.io%2Fagbales%2Fembed%2Fpreview%2FowpeBv%3Fheight%3D600%26slug-hash%3DowpeBv%26default-tabs%3Dhtml%2Cresult%26host%3Dhttps%3A%2F%2Fcodepen.io%26embed-version%3D2&amp;url=https%3A%2F%2Fcodepen.io%2Fagbales%2Fpen%2FowpeBv&amp;image=https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fi.cdpn.io%2F387928.owpeBv.small.de85b646-3aab-4e55-a8fc-8eab2c1ef98c.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=codepen" width="800" height="600" frameborder="0" scrolling="no"><a href="https://medium.com/media/92840f524d82df82cc8c871c55b450de/href">https://medium.com/media/92840f524d82df82cc8c871c55b450de/href</a></iframe><p><strong>HTML</strong></p><p>The div with the class ‘piano’ houses all of our keys, which are labeled with a variety of selectors. Follow the html structure in the pen and paste the piano div and all its keys into your document (/public/index.html).</p><p><strong>CSS</strong></p><p>This is a big update for our styles. We’re importing a google font, assigning a background image to the body, and finally giving colors to differentiate between .user-midi (pink) and .external-midi (blue) signals. The ul and li elements have been styled so that they’ll slant back at a pleasing angle.</p><p>The keyboard styling takes up the remainder of the <a href="https://hackernoon.com/tagged/css">CSS</a>. Worth noting here are the ‘active’ classes like ‘.piano-key-natural:active’ or ‘.piano-key-natural-external:active’. These are triggered by the <a href="https://hackernoon.com/tagged/javascript">Javascript</a> when a note is played. If it matches the data number, the CSS will activate that key to be pink for your notes and blue for any external input.</p><blockquote>When you copy the CSS from the pen into your project’s style sheet (/public/style.css), be sure to follow the notes included inside. Most importantly, you’ll need to update the path to the background.</blockquote><p><strong>JS</strong></p><p>You’ll do the same thing for the Javascript: cut and paste it into /public/index.js below the code we’ve written. Again, it is important to read and follow the few comments within the code.</p><p>This code will manage midi controller connections. But you’ll want to focus in on two functions: onMidiMessage &amp; updateKeyboard. The former handles local input and applies the appropriate class to light up the keys. The latter does the same thing (but with a different class) for external messages.</p><p>To get external notes to light up the keyboard, we need to return to the function gotExternalMidiMessage. After we call play(), add the following code:</p><pre>var msg = { }<br>    msg.data = [];<br>    msg.data.push(data.on);<br>    msg.data.push(data.pitch);<br>    msg.data.push(data.velocity);</pre><pre>updateKeyboard(data);</pre><p>The keyboard needs a specific kind of object data structure to update, so we create msg and fill it with data from our note. That msg object is passed to updateKeyboard which lights up the keys in blue for that external signal.</p><p>You should now see your keys light up pink for your own input! When we connect to Heroku add more users, you’ll see those external midi messages light up in blue!</p><h3>Step 4: Deploy to Heroku</h3><p>Instead of walking through each step here, I’ll direct you towards the comprehensive documentation at Heroku. They’ve done a great job reviewing options for deployment:</p><p><a href="https://devcenter.heroku.com/articles/git">Deploying with Git</a></p><h3>Summary</h3><p>If all went well, you should now have an app that passes signals from one user to the next and plays notes from every user. The keyboard should light up with different colors for your input and that of external sources.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*85QJqEIPDihkwklVMHnqHA.png" /><figcaption>Completed App!</figcaption></figure><p>I’m excited about this collaborative app. It has been a real thrill hooking it up and inviting friends from other states to log on and play music! Looking back, I also see ways that the data could be passed more efficiently and areas where the design could be enhanced — it’s not friendly to smaller screens, for instance.</p><p>I’d love to hear how you’re using the app, and I hope it has given you a better understanding of how to combine Express.js, Socket.io, and the Web MIDI API!</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fupscri.be%2Fdde502%3Fas_embed%3Dtrue&amp;dntp=1&amp;url=https%3A%2F%2Fupscri.be%2Fhackernoon%2F&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=upscri" width="800" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/3c851dac986ab6dbb2d1aaa91205a8eb/href">https://medium.com/media/3c851dac986ab6dbb2d1aaa91205a8eb/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=273663b63201" width="1" height="1" alt=""><hr><p><a href="https://medium.com/hackernoon/how-to-build-a-collaborative-midi-app-with-express-js-socket-io-273663b63201">How to Build a Collaborative MIDI App with Express.js &amp; Socket.io</a> was originally published in <a href="https://medium.com/hackernoon">HackerNoon.com</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>