<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: VBAK</title>
    <description>The latest articles on DEV Community by VBAK (@vbaknation).</description>
    <link>https://dev.to/vbaknation</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F195039%2Ff83dee11-cdcb-4404-85be-06b3d3afb900.png</url>
      <title>DEV Community: VBAK</title>
      <link>https://dev.to/vbaknation</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vbaknation"/>
    <language>en</language>
    <item>
      <title>Converting a Prototype to React Components</title>
      <dc:creator>VBAK</dc:creator>
      <pubDate>Fri, 04 Oct 2019 21:25:31 +0000</pubDate>
      <link>https://dev.to/vbaknation/converting-a-prototype-to-react-components-15f7</link>
      <guid>https://dev.to/vbaknation/converting-a-prototype-to-react-components-15f7</guid>
      <description>&lt;h2&gt;
  
  
  Newbie Nightmare: Open-Ended Tasks
&lt;/h2&gt;

&lt;p&gt;It's what makes web development exciting and drew me to it but---one of the most challenging tasks I have encountered in my first year-ish of learning web development is translating visual designs to code. It's a very open-ended task with many opportunities to make future choices that lead to ugly if not wrong results which I believe is called &lt;em&gt;technical debt&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Newbie Dream: Curated Best Practices
&lt;/h2&gt;

&lt;p&gt;I have deferred and relied on others' best practices when approaching such an open-ended task. However, those best practices &lt;em&gt;probably&lt;/em&gt; or rather &lt;em&gt;hopefully&lt;/em&gt; came from a large number of iterations of experience and following them without sharing the same intuition requires a large amount of faith. Implementing someone else's best practices into whatever app I am applying them to also requires a good amount of luck (hopefully my app doesn't have that one feature/requirement that ends up being the kryptonite of any of the best practices I follow). Lastly, vetting someone's best practice as a newbie is nearly impossible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Newbie Reality: Reinvent the Wheel
&lt;/h2&gt;

&lt;p&gt;While I want to be efficient and resourceful, I also need to build a deeper intuition for tasks related to converting a prototype to UI logic. I think my favorite way to do that is to approach an open-ended task with one outcome: empirical learning. I'll be writing this post in (approximately) realtime while I work on and learn about the project &lt;em&gt;at the same time&lt;/em&gt; &lt;a href="https://www.youtube.com/results?search_query=if+your+mother+only+knew+rahzel+at+the+same+time" rel="noopener noreferrer"&gt;a la Rahzel&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plug
&lt;/h2&gt;

&lt;p&gt;I manage the repo for the OSS project that I'll be talking about in this post. As you will see throughout this post, we need a lot of help building this app so if you are interested in contributing please take a look at our repo at the following link:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/vishalbakshi/CallForCode" rel="noopener noreferrer"&gt;Wildfire Survivor Management System (Link to GitHub Repository)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are building two apps for the staff at the United Way of Northern California to help them manage California wildfire survivor data. This is a project that started out &lt;a href="https://www.42.us.org/42-silicon-valley-hosts-2nd-annual-ibm-call-for-code-global-challenge-hackathon/" rel="noopener noreferrer"&gt;initially as an IBM Call for Code one-day hackathon event (link)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Our hackathon team's UX designer created the Sketch files located &lt;a href="https://sketch.cloud/s/8Az7w/a/5eYv2P" rel="noopener noreferrer"&gt;at this link&lt;/a&gt; and the first non-hackathon-team-member contributor converted them to an interactive prototype for each app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wildfire Survivor Application &lt;a href="https://invis.io/5VTM181XKRW" rel="noopener noreferrer"&gt;(Link to Invision Prototype)&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Users (wildfire survivors) fill out this HTML form-element-based application in order to submit the necessary information for United Way staff members to evaluate and distribute financial assistance. We have a development version which successfully runs locally with minimum functionality (user can enter and preview data in the form elements) but a lot of essential functionality is still missing before users can safely and conveniently use this app (authentication, file storage, CRUD methods, data encryption, and things we haven't even thought of...)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Wildfire Survivor Dashboard: &lt;a href="https://invis.io/CRTM199K49Y" rel="noopener noreferrer"&gt;(Link to Invision Prototype)&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;This is what I'll be tackling in this post!&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Data, Display, Events
&lt;/h2&gt;

&lt;p&gt;Of the many ways to breakdown this problem, the following three components of this project are fundamental:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What data will be displayed to the user?&lt;/li&gt;
&lt;li&gt;How will it be displayed?&lt;/li&gt;
&lt;li&gt;What events will take place?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Data
&lt;/h3&gt;

&lt;p&gt;Since we are making both the app where users submit their data and the app where admin manage it, we have some flexibility in choosing how the data is structured. For now, I'll keep it simple and continue to use the very linear structure I gave to the data in the Wildfire Survivor Application:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;schema.js&lt;/code&gt; &lt;a href="https://github.com/vishalbakshi/CallForCode/blob/master/src/constants/schema.js" rel="noopener noreferrer"&gt;(Link to GitHub repo)&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;This file exports an object (named &lt;code&gt;SCHEMA&lt;/code&gt;) which contains data about each field that will receive some input from the user (inspired by &lt;a href="https://docs.mongodb.com/manual/reference/operator/query/jsonSchema/" rel="noopener noreferrer"&gt;MongoDB's &lt;code&gt;$jsonSchema&lt;/code&gt; object (link to their awesome docs)&lt;/a&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SCHEMA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;survivor_first_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;initial_value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;First Name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;test_value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;test_values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;validation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h4&gt;
  
  
  &lt;code&gt;component_fields.js&lt;/code&gt; &lt;a href="https://github.com/vishalbakshi/CallForCode/blob/master/src/constants/component_fields.js" rel="noopener noreferrer"&gt;(Link to GitHub repo)&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;This file exports an object (named &lt;code&gt;FIELDS&lt;/code&gt;) which lists the field names for each fieldset. (These fieldsets were determined from conversations with the end-users---the staff members who will be managing this information). Right now I'm assuming they are going to turn into separate React components, so I've kept the name as "component_fields". However, this is a fundamental structure I'm giving the data primarily to keep it simple (for now) so it may change over time as the project improves.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FIELDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;general_information&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;survivor_first_name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;survivor_middle_name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;survivor_last_name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;survivor_phone&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;survivor_email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;survivor_address1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;survivor_address2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;survivor_city&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;survivor_state&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;survivor_zip&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;

  &lt;span class="p"&gt;...,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code samples shown represent the following section of the Sketch file which corresponds to the &lt;code&gt;general_information&lt;/code&gt; fieldset:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fovbenaj5czf6oa9xc207.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fovbenaj5czf6oa9xc207.png" alt="Sketch file screenshot showing General Information fieldset UI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The goal is to allow us to add and remove fields from different fieldsets over time as we gather more feedback from our end-users. &lt;/p&gt;

&lt;h3&gt;
  
  
  Display
&lt;/h3&gt;

&lt;p&gt;The Dashboard consists of four main views. Here are my initial thoughts on the views' relationship with the various fields:&lt;/p&gt;




&lt;h4&gt;
  
  
  Dashboard
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Applications are grouped first by &lt;code&gt;fire_name&lt;/code&gt; in a scrolling navigation element and then by status (which is currently not included in either &lt;code&gt;schema.js&lt;/code&gt; or &lt;code&gt;component_fields.js&lt;/code&gt;) in two separate containers beneath it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0vy9w5lnx5qxfrjp4ytm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0vy9w5lnx5qxfrjp4ytm.png" alt="Dashboard Page"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h4&gt;
  
  
  Analytics
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;The financial assistance dollar amount visualizations will be displayed by wildfire and over time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6qedglzrp2dxhxyvc02c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6qedglzrp2dxhxyvc02c.png" alt="Analytics Page"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h4&gt;
  
  
  Applications
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Each wildfire has its own screen displaying a list of all applications that were submitted to receive financial assistance, grouped by status in different tabs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fanbpgrhthyyz7cwj20q3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fanbpgrhthyyz7cwj20q3.png" alt="Applications for Camp Fire"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A single application is displayed as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The main container displays the application data in the same fieldsets that are used in the Survivor Application (i.e. as grouped in &lt;code&gt;component_fields.js&lt;/code&gt;) across different tabs&lt;/li&gt;
&lt;li&gt;A side panel includes options for the application's status&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F53dbu64o3wyruu8f39yb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F53dbu64o3wyruu8f39yb.png" alt="Single Application Page"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h4&gt;
  
  
  Map
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;The Map view displays an embed of CALFIRE's Camp Fire Structure Status &lt;a href="https://arcg.is/1KePmT" rel="noopener noreferrer"&gt;(link)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fe5164a1o4y15l39ch5qi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fe5164a1o4y15l39ch5qi.png" alt="California Wildfire Map"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Events
&lt;/h3&gt;

&lt;p&gt;There are a two broad types of events that the Survivor Dashboard components will need to handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Changes to data from a Survivor Application&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Changes to Admin-only fields (application status, financial assistance, status update notifications, messaging, etc)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Version 0
&lt;/h2&gt;

&lt;p&gt;Okay so talking through that has helped me to mentally organize the different screens a bit to start seeing some patterns across the screens. Time to jump into a codepen!&lt;/p&gt;

&lt;h3&gt;
  
  
  Survivor Application Data
&lt;/h3&gt;

&lt;p&gt;I've created some data for an application to use for this initial version. The file lives in the repo &lt;a href="https://github.com/vishalbakshi/CallForCode/blob/survivor-dashboard/data/fake_data6.json" rel="noopener noreferrer"&gt;at this link&lt;/a&gt; and I used &lt;a href="https://www.jsdelivr.com/" rel="noopener noreferrer"&gt;jsdelivr&lt;/a&gt; to deliver it to my pen. I am avoiding any Firebase functions until I've wrapped my head around the UI.&lt;/p&gt;

&lt;p&gt;I'll start with how the Survivor Dashboard displays a single survivor's application. This screen shows different fields based on different tabs that are selected.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F53dbu64o3wyruu8f39yb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F53dbu64o3wyruu8f39yb.png" alt="Single Application Page"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Here's the pen! Please click through and let me know if you have any feedback! (I am particularly proud of the way I wrote the logic around the UI for the "Notes" section.)&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/vishalbakshi/embed/JjPWxRR?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;




&lt;h4&gt;
  
  
  Bonus Learnings
&lt;/h4&gt;

&lt;p&gt;A few bonus learnings (i.e. things I thought I knew until I spent a couple of hours debugging my misunderstanding for each one):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The C in CDN stands for Content but it could also stand for Cached.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I have been using the super cool jsdelivr for getting a &lt;code&gt;.json&lt;/code&gt; file with fake data from this project's Github repo. However, I made some commits with changes to that file (adding a few key-value pairs) yet my &lt;code&gt;fetch&lt;/code&gt; was not fetching the latest file! I came across &lt;a href="https://github.com/jsdelivr/jsdelivr/issues/18063" rel="noopener noreferrer"&gt;this issue on the jsdelivr Github repo&lt;/a&gt; where one of the comments explains that CDN files are cached and may take up to a day to refresh. So my workaround was changing the file name in my repo which changes the URL and thus counts as a new file.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Another cache-related problem I encountered was the "Cache-Control" request header&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At some point, I had waited long enough for the CDN to update its cache (which I realized after the fact), but my browser cache was still being referenced. &lt;/li&gt;
&lt;li&gt;On a side not, I look forward to referencing these sorts of concepts in Julia Evans' &lt;a href="https://jvns.ca/blog/2019/09/12/new-zine-on-http/" rel="noopener noreferrer"&gt;HTTP zine&lt;/a&gt;, who teaches in a very effective style for me---visual comic:&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;


&lt;blockquote&gt;
&lt;p&gt;getting closer to a final table of contents for the new HTTP zine &lt;a href="https://t.co/fYsGiKQ3Pi" rel="noopener noreferrer"&gt;pic.twitter.com/fYsGiKQ3Pi&lt;/a&gt;&lt;/p&gt;— 🔎Julia Evans🔍 (@b0rk) &lt;a href="https://twitter.com/b0rk/status/1166425814566625280?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;August 27, 2019&lt;/a&gt;
&lt;/blockquote&gt; 

&lt;ul&gt;
&lt;li&gt;I will purchase that zine eventually! For now, I've referenced MDN and added the following &lt;code&gt;init&lt;/code&gt; object to my &lt;code&gt;fetch&lt;/code&gt; call to ignore browser cache:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://cdn.jsdelivr.net/...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;no-cache&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;CORS

&lt;ul&gt;
&lt;li&gt;CORS is a frustrating concept to learn/utilize for a newbie. Big safety and big headaches because of it. I ran into a CORS error when requesting a resource from Firebase Storage and found the following resolution which involved configuring CORS for the Google Cloud Platform project. Let's begin with the shell provided in the Google Cloud Platform console:&lt;/li&gt;
&lt;li&gt;Open the shell (leftmost icon at top right corner of the screen)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fl8rafv31v0mbm0vykmrv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fl8rafv31v0mbm0vykmrv.png" alt="Screenshot of the Google Platform Console project dashboard displaying the shell icon which is the leftmost icon located at the top right corner of the page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If it doesn't exist already, create a file called &lt;code&gt;cors.json&lt;/code&gt; using a text editor. I chose to use &lt;code&gt;pico&lt;/code&gt; for no other reason than it was part of one of the answers &lt;a href="https://stackoverflow.com/questions/37760695/firebase-storage-and-access-control-allow-origin" rel="noopener noreferrer"&gt;for this StackOverflow question&lt;/a&gt;
    - &lt;code&gt;pico cors.json&lt;/code&gt;
    - Add something like this (replace the array mapped to the &lt;code&gt;"origin"&lt;/code&gt; property to an array of strings with domains you wish to allow for a given &lt;code&gt;method&lt;/code&gt; for this project's Storage:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;origin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://s.codepen.io&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;method&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;maxAgeSeconds&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Save that file! I exclaim it because I didn't.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one way of doing that is to type &lt;code&gt;^X&lt;/code&gt; and then &lt;code&gt;Y&lt;/code&gt; when it asks "save modified buffer?"&lt;/li&gt;
&lt;li&gt;another way is to type &lt;code&gt;^O&lt;/code&gt; to "Write Out" the file and hit enter when it prompts &lt;code&gt;File name to write:&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Run the following command (replace &lt;code&gt;exampleproject&lt;/code&gt; in the URL with your actual project ID) to set your saved JSON file as the cors configuration file:&lt;br&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gsutil cors set cors.json gs://exampleproject.appspot.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now you can use Firebase Storage URLs in your codepen!&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Using RunwayML To Create a Lip Sync Animation</title>
      <dc:creator>VBAK</dc:creator>
      <pubDate>Mon, 05 Aug 2019 17:08:01 +0000</pubDate>
      <link>https://dev.to/vbaknation/using-runwayml-to-create-a-lip-sync-animation-4dbf</link>
      <guid>https://dev.to/vbaknation/using-runwayml-to-create-a-lip-sync-animation-4dbf</guid>
      <description>&lt;h2&gt;
  
  
  Related posts:
&lt;/h2&gt;

&lt;p&gt;The goal is to create an open-source app or library which allows musicians to expedite the process of creating visuals for their music:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/vbaknation/creating-visuals-for-my-music-using-speech-recognition-javascript-and-ffmpeg-4a6d"&gt;Version 0 of &lt;code&gt;animatemusic&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/vbaknation/creating-visuals-for-music-using-speech-recognition-javascript-and-ffmpeg-version-1-4mll"&gt;Version 1 of &lt;code&gt;animatemusic&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lip Sync
&lt;/h2&gt;

&lt;p&gt;In parallel with my study of shader functions, I have been exploring ways to incorporate an animation of my face (or any character I wish to create) that will lip-sync to my song in an HTML/Canvas animation.&lt;/p&gt;

&lt;p&gt;This was originally inspired by the output from the forced aligner I used (&lt;a href="https://github.com/lowerquality/gentle" rel="noopener noreferrer"&gt;gentle&lt;/a&gt;), which included the time the word was spoken, as well as the duration of each phoneme of the word (phonemes are fundamental units of a word's sound). &lt;/p&gt;

&lt;p&gt;For example, gentle's result for the word "let" (the duration of the phoneme is shown in seconds):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

{
      "alignedWord": "let",
      "phones": [
        {
          "duration": 0.09,
          "phone": "l_B"
        },
        {
          "duration": 0.09,
          "phone": "eh_I"
        },
        {
          "duration": 0.04,
          "phone": "t_E"
        }
      ]
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;My first plan was to map mouth shape coordinates to each phoneme when rendering the canvas at each frame of the animation. As a first attempt, I have used the following image I found on the web which shows the mouth shape corresponding to different letters:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fiubpy8p58bkfg7vwvxoh.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fiubpy8p58bkfg7vwvxoh.jpg"&gt;&lt;/a&gt;&lt;br&gt;
Source: &lt;a href="https://fmspracticumspring2017.blogs.bucknell.edu/2017/04/18/odds-ends-lip-syncing/" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://fmspracticumspring2017.blogs.bucknell.edu/2017/04/18/odds-ends-lip-syncing/" rel="noopener noreferrer"&gt;https://fmspracticumspring2017.blogs.bucknell.edu/2017/04/18/odds-ends-lip-syncing/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/vishalbakshi/embed/Qeqvqj?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;I've tried to articulate my intention with comments throughout the code, but essentially, the master image (with all of the mouth shapes) is translated to display the desired phonemes for each word as it is displayed.&lt;/p&gt;

&lt;p&gt;I feel confident that this case study can be extended to a full song, with custom mouth shape coordinates (which will probably start out as drawings using &lt;a href="https://vectr.com" rel="noopener noreferrer"&gt;vectr&lt;/a&gt;). This will be likely the next step I take to produce a full song's animation.&lt;/p&gt;

&lt;p&gt;But before I proceed with that route, I wanted to try out something I came across a few days ago: &lt;a href="https://runwayml.com/" rel="noopener noreferrer"&gt;RunwayML&lt;/a&gt;, which is software that provides a GUI to run different open-source ML models. RunwayML is explicitly marketed as software for creators. There's a &lt;a href="https://runwayml.com/download" rel="noopener noreferrer"&gt;free download&lt;/a&gt; and it's unbelievably easy to use so if you are interested in using machine learning for creative endeavors, I highly recommend it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using RunwayML
&lt;/h2&gt;

&lt;p&gt;Instead of using the image of mouth shapes, or drawing my own, I was happy to utilize the power of facial recognition to do that work for me. &lt;/p&gt;

&lt;p&gt;I started by recording a short video of myself with my phone:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/uSg3I2y_fx8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;I then created a new workspace in RunwayML and added to it the &lt;em&gt;Face Landmarks&lt;/em&gt; model, which is described by its author as follows:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A ResNet-32 network was trained from scratch on a dataset of about 3 million faces. This dataset is derived from a number of datasets. The face scrub dataset2, the VGG dataset1, and then a large number of images I personally scraped from the internet. I tried as best I could to clean up the combined dataset by removing labeling errors, which meant filtering out a lot of stuff from VGG. I did this by repeatedly training a face recognition model and then using graph clustering methods and a lot of manual review to clean up the dataset. In the end, about half the images are from VGG and face scrub. Also, the total number of individual identities in the dataset is 7485. I made sure to avoid overlap with identities in LFW so the LFW evaluation would be valid.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The model takes a video file as input and outputs the coordinates (in x,y pixels) for different recognized face features. The output format I chose was &lt;code&gt;.JSON&lt;/code&gt; and the resulting data structure is:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

[
    {
        time: 0.01,
        landmarks: [
           {
               bottom_lip: [[x0,y0], [x1,y1], ...],
               chin: [[x0,y0], [x1,y1], ...],
               left_eye: [[x0,y0], [x1,y1], ...],
               ...
           }
        ]
    }
]


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Each &lt;code&gt;time&lt;/code&gt; value (based on the frame rate of the export, which in this case is 10 fps) has a corresponding set of landmarks (facial features). The facial features have assigned to it an array of [x, y] pixel coordinate arrays.&lt;/p&gt;

&lt;p&gt;Here's RunwayML interface during the export. The top panel shows the uploaded video, the bottom panel shows the export/preview of the model's output, and the side panel has model parameters:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fn2gs60pmub7vqd1np7s8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fn2gs60pmub7vqd1np7s8.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I copied over the &lt;code&gt;.JSON&lt;/code&gt; output to a pen, and built out an 10 fps animation using the face landmark coordinates:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/vishalbakshi/embed/oKoZOW?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Woo!! I think that's pretty awesome, given how smooth the whole process went. Note, I did not adjust or study any of the model parameters so I will explore that next. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A small note if you are new to RunwayML: make sure you download, install and open &lt;a href="https://www.docker.com/products/docker-desktop" rel="noopener noreferrer"&gt;Docker Desktop&lt;/a&gt; if you are running the model locally. RunwayML does give you credits to use a remote GPU to run the model, and I'll be using that this week to run a full video with a higher export frame-rate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Follow Me
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/channel/UCqvA0CVB3QR3TLpGVzkD35g" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.instagram.com/vbaknation" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/vbaknation" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>The Fence Problem from The Book of Shaders</title>
      <dc:creator>VBAK</dc:creator>
      <pubDate>Tue, 30 Jul 2019 02:41:39 +0000</pubDate>
      <link>https://dev.to/vbaknation/the-fence-problem-from-the-book-of-shaders-22jm</link>
      <guid>https://dev.to/vbaknation/the-fence-problem-from-the-book-of-shaders-22jm</guid>
      <description>&lt;p&gt;I am working through &lt;a href="https://thebookofshaders.com/"&gt;Patricio Gonzalez Vivo's &lt;em&gt;The Book of Shaders&lt;/em&gt;&lt;/a&gt; which is turning out to be one of the best (engaging, challenging, fun) technical books I've come across. The &lt;a href="https://greenteapress.com/wp/"&gt;Think series by Allen Downey&lt;/a&gt; is to Python and Statistical Learning as this book is to computer graphics. At least that's how it feels.&lt;/p&gt;

&lt;p&gt;I'm on Chapter 5:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This chapter could be named "Mr. Miyagi's fence lesson."...&lt;/p&gt;

&lt;p&gt;...The following code structure is going to be our fence:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

// Plot a line on Y using a value between 0.0-1.0
float plot(vec2 st, float pct){
  return  smoothstep( pct-0.02, pct, st.y) -
          smoothstep( pct, pct+0.02, st.y);
}

void main() {
    vec2 st = gl_FragCoord.xy/u_resolution;

    float y = st.x;

    vec3 color = vec3(y);

    // Plot a line
    float pct = plot(st,y);
    color = (1.0-pct)*color+pct*vec3(0.0,1.0,0.0);

    gl_FragColor = vec4(color,1.0);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I spent the first day staring at and playing around with that code. I didn't get much out of that (immediately).&lt;/p&gt;

&lt;h2&gt;
  
  
  distraction or my mind digesting information
&lt;/h2&gt;

&lt;p&gt;I took a detour and worked through the wonderfully short and packed tutorials from &lt;a href="http://tobyschachman.com/Shadershop/"&gt;Toby Schachman's Shadershop&lt;/a&gt; which were uh-mazing. If you haven't tried it and are interested in learning about the construction of noise for complex computer graphics you should definitely go through his videos. His pedagogy is built around spatial learning which is a profound concept for learning computer graphics and definitely has influenced my thinking about the field.&lt;/p&gt;

&lt;h2&gt;
  
  
  back to the fence
&lt;/h2&gt;

&lt;p&gt;I was ready to take on the Fence Code after some Shadershop wins. I realized that I was trying to figure out the Fence Code only in my head, which was why I was hitting a roadblock, or rather, fence. The reason Shadershop was so effective was that it allowed visual learners to use a spatial medium to control the code and view the effects on the resulting graphic. I chose to take that as advice.&lt;/p&gt;

&lt;p&gt;I added a few more uniforms to the Fence Code, and used &lt;a href="https://webglfundamentals.org/webgl/lessons/webgl-boilerplate.html"&gt;WebGL Fundamentals' boilerplate&lt;/a&gt; to set up the WebGL context and sliders to control the shader function uniform variables.&lt;/p&gt;

&lt;p&gt;Below is a codepen I created to show the result. Note the change in the (very small) preformatted text when you move the sliders to help you visualize the process better:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/vishalbakshi/embed/GVWqmG?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;I'm itching to jump to some conclusions about the relationships between uniform variable values and the graphical result, but the more I play around with different value combinations, the broader those relationships get. So I'm not quite ready to move on from this chapter.&lt;/p&gt;

&lt;p&gt;The biggest takeaway from this experience: the color value and position of the pixels are handled separately. I know, i know, the shader functions themselves are separate so they are handled separately, but I wasn't comfortable with the concept.&lt;/p&gt;

&lt;p&gt;So I added a &lt;code&gt;u_count&lt;/code&gt; uniform variable in the fragment shader's script, which determines how many of the triangles are drawn on the canvas (there are a total of 24 triangles). Changing that count does not affect the color of the canvas. So the fragment shader is generating a color map of sorts which is applied over &lt;em&gt;all&lt;/em&gt; pixels of the canvas, not just the ones that are specified in the vertex shader. This led me to two conceptual confusions I couldn't quite visualize before: that animations could be made almost entirely with fragment shaders----varying the color of the pixels over time instead of "moving" (more like changing the visibility of) the pixels over time (as you would do with hand-drawn animations) could give similar or same results, and that shader scripts are run by the GPU which processes in parallel (i.e. all the pixels at the same time).&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow Me
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/channel/UCqvA0CVB3QR3TLpGVzkD35g"&gt;YouTube&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.instagram.com/vbaknation"&gt;Instagram&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/vbaknation"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>html</category>
      <category>css</category>
    </item>
    <item>
      <title>My Learnings from Building Forms in React: Part 1</title>
      <dc:creator>VBAK</dc:creator>
      <pubDate>Thu, 18 Jul 2019 07:26:47 +0000</pubDate>
      <link>https://dev.to/vbaknation/my-learnings-from-building-forms-in-react-part-1-3n1k</link>
      <guid>https://dev.to/vbaknation/my-learnings-from-building-forms-in-react-part-1-3n1k</guid>
      <description>&lt;p&gt;Over the last month or so, I've spent a significant amount of time building, pondering, troubleshooting and Googling how React handles forms. This post, and probably more to follow, is a place for me to think out loud about what I've learned! &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As a rule, none of my posts are meant to be interpreted as best practices or tutorials. They are meant to be interpreted as musings and ramblings. Should something come across something as inefficient or incorrect, do me a favor and call it out so I can correct my understanding.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I've been working on a volunteer project with the following requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Users should be able to create an account and submit a form with different field types&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Admin should be able to view registered users' form data and update their account status on a dashboard&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Registration Functionality
&lt;/h1&gt;

&lt;p&gt;The users follow a simple (for them) process to create an account and submit the form:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Login&lt;/li&gt;
&lt;li&gt;Edit a form&lt;/li&gt;
&lt;li&gt;Preview the form&lt;/li&gt;
&lt;li&gt;Submit the form&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Edit or Preview
&lt;/h1&gt;

&lt;p&gt;The data received from form input fields on the Edit page may be different than what is rendered on the Preview page.&lt;/p&gt;

&lt;p&gt;For example, the user inputs their First, Middle and Last Name in three separate fields on the Edit page, but a single Name string is rendered on the Preview page.&lt;/p&gt;

&lt;p&gt;A codepen to illustrate (vanilla JS):&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/vishalbakshi/embed/gVYOwE?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Users input data into separate fields as single strings (&lt;code&gt;first_name&lt;/code&gt;, &lt;code&gt;middle_name&lt;/code&gt;, &lt;code&gt;last_name&lt;/code&gt;) and the preview element displays that data differently (&lt;code&gt;first_name&lt;/code&gt; + ' ' + &lt;code&gt;middle_name&lt;/code&gt; + ' ' + &lt;code&gt;last_name&lt;/code&gt;).&lt;/p&gt;

&lt;h1&gt;
  
  
  Refresh
&lt;/h1&gt;

&lt;p&gt;Should the user mistakenly refresh the Edit page (which I notoriously do, by accident, when using mobile apps) they would hate to see their form data deleted.&lt;/p&gt;

&lt;p&gt;For development purposes, and in order to quickly observe and test the relationship between &lt;code&gt;state&lt;/code&gt; and the Edit/Preview page form elements without introducing a database into the mix, I am using &lt;code&gt;localStorage&lt;/code&gt; method to keep the app's &lt;code&gt;state&lt;/code&gt; updated and saved.&lt;/p&gt;

&lt;p&gt;Here's an example codepen to illustrate (using React Hooks):&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/vishalbakshi/embed/KOKKmw?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Here's a video that shows how Local Storage updates upon user input:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/3fMh8Ex6FrU"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Note that the first time I provide inputs, the associated key is added to the Local Storage object and subsequent updates update the value. The following screenshot shows the value of &lt;code&gt;state&lt;/code&gt; after clicking the Left-Handed checkbox but before &lt;code&gt;state&lt;/code&gt; is updated. Note the absence of the &lt;code&gt;left-handed&lt;/code&gt; property after the first click. I used &lt;code&gt;debugger&lt;/code&gt; to pause chrome at that line.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fn6utrjlb5l7zqj9kstnl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fn6utrjlb5l7zqj9kstnl.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Dynamic Form Elements
&lt;/h1&gt;

&lt;p&gt;It was a seemingly harmless &lt;em&gt;Add a Contact&lt;/em&gt; button but it took a few hours to implement my first untested attempt.&lt;/p&gt;

&lt;p&gt;Here's an example codepen to illustrate (using React Hooks):&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/vishalbakshi/embed/ZgEQVj?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Hot diggity! New components are rendered upon clicking the button, and the &lt;code&gt;contactInfo&lt;/code&gt; object updates accordingly. &lt;/p&gt;

&lt;h1&gt;
  
  
  Looking Ahead
&lt;/h1&gt;

&lt;p&gt;I'll be adding Firebase functionality hopefully tomorrow and will look to write another post about my learnings from that soon enough.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow Me
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/channel/UCqvA0CVB3QR3TLpGVzkD35g" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.instagram.com/vbaknation" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/vbaknation" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>html</category>
      <category>css</category>
    </item>
    <item>
      <title>Creating Visuals for Music Using Speech Recognition, Javascript and ffmpeg: Version 1</title>
      <dc:creator>VBAK</dc:creator>
      <pubDate>Tue, 16 Jul 2019 06:01:39 +0000</pubDate>
      <link>https://dev.to/vbaknation/creating-visuals-for-music-using-speech-recognition-javascript-and-ffmpeg-version-1-4mll</link>
      <guid>https://dev.to/vbaknation/creating-visuals-for-music-using-speech-recognition-javascript-and-ffmpeg-version-1-4mll</guid>
      <description>&lt;p&gt;This is my discussion of version 1 of a project I'm working on called &lt;em&gt;animatemusic&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/vishalbakshi/animatemusic"&gt;Click here to go to its GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My blog post on version 0 of this project &lt;a href="https://dev.to/vbaknation/creating-visuals-for-my-music-using-speech-recognition-javascript-and-ffmpeg-4a6d"&gt;can be found here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  milliseconds, not frames
&lt;/h2&gt;

&lt;p&gt;In version 0 of this project, in my pursuit to render text on a canvas element (which would eventually become a video frame), I chose to design around the essential question:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;which video frame is being rendered?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;which was a reasonable question to ask since I was rendering a finite and known quantity of frames (based on the framerate and duration of the video).&lt;/p&gt;

&lt;p&gt;However, it ended up having a not-so-reasonable solution because it involved a conversion from float (start time in seconds) to integer (frame number) for each set of words that were to be rendered. This caused repetitive rounding, which resulted in the text lagging behind the vocal audio. Here's codepen to articulate the plight:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/vishalbakshi/embed/orrRaE?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;For version 1, I've chosen to steer clear of this issue by designing around a new essential question:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;at what time is the word rendered?&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Examples using &lt;code&gt;requestAnimationFrame&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;I found two resources that reassured me that my new essential question was worth pursuing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://codetheory.in/controlling-the-frame-rate-with-requestanimationframe/"&gt;Controlling frame rate with requestAnimationFrame&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/a/19772220/10244270"&gt;How to throttle requestAnimationFrame to a specific frame rate&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I would eventually like to use a library such as anime.js or three.js, and their documentation and API also catered to a time-based animation approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Refactor and &lt;code&gt;generateFrameImages&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;I took this opportunity to refactor my original script, in addition to adding functions to render the text on the canvas when the current time (elapsed) of the video is within the &lt;code&gt;start&lt;/code&gt; and &lt;code&gt;end&lt;/code&gt; times of the word. Here's a codesandbox and &lt;a href="https://github.com/vishalbakshi/animatemusic/blob/master/20190623C_transcribed_test.json"&gt;sample transcript json for upload&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/canvas-export-fnvws"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;I am trembling with excitement as I present to you the new and improved resulting video!! The lyrics sync with the audio so much better than version 0! I don't see many issues with the text (although there are some blanks and one &lt;code&gt;&amp;lt;unk&amp;gt;&lt;/code&gt;, which is gentle's version of &lt;code&gt;undefined&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/iujaWnphaY4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Thanks for reading! Please comment below if you have something you can share to help me improve this project. Going to try and sleep now amongst the adrenaline rush...&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow Me
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/channel/UCqvA0CVB3QR3TLpGVzkD35g"&gt;YouTube&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.instagram.com/vbaknation"&gt;Instagram&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>html</category>
    </item>
    <item>
      <title>Creating Visuals for Music Using Speech Recognition, Javascript and ffmpeg: Version 0</title>
      <dc:creator>VBAK</dc:creator>
      <pubDate>Mon, 15 Jul 2019 05:01:49 +0000</pubDate>
      <link>https://dev.to/vbaknation/creating-visuals-for-my-music-using-speech-recognition-javascript-and-ffmpeg-4a6d</link>
      <guid>https://dev.to/vbaknation/creating-visuals-for-my-music-using-speech-recognition-javascript-and-ffmpeg-4a6d</guid>
      <description>&lt;p&gt;Hello! This is my first blog post on dev.to&lt;/p&gt;

&lt;p&gt;I make music and I code. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Putting out music and garnering attention to it requires me to wear multiple hats for a variety of tasks: branding, social media marketing, beat production, songwriting, mastering audio, shooting and editing videos, designing graphics, the list goes on...&lt;/p&gt;

&lt;p&gt;In order to create social media audiovisual content for my music, I generally follow this process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1) Make a beat in Garageband&lt;/li&gt;
&lt;li&gt;2) Write lyrics&lt;/li&gt;
&lt;li&gt;3) Practice the song&lt;/li&gt;
&lt;li&gt;4) Setup my the DSLR camera&lt;/li&gt;
&lt;li&gt;5) Setup my microphone&lt;/li&gt;
&lt;li&gt;6) Video myself recording the song&lt;/li&gt;
&lt;li&gt;7) Import the video into Adobe Premiere&lt;/li&gt;
&lt;li&gt;8) Import the song audio into Adobe Premiere&lt;/li&gt;
&lt;li&gt;9) Align the audio with the video&lt;/li&gt;
&lt;li&gt;10) Add and align lyrics (text graphics) with the audio &lt;/li&gt;
&lt;li&gt;11) Add some effects to the video &lt;a href="https://adobemasters.net/how-to-create-an-80s-vintage-filter-in-adobe-premiere-pro-cc-2019/" rel="noopener noreferrer"&gt;I like this 80s look&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;12) Render the video (45 minutes to an hour)&lt;/li&gt;
&lt;li&gt;13) Export to &lt;code&gt;.mp4&lt;/code&gt; (another 30-40 minutes)&lt;/li&gt;
&lt;li&gt;14) Upload to YouTube (another 30-40 minutes)&lt;/li&gt;
&lt;li&gt;15) Upload to IGTV (another 30-40 minutes)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I want to increase the time I spend on steps 1 through 3 and decrease the time I spend on steps 4 through 15.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspiration
&lt;/h2&gt;

&lt;p&gt;Last Sunday (07/07/2019) I was refactoring some of my code on a project from jQuery to Web APIs. One thing led to the next, as they do the longer I am on &lt;a href="https://developer.mozilla.org/en-US/" rel="noopener noreferrer"&gt;MDN&lt;/a&gt;, and I came across the WebRTC (Web Real-Time Communication) standard and the YouTube LiveStream API documentation. This led me to Googling info about audio and video codecs. This finally led me to &lt;code&gt;ffmpeg&lt;/code&gt;, an open source software used for audio and video processing. Sweet--I could start something from there.&lt;/p&gt;

&lt;p&gt;I had used this software sparingly in the past, so I spent a few days experimenting with a few different image-to-video conversions in order to learn the basics. Here I've used &lt;code&gt;ffmpeg&lt;/code&gt; to convert a sort-of timelapse of the BART (Bay Area Rapid Transit) train that passes nearby using 338 images taken throughout the day:&lt;/p&gt;


&lt;div class="instagram-position"&gt;
  &lt;iframe id="instagram-liquid-tag" src="https://www.instagram.com/p/BzzM0xPBpoN/embed/captioned/"&gt;
  &lt;/iframe&gt;
  
&lt;/div&gt;


&lt;p&gt;This inspired and led me to the project I'm working on now.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Project
&lt;/h2&gt;

&lt;p&gt;I've called this project &lt;code&gt;animatemusic&lt;/code&gt; at this &lt;a href="https://github.com/vishalbakshi/animatemusic/blob/master/README.md" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;. My goal is to create a toolchain in order to expedite the creation of visuals for my songs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tech
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Node.js&lt;/li&gt;
&lt;li&gt;DOM Web API&lt;/li&gt;
&lt;li&gt;JSZip&lt;/li&gt;
&lt;li&gt;FileSaver&lt;/li&gt;
&lt;li&gt;ffmpeg&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How it Works Thus Far
&lt;/h3&gt;

&lt;p&gt;The process is a bit choppy right now since I'm running the various responsibilities in series in a semi-manual fashion:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1) Export my vocals from Garageband to a single &lt;code&gt;.wav&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;2) Type the song lyrics into a &lt;code&gt;.txt&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;3) Feed the song vocals and lyrics to a locally run CLI of &lt;a href="https://github.com/lowerquality/gentle" rel="noopener noreferrer"&gt;gentle&lt;/a&gt; and receive a &lt;code&gt;JSON&lt;/code&gt; file with the forced-alignment results&lt;/li&gt;
&lt;li&gt;4) Install and run my &lt;code&gt;animatemusic&lt;/code&gt; repo locally&lt;/li&gt;
&lt;li&gt;5) upload the &lt;code&gt;JSON&lt;/code&gt; file (along with some other parameters) and receive a &lt;code&gt;.zip&lt;/code&gt; folder with individual video frame &lt;code&gt;.png&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;6) Use &lt;code&gt;ffmpeg&lt;/code&gt; to stitch the images into a (lyric) video file&lt;/li&gt;
&lt;li&gt;7) Use &lt;code&gt;ffmpeg&lt;/code&gt; to combine the song audio and the lyric video&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Setting Up gentle
&lt;/h4&gt;

&lt;p&gt;gentle is a forced-alignment tool that relies on &lt;a href="https://github.com/kaldi-asr/kaldi" rel="noopener noreferrer"&gt;kaldi&lt;/a&gt; which is a speech recognition toolkit. Forced-alignment involves matching a text transcript with the corresponding speech audio file.&lt;/p&gt;

&lt;p&gt;The installation process for gentle was rocky, so the following tips and resources may be useful to you, should you choose to install it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/lowerquality/gentle/issues/194" rel="noopener noreferrer"&gt;"Error finding kaldi files"&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;I added &lt;code&gt;branch: "master"&lt;/code&gt; to &lt;a href="https://github.com/lowerquality/gentle/blob/master/.gitmodules" rel="noopener noreferrer"&gt;the gentle &lt;code&gt;.gitmodules&lt;/code&gt; file&lt;/a&gt; in order to capture some of the latest updates in kaldi which resolved some installation issues&lt;/li&gt;
&lt;li&gt;Install gentle in a python &lt;a href="https://docs.python-guide.org/dev/virtualenvs/#basic-usage" rel="noopener noreferrer"&gt;virtual environment&lt;/a&gt; since they expect you to use &lt;code&gt;python@2.7.x&lt;/code&gt; and the corresponding &lt;code&gt;pip&lt;/code&gt; version&lt;/li&gt;
&lt;li&gt;In gentle's &lt;a href="https://github.com/lowerquality/gentle/blob/master/install_deps.sh" rel="noopener noreferrer"&gt;&lt;code&gt;install_deps.sh&lt;/code&gt;&lt;/a&gt; bash script, comment out any of the &lt;code&gt;brew install&lt;/code&gt; software names that you already have installed since any &lt;code&gt;brew&lt;/code&gt; warnings will prevent the bash script from proceeding to the next step, which is the critical &lt;code&gt;setup.py&lt;/code&gt; process&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Generating the Forced-Alignment Results
&lt;/h4&gt;

&lt;p&gt;Once you have gentle running, give yourself a pat on the back and then run the following in your terminal, now outside of the virtual environment which used &lt;code&gt;python@2.7.x&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;python3 align.py path/to/audio path/to/transcript -o path/to/output&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The resulting file is in &lt;code&gt;JSON&lt;/code&gt; format with the following structure:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;transcript&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;words&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;alignedWord&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;case&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;endOffset&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;phones&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
               &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;duration&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;phone&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;startOffset&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;word&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;transcript&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;holds the full text of your transcript in a single string&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;words&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;holds word Objects in an array&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;alignedWord&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;is the word string that gentle recognized from the audio&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;case&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;is a success string with either "success" or "not-in-audio" values&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;end&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;is the time in seconds of when the word ends in the audio&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;endOffset&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;I'm not sure...TBD (comment if you know)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;start&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;is the time in seconds of when the word starts in the audio&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;startOffset&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;I'm not sure...TBD (comment if you know)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;word&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;is the word in the transcript to which it forced-aligned the word in the audio file&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  Converting Forced-Alignment Results to Video Frames
&lt;/h4&gt;

&lt;p&gt;If I can create an image for each video frame, I can render all of those image frames into a video using &lt;code&gt;ffmpeg&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Right now, I have a single script block in my &lt;code&gt;index.html&lt;/code&gt; which performs all of the logic around this process. Here's the minimal interface I've created thus far:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx79bq2szzwedpj2ywfsp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx79bq2szzwedpj2ywfsp.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are the inputs to my script:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"video frame rate" and "full song length"

&lt;ul&gt;
&lt;li&gt;determine the total number of frames in the (eventual) video. Default values: 30 fps (frames per second) and 60 seconds, resulting in 1800 frames.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;"words per frame" determine how many words will be displayed together on the &lt;code&gt;canvas&lt;/code&gt; at any given time

&lt;ul&gt;
&lt;li&gt;right now my script is not optimal--if your cadence is fast, the time between words is short and this causes rounding errors and the script fails. This motivated the addition of this input.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;"video width" and "video height" 

&lt;ul&gt;
&lt;li&gt;set the size for the &lt;code&gt;canvas&lt;/code&gt; element&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;"lyrics"

&lt;ul&gt;
&lt;li&gt;is the &lt;code&gt;JSON&lt;/code&gt; output from gentle&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The following scripts must be loaded first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;jszip.min.js&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;The wonderful &lt;a href="https://stuk.github.io/jszip/" rel="noopener noreferrer"&gt;JSZip&lt;/a&gt; client-side library which generates a zip file&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;FileSaver.js&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;The wonderful &lt;a href="https://github.com/eligrey/FileSaver.js/blob/master/dist/FileSaver.js" rel="noopener noreferrer"&gt;FileSaver&lt;/a&gt; client-side library which, among other functionality, exposes the &lt;code&gt;saveAs&lt;/code&gt; variable to trigger a browser download of a file&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The script I've written right now, can be seen in the repo's &lt;a href="https://github.com/vishalbakshi/animatemusic/blob/master/index.html" rel="noopener noreferrer"&gt;index.html&lt;/a&gt;. It's still a work in progress so please provide feedback. Here's how it works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upon uploading the transcript, the event handler &lt;code&gt;handleFiles&lt;/code&gt; is called. &lt;code&gt;handleFiles&lt;/code&gt;:

&lt;ul&gt;
&lt;li&gt;Parses the file into a regular JS object&lt;/li&gt;
&lt;li&gt;Renders either a blank image (no lyrics being sung for that frame) or an image with the lyrics text (for frames where lyrics are being sung) onto the &lt;code&gt;canvas&lt;/code&gt; element&lt;/li&gt;
&lt;li&gt;Saves the &lt;code&gt;canvas&lt;/code&gt; element first as a &lt;code&gt;dataURL&lt;/code&gt; and then as a &lt;code&gt;.png&lt;/code&gt; file object to the folder object which will eventually be zipped&lt;/li&gt;
&lt;li&gt;Initiates the download of the zipped folder upon completion of all image renders&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;A few helper functions to break up the responsibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;prepareWordData&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;takes the &lt;code&gt;words&lt;/code&gt; &lt;code&gt;Array&lt;/code&gt; from the transcript&lt;/li&gt;
&lt;li&gt;extracts &lt;code&gt;wordsPerFrame&lt;/code&gt; words at a time (default of 3 words)&lt;/li&gt;
&lt;li&gt;creates an &lt;code&gt;Array&lt;/code&gt; of new reduced versions of the original word Objects using the first and last word's &lt;code&gt;start&lt;/code&gt; and &lt;code&gt;end&lt;/code&gt; values, respectively for every set of words:&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;


&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;alignedWord&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// the last word's `end` property&lt;/span&gt;
  &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="c1"&gt;// the first word`s `start` property&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="s2"&gt;```

- `&lt;/span&gt;&lt;span class="nx"&gt;getWordDuration&lt;/span&gt;&lt;span class="s2"&gt;`
   - takes a word object and returns the difference (in seconds) between the `&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="s2"&gt;` and `&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="s2"&gt;` values. 
  - this "duration" is used to determine how many frames need to be rendered for each set of words

- `&lt;/span&gt;&lt;span class="nx"&gt;renderWordFrames&lt;/span&gt;&lt;span class="s2"&gt;`
    - takes the word (empty string if no lyrics are spoken during those frames) and duration of the word
    - creates a new 2D `&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="s2"&gt;` object
    - fills it with the words' text
    - gets the `&lt;/span&gt;&lt;span class="nx"&gt;dataURL&lt;/span&gt;&lt;span class="s2"&gt;` using the `&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toDataURL&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="s2"&gt;` property on the `&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="s2"&gt;` element
   - saves it to the folder-object-to-be-zipped with filenames starting with `&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;png&lt;/span&gt;&lt;span class="s2"&gt;`
    - This filename convention was chosen since it's the default filename sequence that `&lt;/span&gt;&lt;span class="nx"&gt;ffmpeg&lt;/span&gt;&lt;span class="s2"&gt;` expects

#### Generating the Video From Rendered Frames

Now that I have an image file for each frame of the video, I can use `&lt;/span&gt;&lt;span class="nx"&gt;ffmpeg&lt;/span&gt;&lt;span class="s2"&gt;` to stich them together. I have found the following parameters to be successful:

`&lt;/span&gt;&lt;span class="nx"&gt;ffmpeg&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;framerate&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;%d.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="mi"&gt;640&lt;/span&gt;&lt;span class="nx"&gt;x480&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="nx"&gt;libx264&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="nx"&gt;high&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;crf&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;pix_fmt&lt;/span&gt; &lt;span class="nx"&gt;yuv420p&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mp4&lt;/span&gt;&lt;span class="s2"&gt;`

- `&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;framerate&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="s2"&gt;` sets the video frame rate to 30 frames per second
- `&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;%d.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;` matches the sequential filenames
- `&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="s2"&gt;` sets the size of the video frame (corresponding to the `&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="s2"&gt;` element size, in this exampel, 640x480)
- `&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="s2"&gt;` specifies the video codec (I've used `&lt;/span&gt;&lt;span class="nx"&gt;libx264&lt;/span&gt;&lt;span class="s2"&gt;` which is recommended by YouTube and Instagram)
- `&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="s2"&gt;` sets the quality of the video to `&lt;/span&gt;&lt;span class="nx"&gt;high&lt;/span&gt;&lt;span class="s2"&gt;` (haven't fully understood how it works yet) 
- `&lt;/span&gt;&lt;span class="nx"&gt;crf&lt;/span&gt;&lt;span class="s2"&gt;` is the "Constant Rate Factor" which I haven't fully understood, but it ranges from 0 (lossless) to 51 (lowest quality)
- `&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;pix_fmt&lt;/span&gt;&lt;span class="s2"&gt;` sets the pixel format used, in this case, `&lt;/span&gt;&lt;span class="nx"&gt;yuv420&lt;/span&gt;&lt;span class="s2"&gt;` which sets the ratio of pixels for luminance Y (or brightness), chrominance blue U and chrominance red V. I'm pretty rough on these concepts so please correct or enlighten if you are more experienced.

This command generates a video at the output path, stiching the images together at a given framerate.

#### Adding the Song Audio

Now that I have the video for the lyrics, I can add the song audio (full song not just the vocals) using:

`&lt;/span&gt;&lt;span class="nx"&gt;ffmpeg&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;video&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;audio&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;vcodec&lt;/span&gt; &lt;span class="nx"&gt;libx264&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;acodec&lt;/span&gt; &lt;span class="nx"&gt;libmp3lame&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mp4&lt;/span&gt;&lt;span class="s2"&gt;`

The first two input flags identify the video and audio files which will be streamed together using the video codec and audio codec specified.

#### The Result

Here's what I end up with!

&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/4Wpp0ldTscY"&gt;
&lt;/iframe&gt;


It's pretty rough but the adrenaline rush was real when I saw it the first time.

## Next Steps

I consider this a successful Proof-Of-Concept. Here are my next steps:

- Over time, the lyrics fall out of sync with the audio, and this is most likely due to the fact that I rely on rounding the number of frames at 3 different places in the script

- The manner in which the three words align with the vocals is suboptimal. I may consider increasing the number of words shown per set of frames

- It's dull! The project is called `&lt;/span&gt;&lt;span class="nx"&gt;animatemusic&lt;/span&gt;&lt;span class="s2"&gt;` and this video is lacking interesting animations. If you recall, the word objects contain an array of phonemes used to pronounce the word. Mixing this with [anime.js, particularly their morphing animation](https://animejs.com/documentation/#morphing) will lead to some interesting lip sync animation attempts down the road

- The process is fragmented. Generating the forced-alignment output, generating the video frame images and generating the final output video currently takes places in three separate manual steps. I would like to eventually integrate these different services

- Integrations. The eventual goal is to connect this process with my YouTube and Instagram accounts so that I can upload to them upon completion using their APIs

- Refactoring. There's a lot of improvements needed in my script and I now feel confident enough to dive in and build this project out properly with tests

## Feedback

If you can help me improve my code, blog post, or my understanding of the context and concepts around anything you read above, please leave a comment below.

## Follow Me

[YouTube](https://www.youtube.com/channel/UCqvA0CVB3QR3TLpGVzkD35g)
[Instagram](https://www.instagram.com/vbaknation)

Thanks for reading!





&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>javascript</category>
      <category>ffmpeg</category>
      <category>machinelearning</category>
    </item>
  </channel>
</rss>
