<?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: Omar</title>
    <description>The latest articles on Forem by Omar (@omar4ur).</description>
    <link>https://forem.com/omar4ur</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%2F124020%2F7433c45a-c530-4f4f-a5be-f153a506909e.jpg</url>
      <title>Forem: Omar</title>
      <link>https://forem.com/omar4ur</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/omar4ur"/>
    <language>en</language>
    <item>
      <title>Handmade GitHub Pages</title>
      <dc:creator>Omar</dc:creator>
      <pubDate>Wed, 07 Jan 2026 14:50:03 +0000</pubDate>
      <link>https://forem.com/omar4ur/handmade-github-pages-20pi</link>
      <guid>https://forem.com/omar4ur/handmade-github-pages-20pi</guid>
      <description>&lt;p&gt;This is part 2 of the "handmade computational sandbox" class. In part 1 (&lt;a href="https://dev.to/omar4ur/handmade-cellular-automata-1f3c"&gt;https://dev.to/omar4ur/handmade-cellular-automata-1f3c&lt;/a&gt;) we learned how to run a piece of JS code in a single HTML file on your computer, and how to deploy it on CodePen to share. &lt;/p&gt;

&lt;p&gt;Today we're going to use GitHub for free static hosting, instead of CodePen. CodePen is good for quick experiments, GitHub is good for long term hosting. &lt;/p&gt;

&lt;p&gt;For example, if you run a local community and want to set up a web page for it, a simple HTML page + a GitHub repo is perfect because (1) it's 100% free (2) if one day you leave town, the source code is available so someone else can copy it and continue to host it. This is designed to be very &lt;a href="https://wiki.xxiivv.com/site/permacomputing.html" rel="noopener noreferrer"&gt;"permacomputing"&lt;/a&gt; and in line with Derek Siver's &lt;a href="https://sive.rs/ti" rel="noopener noreferrer"&gt;tech independence&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Example of a local community website made this way (just a single HTML file): &lt;a href="https://mplawley.github.io/tundra-tech-talks/" rel="noopener noreferrer"&gt;https://mplawley.github.io/tundra-tech-talks/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're sharing a research prototype, this is also the perfect way to do it (live demo + source code available + a space for people to ask questions on the GitHub repo)&lt;/p&gt;




&lt;p&gt;We're going to take the HTML/JS cellular automata simulation we created in part 1 and host it on GitHub pages. If you don't have your file from part 1, you can use this one that implements the game of life rules:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.githubusercontent.com/OmarShehata/9215085ff48882d1756ddd5ce2e0b912/raw/6ec921b6abd3dfc9f52a9be45989334cb1bb3c15/game_of_life.html" rel="noopener noreferrer"&gt;https://gist.githubusercontent.com/OmarShehata/9215085ff48882d1756ddd5ce2e0b912/raw/6ec921b6abd3dfc9f52a9be45989334cb1bb3c15/game_of_life.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Create a new GitHub repo&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Login or create an account on &lt;a href="https://github.com/" rel="noopener noreferrer"&gt;https://github.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Click "+" button in the top right -&amp;gt; "New repository"&lt;/li&gt;
&lt;li&gt;Give it a name, toggle the "Add README" button. Click "create repository"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Add your HTML file&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click the "+" button in the top right of the repo and "create new file"&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Call it &lt;code&gt;index.html&lt;/code&gt;, and paste in your HTML/JS code&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;"index.html" is a special name, web servers will default to serving this as the homepage if they find it in a directory. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click "commit changes"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Tell GitHub to deploy this website&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to "settings" in the top right &lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Then "pages" in the left side panel&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Here you will find the default settings for GitHub Pages, "deploy from branch". Just select your main branch here:&lt;/p&gt;

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

&lt;p&gt;For bigger projects, you will have the code be in one branch, and the "built" thing to deploy in a different branch. For now, the code we are writing is itself the final code that runs in the browser, so we don't need this build step ("GitHub Actions" can be configured to run these build steps for you)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hit "save"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Check the deployment status&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If it worked, you should see this yellow circle back in the main repo page:&lt;/p&gt;

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

&lt;p&gt;You can click on it to check the status. Once it's done, you can get the URL for your deployed website back in the Settings &amp;gt; Pages &lt;/p&gt;

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

</description>
      <category>javascript</category>
      <category>handmade</category>
      <category>vanilla</category>
    </item>
    <item>
      <title>Handmade Cellular Automata</title>
      <dc:creator>Omar</dc:creator>
      <pubDate>Tue, 06 Jan 2026 18:59:09 +0000</pubDate>
      <link>https://forem.com/omar4ur/handmade-cellular-automata-1f3c</link>
      <guid>https://forem.com/omar4ur/handmade-cellular-automata-1f3c</guid>
      <description>&lt;p&gt;This tutorial will walk you through creating &amp;amp; deploying your first "computational sandbox", an environment where you can define the rules of a simulation and explore the result. This is designed to be walked through together with a mentor.  &lt;/p&gt;

&lt;p&gt;This is the opposite of vibe coding - in order for you to use these simulations to think &amp;amp; learn about the world, you need to know everything about how it works. This is why we're doing everything "by hand". We're going to do the absolute minimum setup you need to (1) run things on a computer by yourself (2) share it with collaborators in a way that they can edit and build on. &lt;/p&gt;

&lt;h3&gt;
  
  
  Inspirations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://wiki.xxiivv.com/site/permacomputing.html" rel="noopener noreferrer"&gt;Permacomputing&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Derek Sivers' &lt;a href="https://sive.rs/ti" rel="noopener noreferrer"&gt;"tech independence"&lt;/a&gt; philosophy

&lt;ul&gt;
&lt;li&gt;See also &lt;a href="https://0data.app/en/" rel="noopener noreferrer"&gt;Rosano's Zero Data Apps&lt;/a&gt; &amp;amp; the &lt;a href="https://kosmos.org/" rel="noopener noreferrer"&gt;Kosmos&lt;/a&gt; network &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Casey Muratori's &lt;a href="https://www.computerenhance.com/about" rel="noopener noreferrer"&gt;"Handmade Hero"&lt;/a&gt; series &lt;/li&gt;

&lt;li&gt;

&lt;a href="https://explorabl.es/" rel="noopener noreferrer"&gt;Explorable Explanations&lt;/a&gt; 

&lt;ul&gt;
&lt;li&gt;Like the &lt;a href="https://ncase.me/polygons/" rel="noopener noreferrer"&gt;Parable of Polygons&lt;/a&gt;, a simulation about how sociology &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ciechanow.ski/cameras-and-lenses/" rel="noopener noreferrer"&gt;Cameras &amp;amp; Lenses&lt;/a&gt;, simulation about how cameras, light, and vision work&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Internet art experiments like those done by Nolan (&lt;a href="https://eieio.games/" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://eieio.games/" rel="noopener noreferrer"&gt;https://eieio.games/&lt;/a&gt;) &amp;amp; Neal (&lt;a href="https://neal.fun/" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://neal.fun/" rel="noopener noreferrer"&gt;https://neal.fun/&lt;/a&gt;). See &lt;a href="https://newslettergoeshere.substack.com/p/projects-for-november-2025" rel="noopener noreferrer"&gt;Nolan's newsletter&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Example of a simple research prototype that is only a few lines of code &amp;amp; a single HTML file, &lt;a href="https://github.com/DefenderOfBasic/good-and-evil-concepts?tab=readme-ov-file#polarized-words" rel="noopener noreferrer"&gt;good &amp;amp; evil words&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Overview of the series
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;(this article) How to run a piece of code in a plain HTML file on your computer, and deploy it on the internet with CodePen&lt;/li&gt;
&lt;li&gt;Deploying a free static website or app on GitHub pages, like &lt;a href="https://ithacasocialcircle.com/" rel="noopener noreferrer"&gt;Ithaca Social Circle&lt;/a&gt;, or an &lt;a href="https://dev.to/defenderofbasic/host-your-obsidian-notebook-on-github-pages-for-free-8l1"&gt;Obsidian based website "digital garden"&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/omar4ur/handmade-github-pages-20pi"&gt;https://dev.to/omar4ur/handmade-github-pages-20pi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;mdBook template &lt;a href="https://github.com/OmarShehata/mdbook-template/tree/main?tab=readme-ov-file#mdbook-template" rel="noopener noreferrer"&gt;https://github.com/OmarShehata/mdbook-template/tree/main?tab=readme-ov-file#mdbook-template&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Very basic backends ((1) using Google Sheets as a free backend, (2) or "BYO server" with &lt;a href="https://remotestorage.io/" rel="noopener noreferrer"&gt;remoteStorage&lt;/a&gt;, or (3) free CloudFlare object storage)&lt;/li&gt;
&lt;li&gt;Basic user management with OAuth (twitter/google login)&lt;/li&gt;
&lt;li&gt;Basic computer graphics visualizations (how to render pixels "raw", how to use a library like PixiJS or ThreeJS)&lt;/li&gt;
&lt;li&gt;Basic data analysis (Jupyter notebooks &amp;amp; Google Colab)

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://colab.research.google.com/drive/109XOgTWj-sajpAYhDCNPfts5zvdkpi_s" rel="noopener noreferrer"&gt;Simple twitter data NLP analysis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://distill.pub/2020/growing-ca/" rel="noopener noreferrer"&gt;ML cellular automata&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Tips for navigating open source codebases, how to fork, edit, and contribute a pull request&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;In this lesson, you're going to setup this cellular automata simulation as a single HTML file on your computer, and copy/paste it onto CodePen to share it on the internet.&lt;/p&gt;

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

&lt;h4&gt;
  
  
  1. Install VS Studio Code
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;https://code.visualstudio.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will use this as our IDE to edit code. The web browser is what will "run" the code.&lt;/p&gt;

&lt;p&gt;You can use VS Code to edit any coding language, by either running the code outside of it, or finding a plugin that runs it for you.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Create an HTML file
&lt;/h4&gt;

&lt;p&gt;This is just a text file with the extension &lt;code&gt;.html&lt;/code&gt;. Inside of the HTML code we will embed the JavaScript code we want to run.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create the file &lt;code&gt;vanilla.html&lt;/code&gt; with VS Code, anywhere on your computer&lt;/li&gt;
&lt;li&gt;Paste in the following code
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Vanilla JS&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Hello!&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;// This is how you embed JavaScript inside of HTML&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;Hello world!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: This series will not cover the basics of JS syntax. It's assumed you have a supplementary source or already know it. I recommend the book &lt;a href="https://eloquentjavascript.net/" rel="noopener noreferrer"&gt;Eloquent Javascript&lt;/a&gt;, and the &lt;a href="https://www.codecademy.com/courses/introduction-to-javascript/lessons/introduction-to-javascript/exercises/console" rel="noopener noreferrer"&gt;Codecademy JavaScript interactive lessons&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run this code by opening this file in your browser&lt;/li&gt;
&lt;li&gt;Check the browser console to see if the JS code snippet ran correctly&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Challenge&lt;/strong&gt;: Can you make the web page display the current time?&lt;/p&gt;

&lt;p&gt;You want to (1) get the current time in the JS code (2) Use &lt;code&gt;document.querySelector()&lt;/code&gt; to select the HTML element and edit it with the local time (3) use &lt;code&gt;setInterval&lt;/code&gt; to auto-update the code&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.githubusercontent.com/OmarShehata/9215085ff48882d1756ddd5ce2e0b912/raw/66a321e74766caa6681d3b532c525abbd8c3cb77/vanilla_date.html" rel="noopener noreferrer"&gt;Solution&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Note: You can use the browser console as a REPL! You can expose a variable from your code by saying &lt;code&gt;window.myVar = myVar&lt;/code&gt;, and then you have access to editing it live in your app&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Deploy your code on the internet
&lt;/h4&gt;

&lt;p&gt;This is the fastest way to get your code to run on millions of devices. It's "infinitely" scalable because the code runs on the user's computer, not on a central server. It's very cheap for CodePen, or any static host, because it's just delivering the code (consuming bandwidth, not CPU). &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make an account on &lt;a href="https://codepen.io/" rel="noopener noreferrer"&gt;https://codepen.io/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Create a new "pen"&lt;/li&gt;
&lt;li&gt;Copy/paste your code into the "HTML" section of the CodePen interface&lt;/li&gt;
&lt;li&gt;Click "Save" in the top right&lt;/li&gt;
&lt;li&gt;To share it, click on the little icon of an arrow going out of a box, in the bottom right (on the same row as "Share" / "Export" / "Embed")&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The shared link should look like this: &lt;a href="https://codepen.io/omarshe7ta/full/wBWBqZq" rel="noopener noreferrer"&gt;https://codepen.io/omarshe7ta/full/wBWBqZq&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can continue writing code in the CodePen interface, or locally in VS Code.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Animated rectangle
&lt;/h4&gt;

&lt;p&gt;Now we're going to run a piece of code that draws a rectangle that moves back and forth. Technically, the rectangle doesn't "move", we just erase the screen and redraw the rectangle at a new location at every frame. &lt;/p&gt;

&lt;p&gt;Replace the code you have with this new template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Vanilla JS&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- the "canvas" element is the box on the page, inside of which all the drawing will happen --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;canvas&amp;gt;&amp;lt;/canvas&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;// set the height &amp;amp; width to be full screen&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="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;canvas&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2d&lt;/span&gt;&lt;span class="dl"&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;positionX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;
        &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Clear the screen&lt;/span&gt;
            &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clearRect&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="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="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;// Draw a rectangle, x, y, width, height&lt;/span&gt;
            &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;positionX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// move the position for the next frame&lt;/span&gt;
            &lt;span class="nx"&gt;positionX&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

            &lt;span class="c1"&gt;// run the function "loop" again&lt;/span&gt;
            &lt;span class="nf"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// run the function "loop" once&lt;/span&gt;
        &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Challenge:&lt;/strong&gt; Can you move the box faster? &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Challenge:&lt;/strong&gt; Can you make the box start at the right edge of the screen and move to the left&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Challenge:&lt;/strong&gt; Can you make the box move back &amp;amp; forth?&lt;/p&gt;

&lt;p&gt;To do this, set the X of the box to be the sine of your count variable. Sine of any number will give you a range of [-1, 1], and you can scale that to the range you want it to go back and forth in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.githubusercontent.com/OmarShehata/9215085ff48882d1756ddd5ce2e0b912/raw/e3dfab1883cd1d6b13da75adb423176ade24ea48/vanilla_sine.html" rel="noopener noreferrer"&gt;Solution&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  5. Cellular Automata Grid
&lt;/h4&gt;

&lt;p&gt;Here we're going to start with a piece of code that sets up a grid of pixels with a random color. We'll use this as a base to explore various "cellular automata" simulations, like the &lt;a href="https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life" rel="noopener noreferrer"&gt;Game of Life&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replace the code you have with this:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Vanilla JS&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;canvas&amp;gt;&amp;lt;/canvas&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&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="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;canvas&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2d&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;grid&lt;/span&gt; &lt;span class="o"&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;gridSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
        &lt;span class="nf"&gt;initGrid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="nf"&gt;updateGrid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;drawGrid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;initGrid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Fill the grid data structure with initial random colors&lt;/span&gt;
            &lt;span class="k"&gt;for&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;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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="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="nx"&gt;gridSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;for&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;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;gridSize&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;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;
                    &lt;span class="nx"&gt;grid&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;x&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;y&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="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;color&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;function&lt;/span&gt; &lt;span class="nf"&gt;updateGrid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;for&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;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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="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="nx"&gt;gridSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;for&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;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;gridSize&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;currentColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;grid&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;x&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;y&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="c1"&gt;// modify the color of the current square&lt;/span&gt;
                    &lt;span class="c1"&gt;// given the color it currently has&lt;/span&gt;
                    &lt;span class="nx"&gt;grid&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;x&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;y&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="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentColor&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;function&lt;/span&gt; &lt;span class="nf"&gt;drawGrid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// draw whatever is currently stored in the grid variable&lt;/span&gt;
            &lt;span class="k"&gt;for&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;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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="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="nx"&gt;gridSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;for&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;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;gridSize&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;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;grid&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;x&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;y&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;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`hsl(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, 70%, 60%)`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gridSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gridSize&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try changing the &lt;code&gt;gridSize&lt;/code&gt; variable at the top. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Challenge:&lt;/strong&gt; Expose the &lt;code&gt;grid&lt;/code&gt; variable to the browser console, so you can edit it in "real-time". Ask your LLM for a one-liner to take this data structure and set it all to 0, or some other constant&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Challenge:&lt;/strong&gt; Edit the &lt;code&gt;updateGrid&lt;/code&gt; function to make the color change every frame &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Challenge:&lt;/strong&gt; Edit the &lt;code&gt;updateGrid&lt;/code&gt; function to set the color of the current pixel, equal to the pixel to its left.&lt;/p&gt;

&lt;p&gt;You'll need to handle the boundary condition to avoid errors.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Next lessons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/omar4ur/handmade-github-pages-20pi"&gt;Deploy with GitHub Pages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>handmade</category>
      <category>vanilla</category>
    </item>
    <item>
      <title>Open source semantic embedding, search &amp; clustering in NodeJS</title>
      <dc:creator>Omar</dc:creator>
      <pubDate>Wed, 25 Sep 2024 15:43:24 +0000</pubDate>
      <link>https://forem.com/omar4ur/open-source-semantic-embedding-search-clustering-in-nodejs-23om</link>
      <guid>https://forem.com/omar4ur/open-source-semantic-embedding-search-clustering-in-nodejs-23om</guid>
      <description>&lt;p&gt;This tutorial walks you through how to do semantic embedding completely offline with open source models in NodeJS. No knowledge of AI/ML is required. Source code: &lt;a href="https://github.com/OmarShehata/minimal-embedding-template" rel="noopener noreferrer"&gt;https://github.com/OmarShehata/minimal-embedding-template&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An "embedding" is a high dimensional vector &lt;code&gt;(x,y,z,...)&lt;/code&gt; that represents a concept. Think of it as the internal representation of words in an LLM. You can compute distances between these vectors.&lt;/p&gt;

&lt;p&gt;Example: "Man" is much closer to "boy" &amp;amp; "woman", compared to "chicken". "Coffee" and "wifi"  are somewhat close, and are both close to "coffee shop". &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fni5gmzofgmlo00ryakgn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fni5gmzofgmlo00ryakgn.png" alt="illustration of what these vectors might look like projected down to 2D" width="800" height="351"&gt;&lt;/a&gt;&lt;/p&gt;
illustration of what these vectors might look like projected down to 2D



&lt;p&gt;I think this is an extremely underutilized feature of modern LLM's, and it's much cheaper compute wise compared to text generation. Most of the time I don't really want the LLM to generate text as much as I want to see &amp;amp; manipulate the semantic concepts like this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;Clone the repo: &lt;a href="https://github.com/OmarShehata/semantic-embedding-template" rel="noopener noreferrer"&gt;https://github.com/OmarShehata/semantic-embedding-template&lt;/a&gt;. This contains a minimal NodeJS template that you can copy/paste and build on.&lt;/p&gt;

&lt;p&gt;It uses (1) &lt;a href="https://www.npmjs.com/package/gpt4all" rel="noopener noreferrer"&gt;gpt4all&lt;/a&gt; as the LLM engine. This is where the open source model comes from, and is what converts a word/string/document into a vector. (2) &lt;a href="https://github.com/Stevenic/vectra/" rel="noopener noreferrer"&gt;Vectra&lt;/a&gt; as a local, single file vector database. Allows us to index &amp;amp; search vectors. &lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;pnpm install&lt;/code&gt;, then run the first example in &lt;code&gt;example-simple-embedding/index.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pnpm simple-embedding
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This takes an array of strings and converts them to vectors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertText&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;coffee shop&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wifi&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can print the vectors with &lt;code&gt;embeddings.getTextMap()&lt;/code&gt;. You can do a search as shown below. This returns a sorted list of the closest vectors in the DB, along with the cosine distance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;coffee&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// returns:&lt;/span&gt;
&lt;span class="c1"&gt;// [&lt;/span&gt;
&lt;span class="c1"&gt;//   [ 'coffee shop', 0.8214959697396015 ],&lt;/span&gt;
&lt;span class="c1"&gt;//   [ 'wifi', 0.711907901740376 ],&lt;/span&gt;
&lt;span class="c1"&gt;//   [ 'hard work', 0.6709908415581982 ],&lt;/span&gt;
&lt;span class="c1"&gt;//   [ 'love peace &amp;amp; joy, relaxation', 0.6495931802131457 ]&lt;/span&gt;
&lt;span class="c1"&gt;// ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(&lt;code&gt;1&lt;/code&gt; means it's exactly the same vector, &lt;code&gt;-1&lt;/code&gt; means it's exactly opposite, &lt;code&gt;0&lt;/code&gt; means no correlation)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;embeddings&lt;/code&gt; is a thin wrapper around gpt4all
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/OmarShehata/semantic-embedding-template/blob/main/lib/embeddings.js" rel="noopener noreferrer"&gt;lib/embeddings.js&lt;/a&gt; implements &lt;code&gt;insertText&lt;/code&gt; which:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;checks to see if these words are already in the DB&lt;/li&gt;
&lt;li&gt;inserts them if they are not, with a batch update&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;search&lt;/code&gt; function takes the query string and converts it to a vector, then runs a query with the vectra DB.&lt;/p&gt;

&lt;p&gt;The specific model I'm using here is &lt;a href="https://huggingface.co/nomic-ai/nomic-embed-text-v1.5" rel="noopener noreferrer"&gt;nomic-embed-text-v1.5&lt;/a&gt; which is an open source model &amp;amp; free to use model that runs locally on your machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenAI embeddings
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/OmarShehata/semantic-embedding-template/blob/main/lib/embeddings-openai.js" rel="noopener noreferrer"&gt;lib/embeddings-openai.js&lt;/a&gt; is a version of this file that has exactly the same API but sends the text to OpenAI. See &lt;a href="https://platform.openai.com/docs/guides/embeddings/what-are-embeddings" rel="noopener noreferrer"&gt;OpenAI's embedding docs&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The OpenAI model captures more nuance in my experience (for example, it captures the semantic meaning of emojis whereas the open source one doesn't seem to).&lt;/p&gt;

&lt;p&gt;Set the &lt;code&gt;OPEN_API_KEY&lt;/code&gt; environment variable to use this. To run the example in &lt;code&gt;example-openai-embedding/index.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pnpm openai-embedding
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Clustering
&lt;/h2&gt;

&lt;p&gt;To run &lt;a href="https://github.com/OmarShehata/semantic-embedding-template/blob/main/example-clustering/index.js" rel="noopener noreferrer"&gt;&lt;code&gt;example-clustering/index.js&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This clusters the vectors in the DB using k-means. You tell it the number of clusters you want to create, and it iterates over each point to find the "k nearest neighbors" to create these clusters.&lt;/p&gt;

&lt;p&gt;Normally, you don't know how many clusters are in the data. There's various techniques to find this. One way is the "elbow method" where you cluster the dataset for increasingly higher cluster sizes and compute a "score". The score represents how close all items in the cluster are to a centroid. So the lower the score the more you end up with clusters of semantically related things.&lt;/p&gt;




&lt;p&gt;I hope you found this useful! You can use the base code here to basically recreate Neal's &lt;a href="https://neal.fun/infinite-craft/" rel="noopener noreferrer"&gt;Infinite Craft&lt;/a&gt; game. Basically put all the words in the dictionary in the vector database. Then to combine two words, add the vectors (or get the average?), then search for the closest thing to that combined vector. &lt;/p&gt;

&lt;p&gt;This is my personal sandbox that I hope to add more stuff to. For example, there are models that can convert an image to a text description. You can then get a vector embedding for that text, and with that you can build an app where you can "CTRL+F" for your images (again, all offline, and free!)&lt;/p&gt;

</description>
      <category>node</category>
      <category>ai</category>
      <category>vectordatabase</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Build &amp; deploy your first web app</title>
      <dc:creator>Omar</dc:creator>
      <pubDate>Fri, 14 Jul 2023 00:28:09 +0000</pubDate>
      <link>https://forem.com/omar4ur/build-deploy-your-first-web-app-48dk</link>
      <guid>https://forem.com/omar4ur/build-deploy-your-first-web-app-48dk</guid>
      <description>&lt;p&gt;This guide is written for students going through &lt;a href="https://labs.codeday.org/" rel="noopener noreferrer"&gt;CodeDay Labs&lt;/a&gt;. You will learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to set up a simple web app from scratch &lt;/li&gt;
&lt;li&gt;How to deploy your applications so others can access it via a URL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This tutorial is meant to teach the basic fundamentals of working with web technologies, regardless of what specific framework you might be using in the future.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1 - Create an HTML page
&lt;/h3&gt;

&lt;p&gt;We are going to create our first web page, by hand. You typically will not do this in your web development career, but it will help you understand what is happening under the hood, and what problems all these frameworks like React etc are trying to help you solve.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new directory for our project. Name it &lt;code&gt;web-dev-101&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Open up your favorite code editor, like VSCode or Sublime Text, and create a new file called &lt;code&gt;index.html&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Copy/paste the following code:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Web Dev 101&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello World!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Now preview your web page by double clicking on &lt;code&gt;index.html&lt;/code&gt;, or dragging and dropping it into your web browser.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It should look like this:&lt;/p&gt;

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

&lt;h4&gt;
  
  
  Extra challenges:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;What does the &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; tag mean? What happens if you do &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt; tag instead? Or &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; tag?&lt;/li&gt;
&lt;li&gt;Can you change the title in the browser tab, to make it say &lt;code&gt;Web Dev 102&lt;/code&gt; instead of &lt;code&gt;Web Dev 101&lt;/code&gt; ?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2 - Add some styling / CSS
&lt;/h3&gt;

&lt;p&gt;HTML is a markup language, it's for controlling &lt;strong&gt;what&lt;/strong&gt; is shown on screen (as in, the content, the text, etc)&lt;/p&gt;

&lt;p&gt;CSS is the styling language, it controls &lt;strong&gt;how&lt;/strong&gt; things are displayed on screen (the color, the size, etc)&lt;/p&gt;

&lt;p&gt;To add CSS/styling rules to your web page:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add the following snippet inside the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; tag:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;style &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should go like this:&lt;/p&gt;

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

&lt;p&gt;Then refresh your web page, the text should now be red:&lt;/p&gt;

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

&lt;p&gt;This CSS rule we added makes ALL &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; tags red. If we wanted to make a specific HTML element red, we have to give it either an ID or a class.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Give your &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; element an id:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Hello World!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Change your CSS rule to target the element with the id &lt;code&gt;title&lt;/code&gt; instead of all &lt;code&gt;h1&lt;/code&gt; tags:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nf"&gt;#title&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;#&lt;/code&gt; symbol prefixing "title" is what tells CSS to apply the rule to any element that has the ID "title". This is called a &lt;strong&gt;CSS selector&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Another common type of CSS selector is a class. It works very similarly to an ID. You can give an element a class name as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;h2 class="subtitle"&amp;gt;My subtitle&amp;lt;/h2&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you can style it like this in CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.subtitle {
  color: blue;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When should you use id vs class? Typically you use id for one specific element (like the title) and a class for something for which there can be multiple elements (like a paragraph, or a button).&lt;/p&gt;

&lt;h4&gt;
  
  
  Extra challenges:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;What happens if you have an element that is BOTH an &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; and has an id &lt;code&gt;title&lt;/code&gt;, and you create a css rule that makes &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; red and a css rule that makes &lt;code&gt;title&lt;/code&gt; elements blue? Will the element be red or blue?&lt;/li&gt;
&lt;li&gt;Can you use CSS to make an element underlined? Or bigger font?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 3 - Move your CSS to another file
&lt;/h3&gt;

&lt;p&gt;You can add CSS into the same file as your HTML. But for bigger projects it is helpful to split it up into another file.&lt;/p&gt;

&lt;p&gt;To do this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add the following line inside the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; tag, this tells the HTML page it should load styles from a file called &lt;code&gt;style.css&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;link rel="stylesheet" href="style.css"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create a file called &lt;code&gt;style.css&lt;/code&gt; and move your CSS in there. It might look like this:&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;Refresh the web page, confirm the CSS still works. Make a change to the style/color and confirm it updates when you refresh.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 4 - Deploy your web page
&lt;/h3&gt;

&lt;p&gt;At this point the web page only exists on your computer. In order for others to access it on the internet, we need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Put it on a web server&lt;/li&gt;
&lt;li&gt;Give it on a domain name&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A "web server" is just a computer. Your personal laptop &lt;em&gt;could&lt;/em&gt; be a web server. You just need to run a program that responds to HTTP requests and returns your web page. &lt;/p&gt;

&lt;p&gt;In fact, let's try that real quick:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install a simple &lt;a href="https://www.npmjs.com/package/http-server" rel="noopener noreferrer"&gt;http server library&lt;/a&gt; by running the following command:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --global http-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(if you don't already have npm installed, get it from here: &lt;a href="https://nodejs.org/en/download" rel="noopener noreferrer"&gt;https://nodejs.org/en/download&lt;/a&gt;. Yarn is also fine, the command might be slightly different.)&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open up a terminal window in the folder of your &lt;code&gt;index.html&lt;/code&gt; and run the following command to run a web server on your laptop:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http-server .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dot (&lt;code&gt;.&lt;/code&gt;) tells it to serve the HTML files in the current directory.&lt;/p&gt;

&lt;p&gt;This should give you an output that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Available on:
  http://172.25.240.1:8080
  http://192.168.1.147:8080
  http://127.0.0.1:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Open up the &lt;strong&gt;second&lt;/strong&gt; URL in your web browser (for me that's &lt;code&gt;http://192.168.1.147:8080&lt;/code&gt;). You should see your website.&lt;/li&gt;
&lt;li&gt;If it works, open up this URL on your phone or another computer. As long as it's connected to the same WiFi as your laptop, it should work.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What you just did is access your web page on your phone, powered by the web server running on your laptop!&lt;/p&gt;

&lt;p&gt;You don't have a domain name, which is why you have to access it trough the IP address of your computer. It has to be on the same WiFi network for reasons outside the scope of this tutorial.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5 - Deploy to a web server with a domain name
&lt;/h3&gt;

&lt;p&gt;To put your web page on a server that others can access anywhere on the internet, we typically need to pay for this service. There's a few free options. One free option is: &lt;a href="https://glitch.com/" rel="noopener noreferrer"&gt;https://glitch.com/&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an account on &lt;a href="https://glitch.com/signup" rel="noopener noreferrer"&gt;https://glitch.com/signup&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Create a new project (top right of the page)&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;Select the first option &lt;strong&gt;glitch-hello-website&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This will take you to the Glitch code editor.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Copy paste your &lt;code&gt;index.html&lt;/code&gt; from your computer into the Glitch editor (select index.html from the sidebar then just replace all its contents)&lt;/li&gt;
&lt;li&gt;Do the same for &lt;code&gt;style.css&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Click the "Preview" button in the bottom panel to open up your website:&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;This is your website deployed to a web server, with a domain name you can now share!&lt;/p&gt;

&lt;h4&gt;
  
  
  Extra challenges:
&lt;/h4&gt;

&lt;p&gt;Another option instead of using Glitch is using Github Pages. This is a way of automatically getting a deployed web page for any repository in your GitHub account.&lt;/p&gt;

&lt;p&gt;The following guide will explain how to do it. Try to create a new repository for your &lt;code&gt;web-dev-101&lt;/code&gt; project and deploy it with GitHub pages:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pages.github.com/" rel="noopener noreferrer"&gt;https://pages.github.com/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Vite Hot Module Replacement - A Complete Example</title>
      <dc:creator>Omar</dc:creator>
      <pubDate>Sun, 17 Apr 2022 15:45:21 +0000</pubDate>
      <link>https://forem.com/omar4ur/vite-hot-module-replacement-a-complete-example-pkg</link>
      <guid>https://forem.com/omar4ur/vite-hot-module-replacement-a-complete-example-pkg</guid>
      <description>&lt;p&gt;This article explains how to set up hot module replacement (HMR) with Vite for a vanilla JS project. The goal is to be able to see changes in the code without refreshing or restarting the application.&lt;/p&gt;

&lt;p&gt;For me this is particularly useful in creative coding when I'm iteratively building a simulation and don't want to lose state as I make live changes.&lt;/p&gt;

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

&lt;p&gt;Complete source code for this article here: &lt;a href="https://github.com/OmarShehata/vite-hot-reload-example" rel="noopener noreferrer"&gt;https://github.com/OmarShehata/vite-hot-reload-example&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Overview of my implementation
&lt;/h2&gt;

&lt;p&gt;I'm going to show you how I've set up my HMR to make it as simple as possible to enable for new modules. In subsequent sections I'll break down how it works so you are aware of all the caveats and can use it in your project as needed.&lt;/p&gt;

&lt;p&gt;To enable HMR on any module, I add the event handler at the top of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HMREventHandler&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;./HotModuleReloadSetup.js&lt;/span&gt;&lt;span class="dl"&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="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HMREventHandler&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;This tells Vite to avoid refreshing the page when this module is updated, and instead fire an HMR event.&lt;/p&gt;

&lt;p&gt;Next, I have a singleton instance of &lt;code&gt;HotModuleReloadSetup&lt;/code&gt; that takes dynamically imported modules, and automatically swaps them out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;HotModuleReloadSetup&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;./HotModuleReloadSetup.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;// Setup HMR&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hmr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HotModuleReloadSetup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// Load a module that will be updated dynamically&lt;/span&gt;
&lt;span class="nx"&gt;hmr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./Draw.js&lt;/span&gt;&lt;span class="dl"&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 now have access to an instance of the Draw class through &lt;code&gt;hmr.instances['Draw']&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally, the draw class defines a &lt;code&gt;hotReload&lt;/code&gt; handler. This passes a reference to the old instance so you can copy over any state variables to the new instance.&lt;/p&gt;

&lt;p&gt;So when I call &lt;code&gt;hmr.instances['Draw'].draw()&lt;/code&gt; in the render loop, it will always use the latest code.&lt;/p&gt;

&lt;p&gt;This pattern is modelled after how &lt;a href="https://developer.playcanvas.com/en/user-manual/scripting/hot-reloading/" rel="noopener noreferrer"&gt;PlayCanvas does their hot reloading&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Here is what happens when you make a change in an HMR module (&lt;code&gt;Draw.js&lt;/code&gt; in my case) &amp;amp; save the file:&lt;/p&gt;

&lt;p&gt;1 - Vite triggers an HMR event (which we've added a listener to via &lt;code&gt;import.meta.hot.accept&lt;/code&gt;)&lt;br&gt;
2 - I then dispatch a custom event on the DOM with that new module (this is in &lt;a href="https://github.com/OmarShehata/vite-hot-reload-example/blob/f831efd75129de25392e023d12fc2a171545c312/src/HotModuleReloadSetup.js#L36" rel="noopener noreferrer"&gt;HotModuleReloadSetup.js&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;HMREventHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newModule&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;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hot-module-reload&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;detail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;newModule&lt;/span&gt; &lt;span class="p"&gt;}&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;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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;This allows &lt;code&gt;HotModuleReloadSetup&lt;/code&gt; to listen for these events globally.&lt;/p&gt;

&lt;p&gt;3 - We search our map of &lt;code&gt;modules&lt;/code&gt; to see if the new module we received exists&lt;br&gt;
4 - If so, we create a new instance from the new module, call &lt;code&gt;newInstance.hotReload(oldInstance)&lt;/code&gt;, and discard the old module &amp;amp; old instance&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;swapModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newModule&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;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;oldModule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;modules&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;oldInstance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instances&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;oldModule&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newInstance&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;newModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;newInstance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hotReload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oldInstance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;modules&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="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newModule&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instances&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="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newInstance&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way you can access either one instance of a class that is always updated through the &lt;code&gt;instances&lt;/code&gt; map, or you can access the latest module through the &lt;code&gt;modules&lt;/code&gt; map. The latter can be used if your code is spawning many instances of a class (like enemies or bullets), and you want your live code changes to affect newly spawned instances.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveats
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If you need to have multiple instances of a class that are all updated when the code changes, you'll need to keep an array of these instances and swap them all out in the same way &lt;code&gt;HotModuleReloadSetup.swapModule&lt;/code&gt; does&lt;/li&gt;
&lt;li&gt;If your HMR class needs constructor arguments, you'll need to store those in the global manager that creates the new instances so they can be passed to them as well.&lt;/li&gt;
&lt;li&gt;I turn off Rollup minification because it rewrites function names, but cannot update all uses of the function when using dynamic imports like this. That causes &lt;code&gt;hmr.instances['Draw'].draw()&lt;/code&gt; to break because the function name is no longer &lt;code&gt;draw&lt;/code&gt; in a production build.&lt;/li&gt;
&lt;li&gt;I import the module by name and pass it to &lt;code&gt;hmr.import()&lt;/code&gt; due to the limitations on dynamic imports in Vite, see: &lt;a href="https://github.com/rollup/plugins/tree/master/packages/dynamic-import-vars#limitations" rel="noopener noreferrer"&gt;https://github.com/rollup/plugins/tree/master/packages/dynamic-import-vars#limitations&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thanks for reading! I hope you found this useful. If you find a bug or a better way to streamline HMR, especially for creative coding, I'd love to hear. You can find me on Twitter: &lt;a href="https://twitter.com/omar4ur" rel="noopener noreferrer"&gt;https://twitter.com/omar4ur&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>javascript</category>
      <category>vite</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to create bitmap fonts for Phaser JS with BMFont</title>
      <dc:creator>Omar</dc:creator>
      <pubDate>Mon, 21 Feb 2022 12:55:21 +0000</pubDate>
      <link>https://forem.com/omar4ur/how-to-create-bitmap-fonts-for-phaser-js-with-bmfont-2ndc</link>
      <guid>https://forem.com/omar4ur/how-to-create-bitmap-fonts-for-phaser-js-with-bmfont-2ndc</guid>
      <description>&lt;p&gt;This guide explains how to generate bitmap fonts from TTF or OTF files for use in PhaserJS. I'll be using &lt;a href="https://www.angelcode.com/products/bmfont/" rel="noopener noreferrer"&gt;BMFont&lt;/a&gt; which is Windows only.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why bitmap fonts?
&lt;/h3&gt;

&lt;p&gt;The main use case is if you're creating a pixel art game &amp;amp; want your text to match the retro style and have no antialiasing. &lt;/p&gt;

&lt;p&gt;Below is an example from a recent game I made. The top is the standard font rendering in Phaser. The bottom is a bitmap version of the same font.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Note the antialiasing inside letters like &lt;code&gt;g&lt;/code&gt;, &lt;code&gt;a&lt;/code&gt;, or &lt;code&gt;o&lt;/code&gt;&lt;/strong&gt; in the top image. This creates what looks like blurry artifacts at the small scale of pixel art games. The bottom image has the crisp, pixelated rendering expected in retro games. &lt;/p&gt;

&lt;p&gt;Bitmap fonts may also be faster to render. &lt;a href="https://photonstorm.github.io/phaser3-docs/Phaser.GameObjects.BitmapText.html" rel="noopener noreferrer"&gt;From the Phaser docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;BitmapText objects are less flexible than Text objects, in that they have less features such as shadows, fills and the ability to use Web Fonts, however you trade this flexibility for rendering speed&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To use a bitmap font, Phaser needs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;An image&lt;/strong&gt; containing all the possible characters&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An XML file&lt;/strong&gt; that defines the x/y/width/height of each character in the image. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's basically a spritesheet of letters.&lt;/p&gt;

&lt;p&gt;Here's an example I generated from this public domain font: &lt;a href="https://www.fontspace.com/public-pixel-font-f72305" rel="noopener noreferrer"&gt;https://www.fontspace.com/public-pixel-font-f72305&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The image with all the letters: &lt;/p&gt;

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

&lt;p&gt;Snippet from the XML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;char&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"32"&lt;/span&gt; &lt;span class="na"&gt;x=&lt;/span&gt;&lt;span class="s"&gt;"507"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="na"&gt;xoffset=&lt;/span&gt;&lt;span class="s"&gt;"-1"&lt;/span&gt; &lt;span class="na"&gt;yoffset=&lt;/span&gt;&lt;span class="s"&gt;"31"&lt;/span&gt; &lt;span class="na"&gt;xadvance=&lt;/span&gt;&lt;span class="s"&gt;"32"&lt;/span&gt; &lt;span class="na"&gt;page=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;chnl=&lt;/span&gt;&lt;span class="s"&gt;"15"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;char&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"33"&lt;/span&gt; &lt;span class="na"&gt;x=&lt;/span&gt;&lt;span class="s"&gt;"285"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"87"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"18"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"28"&lt;/span&gt; &lt;span class="na"&gt;xoffset=&lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt; &lt;span class="na"&gt;yoffset=&lt;/span&gt;&lt;span class="s"&gt;"4"&lt;/span&gt; &lt;span class="na"&gt;xadvance=&lt;/span&gt;&lt;span class="s"&gt;"32"&lt;/span&gt; &lt;span class="na"&gt;page=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;chnl=&lt;/span&gt;&lt;span class="s"&gt;"15"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;char&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"34"&lt;/span&gt; &lt;span class="na"&gt;x=&lt;/span&gt;&lt;span class="s"&gt;"441"&lt;/span&gt; &lt;span class="na"&gt;y=&lt;/span&gt;&lt;span class="s"&gt;"108"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"22"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"16"&lt;/span&gt; &lt;span class="na"&gt;xoffset=&lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt; &lt;span class="na"&gt;yoffset=&lt;/span&gt;&lt;span class="s"&gt;"4"&lt;/span&gt; &lt;span class="na"&gt;xadvance=&lt;/span&gt;&lt;span class="s"&gt;"32"&lt;/span&gt; &lt;span class="na"&gt;page=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;chnl=&lt;/span&gt;&lt;span class="s"&gt;"15"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can download this generated example in the format that Phaser can use here: &lt;a href="https://github.com/OmarShehata/webgl-outlines/files/8104312/pixel_bitmap_font.zip" rel="noopener noreferrer"&gt;pixel_bitmap_font.zip&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1 - Download BMFont
&lt;/h3&gt;

&lt;p&gt;Download the executable on this page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.angelcode.com/products/bmfont/" rel="noopener noreferrer"&gt;https://www.angelcode.com/products/bmfont/&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 - Load the font
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Prepare your font as a TTF file or similar &lt;/li&gt;
&lt;li&gt;Open up &lt;code&gt;bmfont64.exe&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;Options&lt;/code&gt; &amp;gt; &lt;code&gt;Font settings&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Select your font file in &lt;code&gt;Add font file&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Then select the name of the font in the &lt;code&gt;Font&lt;/code&gt; dropdown&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;If your font is installed system-wide you can skip the &lt;code&gt;Add font file&lt;/code&gt; step and just select the name of the font directly.&lt;/p&gt;

&lt;p&gt;Now you should see your font loaded:&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Step 3 - Export
&lt;/h3&gt;

&lt;p&gt;First, we change the export settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select &lt;code&gt;Options&lt;/code&gt; &amp;gt; &lt;code&gt;Export options&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;XML&lt;/code&gt; as the Font descriptor&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;PNG&lt;/code&gt; as the textures option&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Press OK&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then to export:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select all letters for export (Ctrl + A)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;File&lt;/code&gt; &amp;gt; &lt;code&gt;Save bitmap font as&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This will generate an XML file (it'll have the extension .fnt, you can rename that to .xml or leave it as-is, Phaser will be able to read it as an XML either way) and a PNG file. &lt;/p&gt;

&lt;p&gt;You may need to increase the width/height in the export options to keep all the letters in one image.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4 - Use it in Phaser
&lt;/h3&gt;

&lt;p&gt;Tell Phaser where to load the PNG &amp;amp; XML files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Load it&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bitmapFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bitmapFontName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;font.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;font.fnt&lt;/span&gt;&lt;span class="dl"&gt;'&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="k"&gt;this&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="nf"&gt;bitmapText&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bitmapFontName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Lorem ipsum&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;dolor sit amet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full example here: &lt;a href="https://labs.phaser.io/edit.html?src=src/loader/bitmap%20text/load%20bitmap%20text.js" rel="noopener noreferrer"&gt;https://labs.phaser.io/edit.html?src=src/loader/bitmap%20text/load%20bitmap%20text.js&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Final thoughts
&lt;/h3&gt;

&lt;p&gt;Note that a generated bitmap font has a font size baked in. Phaser can scale the font up and down but that may introduce artifacts in some cases. If you know the font size you want ahead of time you can set it in &lt;code&gt;Options&lt;/code&gt; &amp;gt; &lt;code&gt;Font settings&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;I used a font size of 32px in my game which was big enough so that it still looked good when scaled down or up a bit.&lt;/p&gt;

&lt;p&gt;I hope you found this useful! If you have any corrections or find a better way to generate bitmap fonts for Phaser I'm happy to update this article. Find me on Twitter (&lt;a href="https://twitter.com/Omar4ur" rel="noopener noreferrer"&gt;@Omar4ur&lt;/a&gt;) or my website (&lt;a href="https://omarshehata.me/" rel="noopener noreferrer"&gt;https://omarshehata.me/&lt;/a&gt;).&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>Create a satellite tracker from scratch in 30 lines of JavaScript</title>
      <dc:creator>Omar</dc:creator>
      <pubDate>Thu, 06 May 2021 02:32:25 +0000</pubDate>
      <link>https://forem.com/omar4ur/create-a-satellite-tracker-from-scratch-in-30-lines-of-javascript-32gk</link>
      <guid>https://forem.com/omar4ur/create-a-satellite-tracker-from-scratch-in-30-lines-of-javascript-32gk</guid>
      <description>&lt;p&gt;This tutorial will walk you through how to create a web app that visualizes the location of any satellite in real-time, like the International Space Station. &lt;/p&gt;

&lt;p&gt;We're going to do this from scratch, using the same techniques a real rocket scientist would! &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We'll look at where to get the data about individual satellites that the government publishes, known as &lt;strong&gt;Two-Line Element Sets&lt;/strong&gt;, or TLE's. &lt;/li&gt;
&lt;li&gt;We'll use &lt;a href="https://github.com/shashwatak/satellite-js" rel="noopener noreferrer"&gt;satellite-js&lt;/a&gt; to predict the orbit of the satellite given the TLE's (this is the rocket science part).&lt;/li&gt;
&lt;li&gt;We'll use &lt;a href="https://github.com/CesiumGS/cesium#readme" rel="noopener noreferrer"&gt;CesiumJS&lt;/a&gt; to visualize the result, but you can use any library/engine that can take in longitude, latitude, and height. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Below is a preview of the final result.&lt;/p&gt;


&lt;div class="glitch-embed-wrap"&gt;
  &lt;iframe src="https://glitch.com/embed/#!/embed/satellite-viewer?previewSize=100&amp;amp;path=index.html" alt="satellite-viewer on glitch"&gt;&lt;/iframe&gt;
&lt;/div&gt;


&lt;p&gt;This is showing the path of the International Space Station, sped up by 40x. To &lt;strong&gt;see its current location in real-time&lt;/strong&gt;, click the clock icon at the top-left of the clock wheel. &lt;/p&gt;

&lt;p&gt;Here's a &lt;a href="https://satellite-viewer.glitch.me/" rel="noopener noreferrer"&gt;direct link to the app&lt;/a&gt;. And the &lt;a href="https://glitch.com/edit/#!/satellite-viewer" rel="noopener noreferrer"&gt;source code on Glitch&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  1 - Get the satellite's Two-Line Element Set
&lt;/h2&gt;

&lt;p&gt;A Two-Line Element Set, or TLE, is a data format that describes the motion of an object orbiting the Earth. It was created by the North American Aerospace Defense Command (NORAD). You can &lt;a href="https://celestrak.com/columns/v04n03/" rel="noopener noreferrer"&gt;read more about it and its history here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Given this description of the orbit, we can predict the location of where it's going to be at any moment in time (which is step 2 below).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This means that most "live" satellite trackers are not live in the same way that tracking a delivery car on a map is&lt;/strong&gt;. Instead of relying on constantly receiving position updates, those who track objects in space will often get the latest TLE's (which are regularly updated) and use that to predict where the object is right now.&lt;/p&gt;

&lt;p&gt;Where do we get the TLE's? There is no one global official registry. Whoever owns the satellite and is monitoring it is responsible for updating and publishing the TLE for the benefit of the global space community (unless it's a spy satellite). &lt;/p&gt;

&lt;p&gt;We can find these TLE's on &lt;a href="https://www.space-track.org" rel="noopener noreferrer"&gt;Space Track&lt;/a&gt; which is a registry run by the United States Space Command. &lt;/p&gt;

&lt;p&gt;Another source is &lt;a href="https://celestrak.com/NORAD/elements/" rel="noopener noreferrer"&gt;this list on CeleStrak&lt;/a&gt; maintained by Dr. T.S. Kelso.&lt;/p&gt;

&lt;p&gt;We're going to use CeleStrak since it doesn't require a login. To find the TLE for the International Space Station, click on the &lt;a href="https://celestrak.com/NORAD/elements/stations.txt" rel="noopener noreferrer"&gt;Space Stations&lt;/a&gt; link. &lt;/p&gt;

&lt;p&gt;The first one is the TLE for the ISS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ISS (ZARYA)             
1 25544U 98067A   21122.75616700  .00027980  00000-0  51432-3 0  9994
2 25544  51.6442 207.4449 0002769 310.1189 193.6568 15.48993527281553
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The meaning of these numbers is &lt;a href="https://celestrak.com/columns/v04n03/#FAQ01" rel="noopener noreferrer"&gt;listed in table 1 in Dr T.S. Kelso's column&lt;/a&gt;. Most of them are identifiers and metadata about the satellite, like when it was launched. &lt;/p&gt;

&lt;p&gt;You can find TLE's for weather satellites, GPS satellites, and even &lt;a href="https://celestrak.com/NORAD/elements/starlink.txt" rel="noopener noreferrer"&gt;SpaceX's Starlink constellation&lt;/a&gt; in this same format.&lt;/p&gt;

&lt;h2&gt;
  
  
  2 - Predict the satellite orbit
&lt;/h2&gt;

&lt;p&gt;Now that you know how to get the TLE of the object you're interested in tracking, the next step is converting that to a position in time.&lt;/p&gt;

&lt;p&gt;We're going to use &lt;a href="https://github.com/shashwatak/satellite-js" rel="noopener noreferrer"&gt;satellite-js&lt;/a&gt; for this. &lt;/p&gt;

&lt;p&gt;Include the library from a CDN:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdnjs.cloudflare.com/ajax/libs/satellite.js/4.0.0/satellite.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then pass the TLE to it, and a time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ISS_TLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
    &lt;span class="s2"&gt;`1 25544U 98067A   21122.75616700  .00027980  00000-0  51432-3 0  9994
     2 25544  51.6442 207.4449 0002769 310.1189 193.6568 15.48993527281553`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Initialize the satellite record with this TLE&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;satrec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;satellite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;twoline2satrec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;ISS_TLE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&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="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; 
  &lt;span class="nx"&gt;ISS_TLE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&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="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Get the position of the satellite at the given date&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&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;positionAndVelocity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;satellite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;propagate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;satrec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;date&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;gmst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;satellite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gstime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&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;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;satellite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eciToGeodetic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;positionAndVelocity&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;gmst&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="nf"&gt;log&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;longitude&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="c1"&gt;// in radians&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&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;latitude&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="c1"&gt;// in radians&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&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;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="c1"&gt;// in km&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have the position of the satellite at the current time, &lt;code&gt;new Date()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This position is produced as a result of simulating a specific model of satellite motion. This model is called SGP4/SDP4. All TLE's assume this specific model. &lt;/p&gt;

&lt;p&gt;If you're wondering about the accuracy of this model, the short answer is, &lt;a href="https://celestrak.com/columns/v04n05/#FAQ06" rel="noopener noreferrer"&gt;it depends&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Accuracy of the two-line element sets is dependent upon a number of factors. These range from the particular sensors used and amount of data collected to the type of orbit and condition of the space environment. Unfortunately, since these factors vary for each element set, so does the accuracy. While NORAD has experimented with methods to incorporate prediction quality into the element sets, none of these methods has yet proved successful.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  3 - Visualize the result
&lt;/h2&gt;

&lt;p&gt;Now we have a way to get the location of any satellite, at any given time. We can pass in future times to animate its path, which we'll do in the next step.&lt;/p&gt;

&lt;p&gt;First, let's see how to visualize an individual point in space using CesiumJS.&lt;/p&gt;

&lt;p&gt;We load the library from CDN:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cesium.com/downloads/cesiumjs/releases/1.81/Build/Cesium/Cesium.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cesium.com/downloads/cesiumjs/releases/1.81/Build/Cesium/Widgets/widgets.css"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And create a container element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;"cesiumContainer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then initialize the viewer. Here we pass in some extra options to disable functionality that requires an access token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Initialize the Cesium viewer.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;viewer&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;Cesium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Viewer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cesiumContainer&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;imageryProvider&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;Cesium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TileMapServiceImageryProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Cesium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;buildModuleUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Assets/Textures/NaturalEarthII&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;baseLayerPicker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;geocoder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;homeButton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;infoBox&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;navigationHelpButton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;sceneModePicker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;viewer&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;globe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enableLighting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we'll visualize the satellite position as a red dot in space:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;satellitePoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Cesium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Cartesian3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromRadians&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;longitude&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;latitude&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;height&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="na"&gt;point&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pixelSize&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="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Cesium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RED&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;See the &lt;a href="https://glitch.com/edit/#!/satellite-viewer?path=simple.html" rel="noopener noreferrer"&gt;complete source code of this step in simple.html on Glitch&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4 - Animate the path
&lt;/h2&gt;

&lt;p&gt;To animate the path, we just need to sample more positions in the future. CesiumJS has a built-in way to interpolate between these samples over time.&lt;/p&gt;

&lt;p&gt;The setup for this is a bit verbose. &lt;a href="https://glitch.com/edit/#!/satellite-viewer?path=index.html%3A6%3A82" rel="noopener noreferrer"&gt;You can see the full code on Glitch&lt;/a&gt;. The important concepts are described below.&lt;/p&gt;

&lt;p&gt;We create a &lt;code&gt;SampledPositionProperty&lt;/code&gt;. This is an object that will hold position samples over time and will interpolate between them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;positionsOverTime&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;Cesium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SampledPositionProperty&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We loop through however many samples we want to get, and for each sample, we construct a time object, called &lt;code&gt;JulianDate&lt;/code&gt; in CesiumJS, and a position, and we add that as a sample:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for &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;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;totalSeconds&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;timestepInSeconds&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;time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Cesium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;JulianDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&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;Cesium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;JulianDate&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="c1"&gt;// ...Get position from satellite-js...&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Cesium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Cartesian3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromRadians&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;p&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="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;positionsOverTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addSample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we pass this &lt;code&gt;positionsOverTime&lt;/code&gt; object to our point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;satellitePoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;positionsOverTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;point&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pixelSize&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="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Cesium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RED&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The point will move as the timeline at the bottom moves. To attach the camera to the moving point we do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;viewer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trackedEntity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;satellitePoint&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I hope you enjoyed learning a little bit about what goes into building a satellite tracker. There's a lot more to the topic we didn't touch on, like what exactly do the parameters in the TLE mean? How often are they updated? How are they updated?&lt;/p&gt;

&lt;p&gt;I don't know, but I find it really empowering to know what formats this kind of data is published in &amp;amp; where to get it, and quite amazing that we can do all this directly in the browser with JavaScript!&lt;/p&gt;

&lt;p&gt;Here's a couple fun ideas to explore now that we can do this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Visualize multiple satellites&lt;/strong&gt;, like the entire Starlink constellation. Inspired by &lt;a href="https://celestrak.com/cesium/orbit-viz.php?tle=/pub/TLE/catalog.txt&amp;amp;satcat=/pub/satcat.txt&amp;amp;referenceFrame=1" rel="noopener noreferrer"&gt;Celestrak's viewer&lt;/a&gt; which shows every satellite in its catalog. Perhaps visualize how the number of Starlink satellites grew over time? &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Or simulate what it would look from street level&lt;/strong&gt;. Maybe add buildings/elevation data to find the best place in the city to see the satellite? &lt;/p&gt;

&lt;p&gt;There's a prototype of this in &lt;a href="https://glitch.com/edit/#!/satellite-viewer?path=street-level.html%3A1%3A0" rel="noopener noreferrer"&gt;street-level.html&lt;/a&gt; in the Glitch source code. Demo: &lt;a href="https://satellite-viewer.glitch.me/street-level.html" rel="noopener noreferrer"&gt;https://satellite-viewer.glitch.me/street-level.html&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc1zoskrt1v9287t441wo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc1zoskrt1v9287t441wo.gif" alt="satellite-street" width="600" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See also &lt;a href="https://james.darpinian.com/satellites/" rel="noopener noreferrer"&gt;James Darpinian's "See a satellite tonight"&lt;/a&gt; app which uses a combination of CesiumJS and Google street view.&lt;/p&gt;

&lt;p&gt;It might also be fun to use a 3D model of the right scale instead of a dot, and get a real sense of how close satellites get to each other in space.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>glitch</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to load Sketchfab models directly in a ThreeJS app</title>
      <dc:creator>Omar</dc:creator>
      <pubDate>Wed, 28 Apr 2021 01:34:15 +0000</pubDate>
      <link>https://forem.com/omar4ur/how-to-load-sketchfab-models-directly-in-a-threejs-app-5anb</link>
      <guid>https://forem.com/omar4ur/how-to-load-sketchfab-models-directly-in-a-threejs-app-5anb</guid>
      <description>&lt;p&gt;&lt;a href="https://sketchfab.com/" rel="noopener noreferrer"&gt;Sketchfab's&lt;/a&gt; API gives you programmatic access to the largest collection of glTF 3D models on the web. This article walks you through a minimal code example to show you how to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Let your users authenticate with the Sketchfab API&lt;/li&gt;
&lt;li&gt;Download a 3D model as a zip file containing the glTF&lt;/li&gt;
&lt;li&gt;Load this zip file into ThreeJS&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Source code: &lt;a href="https://github.com/OmarShehata/threejs-sketchfab-example" rel="noopener noreferrer"&gt;https://github.com/OmarShehata/threejs-sketchfab-example&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;p&gt;I originally implemented this to let readers of my &lt;a href="https://omar-shehata.medium.com/how-to-render-outlines-in-webgl-8253c14724f9" rel="noopener noreferrer"&gt;WebGL outlines &lt;/a&gt; tutorial see how the effect looked on test cases of their choosing. Since I kept finding algorithms that didn't work on my specific corner cases (but I wouldn't find out until after I implemented it/downloaded &amp;amp; ran it!)&lt;/p&gt;

&lt;p&gt;It's a really easy way to let users bring in their own data (or millions of example models).&lt;/p&gt;

&lt;p&gt;You can see how this works in this outlines live demo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the demo: &lt;a href="https://omarshehata.github.io/csb-l01dp/" rel="noopener noreferrer"&gt;https://omarshehata.github.io/csb-l01dp/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Click Login to &lt;strong&gt;Sketchfab&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;When directed back to the app, paste a link of a model in the &lt;strong&gt;Sketchfab URL&lt;/strong&gt; field, like this: &lt;a href="https://sketchfab.com/3d-models/skull-downloadable-1a9db900738d44298b0bc59f68123393" rel="noopener noreferrer"&gt;https://sketchfab.com/3d-models/skull-downloadable-1a9db900738d44298b0bc59f68123393&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ldinswfyw845o36hzn8.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ldinswfyw845o36hzn8.gif" alt="load-example" width="600" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The model must be downloadable by the Sketchfab account that's logged in.&lt;/p&gt;

&lt;h3&gt;
  
  
  1 - Authenticate with the Sketchfab API
&lt;/h3&gt;

&lt;p&gt;The first step is to register your app with Sketchfab. The instructions for this are here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sketchfab.com/developers/oauth#registering-your-app" rel="noopener noreferrer"&gt;https://sketchfab.com/developers/oauth#registering-your-app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The current process at the time of writing is to contact them.&lt;/p&gt;

&lt;p&gt;You'll need to pick a &lt;strong&gt;redirect URI&lt;/strong&gt;. This should be the final URI where you will deploy your app. &lt;/p&gt;

&lt;p&gt;You'll want to use the &lt;strong&gt;Implicit&lt;/strong&gt; grant type. We can't keep an API key a secret in a web app, so we instead rely on the redirect URI (if someone malicious uses your client ID, Sketchfab will redirect them to your real app after login, regardless of who may have initiated the login).&lt;/p&gt;

&lt;p&gt;After your register your app you'll have a Client ID.&lt;br&gt;
You'll use this to send the user to Sketchfab for login as shown here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CLIENT_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_CLIENT_ID_HERE&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;AUTHENTICATION_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://sketchfab.com/oauth2/authorize/?state=123456789&amp;amp;response_type=token&amp;amp;client_id=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;CLIENT_ID&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AUTHENTICATION_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_blank&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the login is done, you'll need to &lt;strong&gt;grab the access token from the URL&lt;/strong&gt;. Here's a snippet that does this and stores it in local storage here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;checkToken&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Check if there's a new token from the URL&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&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;location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// Extract the token and save it&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hashParams&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="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;amp;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;for &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;param&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;hashParams&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;param&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;access_token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&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="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;param&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#access_token=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="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="nf"&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;Detected Sketchfab token: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sb_token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&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="c1"&gt;// Load token from local storage&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sb_token&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/OmarShehata/threejs-sketchfab-example/blob/b780b8c1eac37fc71d8d803fc465c874efd5954c/SketchfabIntegration.js#L77-L92" rel="noopener noreferrer"&gt;Source code on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You'll use this token for subsequent API calls.&lt;/p&gt;

&lt;p&gt;Note: while you're developing locally, Sketchfab will redirect to the production URI. You'll need to copy the URI params to your localhost to test.&lt;/p&gt;

&lt;h3&gt;
  
  
  2 - Download the 3D model
&lt;/h3&gt;

&lt;p&gt;Once you have a token, you can use this to fetch a download URI for the glTF model.&lt;/p&gt;

&lt;p&gt;Here is the code snippet for fetching the download URI given any Sketchfab URI like this: &lt;a href="https://sketchfab.com/3d-models/skull-downloadable-1a9db900738d44298b0bc59f68123393" rel="noopener noreferrer"&gt;https://sketchfab.com/3d-models/skull-downloadable-1a9db900738d44298b0bc59f68123393&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getModelDownloadUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Extract the model ID from the URL&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// The ID is always the last string when seperating by '-'&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pieces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&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;modelID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pieces&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;pieces&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadataUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://api.sketchfab.com/v3/models/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;modelID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/download`&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;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&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="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// This call will fail if model can't be downloaded&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadataUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&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;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gltf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&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;a href="https://github.com/OmarShehata/threejs-sketchfab-example/blob/b780b8c1eac37fc71d8d803fc465c874efd5954c/SketchfabIntegration.js#L95-L115" rel="noopener noreferrer"&gt;Source code on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Note that this request will fail if the user who is logged in does not have access to download this model. This will be true for store models that require a purchase, or free models that are not downloadable.&lt;/p&gt;

&lt;p&gt;You can filter for downloadable models in the Sketchfab search:&lt;/p&gt;

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

&lt;p&gt;This will show you either free models you can download or models that can be purchased.&lt;/p&gt;

&lt;p&gt;Now that we have a URL to a zip file containing a glTF model, we can pass that to ThreeJS to load it.&lt;/p&gt;

&lt;h3&gt;
  
  
  3 - Load the ZIP file into ThreeJS
&lt;/h3&gt;

&lt;p&gt;This was the tricky part of me. Normally ThreeJS requires a direct URL to the glTF file. To load it from a zip file we're going to (1) unzip the contents into memory using JSZip.&lt;/p&gt;

&lt;p&gt;We can't just pass the unzipped raw content to ThreeJS, because a glTF file may reference other files by a filepath (image or geometry resources). So we need to (2) create a Blob for each resource and (3) override the resources the glTF file is requesting with the Blob URI's.&lt;/p&gt;

&lt;p&gt;For example, if the glTF file is trying to load &lt;code&gt;textures/defaultMat_baseColor.jpeg&lt;/code&gt; as a relative filepath, we detect this, and instead pass this URI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//Unzip from JSZip&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;unzippedBaseColorJPEGFile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Create a Blob from this file&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;blob&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 a URL to this Blob&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseColorBlobUrl&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="nf"&gt;createObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Use ThreeJS's loading manager so instead of loading from relative filepaths we load from the blob URI's we created&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loadingManager&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="nc"&gt;LoadingManager&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;                            &lt;span class="nx"&gt;loadingManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setURLModifier&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;url&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;textures/defaultMat_baseColor.jpeg&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;baseColorBlobUrl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use &lt;a href="https://threejs.org/docs/#api/en/loaders/managers/LoadingManager" rel="noopener noreferrer"&gt;ThreeJS's LoadingManager&lt;/a&gt; to do this.&lt;/p&gt;

&lt;p&gt;Here is the code snippet that can take a URL to any zipped glTF and load it in ThreeJS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;readZip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;zipUrl&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="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;zipUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;checkStatus&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;arrayBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;JSZip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arrayBuffer&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;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dir&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;entryFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getExtension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&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="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gltf&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 blobs for every file resource&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blobUrls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
    &lt;span class="k"&gt;for &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;file&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;files&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Loading &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;file&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="s2"&gt;...`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;blobUrls&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;file&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="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getFileUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&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;fileUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;blobUrls&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;entryFile&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;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// Re-add the light&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;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&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="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;light&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;light&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="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.7&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="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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loadingManager&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="nc"&gt;LoadingManager&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;loadingManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setURLModifier&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;url&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsedUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&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;const&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parsedUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;origin&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;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parsedUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&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;relativeUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="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;blobUrls&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;relativeUrl&lt;/span&gt;&lt;span class="p"&gt;]&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;blobUrls&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;relativeUrl&lt;/span&gt;&lt;span class="p"&gt;];&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;url&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;gltfLoader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GLTFLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loadingManager&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;gltfLoader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gltf&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="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gltf&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="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;a href="https://github.com/OmarShehata/threejs-sketchfab-example/blob/b780b8c1eac37fc71d8d803fc465c874efd5954c/SketchfabIntegration.js#L30-L70" rel="noopener noreferrer"&gt;Source code on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  4 - Display attribution
&lt;/h3&gt;

&lt;p&gt;Using 3D models from Sketchfab in your application requires that you display attribution to the original author. &lt;a href="https://help.sketchfab.com/hc/en-us/articles/360038413232-Crediting-users-for-3D-model-downloads" rel="noopener noreferrer"&gt;Read more about this on Sketchfab&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We can automatically get the attribution &amp;amp; license information from this &lt;a href="https://docs.sketchfab.com/data-api/v3/index.html#!/models/get_v3_models_uid" rel="noopener noreferrer"&gt;Data API route&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is a function that will construct the attribution text given a modelID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getAttributionText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;modelID&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;modelDataUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://api.sketchfab.com/v3/models/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;modelID&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;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&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="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cors&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;modelDataUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&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;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;license&lt;/span&gt; &lt;span class="o"&gt;=&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="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;license&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;license&lt;/span&gt;&lt;span class="p"&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;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&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="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;displayName&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;profileUrl&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;model&lt;/span&gt; &lt;span class="o"&gt;=&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="nx"&gt;metadata&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="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;viewerUrl&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;attributionText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
        &lt;span class="s2"&gt;`This work is based on &amp;lt;a href="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" target=_blank&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;model&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="s2"&gt;&amp;lt;/a&amp;gt;
        by &amp;lt;a href="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" target=_blank&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&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="s2"&gt;&amp;lt;/a&amp;gt; 
        licensed under &amp;lt;a href="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;license&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" target=_blank&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;license&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="s2"&gt;&amp;lt;/a&amp;gt;.`&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;attributionText&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;a href="https://github.com/OmarShehata/threejs-sketchfab-example/blob/8d126d00faf47fb0b99f9f2f7c78a25332ccfb7b/SketchfabIntegration.js#L121-L142" rel="noopener noreferrer"&gt;Source code on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The example app will display the attribution in the bottom left corner, linking to the original model URL, author's Sketchfab profile, and the license.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Known issues
&lt;/h3&gt;

&lt;p&gt;One problem with loading some Sketchfab models is that their scale will be much bigger than the current viewport. Another problem is some models may not be centered around the origin, so they may not be visible when loaded.&lt;/p&gt;

&lt;p&gt;Normalizing and scaling models when loading them in ThreeJS would help solve this, similar to how &lt;a href="https://gltf-viewer.donmccurdy.com/" rel="noopener noreferrer"&gt;Don McCurdy's glTF Viewer&lt;/a&gt; works.&lt;/p&gt;




&lt;p&gt;Thanks for reading! If you found this helpful, follow me on Twitter &lt;a class="mentioned-user" href="https://dev.to/omar4ur"&gt;@omar4ur&lt;/a&gt; to see more of my work. Other ways to reach me at &lt;a href="https://omarshehata.me/" rel="noopener noreferrer"&gt;https://omarshehata.me/&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>threejs</category>
      <category>javascript</category>
      <category>gltf</category>
      <category>3d</category>
    </item>
  </channel>
</rss>
