<?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>Forem: Manuele J Sarfatti</title>
    <description>The latest articles on Forem by Manuele J Sarfatti (@mjsarfatti).</description>
    <link>https://forem.com/mjsarfatti</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%2F229865%2F1c61773e-83de-4486-91b0-1aaf90d2e11e.jpg</url>
      <title>Forem: Manuele J Sarfatti</title>
      <link>https://forem.com/mjsarfatti</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/mjsarfatti"/>
    <language>en</language>
    <item>
      <title>Is it now possible to react with a thumb down 👎 on DEV?</title>
      <dc:creator>Manuele J Sarfatti</dc:creator>
      <pubDate>Sun, 14 Jun 2020 16:16:06 +0000</pubDate>
      <link>https://forem.com/mjsarfatti/is-it-now-possible-to-react-with-a-thumb-down-on-dev-5d15</link>
      <guid>https://forem.com/mjsarfatti/is-it-now-possible-to-react-with-a-thumb-down-on-dev-5d15</guid>
      <description>&lt;p&gt;I wrote a (knowingly) controversial comment to a post, and in my notifications I see this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hSdwKrAv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/p8g5nt4asj19e2nakxuu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hSdwKrAv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/p8g5nt4asj19e2nakxuu.png" alt="Screenshot showing the thumb down reaction"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But when I look at the reactions of the comment itself the "thumb down" does not appear, nor have I ever been able to "thumb down" anything.&lt;/p&gt;

&lt;p&gt;Is this some kind of superpower granted to this website's VIPs? Is it an experimental feature? Does anyone have any information on this?&lt;/p&gt;

</description>
      <category>meta</category>
    </item>
    <item>
      <title>🤖 I Was Bored, so I made a fun little Twitter bot</title>
      <dc:creator>Manuele J Sarfatti</dc:creator>
      <pubDate>Mon, 25 May 2020 20:28:37 +0000</pubDate>
      <link>https://forem.com/mjsarfatti/i-was-bored-so-i-made-a-fun-little-twitter-bot-208o</link>
      <guid>https://forem.com/mjsarfatti/i-was-bored-so-i-made-a-fun-little-twitter-bot-208o</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was first published on &lt;a href="https://words.mjsarfatti.com/i-was-bored-so-i-made-a-fun-little-twitter-bot"&gt;my blog&lt;/a&gt; 🔗&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Two things happened tonight as I was logging off work:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I still felt a good level of mental energy and capacity&lt;/li&gt;
&lt;li&gt;I realized most tutorials for people learning web-dev are &lt;strong&gt;boring&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So I thought to myself: wouldn't it be nice if there was a resource that collected the fun-nest and most interesting tutorials out there? And since I am investing more and more on Twitter, I settled on making this a 🤖 Twitter bot: &lt;a href="https://twitter.com/_andfun"&gt;@_andfun&lt;/a&gt;.&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--IOsf6B7K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1264969729455906816/9N2tIEmb_normal.jpg" alt="Make WebDev Fun Again™ 🌈 ✨ 🦄 profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Make WebDev Fun Again™ 🌈 ✨ 🦄
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @_andfun
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--52oNvK_0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-ff4bdab814039c4cb172a35ea369e0ea9c6a4b59b631a293896ae195fa26a99d.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Hello world 👋&lt;br&gt;&lt;br&gt;This is my first tweet. In a way, I am born today.&lt;br&gt;&lt;br&gt;🤓
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      18:18 PM - 25 May 2020
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1264984261637091330" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-reply-action.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1264984261637091330" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-retweet-action.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      1
      &lt;a href="https://twitter.com/intent/like?tweet_id=1264984261637091330" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-like-action.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
      0
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;The whole thing took me only a couple of hours. It's amazing what we can do with technology today!&lt;/p&gt;

&lt;p&gt;So let's dive right in and see what it takes to make a simple bot, step by step 👇&lt;/p&gt;

&lt;h2&gt;
  
  
  1. I like Airtable
&lt;/h2&gt;

&lt;p&gt;I decided to host my curated list of tweets on &lt;a href="https://airtable.com/"&gt;Airtable&lt;/a&gt;, because it's fun and easy. The &lt;em&gt;base&lt;/em&gt; is a very simple table with the URL to the tutorial, a hand-written text for the tweet, a helpful character counter and a field to set the date on which I wish the tweet to be published.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hn6fj-3S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ndjxbi18avm7lse5e2wx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hn6fj-3S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ndjxbi18avm7lse5e2wx.png" alt="Screenshot of the Airtable base"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since I plan to only tweet once a day I made a separate view, called &lt;strong&gt;Today&lt;/strong&gt; (the one screenshotted is &lt;strong&gt;Grid view&lt;/strong&gt;), filtered to list only the tweets with the "Post On" column equal to &lt;code&gt;TODAY()&lt;/code&gt;. Which will actually be only one tweet. You'll see how this comes handy later on.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. I also like Glitch
&lt;/h2&gt;

&lt;p&gt;While I've never used &lt;a href="https://glitch.com/"&gt;Glitch&lt;/a&gt; before, I found out there is &lt;a href="https://glitch.com/edit/#!/twitterbot?path=README.md%3A1%3A0"&gt;a project&lt;/a&gt; that you can easily fork (or, &lt;em&gt;remix&lt;/em&gt;, in Glitch slang) aptly named &lt;strong&gt;twitterbot&lt;/strong&gt;. The project is written in JavaScript, which is very nice since Airtable provides a useful little &lt;strong&gt;npm&lt;/strong&gt; package to work with its APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. But first: Twitter apps
&lt;/h2&gt;

&lt;p&gt;Before going forward, I needed to create a new Twitter account for my bot, and a new Twitter app. &lt;strong&gt;twitterbot&lt;/strong&gt;'s README file points to a well made resource for this (&lt;a href="https://botwiki.org/tutorials/how-to-create-a-twitter-app/"&gt;How to create a twitter app&lt;/a&gt;), so I won't repeat the steps here. Just a couple of notes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I needed a new email. I submitted a modified version of my personal email, in the form: [myactualgmailaccount]&lt;a href="mailto:+twitterbot@gmail.com"&gt;+twitterbot@gmail.com&lt;/a&gt;. It's awesome: it's a different email address, but it gets automatically redirected to my main account (everything after the &lt;code&gt;+&lt;/code&gt; sign is effectively ignored).&lt;/li&gt;
&lt;li&gt;I also had to provide and validate a real phone number. Just so you know if you try this.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  4. Creating the Twitter profile
&lt;/h2&gt;

&lt;p&gt;I wanted something fun and cute. I found the perfect robot face on &lt;a href="https://www.flaticon.com/free-icon/android_1462493"&gt;Flaticon&lt;/a&gt;. it's a great resource for illustrations and icons, some are free (with attribution), others come with a paid subscription. For the background I screenshotted a frame from this super cool &lt;a href="https://grad.superhi.com/"&gt;gradient generator&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Putting it all together
&lt;/h2&gt;

&lt;p&gt;With the Twitter profile ready, and the Twitter app approved (it took only a few seconds for Twitter to approve my app), it was time to get my hands dirty. Luckily, Airtable provides a well made API with very good &lt;a href="https://airtable.com/api"&gt;docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Glitch template also provides a &lt;code&gt;server.js&lt;/code&gt; file skeleton, and it wasn't too hard to put the two together. Here is my full code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// File: server.js&lt;/span&gt;

&lt;span class="cm"&gt;/* Setting things up. */&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="nx"&gt;Twit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;twit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* Be sure to update the .env file with your API keys. See how to get them: https://botwiki.org/tutorials/how-to-create-a-twitter-app */&lt;/span&gt;

    &lt;span class="na"&gt;twitter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;consumer_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TWITTER_CONSUMER_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;consumer_secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TWITTER_CONSUMER_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TWITTER_ACCESS_TOKEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;access_token_secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TWITTER_ACCESS_TOKEN_SECRET&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Twit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;twitter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/* Set up connection to Airtable base */&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;airtable&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[yourbaseid]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="cm"&gt;/* Using cron-job.org to periodically visit /BOT_ENDPOINT to wake up the app and make Twitter bot tweet. */&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BOT_ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tweets&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="nx"&gt;select&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="cm"&gt;/* Selecting the first record in "Today" (it should only be one anyway) */&lt;/span&gt;

      &lt;span class="na"&gt;maxRecords&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;view&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Today&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="nx"&gt;firstPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;airtable error!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="cm"&gt;/* This will only actually run once */&lt;/span&gt;

      &lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;URL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tweet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;statuses/update&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;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;data&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="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&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;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;listener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Your bot is running on port &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;port&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;I made sure to also update the &lt;code&gt;.env&lt;/code&gt; file with:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;My Twitter API tokens&lt;/li&gt;
&lt;li&gt;The secret endpoint that will trigger a Tweet (keep it secret, you don't want randoms to be able to trigger it just by visiting a URL)&lt;/li&gt;
&lt;li&gt;My Airtable API token&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  6. Time to test 🙌
&lt;/h2&gt;

&lt;p&gt;I added mw first tweet to the Airtable (making sure it had the date of today), and visited my endpoint (yes, a regular browser will do!). The endpoint looks something like https://[my-glitch-project-name].glitch.com/[the-super-secret-endpoint].&lt;/p&gt;

&lt;p&gt;I saw an &lt;code&gt;OK&lt;/code&gt;, and my bot's account had just tweeted it's very first tweet!&lt;/p&gt;

&lt;h2&gt;
  
  
  7. One last thing
&lt;/h2&gt;

&lt;p&gt;Now, I still needed to automate this though, right?&lt;/p&gt;

&lt;p&gt;It was simple: I opened a free account on &lt;a href="https://cron-job.org/"&gt;cron-job.org&lt;/a&gt; and activated a new cron job to run daily at 3pm (European time, which would be morning US time). The job simply calls the endpoint (see above), and the endpoint will tweet the tweet of the day according to what's on my Airtable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Blm22wZ0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/leebjg5o7z042pxaldvr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Blm22wZ0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/leebjg5o7z042pxaldvr.png" alt="Screenshot of cron-job.org setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tadaaaa 🎉&lt;/p&gt;




&lt;p&gt;I hope that you found this interesting, and saw how making a Twitter bot is approachable and requires very little code to get started. Are you going to try? Let me know what your bot will tweet about!&lt;/p&gt;

&lt;p&gt;So, follow 👉 &lt;a href="https://twitter.com/_andfun"&gt;@_andfun&lt;/a&gt; and let's be friends on Twitter (&lt;a href="https://twitter.com/mjsarfatti"&gt;@mjsarfatti&lt;/a&gt;, DMs are open) and on &lt;a href="https://dev.to/mjsarfatti"&gt;dev.to&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you'd like to be notified of the next article, you can subscribe to my &lt;a href="https://buttondown.email/mjsarfatti"&gt;email list&lt;/a&gt;. No spam ever, cancel anytime, and never more than one email per week (most probably fewer).&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>bot</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Property 'id' does not exist on type '{}'.</title>
      <dc:creator>Manuele J Sarfatti</dc:creator>
      <pubDate>Mon, 18 May 2020 10:03:20 +0000</pubDate>
      <link>https://forem.com/mjsarfatti/property-id-does-not-exist-on-type-2c0l</link>
      <guid>https://forem.com/mjsarfatti/property-id-does-not-exist-on-type-2c0l</guid>
      <description>&lt;p&gt;&lt;small&gt;&lt;em&gt;(Photo by Daniel Jensen on Unsplash)&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was first published on &lt;a href="https://words.mjsarfatti.com/typescript-cheats:-property-'id'-does-not-exist-on-type-''."&gt;my blog&lt;/a&gt;🔗.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Either:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isValidObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myObject&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ValidObject&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Or, better, define a type guard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;isValidObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myObject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ValidObject&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{}):&lt;/span&gt; &lt;span class="nx"&gt;myObject&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;ValidObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myObject&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ValidObject&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&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;&lt;em&gt;I'm publishing this tip mainly because it's the third time I run into this problem, and the third time I get lost on the internet trying to understand what it is I'm doing wrong. I hope next time I search for this, this post will come up! Read on if you want a better understandind of what the cheat-sheet code above does and where it comes from:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When writing regular JavaScript we are used to a certain degree of flexibility when it comes to objects. Take the following example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Imaginary call to an API that returns the venue with ID 1,&lt;/span&gt;
&lt;span class="c1"&gt;// or an empty object if there is no venue with that ID&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;venue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getVenue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Let's check if a venue was found by verifying the existence of  the `id` property&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;weHaveVenue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;venue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;weHaveVenue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// do something&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// do something else...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Pretty straightforward, right?&lt;/p&gt;

&lt;p&gt;Well, the moment we use TypeScript, things don't work so smoothly anymore. Have a look a this implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Let's define the type of our imaginary API function first&lt;/span&gt;
&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;GetVenue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;id&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&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="nl"&gt;name&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="nl"&gt;location&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="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

&lt;span class="c1"&gt;// And then write a sample (and NOT real world production code) implementation&lt;/span&gt;
&lt;span class="c1"&gt;// faking an API call that might or might not find (and return) a venue&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getVenue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GetVenue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Meetings Central&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;North Pole&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="p"&gt;{};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;venue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getVenue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;weHaveVenue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;venue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ❌ Property 'id' does not exist on type '{}'.&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;weHaveVenue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// do something&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// do something else...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I know what you are thinking: "Wait, I &lt;em&gt;know&lt;/em&gt; that. That's exactly why I'm checking on the id!". But TypeScript needs a little more holding hands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Let's define two more types since we will have to reuse them in our code&lt;/span&gt;
&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Venue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&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="nl"&gt;name&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="nl"&gt;location&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="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;NoVenue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;GetVenue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Venue&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;NoVenue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getVenue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GetVenue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Meetings Central&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;North Pole&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="p"&gt;{};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;venue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getVenue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// By casting our `venue` to a `Venue` type, and then checking on the `id` property,&lt;/span&gt;
&lt;span class="c1"&gt;// we are basically telling TypeScript: "trust us, at runtime we're gonna be fine"&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;weHaveVenue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;venue&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Venue&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ✅&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;weHaveVenue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// do something&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// do something else...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Hurrah 🙌&lt;/p&gt;

&lt;p&gt;This may (and will) work well in several, simple cases. But what if further down we also want to use that &lt;code&gt;venue&lt;/code&gt; object? Let's say we need an upper-cased version of the venue name, and add one line of code to our if/else statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;[...]&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;weHaveVenue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// do something with our venue object&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;upperName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;venue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// ❌ Property 'name' does not exist on type 'NoVenue'.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// do something else...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Whoops 😕. Back at square one.&lt;/p&gt;

&lt;p&gt;In this case we need to move our check in a custom &lt;em&gt;type guard&lt;/em&gt;, which is fancy wording "a function that checks a type". Check out the full code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Venue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&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="nl"&gt;name&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="nl"&gt;location&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="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;NoVenue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;GetVenue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Venue&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;NoVenue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// We move our id check into a function whose return type is "value is Type"&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;isVenue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;venue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Venue&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;NoVenue&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;venue&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;Venue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;venue&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Venue&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getVenue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GetVenue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Meetings Central&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;North Pole&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="p"&gt;{};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;venue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getVenue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// We can now call our type guard to be sure we are dealing with one type, and not the other&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isVenue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;venue&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// do something with our venue object&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;upperName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;venue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// ✅&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// do something else...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To paraphrase the official &lt;a href="https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-differentiating-types"&gt;TypeScript documentation&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Any time &lt;code&gt;isVenue&lt;/code&gt; is called with some variable, TypeScript will narrow that variable to that specific type if the original type is compatible.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;This brief excursion should've clarified a feature of TypeScript that may leave someone coming from JavaScript perplexed. At least, it troubled me a few times! I'd love to hear your comments: let's be friends on Twitter (&lt;a href="https://twitter.com/mjsarfatti"&gt;@mjsarfatti&lt;/a&gt;, DMs are open) and on &lt;a href="https://dev.to/mjsarfatti"&gt;dev.to&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you'd like to be notified of the next article, please do subscribe to my &lt;a href="https://buttondown.email/mjsarfatti"&gt;email list&lt;/a&gt;. No spam ever, cancel anytime, and never more than one email per week (actually probably much fewer).&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>tip</category>
      <category>javascript</category>
    </item>
    <item>
      <title>I'm slashing my salary to 1/3 of what I make. Here's why (and ask me anything, ofc):</title>
      <dc:creator>Manuele J Sarfatti</dc:creator>
      <pubDate>Fri, 15 May 2020 13:50:32 +0000</pubDate>
      <link>https://forem.com/mjsarfatti/i-m-slashing-my-salary-to-1-3-of-what-i-make-here-s-why-and-ask-me-anything-ofc-1cm5</link>
      <guid>https://forem.com/mjsarfatti/i-m-slashing-my-salary-to-1-3-of-what-i-make-here-s-why-and-ask-me-anything-ofc-1cm5</guid>
      <description>&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--zbJ_oaS_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1163776349041057792/Vi5Yxin5_normal.jpg" alt="manuele ᐝ profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        manuele ᐝ
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="comment-mentioned-user" href="https://dev.to/mjsarfatti"&gt;@mjsarfatti&lt;/a&gt;

      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--52oNvK_0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-ff4bdab814039c4cb172a35ea369e0ea9c6a4b59b631a293896ae195fa26a99d.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Today is my last day at a job that pays 2000€/week, which I'm leaving for a job that will pay a bit less than 3000€/month.&lt;br&gt;&lt;br&gt;&lt;a href="https://twitter.com/hashtag/devdiscuss"&gt;#devdiscuss&lt;/a&gt; &lt;a href="https://twitter.com/hashtag/development"&gt;#development&lt;/a&gt; &lt;a href="https://twitter.com/hashtag/tech"&gt;#tech&lt;/a&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      13:46 PM - 15 May 2020
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1261291791078297602" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-reply-action.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1261291791078297602" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-retweet-action.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      0
      &lt;a href="https://twitter.com/intent/like?tweet_id=1261291791078297602" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-like-action.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
      0
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;"Really?"&lt;/p&gt;

&lt;p&gt;Yes.&lt;/p&gt;

&lt;p&gt;"But why?"&lt;/p&gt;

&lt;p&gt;I believe the Real Life Value™ of my next job is at least 5x the salary on paper. I'm scaling up.&lt;/p&gt;

&lt;p&gt;"What's Real Life Value?"&lt;/p&gt;

&lt;p&gt;People you get to meet, network you get to grow, experiences you get to make, lessons you get to teach, and most of all lessons you get to learn. Can you honestly put a price tag on that?&lt;/p&gt;

&lt;p&gt;It's time we start changing the language around (tech) salaries.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>ama</category>
      <category>career</category>
    </item>
    <item>
      <title>What is the ⚡️FASTEST⚡️ way of building a "nocode" desktop and mobile app?</title>
      <dc:creator>Manuele J Sarfatti</dc:creator>
      <pubDate>Tue, 17 Mar 2020 19:31:30 +0000</pubDate>
      <link>https://forem.com/mjsarfatti/what-is-the-fastest-way-of-building-a-nocode-desktop-and-mobile-app-1hn6</link>
      <guid>https://forem.com/mjsarfatti/what-is-the-fastest-way-of-building-a-nocode-desktop-and-mobile-app-1hn6</guid>
      <description>&lt;p&gt;Requirements:&lt;/p&gt;

&lt;p&gt;1) it needs user registration&lt;br&gt;
2) it needs to match users based on location&lt;br&gt;
3) there is no three, that's all&lt;/p&gt;

&lt;p&gt;Some more specifics: it only has to hold a handful of users, say 1000 AT MOST (but probably much less); "some" coding is fine, I have extensive JavaScript knowledge and integrating a couple of APIs could be OK as well, as long as they are near-zero configuration.&lt;/p&gt;

</description>
      <category>nocode</category>
      <category>discuss</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Is forcing full-screen a bad practice, or is this acceptable? </title>
      <dc:creator>Manuele J Sarfatti</dc:creator>
      <pubDate>Tue, 25 Feb 2020 12:11:42 +0000</pubDate>
      <link>https://forem.com/mjsarfatti/is-forcing-full-screen-a-bad-practice-or-is-this-acceptable-3fpg</link>
      <guid>https://forem.com/mjsarfatti/is-forcing-full-screen-a-bad-practice-or-is-this-acceptable-3fpg</guid>
      <description>&lt;p&gt;I'm posting it here for reach, I'd be happy if you gave your opinion &lt;em&gt;and&lt;/em&gt; discussed why in the comments 🙏&lt;/p&gt;

&lt;p&gt;If you click through the tweet it's actually a poll, with the following options:&lt;/p&gt;

&lt;p&gt;[ ] Hell yeah!&lt;br&gt;
[ ] Please no.&lt;br&gt;
[ ] Suggest, but don't force&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--zbJ_oaS_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1163776349041057792/Vi5Yxin5_normal.jpg" alt="i know regular expressions profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        i know regular expressions
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="comment-mentioned-user" href="https://dev.to/mjsarfatti"&gt;@mjsarfatti&lt;/a&gt;

      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      &lt;a href="https://twitter.com/hashtag/ux"&gt;#ux&lt;/a&gt; &lt;a href="https://twitter.com/hashtag/frontend"&gt;#frontend&lt;/a&gt; &lt;a href="https://twitter.com/hashtag/design"&gt;#design&lt;/a&gt; &lt;a href="https://twitter.com/hashtag/devdiscuss"&gt;#devdiscuss&lt;/a&gt; twitter, I'm developing a website for a photographer 📸 which would benefit immensely from being viewed full-screen 🖥️. &lt;br&gt;&lt;br&gt;Would you force-trigger full-screen when the user clicks to view a gallery?&lt;br&gt;&lt;br&gt;(Please explain your choice in the comments)
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      12:05 PM - 25 Feb 2020
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1232275379118841856" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-reply-action.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1232275379118841856" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-retweet-action.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      0
      &lt;a href="https://twitter.com/intent/like?tweet_id=1232275379118841856" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-like-action.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
      0
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


</description>
      <category>ux</category>
      <category>discuss</category>
      <category>javascript</category>
    </item>
    <item>
      <title>💊 Pills of WebGL: An Introduction</title>
      <dc:creator>Manuele J Sarfatti</dc:creator>
      <pubDate>Mon, 03 Feb 2020 15:46:36 +0000</pubDate>
      <link>https://forem.com/mjsarfatti/pills-of-webgl-an-introduction-1gm9</link>
      <guid>https://forem.com/mjsarfatti/pills-of-webgl-an-introduction-1gm9</guid>
      <description>&lt;center&gt;&lt;small&gt;&lt;em&gt;Photo by Andy Holmes on Unsplash · Sandwich by John Vestevich from the Noun Project&lt;/em&gt;&lt;/small&gt;&lt;/center&gt;

&lt;p&gt;(This post first appeared on my &lt;a href="https://words.mjsarfatti.com/Pills-of-WebGL:-An-Introduction"&gt;blog&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is the first in a series of articles that will explore the &lt;strong&gt;magical world of drawing in a browser&lt;/strong&gt;. The idea is to publish a run of practical micro tutorials - illustrated and in plain english - to make WebGL clear and accessible, and allow anyone to start creating wonders such as &lt;a href="http://ykob.github.io/sketch-threejs/"&gt;this&lt;/a&gt;, or &lt;a href="https://sid.mgz.me/"&gt;this&lt;/a&gt;, or &lt;a href="https://bruno-simon.com/"&gt;this&lt;/a&gt;, or &lt;a href="https://threejs.org/examples/#webgl_animation_keyframes"&gt;this&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What drives me to write this series is that, as I approach WebGL myself, too often I lose my way in a sea of technical terms and foreign concepts (what even is a "shader"?). I spend hours on official and unofficial educational material until, at one point, it clicks. But it could've clicked a lot sooner and a lot easier, had the concepts been explained in more basic terms. (By the way, a shader is nothing but a material. With some extra magic. We'll see more in due time.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;My first post will not actually be a pill, nor micro, but I promise every other post will be published in an easy to digest form. I want to offer you something that can provide you with the basics for understanding a new concept or tool in just a few minutes. But as I said, this first post will be a little bit longer in order to establish a good enough foundation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Oh, one last thing before we begin. Here is a tentative outline of the structure of the series (I'm sure it will change and adapt as we go on, but it should give you an idea of what to expect):&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;em&gt;Introduction, what is WebGL, what are its potentialities, "Hello Cube"&lt;/em&gt;
👆 we are here&lt;/li&gt;
&lt;li&gt;&lt;em&gt;What is a "scene"? Let's build one.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;What is a "shader"? Let's make one.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Let's make some objects with code!&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Let's make some objects with an external program and import them!&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Let's play with lights&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Let's play with materials&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;How do I interact with my scene? Mouse and keyboard&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Sound&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;React and three.js (react-three-fiber)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Advanced: let's build a browser game&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Advanced: let's build a music visualizer&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Advanced: let's build a website that lives in 3D space&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Advanced: physics and collisions&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Note: a single "chapter" may be spread out into multiple pills.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is a bit of a long introduction, but I felt it was important to give you the context into which to read this article. And now it's time to get down to business and talk about what you are here for: WebGL.&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  WebGL (is not a 3D API)
&lt;/h2&gt;

&lt;p&gt;Didn't expect this, did you? While there are controversial opinions on the matter, the truth is that WebGL doesn't provide a lot in terms of 3D out of the box. In fact, 3D is &lt;em&gt;not&lt;/em&gt; the primary goal of WebGL, and this is why in your day-to-day work you will probably want to make use of libraries such as &lt;a href="https://github.com/oframe/ogl"&gt;OGL&lt;/a&gt;, &lt;a href="https://threejs.org/"&gt;three.js&lt;/a&gt; or &lt;a href="https://www.babylonjs.com/"&gt;Babylon&lt;/a&gt;. We will cover them later in this article, but let's get back to WebGL for a moment. If it doesn't give us 3D tools, what is it that it does?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WebGL draws points, lines and triangles in &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; elements using the GPU.&lt;/strong&gt; That's it. That's the tweet. It's that simple. Ok, it's not actually that simple, and if you are looking for a rabbit hole feel free to search "GPU vs CPU" and what are the benefits and drawbacks of utilizing the GPU to run programs.&lt;/p&gt;

&lt;p&gt;But if there is one piece of information that we should keep from this whole article is that WebGL is a &lt;em&gt;low level&lt;/em&gt; library, and you are probably not interested in learning it in depth &lt;em&gt;right now&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  A world of possibilities
&lt;/h2&gt;

&lt;p&gt;As you may have seen if you followed the links at the beginning of the article (if not, I recommend doing it now, I'll be here waiting) &lt;strong&gt;WebGL does seem to open up a whole world of possibilities&lt;/strong&gt;. If you are like me, you will almost feel overwhelmed by the sheer diversity of things you can do with WebGL. Surely learning to do all that must be a ginormous effort, right? And surely you must dedicate hours and hours of research and development day in and day out for months, or even years, before you can build something beautiful, right?&lt;/p&gt;

&lt;p&gt;Wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It takes 5 minutes to render a pink spinning cube on the web page of your choice.&lt;/strong&gt; 2 if it's the third time you do it. Does it sound more interesting now?&lt;/p&gt;

&lt;p&gt;Seriously though, this is what WebGL is for me: possibilities (notice the plural). You can build pretty much anything you want, 2D or 3D, from music players to browser games to fancy hover effects. Sky is the limit, and creativity your friend. We will explore how in a series of simple and non-overwhelming steps over the next few weeks. Or months. We'll see.&lt;/p&gt;
&lt;h2&gt;
  
  
  3D libraries
&lt;/h2&gt;

&lt;p&gt;Alright, so. WebGL is an overly complicated low level library, but animating 3D stuff in the browser is supposed to be simple? In a way, yes, thanks to a number of libraries that provide useful abstractions on top of WebGL. The three most popular, ordered by most essential to most complete, are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://github.com/oframe/ogl"&gt;OGL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://threejs.org/"&gt;three.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.babylonjs.com/"&gt;Babylon.js&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this article we will create a &lt;strong&gt;pink spinning cube&lt;/strong&gt; in all three of them, in order to get a taste of each. But first, how do they compare?&lt;/p&gt;

&lt;p&gt;Generally speaking, &lt;strong&gt;OGL&lt;/strong&gt; does its best to be minimal and abstract as little as possible, to the point where you will often have to write native WebGL commands. It does provide a few out-of-the-box shapes and utilities (cube, sphere, fog, shadow...), but not nearly as many as a more complete library such as &lt;strong&gt;three.js&lt;/strong&gt;. It's a good choice if you aren't planning on building anything overly complicated, and you'd like to have the perfect excuse to learn a bit more of WebGL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three.js&lt;/strong&gt; is by far the most used 3D library out there. It sometimes has a bad reputation, since the developers tend to "move fast and break things", so your code might be working with today's &lt;strong&gt;r113&lt;/strong&gt; version, but something might break if tomorrow you upgrade to &lt;strong&gt;r114&lt;/strong&gt;. Yes, they do not use &lt;a href="https://semver.org/"&gt;semver&lt;/a&gt;. Still, due to its ubiquity and popularity, it's hard to go wrong if you choose it (just look at their &lt;a href="https://threejs.org/examples/"&gt;examples page&lt;/a&gt;). In fact in most of the future &lt;em&gt;💊 pills&lt;/em&gt; I will be using three.js.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Babylon.js&lt;/strong&gt; is probably the most powerful and complete library of the bunch. While it's less popular than three.js, it is sponsored (developed?) by Microsoft. It has many features you probably don't even know are a thing (and neither do I), but most importantly it comes with a set of tools for building games. It would be the library of choice if I had to build something complex, or a browser game.&lt;/p&gt;
&lt;h2&gt;
  
  
  Hello Cube
&lt;/h2&gt;

&lt;p&gt;I realize I spent &lt;em&gt;a lot&lt;/em&gt; of words introducing first this series, and then the world of WebGL. I tried to keep it at a minimum, and we'll certainly learn a lot more in the following weeks, but now a piece of good news: &lt;strong&gt;the time has finally come for the "Hello world" of WebGL 🙌&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Please note: the goal of this exercise is to get something done. There will be terms and concepts that may not make much sense yet. I suggest you suspend your curiosity for a moment and try to follow along and put a quick win in your pocket (and maybe show it to your friends). There will be plenty of time to understand everything else as we proceed further along the series!&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;I suggest you create, on &lt;a href="https://codesandbox.io"&gt;CodeSandbox&lt;/a&gt;, a sandbox for each cube we will make. The code I will show can be pasted in the &lt;code&gt;index.js&lt;/code&gt; file provided, and you'll get an immediate preview on the right side of the screen. For your convenience you can simply open this template: &lt;a href="https://codesandbox.io/s/pills-of-webgl-hello-cube-8tft5"&gt;https://codesandbox.io/s/pills-of-webgl-hello-cube-8tft5&lt;/a&gt; and click &lt;code&gt;Fork&lt;/code&gt; on the top right.&lt;/p&gt;
&lt;h3&gt;
  
  
  OGL
&lt;/h3&gt;

&lt;p&gt;Let's start with the most difficult library :)&lt;/p&gt;

&lt;p&gt;First things first: in our &lt;em&gt;newly forked&lt;/em&gt; sandbox, click on &lt;code&gt;Add Dependency&lt;/code&gt; (you'll find it on the sidebar), search for &lt;strong&gt;ogl&lt;/strong&gt; and click on it to add it to our project.&lt;/p&gt;

&lt;p&gt;Let's start by initializing the &lt;strong&gt;Renderer&lt;/strong&gt;, which is ultimately responsible for talking to WebGL and drawing pixels on a canvas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Renderer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Camera&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Mesh&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Transform&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ogl/dist/ogl.umd.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Initialize the OGL renderer and attach the canvas to our document&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renderer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Renderer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Append the canvas which will be used by OGL to our document&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Please note: normally it would be enough to write&lt;/em&gt; &lt;code&gt;import { ... } from 'ogl';&lt;/code&gt;&lt;em&gt;, but due to a bug in CodeSandbox we need to specify that we want the&lt;/em&gt; UMD &lt;em&gt;version.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If we glance at the preview we will see a single black rectangle measuring 300x150px. Perfect. That's the default size of the &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element, and it renders all black because, well, we haven't done much yet:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zlbK4rt---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pzqo1zqduh26pm48av5d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zlbK4rt---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pzqo1zqduh26pm48av5d.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's add a &lt;strong&gt;Camera&lt;/strong&gt;. And since we are at it, let's set the size of our &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; to cover the whole page. Add the following code to &lt;code&gt;index.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;// Append the canvas which will be used by OGL to our document&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Add a camera&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;camera&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Camera&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;- this moves the camera "back" 5 units&lt;/span&gt;

&lt;span class="c1"&gt;// Set the size of the canvas&lt;/span&gt;
&lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHeight&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Set the aspect ratio of the camera to the canvas size&lt;/span&gt;
&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;perspective&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;aspect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Mmm 🤔 the white turned grey, but that 300x150px black box is still there. What gives? That's ok. We have a renderer who renders in a canvas (if you check the dev tools you'll see the canvas actually covers the whole window), and we have a camera through which to look at. What's missing is what the camera should actually look at. Let's add a &lt;strong&gt;Scene&lt;/strong&gt;, and tell the renderer to render the scene through our camera:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;// Set the aspect ratio of the camera to the canvas size&lt;/span&gt;
&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;perspective&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;aspect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Add a scene (don't worry about what Transform actually does for the moment)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scene&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Transform&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Draw!&lt;/span&gt;
&lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;camera&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Yay! The whole page is finally black. Good job!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b5QYcoE4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7egxyojej92314pjlnho.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b5QYcoE4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7egxyojej92314pjlnho.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we need a &lt;strong&gt;Cube&lt;/strong&gt;. Here things get a little tricky: you will see some stuff, and it will not make much sense, and then you will see similar patterns repeat on the &lt;strong&gt;three.js&lt;/strong&gt; and &lt;strong&gt;Babylon.js&lt;/strong&gt; examples, and then in my next article I will explain what's actually going on. Just trust the following code for a moment, and add it to your &lt;code&gt;index.js&lt;/code&gt; before the &lt;em&gt;draw&lt;/em&gt; instruction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;// Add a scene (don't worry about what Transform actually does for the moment)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scene&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Transform&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Let's use the Box helper from OGL&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;geometry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// This complicated set of instructions tells our box to be pink. It's called&lt;/span&gt;
&lt;span class="c1"&gt;// "program" for a reason, but it doesn't matter right now.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;vertex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
            attribute vec3 position;

            uniform mat4 modelViewMatrix;
            uniform mat4 projectionMatrix;

            void main() {
                gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
            }
            `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
            void main() {
                gl_FragColor = vec4(0.92, 0.48, 0.84, 1.0); // Pink!
            }
        `&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Here we say that we want our box (geometry), to be pink (program)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mesh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Mesh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// And finally we add it to the scene&lt;/span&gt;
&lt;span class="nx"&gt;mesh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setParent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Draw!&lt;/span&gt;
&lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;camera&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Getting there? You should now see a pink square centered in our canvas. It's actually a cube, but we are looking at it flat front. Let's give it a spin, shall we?&lt;/p&gt;

&lt;p&gt;Add the following lines before &lt;code&gt;renderer.render({ scene, camera });&lt;/code&gt;, and hit &lt;code&gt;Save&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;// And finally we add it to the scene&lt;/span&gt;
&lt;span class="nx"&gt;mesh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setParent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Remember, `mesh` is our pink cube.&lt;/span&gt;
&lt;span class="c1"&gt;// And we can directly mutate some of it's properties!&lt;/span&gt;
&lt;span class="nx"&gt;mesh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rotation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mf"&gt;0.04&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;mesh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rotation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;0.03&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// One last thing: MOVE the `draw` instruction that we added earlier down here:&lt;/span&gt;
&lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;camera&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Alright I was kidding. That's definitely not enough to animate our object. We need a little helper, and our little helper is called &lt;code&gt;requestAnimationFrame&lt;/code&gt;. Very briefly, &lt;code&gt;requestAnimationFrame&lt;/code&gt; is a browser API that allows us to run a function right before the browser repaints the window. If we keep our animation simple enough, the repaint will happen 60 times per second, which is about once every 16ms. This is also known as "buttery smooth".&lt;/p&gt;

&lt;p&gt;Delete the previous two lines and the one reading &lt;code&gt;renderer.render({...&lt;/code&gt;, and add the following instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;// And finally we add it to the scene&lt;/span&gt;
&lt;span class="nx"&gt;mesh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setParent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Update the cube spin every 16ms&lt;/span&gt;
&lt;span class="nx"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;mesh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rotation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mf"&gt;0.04&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;mesh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rotation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;0.03&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;camera&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We did it 🥳&lt;br&gt;
Here is the final result:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/keen-frog-2xpoy?runonclick=1"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;If your program is not working as intended, click on the "Open Sandbox" button to see the commented source code and compare it with your result!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exercise for the reader:&lt;/strong&gt; &lt;em&gt;see if you can give it different colors, spins, and animate it's position instead.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  three.js
&lt;/h2&gt;

&lt;p&gt;I understand this is starting to be a lot to take in, and the article is getting long, but I wanted to build our first Hello Cube step by step in order to dissect all that is needed to animate stuff on our browser. The good news is that that's it. Everything that will follow from now on will basically be a variation of what we've seen so far.&lt;/p&gt;

&lt;p&gt;Let's get our &lt;strong&gt;three.js&lt;/strong&gt; example running and see how they do things instead. This time I'll skip some steps and we'll be done before you know it, I promise.&lt;/p&gt;

&lt;p&gt;Let's fork our template &lt;a href="https://codesandbox.io/s/pills-of-webgl-hello-cube-8tft5"&gt;https://codesandbox.io/s/pills-of-webgl-hello-cube-8tft5&lt;/a&gt; (again), and this time add the &lt;code&gt;three&lt;/code&gt; dependency. Next, let's set up our scene. Add the following to our &lt;code&gt;index.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;three&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Create our renderer and append the canvas to our document&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renderer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WebGLRenderer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHeight&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Add a camera, and move it back 5 units&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FOV&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// This corresponds approximately to a 30mm lens&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ASPECT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerWidth&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;NEAR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Anything closer than 0.1 units will not be visible&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FAR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Anything further than 0.1 units will not be visible&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;camera&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PerspectiveCamera&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FOV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ASPECT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NEAR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;FAR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Make a scene (lol)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scene&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Scene&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Draw!&lt;/span&gt;
&lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So far nothing new, we are at the "all black" stage. The APIs provided by &lt;strong&gt;three.js&lt;/strong&gt; are a little different, but it's still mostly english, and we can easily spot a lot of similarities with &lt;strong&gt;OGL&lt;/strong&gt;. Let's proceed with our &lt;strong&gt;Cube&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;// Make a scene (lol)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scene&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Scene&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Our helper from three.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;geometry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BoxGeometry&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// In OGL, this was called `program`. It's the same thing, just easier.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;material&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MeshBasicMaterial&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
  &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;0xea7ad7&lt;/span&gt; &lt;span class="c1"&gt;// Pink!&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Putting everything together&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cube&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Mesh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;material&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// And finally adding the cube to the scene&lt;/span&gt;
&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cube&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Draw!&lt;/span&gt;
&lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Remember that lot of confusing lines called &lt;code&gt;program&lt;/code&gt;? A program is a shader is a material. &lt;strong&gt;Three.js&lt;/strong&gt; calls it a material, and gives us a bunch of useful presets to start out with, such as &lt;em&gt;MeshBasicMaterial&lt;/em&gt;. Let's animate the cube now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;// And finally adding the cube to the scene&lt;/span&gt;
&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cube&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Update the cube spin every 16ms&lt;/span&gt;
&lt;span class="nx"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;cube&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rotation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mf"&gt;0.04&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;cube&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rotation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;0.03&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Tadaaa!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IsLRfW7N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pb75rsx8i75xo1r4d10b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IsLRfW7N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pb75rsx8i75xo1r4d10b.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All done. But, you know what? Let's go one tiny step further. I don't really like that flat look, that's not what cubes look like, right? Look for the line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;material&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MeshBasicMaterial&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;...and change it to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;material&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MeshLambertMaterial&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Do you see all black now? Good. We just set our cube to use a physically-based material. This means we now need to add... a &lt;strong&gt;Light&lt;/strong&gt;!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;// And finally adding the cube to the scene&lt;/span&gt;
&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cube&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// White directional light (by default it looks at the center of the scene)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;directionalLight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DirectionalLight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0xffffff&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Position it to the top left&lt;/span&gt;
&lt;span class="nx"&gt;directionalLight&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Add it to the scene&lt;/span&gt;
&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;directionalLight&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Update the cube spin every 16ms&lt;/span&gt;
&lt;span class="nx"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;cube&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rotation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mf"&gt;0.04&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;cube&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rotation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;0.03&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/peaceful-water-z6hmw?runonclick=1"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Isn't this a lot better? And with fewer lines of code than in the &lt;strong&gt;OGL&lt;/strong&gt; example.&lt;br&gt;&lt;br&gt;
This is the power of &lt;strong&gt;three.js&lt;/strong&gt;: we have a set of utilities that can make setting up a scene a breeze. Of course, if we wanted we could always opt out of the helpers and apply a custom program/shader to our cube. That's how some of the coolest stuff is done. But it's optional and for the moment we have more than we need to get started.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exercise for the reader:&lt;/strong&gt; &lt;em&gt;three.js provides a complete set of basic shapes, try to see what else you can spin.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Finally, let's look at the &lt;strong&gt;Babylon.js&lt;/strong&gt; example.&lt;/p&gt;
&lt;h2&gt;
  
  
  Babylon.js
&lt;/h2&gt;

&lt;p&gt;As usual, fork our template &lt;a href="https://codesandbox.io/s/pills-of-webgl-hello-cube-8tft5"&gt;https://codesandbox.io/s/pills-of-webgl-hello-cube-8tft5&lt;/a&gt; (once again), and this time add the &lt;code&gt;@babylonjs/core&lt;/code&gt; dependency (watch out, there is a package called simply &lt;code&gt;babylon&lt;/code&gt; which is a parser, NOT the 3D library we are looking for). And let’s set up our scene.&lt;/p&gt;

&lt;p&gt;If you remember, in our previous two examples the libraries themselves took charge of creating a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element, which then we attached to our &lt;code&gt;#app&lt;/code&gt; element. &lt;strong&gt;Babylon.js&lt;/strong&gt; instead wants a ready to use canvas, so open &lt;code&gt;index.html&lt;/code&gt; and add the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;...

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;canvas&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"renderCanvas"&lt;/span&gt; &lt;span class="na"&gt;touch-action=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Going back to &lt;code&gt;index.js&lt;/code&gt;, let's add the usual &lt;em&gt;renderer&lt;/em&gt;, &lt;em&gt;camera&lt;/em&gt;, and &lt;em&gt;scene&lt;/em&gt;, and draw our black rectangle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nx"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Scene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;UniversalCamera&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;MeshBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;StandardMaterial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DirectionalLight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Vector3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Color3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@babylonjs/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Get the canvas element and resize it to cover the full window&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;renderCanvas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerWidth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// In the previous examples this was called "renderer"&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Create the scene&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scene&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Scene&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Add a camera called "Camera" 🤓, and move it back 5 units&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;camera&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;UniversalCamera&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Camera&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Vector3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Point the camera towards the scene origin&lt;/span&gt;
&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setTarget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Vector3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Zero&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// And finally attach it to the canvas&lt;/span&gt;
&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attachControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Draw!&lt;/span&gt;
&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you hit &lt;code&gt;Save&lt;/code&gt; now you'll see the preview turns purple-ish, and not black. That's ok, it's just that &lt;strong&gt;Babylon.js&lt;/strong&gt; likes it less dark than our other friends 🙃. Still, this does not mean that there is a default light illuminating our scene. It's just sort of the background color of the canvas (not exactly, but it's a good enough explanation for the moment).&lt;/p&gt;

&lt;p&gt;Let's add our &lt;strong&gt;Cube&lt;/strong&gt;, and &lt;strong&gt;Light&lt;/strong&gt; it up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;// And finally attach it to the canvas&lt;/span&gt;
&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attachControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Create a 1x1 cube (Babylon.js automatically adds it to our scene)&lt;/span&gt;
&lt;span class="c1"&gt;// Note: there is an odler method called simply "Mesh". It is recommended&lt;/span&gt;
&lt;span class="c1"&gt;// to use the newer "MeshBuilder" instead.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;box&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;MeshBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CreateBox&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="p"&gt;{});&lt;/span&gt;

&lt;span class="c1"&gt;// Make it pink&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;StandardMaterial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Pink&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;pink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;diffuseColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Color3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.92&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.84&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;material&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pink&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// And add a light source. Note that it works slightly differently than in&lt;/span&gt;
&lt;span class="c1"&gt;// three.js. The Vector here is not the light's position, but the direction&lt;/span&gt;
&lt;span class="c1"&gt;// it points to.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;light&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;DirectionalLight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DirectionalLight&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Vector3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Draw!&lt;/span&gt;
&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BkUKrONL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kc0dicbnjqf8xf8c8hr0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BkUKrONL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kc0dicbnjqf8xf8c8hr0.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As usual, our final step will be to give it a spin! You will notice that this time instead of directly using the &lt;code&gt;requestAnimationFrame&lt;/code&gt; browser API, we'll be calling a couple of utilities provided by &lt;strong&gt;Babylon.js&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;First, we tell the renderer that before each pass we want to modify the rotation of our cube. Next, we modify our &lt;em&gt;draw&lt;/em&gt; instruction to use the engine's built in loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;light&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;DirectionalLight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DirectionalLight&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Vector3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Our beforeRender function&lt;/span&gt;
&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;registerBeforeRender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rotation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;0.03&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rotation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;0.04&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Register a render loop to repeatedly render the scene&lt;/span&gt;
&lt;span class="nx"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runRenderLoop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/ecstatic-violet-ovsvx?runonclick=1"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Hurray 🙌&lt;br&gt;&lt;br&gt;
Again, if you are stuck somewhere, or are not getting this result, open the sandbox and look through the commented code to spot any differences!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exercise for the reader:&lt;/strong&gt; &lt;em&gt;different materials react differently to different lights, explore what else Babylon.js provides.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;Well, that's it for this first installment :)  &lt;/p&gt;

&lt;p&gt;In this article we went through a few basic concepts, just enough to understand what this WebGL thing is and start getting our hands dirty. We also explored a number of tools that make our life easier when dealing with drawing in the browser. Hopefully seeing differences and similarities in the approaches of these libraries will help you in defining your mental map around WebGL. For example, &lt;strong&gt;OGL&lt;/strong&gt; showed us how to create a material (or &lt;code&gt;program&lt;/code&gt;, or &lt;code&gt;shader&lt;/code&gt;) writing WebGL instructions (in a next &lt;em&gt;💊 pill&lt;/em&gt; we'll explore this in more details), and then we saw how &lt;strong&gt;three.js&lt;/strong&gt; and &lt;strong&gt;Babylon.js&lt;/strong&gt; provide their own abstractions.&lt;/p&gt;

&lt;p&gt;I hope you enjoyed, and I hope it sparked interest and curiosity on the topic. I also hope my words were approachable, and the hands-on useful and practical. I'd love to hear your comments: you can find me on Twitter (&lt;a href="https://twitter.com/mjsarfatti"&gt;@mjsarfatti&lt;/a&gt;, DMs are open) and, of course, in here!&lt;/p&gt;

&lt;p&gt;If you'd like to be notified of the next article you can either follow me, or head to my blog to subscribe to my email list (no spam ever, cancel anytime, and never more than one email per week - actually probably much fewer).&lt;/p&gt;

&lt;p&gt;Thanks for reading, and see you soon 👋&lt;/p&gt;

</description>
      <category>webgl</category>
      <category>creativecoding</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Designers of DEV ✍️, are you there? </title>
      <dc:creator>Manuele J Sarfatti</dc:creator>
      <pubDate>Sun, 26 Jan 2020 10:10:06 +0000</pubDate>
      <link>https://forem.com/mjsarfatti/designers-of-dev-are-you-there-4h7i</link>
      <guid>https://forem.com/mjsarfatti/designers-of-dev-are-you-there-4h7i</guid>
      <description>&lt;p&gt;Is there any "pure designer", or "designer who codes" in this community?&lt;br&gt;
Emphasis on the "designer" part (I know there are many "coders who design").&lt;/p&gt;

&lt;p&gt;I'd like to (virtually) meet you! Please comment or DM me on twitter (&lt;a class="comment-mentioned-user" href="https://dev.to/mjsarfatti"&gt;@mjsarfatti&lt;/a&gt;
), I'd like to learn more about your interests and challenges when having to relate to code, or even coding yourself.&lt;/p&gt;

</description>
      <category>design</category>
      <category>watercooler</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>It's 5:47 pm on a Friday and I'm deploying to production. Ask me anything</title>
      <dc:creator>Manuele J Sarfatti</dc:creator>
      <pubDate>Fri, 17 Jan 2020 16:47:14 +0000</pubDate>
      <link>https://forem.com/mjsarfatti/it-s-5-47-pm-on-a-friday-and-i-m-deploying-to-production-ask-me-anything-49fj</link>
      <guid>https://forem.com/mjsarfatti/it-s-5-47-pm-on-a-friday-and-i-m-deploying-to-production-ask-me-anything-49fj</guid>
      <description>&lt;p&gt;Fire away :)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATE&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's 6:03 now and I've already found a glitch and re-deployed 🍿&lt;/p&gt;

</description>
      <category>ama</category>
    </item>
    <item>
      <title>Fellow freelancers, is any of you successful at estimates?</title>
      <dc:creator>Manuele J Sarfatti</dc:creator>
      <pubDate>Mon, 13 Jan 2020 17:36:55 +0000</pubDate>
      <link>https://forem.com/mjsarfatti/fellow-freelancers-is-any-of-you-successful-at-estimates-3gpd</link>
      <guid>https://forem.com/mjsarfatti/fellow-freelancers-is-any-of-you-successful-at-estimates-3gpd</guid>
      <description>&lt;p&gt;I'm talking about time estimates.&lt;/p&gt;

&lt;p&gt;I just got off the phone with a client who (with extreme kindness) made me notice how I am quite consistent at giving overly optimistic deadlines.&lt;/p&gt;

&lt;p&gt;I've been doing this job for 10 years and apparently no matter how hard I try to fix my calculations I keep failing.&lt;/p&gt;

&lt;p&gt;Has any of you mastered the game? Any tips to spare? 🙏&lt;/p&gt;

</description>
      <category>freelancing</category>
      <category>discuss</category>
      <category>watercooler</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Your boat is sinking, you think you can make it to land, but you can only...</title>
      <dc:creator>Manuele J Sarfatti</dc:creator>
      <pubDate>Sat, 30 Nov 2019 12:34:38 +0000</pubDate>
      <link>https://forem.com/mjsarfatti/your-boat-is-sinking-you-think-you-can-make-it-to-land-but-you-can-only-3dha</link>
      <guid>https://forem.com/mjsarfatti/your-boat-is-sinking-you-think-you-can-make-it-to-land-but-you-can-only-3dha</guid>
      <description>&lt;p&gt;...save one flavour of JavaScript: &lt;em&gt;OO&lt;/em&gt; or &lt;em&gt;functional&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Which one do you bring with you and why?&lt;/p&gt;

</description>
      <category>watercooler</category>
      <category>discuss</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How NOT to apply for a job</title>
      <dc:creator>Manuele J Sarfatti</dc:creator>
      <pubDate>Fri, 29 Nov 2019 17:35:14 +0000</pubDate>
      <link>https://forem.com/mjsarfatti/how-not-to-apply-for-a-job-2em1</link>
      <guid>https://forem.com/mjsarfatti/how-not-to-apply-for-a-job-2em1</guid>
      <description>&lt;p&gt;It's full of very good articles on how to apply out there (and in here, DEV community!), some with crazy good tips that with a little persistence will actually get you places.&lt;/p&gt;

&lt;p&gt;But for a change of scenery, here is a rock solid way of NOT getting a job:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5_qtxjyg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/kjhwc211tcb3vuu89jxg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5_qtxjyg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/kjhwc211tcb3vuu89jxg.png" alt="Screenshot of a reply to a job opening screening questionnair, full of arrogance"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclaimer: rant to follow, feel free to skip!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I spent time and good will trying to design a screening questionnaire that's aimed at surfacing the best in a candidate in as few questions as possible, without lengthy cover-letter requirements and stuff. Not only answering like that shows arrogance, a deeply unprofessional attitude, and several other negative traits, but it's also offensive for those on the other side of the form like myself who went out of their way trying to make your life simpler.&lt;/p&gt;

&lt;p&gt;If that's the way you are going to answer, please just don't 🙌 💙&lt;/p&gt;

</description>
      <category>career</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
