<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Hunor Márton Borbély - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Hunor Márton Borbély - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 23 May 2026 22:19:50 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/hunor/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Code a Crossy Road Game Clone with React Three Fiber ]]>
                </title>
                <description>
                    <![CDATA[ In this tutorial, you’ll learn how to create a clone of the mobile game Crossy Road with React Three Fiber. In a previous tutorial, I taught you how to build this game using Three.js and vanilla JavaScript. And here, you’ll learn how to make the same... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-code-a-crossy-road-game-clone-with-react-three-fiber/</link>
                <guid isPermaLink="false">67bf859b948d001fe6c9ab99</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Game Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ reactthreefiber ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Hunor Márton Borbély ]]>
                </dc:creator>
                <pubDate>Wed, 26 Feb 2025 21:20:27 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1740599557930/dcbb214e-c6d2-400e-8b2a-25fd81ac3c47.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this tutorial, you’ll learn how to create a clone of the mobile game Crossy Road with React Three Fiber. In a previous <a target="_blank" href="https://www.freecodecamp.org/news/how-to-code-a-crossy-road-game-clone-with-threejs/">tutorial</a>, I taught you how to build this game using Three.js and vanilla JavaScript. And here, you’ll learn how to make the same game with React Three Fiber instead.</p>
<p>The goal of this game is to move a character through an endless path of static and moving obstacles. You have to go around trees and avoid getting hit by cars.</p>
<p>There's a lot to cover in this tutorial: we will start with setting up the scene, the camera, and the lights. Then you’ll learn how to draw the player and the map with the trees and the cars. We’ll also cover how to animate the vehicles, and we’ll add event handlers to move the player through the map. Finally, we’ll add hit detection between the cars and the player.</p>
<p>This article is a shortened version of the Crossy Road tutorial from my site <a target="_blank" href="https://javascriptgametutorials.com/">JavaScriptGameTutorials.com</a>. The extended tutorial is also available as a video on <a target="_blank" href="https://www.youtube.com/watch?v=ccYrSACDNsw&amp;ab_channel=HunorM%C3%A1rtonBorb%C3%A9ly">YouTube</a>.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-react-three-fiber-vs-threejs">React Three Fiber vs Three.js</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-the-game">How to Set Up the Game</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-render-a-map">How to Render a Map</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-animate-the-cars">How to Animate the Cars</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-move-the-player">How to Move the Player</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-hit-detection">Hit Detection</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-next-steps">Next Steps</a></p>
</li>
</ol>
<h2 id="heading-react-three-fiber-vs-threejs">React Three Fiber vs Three.js</h2>
<p>So you might be wondering – what is React Three Fiber, and how does it compare to Three.js? React Three Fiber uses Three.js under the hood, but it gives us a different way to build up our game with React. It’s also easier to to set up, as React Three Fiber comes with sensible defaults for things like the camera.</p>
<p>React has became a leading front-end framework, and React Three Fiber lets you define a 3D scene using React's well-established patterns. You can break down the game into React components and use hooks for animation, event handling, and hit detection.</p>
<p>Under the hood, React Three Fiber still uses Three.js objects. In fact, in some cases, we will access the underlying Three.js objects and manipulate them directly for better performance. But as we build up the game, we use the familiar React patterns.</p>
<p>So which one should you use? If you are already familiar with React, then React Three Fiber might give more structure to your games. And after reading through this and building along with me, you’ll be better equipped to choose.</p>
<h2 id="heading-how-to-set-up-the-game"><strong>How to Set Up the Game</strong></h2>
<p>In this chapter, we’ll set up the drawing canvas, camera, and lights and render a box representing our player.</p>
<h3 id="heading-initializing-the-project"><strong>Initializing the Project</strong></h3>
<p>I recommend using Vite to initialize the project. To do so, go to your terminal and type <code>npm create vite</code>, which will create an initial project for you.</p>
<p>When generating the project, select <strong>React</strong> (because React Three Fiber uses React).</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Create app</span>
npm create vite my-crossy-road-game
<span class="hljs-comment"># Select React as framework</span>
<span class="hljs-comment"># Select JavaScript</span>

<span class="hljs-comment"># Navigate to the project</span>
<span class="hljs-built_in">cd</span> my-crossy-road-game

<span class="hljs-comment"># Update react and react-dom</span>
npm install react@latest react-dom@latest

<span class="hljs-comment"># Install dependencies</span>
npm install three @react-three/fiber

<span class="hljs-comment"># Start development server</span>
npm run dev
</code></pre>
<p>At the time of writing this article, Vite will use React 18 by default. Meanwhile, React 19 is out, and the latest version of React Three Fiber is only compatible with React 19. So let’s update React and react-dom with <code>npm install react@latest react-dom@latest</code>.</p>
<p>After initializing the project, navigate to the project folder and install the additional dependencies. We will use Three.js and React Three Fiber with <code>npm install three @react-three/fiber</code>.</p>
<p>Finally, you can go to the terminal and type <code>npm run dev</code> to start a development server. This way, you can see live the result of your coding in the browser.</p>
<h3 id="heading-the-drawing-canvas"><strong>The Drawing Canvas</strong></h3>
<p>Let’s create a new component called <code>src/Game.jsx</code>. This will be the root of our game.</p>
<p>The <code>Scene</code> component will contain the drawing canvas, the camera, and the lights. We’ll pass on the <code>Player</code> component as its child, which will render a box. Later, we will add the <code>Map</code> component, including the trees, cars, and trucks. This component is also where the score indicator and the controls come later.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Scene } <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Scene"</span>;
<span class="hljs-keyword">import</span> { Player } <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Player"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Game</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Scene</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Player</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Scene</span>&gt;</span></span>
  );
}
</code></pre>
<h3 id="heading-the-mainjsx-file"><strong>The main.jsx file</strong></h3>
<p>To use the new <code>Game</code> component as our root, we need to replace the original <code>App</code> component in the <code>src/main.jsx</code> file.</p>
<p>This will give you an error for now because we didn’t implement the <code>Scene</code> and <code>Player</code> components.</p>
<p>Now that we’ve replaced the <code>App</code> component, we can delete the original <code>App.jsx</code>, <code>App.css</code>, and the <code>assets</code> folder.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { StrictMode } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { createRoot } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-dom/client"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./index.css"</span>;
<span class="hljs-keyword">import</span> Game <span class="hljs-keyword">from</span> <span class="hljs-string">"./Game.jsx"</span>;

createRoot(<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"root"</span>)).render(
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">StrictMode</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Game</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">StrictMode</span>&gt;</span></span>
);
</code></pre>
<p>Let’s also update the <code>index.css</code> file to make sure our drawing canvas fills the entire screen.</p>
<pre><code class="lang-javascript">body {
  <span class="hljs-attr">margin</span>: <span class="hljs-number">0</span>;
  display: flex;
  min-height: <span class="hljs-number">100</span>vh;
}

#root {
  <span class="hljs-attr">width</span>: <span class="hljs-number">100</span>%;
}
</code></pre>
<h3 id="heading-the-player"><strong>The Player</strong></h3>
<p>Let's start adding the necessary objects to render the first scene. Let's add a simple box to represent the player. We already added the player to the scene, so let's see how to define this player.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739804077435/9667f94f-5005-41f8-a6ed-faa6706f0be1.png" alt="The player" width="3840" height="2160" loading="lazy"></p>
<p>The player will be a simple box. To draw a 3D object, we’ll define a geometry and a material. The geometry defines the object's shape, and the material defines its appearance. Here, we’re using box geometry to define a box. The box geometry takes three arguments: the width, depth, and height of the box along the x, y, and z axes.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Player</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">group</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">mesh</span> <span class="hljs-attr">position</span>=<span class="hljs-string">{[0,</span> <span class="hljs-attr">0</span>, <span class="hljs-attr">10</span>]}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">boxGeometry</span> <span class="hljs-attr">args</span>=<span class="hljs-string">{[15,</span> <span class="hljs-attr">15</span>, <span class="hljs-attr">20</span>]} /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meshLambertMaterial</span> <span class="hljs-attr">color</span>=<span class="hljs-string">{0xffffff}</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">mesh</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">group</span>&gt;</span></span>
  );
}
</code></pre>
<p>We have different options for the material. The main difference between them is how they react to light, if at all. Here, we're using <code>meshLambertMaterial</code>, a simple material that responds to light. We set the color property to white.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739804142917/7b6a49e2-30df-40d8-bd4c-7ac1a2725931.png" alt="Different light options" width="3840" height="2160" loading="lazy"></p>
<p>Then, we wrap the geometry and the material into a mesh, which we can add to the scene. We can also position this mesh by setting its X, Y, and Z positions. In the case of a box, these set the center position. By setting the Z position of this box, we’re elevating it above the ground by half its height. As a result, the bottom of the box will be standing on the ground.</p>
<p>We also wrap the mesh into a group element. This is not necessary at this point, but having this structure will be handy when animating the player. When it comes to player animation, we want to separate the horizontal and vertical movement. We want this to be able to follow the player with the camera as it moves but not to move the camera up and down when the player is jumping. We will move the group horizontally along the XY plane together with the camera and move the mesh vertically.</p>
<h3 id="heading-the-camera"><strong>The Camera</strong></h3>
<p>Now, let's look into different camera options. There are two main camera options: the perspective camera, as you can see on the left in the image below, and the orthographic camera, which you can see on the right.</p>
<p>The perspective camera is the default camera in Three.js and is the most common camera type across all video games. It creates a perspective projection, which makes things further away appear smaller and things right in front of the camera appear bigger.</p>
<p>On the other hand, the orthographic camera creates parallel projections, which means that objects are the same size regardless of their distance from the camera. We’ll use an orthographic camera here to give our game more of an arcade look.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739800656673/d2643544-b44c-418e-a475-2844e6626dbb.png" alt="Perspective vs orthographic camera" width="2420" height="1224" loading="lazy"></p>
<p>In Three.js, we place the 3D objects along the X, Y, and Z axes. We define the coordinate system in a way where the ground is on the XY plane so the player can move left and right along the x-axis, forward and backward along the y-axis, and when the player is jumping, it will go up along the z-axis.</p>
<p>We place the camera in this coordinate system to the right along the x-axis, behind the player along the y-axis, and above the ground. Then, the camera will look back at the origin of the coordinate system to the 0,0,0 coordinate, where the player will be placed initially.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739803742848/d202d09f-1c1b-4246-be23-2a6f6af52423.png" alt="The coordinate system" width="1576" height="892" loading="lazy"></p>
<h3 id="heading-the-lights"><strong>The Lights</strong></h3>
<p>There are many types of lights in Three.js. Here, we're going to use an ambient light and a directional light.</p>
<p>You can see the result of ambient light only on the left side of the below image. The ambient light brightens the entire scene. It doesn't have a specific position or direction. You can think of it like the light on a cloudy day when it's bright, but there are no shadows. The ambient light is used to simulate indirect light.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739800781538/9e140ef9-4133-4151-9fc5-b629504259a8.png" alt="Ambient vs directional light" width="2420" height="1224" loading="lazy"></p>
<p>Now, let's look at the directional light that you can see on the right of the image above. A directional light has a position and a target. It shines light in a specific direction with parallel light rays. Even though it has a position, you can rather think of it as the sun that is shining from very far away. The position here is more to define the direction of the light, but then all the other light rays are also parallel with this light ray. So you can think of it like the sun.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739805160031/4742e1de-5dc0-4443-9716-af18581bfa1e.png" alt="The directional light shines with parallel light rays" width="3840" height="2160" loading="lazy"></p>
<p>That's why we're combining an ambient light (so that we have a base brightness all around the scene) with a directional light (to illuminate specific sides of our objects with a brighter color).</p>
<h3 id="heading-the-scene"><strong>The Scene</strong></h3>
<p>After reviewing the different camera and light options, let’s put them together in the <code>Scene</code> component. We set up the canvas with an orthographic camera and lights.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Canvas } <span class="hljs-keyword">from</span> <span class="hljs-string">"@react-three/fiber"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Scene = <span class="hljs-function">(<span class="hljs-params">{ children }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Canvas</span>
      <span class="hljs-attr">orthographic</span>=<span class="hljs-string">{true}</span>
      <span class="hljs-attr">camera</span>=<span class="hljs-string">{{</span>
        <span class="hljs-attr">up:</span> [<span class="hljs-attr">0</span>, <span class="hljs-attr">0</span>, <span class="hljs-attr">1</span>],
        <span class="hljs-attr">position:</span> [<span class="hljs-attr">300</span>, <span class="hljs-attr">-300</span>, <span class="hljs-attr">300</span>],
      }}
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ambientLight</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">directionalLight</span> <span class="hljs-attr">position</span>=<span class="hljs-string">{[-100,</span> <span class="hljs-attr">-100</span>, <span class="hljs-attr">200</span>]} /&gt;</span>
      {children}
    <span class="hljs-tag">&lt;/<span class="hljs-name">Canvas</span>&gt;</span></span>
  );
};
</code></pre>
<p>We use the <code>Canvas</code> component from <code>@react-three/fiber</code>. This component will contain every 3D object on the scene, so it has a <code>children</code> prop.</p>
<p>We set the <code>orthographic</code> prop to <code>true</code> to use an orthographic camera and the <code>camera</code> prop to define the camera’s position and orientation. The camera props require vectors or coordinates that are defined by the x, y, and z values.</p>
<p>The <code>up</code> prop sets the camera’s up vector. We set it to <code>[0, 0, 1]</code> to make the z-axis the up vector. The <code>position</code> prop sets the camera’s position. We move the camera to the right along the x-axis, backward along the y-axis, and up along the z-axis.</p>
<p>We also add the lights. We can use React Three Fiber-specific elements within the <code>Canvas</code> element. We add the <code>ambientLight</code> and <code>directionalLight</code> components to add lights to the scene. We position the directional light to the left along the x-axis, backward along the y-axis, and up along the z-axis.</p>
<p>This is how our first scene comes together. We rendered a simple box.</p>
<h2 id="heading-how-to-render-a-map"><strong>How to Render a Map</strong></h2>
<p>Now, let's add all the other objects to the scene. In this chapter, we’ll define the map. The map will consist of multiple rows, each described by metadata. Each row can be a forest, a car, or a truck lane. We’ll go through each type and define the 3D objects representing them.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739803813951/3796e0c0-6f02-4c82-979b-a5192d8c3b8c.png" alt="The different row types" width="2560" height="1442" loading="lazy"></p>
<p>The map can be broken down into rows, and each row can be broken down into multiple tiles. The player will move from tile to tile. Trees are also placed on a distinct tile. Cars, on the other hand, do not relate to tiles. They move freely through the lane.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739803829719/38318821-8496-43d1-9b1c-a5b36d78af14.png" alt="A row can be broken down into a tile" width="2560" height="1442" loading="lazy"></p>
<p>We define a file for the constants. Here, we define the number of tiles in each row. In this case, there are 17 ties per row, going from -8 to +8. The player will start in the middle at tile zero.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> minTileIndex = <span class="hljs-number">-8</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> maxTileIndex = <span class="hljs-number">8</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> tilesPerRow = maxTileIndex - minTileIndex + <span class="hljs-number">1</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> tileSize = <span class="hljs-number">42</span>;
</code></pre>
<h3 id="heading-the-starting-row"><strong>The Starting Row</strong></h3>
<p>First, let's add the starting row. We’ll define a couple of components that we’re going to use to render the map, and we’ll render the initial row.</p>
<p>Let's create a new component called <code>Map</code>. Soon, we will add this group to the scene.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Grass } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Grass"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Map</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Grass</span> <span class="hljs-attr">rowIndex</span>=<span class="hljs-string">{0}</span> /&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
}
</code></pre>
<p>Then, we set the map's content. Later, we will generate the 3D objects based on the metadata and use it to render the map. For now, let's just use the Grass component. We call the Grass component with the row index, so the grass component will position itself based on this row index.</p>
<p>Now, let's define the Grass component. The Grass component is the foundation and container of the forest rows and is also used for the starting row. It renders a group containing a flat, wide, green box. The dimensions of this box are determined by the constants <code>tileSize</code> and <code>tilesPerRow</code>. The box also has some height, so it sticks out compared to the road, which will be completely flat.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { tilesPerRow, tileSize } <span class="hljs-keyword">from</span> <span class="hljs-string">"../constants"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Grass</span>(<span class="hljs-params">{ rowIndex, children }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">group</span> <span class="hljs-attr">position-y</span>=<span class="hljs-string">{rowIndex</span> * <span class="hljs-attr">tileSize</span>}&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">mesh</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">boxGeometry</span> <span class="hljs-attr">args</span>=<span class="hljs-string">{[tilesPerRow</span> * <span class="hljs-attr">tileSize</span>, <span class="hljs-attr">tileSize</span>, <span class="hljs-attr">3</span>]} /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meshLambertMaterial</span> <span class="hljs-attr">color</span>=<span class="hljs-string">{0xbaf455}</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">mesh</span>&gt;</span>
      {children}
    <span class="hljs-tag">&lt;/<span class="hljs-name">group</span>&gt;</span></span>
  );
}
</code></pre>
<p>The grass can serve as a container for the trees in the row. That's why we wrap the green box into a group so that later, we can also add children to this group. We position the group along the y-axis based on the row index that we received from the Map component. For the initial lane, this is zero, but as we're going to have multiple lanes, we need to place them according to this position.</p>
<p>Now that we have the map container and the grass component, we can finally add the map to the scene.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Scene } <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Scene"</span>;
<span class="hljs-keyword">import</span> { Player } <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Player"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-built_in">Map</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Map"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Game</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Scene</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Player</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Map</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Scene</span>&gt;</span></span>
  );
}
</code></pre>
<h3 id="heading-how-to-add-a-forest-row"><strong>How to Add a Forest Row</strong></h3>
<p>Now that we have an empty forest, let's add another row containing trees. We define the map's metadata and render the rows based on this metadata.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739806992172/139197f0-f2c1-4f11-aacf-f1fedeeaf9b0.png" alt="A forest row" width="2560" height="1442" loading="lazy"></p>
<p>Let's define the map's metadata. The metadata is an array of objects that contain information about each row. Each row will contain a type that will determine the kind of the row and the rest of the properties depending on the row type.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> rows = [
  {
    <span class="hljs-attr">type</span>: <span class="hljs-string">"forest"</span>,
    <span class="hljs-attr">trees</span>: [
      { <span class="hljs-attr">tileIndex</span>: <span class="hljs-number">-3</span>, <span class="hljs-attr">height</span>: <span class="hljs-number">50</span> },
      { <span class="hljs-attr">tileIndex</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">height</span>: <span class="hljs-number">30</span> },
      { <span class="hljs-attr">tileIndex</span>: <span class="hljs-number">5</span>, <span class="hljs-attr">height</span>: <span class="hljs-number">50</span> },
    ],
  },
];
</code></pre>
<p>The metadata for a forest includes the type of “forest” and a list of trees. Each tree has a tile index, which represents which tile it is standing on. In this case, we have 17 tiles per row, going from -8 to +8. The trees also have a height, which is actually the height of the crown.</p>
<p>To render the rows, let’s extend the <code>Map</code> component to render the rows based on the metadata. We import the metadata and map each row to a separate <code>Row</code> component.</p>
<p>Note that the <code>rowIndex</code> is off by one compared to the array index because the first item in the metadata array will become the second row (after the starting row).</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { rows } <span class="hljs-keyword">from</span> <span class="hljs-string">"../metadata"</span>;
<span class="hljs-keyword">import</span> { Grass } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Grass"</span>;
<span class="hljs-keyword">import</span> { Row } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Row"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Map</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Grass</span> <span class="hljs-attr">rowIndex</span>=<span class="hljs-string">{0}</span> /&gt;</span>

      {rows.map((rowData, index) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">Row</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span> <span class="hljs-attr">rowIndex</span>=<span class="hljs-string">{index</span> + <span class="hljs-attr">1</span>} <span class="hljs-attr">rowData</span>=<span class="hljs-string">{rowData}</span> /&gt;</span>
      ))}
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
}
</code></pre>
<p>Now, let’s define the <code>Row</code> component. The <code>Row</code> component is essentially a switch case that renders the correct row based on the <code>type</code> property of the row. We only support the <code>forest</code> type for now, but we will extend this file later to support car and truck lanes.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Forest } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Forest"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Row</span>(<span class="hljs-params">{ rowIndex, rowData }</span>) </span>{
  <span class="hljs-keyword">switch</span> (rowData.type) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">"forest"</span>: {
      <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Forest</span> <span class="hljs-attr">rowIndex</span>=<span class="hljs-string">{rowIndex}</span> <span class="hljs-attr">rowData</span>=<span class="hljs-string">{rowData}</span> /&gt;</span></span>;
    }
  }
}
</code></pre>
<p>The <code>Forest</code> component contains the row’s foundation, a <code>Grass</code> component, and the trees in the row.</p>
<p>The <code>Grass</code> component can receive children. We map trees’ metadata to <code>Tree</code> components and pass them on as children to the <code>Grass</code> component. Each tree gets its <code>tileIndex</code>, which will be used for positioning the tree within the row, and its <code>height</code>.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Grass } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Grass"</span>;
<span class="hljs-keyword">import</span> { Tree } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Tree"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Forest</span>(<span class="hljs-params">{ rowIndex, rowData }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Grass</span> <span class="hljs-attr">rowIndex</span>=<span class="hljs-string">{rowIndex}</span>&gt;</span>
      {rowData.trees.map((tree, index) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">Tree</span>
          <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span>
          <span class="hljs-attr">tileIndex</span>=<span class="hljs-string">{tree.tileIndex}</span>
          <span class="hljs-attr">height</span>=<span class="hljs-string">{tree.height}</span>
        /&gt;</span>
      ))}
    <span class="hljs-tag">&lt;/<span class="hljs-name">Grass</span>&gt;</span></span>
  );
}
</code></pre>
<p>Forest rows also have trees. For each item in the trees array, we render a tree. The Tree component will render a 3D object representing the tree. We pass on to this component the tile index that we will use to position the tree within the row and the height.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739807310182/40cbb90b-0356-4db5-97bb-cdbe0c8e8cb8.png" alt="A tree" width="2560" height="1442" loading="lazy"></p>
<p>Since we’ve already added the map to the scene, the forest will appear on the screen. But first, we need to define how to render a tree. We are going to represent a tree with two boxes. We're going to have a box for the trunk and one for the crown.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { tileSize } <span class="hljs-keyword">from</span> <span class="hljs-string">"../constants"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Tree</span>(<span class="hljs-params">{ tileIndex, height }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">group</span> <span class="hljs-attr">position-x</span>=<span class="hljs-string">{tileIndex</span> * <span class="hljs-attr">tileSize</span>}&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">mesh</span> <span class="hljs-attr">position-z</span>=<span class="hljs-string">{height</span> / <span class="hljs-attr">2</span> + <span class="hljs-attr">20</span>}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">boxGeometry</span> <span class="hljs-attr">args</span>=<span class="hljs-string">{[30,</span> <span class="hljs-attr">30</span>, <span class="hljs-attr">height</span>]} /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meshLambertMaterial</span> <span class="hljs-attr">color</span>=<span class="hljs-string">{0x7aa21d}</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">mesh</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">mesh</span> <span class="hljs-attr">position-z</span>=<span class="hljs-string">{10}</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">boxGeometry</span> <span class="hljs-attr">args</span>=<span class="hljs-string">{[15,</span> <span class="hljs-attr">15</span>, <span class="hljs-attr">20</span>]} /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meshLambertMaterial</span> <span class="hljs-attr">color</span>=<span class="hljs-string">{0x4d2926}</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">mesh</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">group</span>&gt;</span></span>
  );
}
</code></pre>
<p>These are both simple boxes, just like we had before with the player and also in the Grass component. The trunk is placed on top of the ground. We lift it along the Z-axis by half of its height, and the crown is placed on top of the trunk. The crown's height is also based on the height property. These two meshes are wrapped together into a group, and then we position this group along the X-axis based on the tile index property.</p>
<h3 id="heading-car-lanes"><strong>Car Lanes</strong></h3>
<p>Now, let's add another row type: car lanes. The process of adding car lanes will follow a similar structure. We define the lanes' metadata, including the vehicles, and then map them into 3D objects.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739807532566/1c9cfdc9-2c45-476c-9ada-09f7abf79328.png" alt="The car lane" width="2560" height="1442" loading="lazy"></p>
<p>In the metadata, let's replace the first row with a car lane. The car lane will contain a single red car moving to the left. We have a direction property, which is a boolean flag. If this is true, that means the cars are moving to the right in the lane, and if it's false, then the vehicles are moving to the left. We also have a speed property, which defines how many units each vehicle takes every second.</p>
<p>Finally, we have an array of vehicles. Each car will have an initial tile index, which represents only its initial position because the cars will move later. Each car will also have a color property, which is a hexadecimal color value.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> rows = [
  {
    <span class="hljs-attr">type</span>: <span class="hljs-string">"car"</span>,
    <span class="hljs-attr">direction</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">speed</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">vehicles</span>: [{ <span class="hljs-attr">initialTileIndex</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">color</span>: <span class="hljs-number">0xff0000</span> }],
  },
];
</code></pre>
<p>Now, to render this lane type, we have to extend our logic to support car lanes. Let’s extend the <code>Row</code> Component with support for car lanes. If the type of a row is <code>car</code> we map it to a <code>CarLane</code> component.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Forest } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Forest"</span>;
<span class="hljs-keyword">import</span> { CarLane } <span class="hljs-keyword">from</span> <span class="hljs-string">"./CarLane"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Row</span>(<span class="hljs-params">{ rowIndex, rowData }</span>) </span>{
  <span class="hljs-keyword">switch</span> (rowData.type) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">"forest"</span>: {
      <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Forest</span> <span class="hljs-attr">rowIndex</span>=<span class="hljs-string">{rowIndex}</span> <span class="hljs-attr">rowData</span>=<span class="hljs-string">{rowData}</span> /&gt;</span></span>;
    }
    <span class="hljs-keyword">case</span> <span class="hljs-string">"car"</span>: {
      <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">CarLane</span> <span class="hljs-attr">rowIndex</span>=<span class="hljs-string">{rowIndex}</span> <span class="hljs-attr">rowData</span>=<span class="hljs-string">{rowData}</span> /&gt;</span></span>;
    }
  }
}
</code></pre>
<p>The <code>CarLane</code> component renders the cars on the road. It has a similar structure to the <code>Forest</code> component.</p>
<p>It receives a <code>rowData</code> object as a prop, which contains the cars to be rendered. It wraps the cars in a <code>Road</code> component and maps over the <code>rowData.vehicles</code> array to render each car.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Road } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Road"</span>;
<span class="hljs-keyword">import</span> { Car } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Car"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">CarLane</span>(<span class="hljs-params">{ rowIndex, rowData }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Road</span> <span class="hljs-attr">rowIndex</span>=<span class="hljs-string">{rowIndex}</span>&gt;</span>
      {rowData.vehicles.map((vehicle, index) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">Car</span>
          <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span>
          <span class="hljs-attr">rowIndex</span>=<span class="hljs-string">{rowIndex}</span>
          <span class="hljs-attr">initialTileIndex</span>=<span class="hljs-string">{vehicle.initialTileIndex}</span>
          <span class="hljs-attr">direction</span>=<span class="hljs-string">{rowData.direction}</span>
          <span class="hljs-attr">speed</span>=<span class="hljs-string">{rowData.speed}</span>
          <span class="hljs-attr">color</span>=<span class="hljs-string">{vehicle.color}</span>
        /&gt;</span>
      ))}
    <span class="hljs-tag">&lt;/<span class="hljs-name">Road</span>&gt;</span></span>
  );
}
</code></pre>
<p>The Road and Car functions are new here, so let's examine them next. The Road function returns the foundation and container of the car and truck lanes. Similar to the Grass component, it also returns a group containing a gray plane.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739807755293/e731a758-ac20-40f5-819b-82a5ae81e65f.png" alt="The Road component" width="2560" height="1442" loading="lazy"></p>
<p>The size of the plane is also determined by the constants <code>tileSize</code> and <code>tilesPerRow</code>. Unlike the Grass component, though, it doesn't have any height. It's completely flat. The road will also serve as a container for the cars and trucks in the row, so that's why we wrap the plane into a group – so that we can add children to it.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { tilesPerRow, tileSize } <span class="hljs-keyword">from</span> <span class="hljs-string">"../constants"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Road</span>(<span class="hljs-params">{ rowIndex, children }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">group</span> <span class="hljs-attr">position-y</span>=<span class="hljs-string">{rowIndex</span> * <span class="hljs-attr">tileSize</span>}&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">mesh</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">planeGeometry</span> <span class="hljs-attr">args</span>=<span class="hljs-string">{[tilesPerRow</span> * <span class="hljs-attr">tileSize</span>, <span class="hljs-attr">tileSize</span>]} /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meshLambertMaterial</span> <span class="hljs-attr">color</span>=<span class="hljs-string">{0x454a59}</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">mesh</span>&gt;</span>
      {children}
    <span class="hljs-tag">&lt;/<span class="hljs-name">group</span>&gt;</span></span>
  );
}
</code></pre>
<p>Now, let's look at the Car. The Car function returns a very simple 3D car model.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739807975022/dc04c3c1-de94-49fd-ad85-ade999c8862a.png" alt="A car" width="2560" height="1442" loading="lazy"></p>
<p>It contains a box for the body and a smaller box for the top part. We also have two wheel meshes. Because we never see the cars from underneath, we don't need to separate the wheels into left and right. We can just use one long box for the front wheels and another one for the back wheels.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { tileSize } <span class="hljs-keyword">from</span> <span class="hljs-string">"../constants"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Car</span>(<span class="hljs-params">{
  rowIndex,
  initialTileIndex,
  direction,
  speed,
  color,
}</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">group</span>
      <span class="hljs-attr">position-x</span>=<span class="hljs-string">{initialTileIndex</span> * <span class="hljs-attr">tileSize</span>}
      <span class="hljs-attr">rotation-z</span>=<span class="hljs-string">{direction</span> ? <span class="hljs-attr">0</span> <span class="hljs-attr">:</span> <span class="hljs-attr">Math.PI</span>}
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">mesh</span> <span class="hljs-attr">position</span>=<span class="hljs-string">{[0,</span> <span class="hljs-attr">0</span>, <span class="hljs-attr">12</span>]}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">boxGeometry</span> <span class="hljs-attr">args</span>=<span class="hljs-string">{[60,</span> <span class="hljs-attr">30</span>, <span class="hljs-attr">15</span>]} /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meshLambertMaterial</span> <span class="hljs-attr">color</span>=<span class="hljs-string">{color}</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">mesh</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">mesh</span> <span class="hljs-attr">position</span>=<span class="hljs-string">{[-6,</span> <span class="hljs-attr">0</span>, <span class="hljs-attr">25.5</span>]}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">boxGeometry</span> <span class="hljs-attr">args</span>=<span class="hljs-string">{[33,</span> <span class="hljs-attr">24</span>, <span class="hljs-attr">12</span>]} /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meshLambertMaterial</span> <span class="hljs-attr">color</span>=<span class="hljs-string">{0xffffff}</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">mesh</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">mesh</span> <span class="hljs-attr">position</span>=<span class="hljs-string">{[-18,</span> <span class="hljs-attr">0</span>, <span class="hljs-attr">6</span>]}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">boxGeometry</span> <span class="hljs-attr">args</span>=<span class="hljs-string">{[12,</span> <span class="hljs-attr">33</span>, <span class="hljs-attr">12</span>]} /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meshLambertMaterial</span> <span class="hljs-attr">color</span>=<span class="hljs-string">{0x333333}</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">mesh</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">mesh</span> <span class="hljs-attr">position</span>=<span class="hljs-string">{[18,</span> <span class="hljs-attr">0</span>, <span class="hljs-attr">6</span>]}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">boxGeometry</span> <span class="hljs-attr">args</span>=<span class="hljs-string">{[12,</span> <span class="hljs-attr">33</span>, <span class="hljs-attr">12</span>]} /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meshLambertMaterial</span> <span class="hljs-attr">color</span>=<span class="hljs-string">{0x333333}</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">mesh</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">group</span>&gt;</span></span>
  );
}
</code></pre>
<p>We group all these elements, position them based on the <code>initialTileIndex</code> property, and turn them based on the <code>direction</code> property. If the car goes to the left, we rotate it by 180°. When we set rotation values in Three.js. We have to set them in radians, so that's why we set it to Math.Pi, which is equivalent to 180°.</p>
<p>You can also find a more extended version of how to draw this car with textures <a target="_blank" href="https://www.freecodecamp.org/news/three-js-tutorial/">in this article</a>.</p>
<p>Based on the metadata, we can now render a map with several rows. Here’s an example with a few more lanes. Of course, feel free to define your own map.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> rows = [
  {
    <span class="hljs-attr">type</span>: <span class="hljs-string">"car"</span>,
    <span class="hljs-attr">direction</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">speed</span>: <span class="hljs-number">188</span>,
    <span class="hljs-attr">vehicles</span>: [
      { <span class="hljs-attr">initialTileIndex</span>: <span class="hljs-number">-4</span>, <span class="hljs-attr">color</span>: <span class="hljs-number">0xbdb638</span> },
      { <span class="hljs-attr">initialTileIndex</span>: <span class="hljs-number">-1</span>, <span class="hljs-attr">color</span>: <span class="hljs-number">0x78b14b</span> },
      { <span class="hljs-attr">initialTileIndex</span>: <span class="hljs-number">4</span>, <span class="hljs-attr">color</span>: <span class="hljs-number">0xa52523</span> },
    ],
  },
  {
    <span class="hljs-attr">type</span>: <span class="hljs-string">"forest"</span>,
    <span class="hljs-attr">trees</span>: [
      { <span class="hljs-attr">tileIndex</span>: <span class="hljs-number">-5</span>, <span class="hljs-attr">height</span>: <span class="hljs-number">50</span> },
      { <span class="hljs-attr">tileIndex</span>: <span class="hljs-number">0</span>, <span class="hljs-attr">height</span>: <span class="hljs-number">30</span> },
      { <span class="hljs-attr">tileIndex</span>: <span class="hljs-number">3</span>, <span class="hljs-attr">height</span>: <span class="hljs-number">50</span> },
    ],
  },
  {
    <span class="hljs-attr">type</span>: <span class="hljs-string">"car"</span>,
    <span class="hljs-attr">direction</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">speed</span>: <span class="hljs-number">125</span>,
    <span class="hljs-attr">vehicles</span>: [
      { <span class="hljs-attr">initialTileIndex</span>: <span class="hljs-number">-4</span>, <span class="hljs-attr">color</span>: <span class="hljs-number">0x78b14b</span> },
      { <span class="hljs-attr">initialTileIndex</span>: <span class="hljs-number">0</span>, <span class="hljs-attr">color</span>: <span class="hljs-number">0xbdb638</span> },
    ],
  },
  {
    <span class="hljs-attr">type</span>: <span class="hljs-string">"forest"</span>,
    <span class="hljs-attr">trees</span>: [
      { <span class="hljs-attr">tileIndex</span>: <span class="hljs-number">-8</span>, <span class="hljs-attr">height</span>: <span class="hljs-number">30</span> },
      { <span class="hljs-attr">tileIndex</span>: <span class="hljs-number">-3</span>, <span class="hljs-attr">height</span>: <span class="hljs-number">50</span> },
      { <span class="hljs-attr">tileIndex</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">height</span>: <span class="hljs-number">30</span> },
    ],
  },
];
</code></pre>
<p>This article does not cover truck lanes, but they follow a similar structure. The code for it can be found at <a target="_blank" href="http://javascriptgametutorials.com/">JavaScriptGameTutorials.com</a>.</p>
<h2 id="heading-how-to-animate-the-cars"><strong>How to Animate the Cars</strong></h2>
<p>Let's move on and animate the cars in their lanes according to their speed and direction.</p>
<p>This is where things start to diverge from how you would typically use React. The React way would be to update a state or a prop and let React re-render the whole component. This is fast when working with HTML elements, but it is not very effective when working with 3D objects. We want to <a target="_blank" href="https://r3f.docs.pmnd.rs/advanced/pitfalls#avoid-setstate-in-loops">avoid re-rendering</a> the whole scene and, instead, update the position of the underlying objects directly.</p>
<p>We only use React to set up the scene and the objects, and then we let Three.js do the heavy lifting. React Three Fiber is just a thin layer on top of Three.js, so we can access the underlying Three.js objects directly to update the position of the cars and trucks.</p>
<p>We are going to use a custom hook, <code>useVehicleAnimation</code><strong>,</strong> to animate the vehicles. This hook will need a reference to the 3D object it should manipulate. Before defining this hook, let’s get a reference to the Three.js group, which represents the car. We use React’s <code>useRef</code> hook to store the reference and bind it to the <code>group</code> element.</p>
<p>Then, we pass on this reference to the <code>useVehicleAnimation</code> hook, along with the direction and speed of the car.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useRef } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { tileSize } <span class="hljs-keyword">from</span> <span class="hljs-string">"../constants"</span>;
<span class="hljs-keyword">import</span> useVehicleAnimation <span class="hljs-keyword">from</span> <span class="hljs-string">"../hooks/useVehicleAnimation"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Car</span>(<span class="hljs-params">{
  rowIndex,
  initialTileIndex,
  direction,
  speed,
  color,
}</span>) </span>{
  <span class="hljs-keyword">const</span> car = useRef(<span class="hljs-literal">null</span>);
  useVehicleAnimation(car, direction, speed);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">group</span>
      <span class="hljs-attr">position-x</span>=<span class="hljs-string">{initialTileIndex</span> * <span class="hljs-attr">tileSize</span>}
      <span class="hljs-attr">rotation-z</span>=<span class="hljs-string">{direction</span> ? <span class="hljs-attr">0</span> <span class="hljs-attr">:</span> <span class="hljs-attr">Math.PI</span>}
      <span class="hljs-attr">ref</span>=<span class="hljs-string">{car}</span>
    &gt;</span>
      . . . 
    <span class="hljs-tag">&lt;/<span class="hljs-name">group</span>&gt;</span></span>
  );
}
</code></pre>
<p>Let’s implement the <code>useVehicleAnimation</code> hook to animate the vehicles. It moves them based on their speed and direction until the end of the lane and then re-spawns them at the other end. This way, the vehicles move in an infinite loop.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739808359961/eca84295-09d7-463a-8d29-e5f8069bb673.png" alt="The cars move in an infinite loop" width="3840" height="2160" loading="lazy"></p>
<p>This hook uses the <code>useFrame</code> hook that React Three Fiber provides. This hook is similar to <code>setAnimationLoop</code> in Three.js. It runs a function on every animation frame.</p>
<p>Conveniently, this function receives the time <code>delta</code>—the time that passed since the previous animation frame. We multiply this value by the vehicle’s <code>speed</code> to get the distance the car took during this time.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useFrame } <span class="hljs-keyword">from</span> <span class="hljs-string">"@react-three/fiber"</span>;
<span class="hljs-keyword">import</span> { tileSize, minTileIndex, maxTileIndex } <span class="hljs-keyword">from</span> <span class="hljs-string">"../constants"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useVehicleAnimation</span>(<span class="hljs-params">ref, direction, speed</span>) </span>{
  useFrame(<span class="hljs-function">(<span class="hljs-params">state, delta</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (!ref.current) <span class="hljs-keyword">return</span>;
    <span class="hljs-keyword">const</span> vehicle = ref.current;

    <span class="hljs-keyword">const</span> beginningOfRow = (minTileIndex - <span class="hljs-number">2</span>) * tileSize;
    <span class="hljs-keyword">const</span> endOfRow = (maxTileIndex + <span class="hljs-number">2</span>) * tileSize;

    <span class="hljs-keyword">if</span> (direction) {
      vehicle.position.x =
        vehicle.position.x &gt; endOfRow
          ? beginningOfRow
          : vehicle.position.x + speed * delta;
    } <span class="hljs-keyword">else</span> {
      vehicle.position.x =
        vehicle.position.x &lt; beginningOfRow
          ? endOfRow
          : vehicle.position.x - speed * delta;
    }
  });
}
</code></pre>
<p>We directly update the <code>position.x</code> property of the underlying Three.js group. If the vehicle reaches the end of the lane, we re-spawn it at the other end.</p>
<p>Note that the reference passed to the hook might be <code>null</code> because it is only set after the first render. If the reference is not set, we return early from the function. Then, the animation starts in the next frame.</p>
<h2 id="heading-how-to-move-the-player"><strong>How to Move the Player</strong></h2>
<p>Now, let's move on to animating the player. Moving the player on the map is more complex than moving the vehicles. The player can move in all directions, bump into trees, or get hit by cars, and it shouldn't be able to move outside the map.</p>
<p>In this chapter, we are focusing on two parts: collecting user inputs and executing the movement commands. Player movement is not instant – we need to collect the movement commands into a queue and execute them one by one. We are going to collect user inputs and put them into a queue.</p>
<h3 id="heading-collecting-user-inputs"><strong>Collecting User Inputs</strong></h3>
<p>To track the movement commands, we create a store for the player. We do not use a state management library, as we don’t need a reactive store. We simply define our state in a regular JavaScript file.</p>
<p>The store will keep track of the player’s position and movement queue. The player starts at the middle of the first row, and the move queue is initially empty.</p>
<p>We will also export two functions: <strong>queueMove</strong> adds the movement command to the end of the move queue, and the <strong>stepCompleted</strong> function removes the first movement command from the queue and updates the player's position accordingly.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> state = {
  <span class="hljs-attr">currentRow</span>: <span class="hljs-number">0</span>,
  <span class="hljs-attr">currentTile</span>: <span class="hljs-number">0</span>,
  <span class="hljs-attr">movesQueue</span>: [],
};

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">queueMove</span>(<span class="hljs-params">direction</span>) </span>{
  state.movesQueue.push(direction);
}

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">stepCompleted</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> direction = state.movesQueue.shift();

  <span class="hljs-keyword">if</span> (direction === <span class="hljs-string">"forward"</span>) state.currentRow += <span class="hljs-number">1</span>;
  <span class="hljs-keyword">if</span> (direction === <span class="hljs-string">"backward"</span>) state.currentRow -= <span class="hljs-number">1</span>;
  <span class="hljs-keyword">if</span> (direction === <span class="hljs-string">"left"</span>) state.currentTile -= <span class="hljs-number">1</span>;
  <span class="hljs-keyword">if</span> (direction === <span class="hljs-string">"right"</span>) state.currentTile += <span class="hljs-number">1</span>;
}
</code></pre>
<p>Now we can add event listeners for keyboard events to listen to the arrow keys. The <code>useEventListeners</code> hook listens to the arrow keys and calls the <code>queueMove</code> function of the player store with the corresponding direction.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { queueMove } <span class="hljs-keyword">from</span> <span class="hljs-string">"../stores/player"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useEventListeners</span>(<span class="hljs-params"></span>) </span>{
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> handleKeyDown = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
      <span class="hljs-keyword">if</span> (event.key === <span class="hljs-string">"ArrowUp"</span>) {
        queueMove(<span class="hljs-string">"forward"</span>);
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (event.key === <span class="hljs-string">"ArrowDown"</span>) {
        queueMove(<span class="hljs-string">"backward"</span>);
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (event.key === <span class="hljs-string">"ArrowLeft"</span>) {
        queueMove(<span class="hljs-string">"left"</span>);
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (event.key === <span class="hljs-string">"ArrowRight"</span>) {
        queueMove(<span class="hljs-string">"right"</span>);
      }
    };

    <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"keydown"</span>, handleKeyDown);

    <span class="hljs-comment">// Cleanup function to remove the event listener</span>
    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-built_in">window</span>.removeEventListener(<span class="hljs-string">"keydown"</span>, handleKeyDown);
    };
  }, []);
}
</code></pre>
<p>After defining the event listeners, we also have to import them into the Game component so that they work.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Scene } <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Scene"</span>;
<span class="hljs-keyword">import</span> { Player } <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Player"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-built_in">Map</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Map"</span>;
<span class="hljs-keyword">import</span> useEventListeners <span class="hljs-keyword">from</span> <span class="hljs-string">"./hooks/useEventListeners"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Game</span>(<span class="hljs-params"></span>) </span>{
  useEventListeners();

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Scene</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Player</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Map</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Scene</span>&gt;</span></span>
  );
}
</code></pre>
<h3 id="heading-executing-movement-commands"><strong>Executing Movement Commands</strong></h3>
<p>So far, we have collected user inputs and put each command into the <strong>movesQueue</strong> array in the player component. Now, it's time to execute these commands one by one and animate the player.</p>
<p>Let's create a new hook called <strong>usePlayerAnimation</strong>. Its main goal is to take each move command from the <strong>moveQueue</strong> one by one, calculate the player's progress toward executing a step, and position the player accordingly.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739808997988/8adb70d2-bf5d-4e65-8ed9-29e8f297b487.png" alt="The player movement" width="1590" height="892" loading="lazy"></p>
<p>This hook animates the player frame by frame. It uses the <code>useFrame</code> hook, just like the <code>useVehicleAnimation</code> hook. This time, however, we use a separate <code>moveClock</code> that measures each step individually. We pass on <code>false</code> to the clock constructor so it doesn’t start automatically. The clock starts at the beginning of a step. At each animation frame, first, we check if there are any more steps to take, and if there are and we’re not currently processing a step, we start the clock.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { useFrame } <span class="hljs-keyword">from</span> <span class="hljs-string">"@react-three/fiber"</span>;
<span class="hljs-keyword">import</span> { state, stepCompleted } <span class="hljs-keyword">from</span> <span class="hljs-string">"../stores/player"</span>;
<span class="hljs-keyword">import</span> { tileSize } <span class="hljs-keyword">from</span> <span class="hljs-string">"../constants"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">usePlayerAnimation</span>(<span class="hljs-params">ref</span>) </span>{
  <span class="hljs-keyword">const</span> moveClock = <span class="hljs-keyword">new</span> THREE.Clock(<span class="hljs-literal">false</span>);

  useFrame(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (!ref.current) <span class="hljs-keyword">return</span>;
    <span class="hljs-keyword">if</span> (!state.movesQueue.length) <span class="hljs-keyword">return</span>;
    <span class="hljs-keyword">const</span> player = ref.current;

    <span class="hljs-keyword">if</span> (!moveClock.running) moveClock.start();

    <span class="hljs-keyword">const</span> stepTime = <span class="hljs-number">0.2</span>; <span class="hljs-comment">// Seconds it takes to take a step</span>
    <span class="hljs-keyword">const</span> progress = <span class="hljs-built_in">Math</span>.min(
      <span class="hljs-number">1</span>,
      moveClock.getElapsedTime() / stepTime
    );

    setPosition(player, progress);

    <span class="hljs-comment">// Once a step has ended</span>
    <span class="hljs-keyword">if</span> (progress &gt;= <span class="hljs-number">1</span>) {
      stepCompleted();
      moveClock.stop();
    }
  });
}

. . .
</code></pre>
<p>We use the move clock to calculate the progress between the two tiles. The progress indicator can be a number between zero and one. Zero means that the player is still at the beginning of the step, and one means that it’s arrived at its new position.</p>
<p>At each animation frame, we call the <strong>setPosition</strong> function to set the player’s position according to the progress. Once we finish a step, we call the <strong>stepCompleted</strong> function to update the player's position and stop the clock. If there are any more move commands in the <strong>movesQueue</strong>, the clock will restart in the following animation frame.</p>
<p>Now that we know how to calculate the progress for each step, let's look into how to set the player's position based on the progress. The player will jump from tile to tile. Let's break this down into two parts: the movement's horizontal and vertical components.</p>
<p>The player moves from the current tile to the next tile in the direction of the move command. We calculate the player's start and end position based on the current tile and the direction of the move command. Then, we use linear interpolation with a utility function that Three.js provides. This will interpolate between the start and end positions based on the progress.</p>
<pre><code class="lang-javascript">. . .

function setPosition(player, progress) {
  <span class="hljs-keyword">const</span> startX = state.currentTile * tileSize;
  <span class="hljs-keyword">const</span> startY = state.currentRow * tileSize;
  <span class="hljs-keyword">let</span> endX = startX;
  <span class="hljs-keyword">let</span> endY = startY;

  <span class="hljs-keyword">if</span> (state.movesQueue[<span class="hljs-number">0</span>] === <span class="hljs-string">"left"</span>) endX -= tileSize;
  <span class="hljs-keyword">if</span> (state.movesQueue[<span class="hljs-number">0</span>] === <span class="hljs-string">"right"</span>) endX += tileSize;
  <span class="hljs-keyword">if</span> (state.movesQueue[<span class="hljs-number">0</span>] === <span class="hljs-string">"forward"</span>) endY += tileSize;
  <span class="hljs-keyword">if</span> (state.movesQueue[<span class="hljs-number">0</span>] === <span class="hljs-string">"backward"</span>) endY -= tileSize;

  player.position.x = THREE.MathUtils.lerp(startX, endX, progress);
  player.position.y = THREE.MathUtils.lerp(startY, endY, progress);
  player.children[<span class="hljs-number">0</span>].position.z = <span class="hljs-built_in">Math</span>.sin(progress * <span class="hljs-built_in">Math</span>.PI) * <span class="hljs-number">8</span> + <span class="hljs-number">10</span>;
}
</code></pre>
<p>For the vertical component, we use a sine function to make it look like jumping. We are basically mapping the progress to the first part of a sine wave.</p>
<p>Below you can see what a sine wave looks like. It goes from 0 to 2 Pi. So if you multiply the progress value, which is going from 0 to 1 with Pi, then the progress will map into the first half of this sine wave. The sign function then will give us a value between zero and one.</p>
<p>To make the jump look higher, we can multiply this with a value. In this case, we multiply the result of the sine function by eight, so as a result, the player will have a jump where the maximum height of the jump will be eight units.</p>
<p>We also need to add the original Z position to the value – otherwise, the player will sink halfway into the ground after the first step.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739810397620/89119f6e-ced5-4cdb-8254-96fbf66479ed.png" alt="For the vertical movement we use a sine wave" width="2032" height="1146" loading="lazy"></p>
<p>It’s finally time to update the <code>Player</code> component to make it all come together. We create a new reference with <code>useRef</code> and assign it to the <code>group</code> element. Finally, we pass this reference to the <code>usePlayerAnimation</code> hook we just implemented.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useRef } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> usePlayerAnimation <span class="hljs-keyword">from</span> <span class="hljs-string">"../hooks/usePlayerAnimation"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Player</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> player = useRef(<span class="hljs-literal">null</span>);
  usePlayerAnimation(player);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">group</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">{player}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">mesh</span> <span class="hljs-attr">position</span>=<span class="hljs-string">{[0,</span> <span class="hljs-attr">0</span>, <span class="hljs-attr">10</span>]} <span class="hljs-attr">castShadow</span> <span class="hljs-attr">receiveShadow</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">boxGeometry</span> <span class="hljs-attr">args</span>=<span class="hljs-string">{[15,</span> <span class="hljs-attr">15</span>, <span class="hljs-attr">20</span>]} /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meshLambertMaterial</span> <span class="hljs-attr">color</span>=<span class="hljs-string">{0xffffff}</span> <span class="hljs-attr">flatShading</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">mesh</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">group</span>&gt;</span></span>
  );
}
</code></pre>
<p>If you did everything right, the player should be able to move around the game board, moving forward, backward, left, and right. But we haven't added any hit detection. So far, the player can move through trees and vehicles and even get off the game board. Let's fix these issues in the following steps.</p>
<h3 id="heading-follow-the-player-with-the-camera">Follow the Player with the Camera</h3>
<p>We defined the camera in the <code>Scene</code> component. By default, it has a static position. Instead of that, we want to move it with the player. We could adjust its position at every animation frame just like the player, but it’s easier to attach the camera to the <code>Player</code> component so that they move together.</p>
<p>We can access the camera using the <code>useThree</code> hook from <code>@react-three/fiber</code>. This returns a Three.js camera object that we can add to the player group.</p>
<p>We already have a reference to the group representing the player. We can attach the camera to the player by adding it as a child of the player group. Because the player reference is undefined on the first render, we need to use the <code>useEffect</code> hook to attach the camera only once the player reference is set.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useRef, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { useThree } <span class="hljs-keyword">from</span> <span class="hljs-string">"@react-three/fiber"</span>;
<span class="hljs-keyword">import</span> usePlayerAnimation <span class="hljs-keyword">from</span> <span class="hljs-string">"../hooks/usePlayerAnimation"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Player</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> player = useRef(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> camera = useThree(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> state.camera);

  usePlayerAnimation(player);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (!player.current) <span class="hljs-keyword">return</span>;

    <span class="hljs-comment">// Attach the camera to the player</span>
    player.current.add(camera);
  });

  <span class="hljs-keyword">return</span> (
    . . .
  );
}
</code></pre>
<h3 id="heading-restricting-player-movement"><strong>Restricting Player Movement</strong></h3>
<p>Let’s make sure that the player can’t end up in a position that’s invalid. We will check if a move is valid by calculating where it will take the player. If the player would end up in a position outside of the map or in a tile occupied by a tree, we will ignore that move command.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739810555283/310afd32-6e5a-4143-bbe2-e0da558bd6d7.png" alt="Calculating where the player will end up" width="1662" height="1146" loading="lazy"></p>
<p>First, we need to calculate where the player would end up if they made a particular move. Whenever we add a new move to the queue, we need to calculate where the player would end up if they made all the moves in the queue and take the current move command. We create a utility function that takes the player's current position and an array of moves and returns the player's final position.</p>
<p>For instance, if the player's current position is 0,0, staying in the middle of the first row, and the moves are forward and left, then the final position will be row 1 tile -1.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">calculateFinalPosition</span>(<span class="hljs-params">currentPosition, moves</span>) </span>{
  <span class="hljs-keyword">return</span> moves.reduce(<span class="hljs-function">(<span class="hljs-params">position, direction</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (direction === <span class="hljs-string">"forward"</span>)
      <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">rowIndex</span>: position.rowIndex + <span class="hljs-number">1</span>,
        <span class="hljs-attr">tileIndex</span>: position.tileIndex,
      };
    <span class="hljs-keyword">if</span> (direction === <span class="hljs-string">"backward"</span>)
      <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">rowIndex</span>: position.rowIndex - <span class="hljs-number">1</span>,
        <span class="hljs-attr">tileIndex</span>: position.tileIndex,
      };
    <span class="hljs-keyword">if</span> (direction === <span class="hljs-string">"left"</span>)
      <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">rowIndex</span>: position.rowIndex,
        <span class="hljs-attr">tileIndex</span>: position.tileIndex - <span class="hljs-number">1</span>,
      };
    <span class="hljs-keyword">if</span> (direction === <span class="hljs-string">"right"</span>)
      <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">rowIndex</span>: position.rowIndex,
        <span class="hljs-attr">tileIndex</span>: position.tileIndex + <span class="hljs-number">1</span>,
      };
    <span class="hljs-keyword">return</span> position;
  }, currentPosition);
}
</code></pre>
<p>Now that we have this utility function to calculate where the player will end up after taking a move, let's create another utility function to calculate whether the player would end up in a valid or invalid position. In this function, we use the <strong>calculateFinalPosition</strong> function that we just created. Then, we’ll check if the player would end up outside the map or on a tile occupied by a tree.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739810684106/71c45122-b10c-4623-b575-184c1cf1fecb.png" alt="Check if the player bumps into a tree" width="1846" height="1146" loading="lazy"></p>
<p>If the move is invalid, we return false. First, we check if the final position is before the starting row or if the tile number is outside the range of the tiles. Then, we check the metadata of the row the player will end up in. Here, the index is off by one because the row metadata doesn't include the starting row. If we end up in a forest row, we check whether a tree occupies the tile we move to. If any of this is true, we return false.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { calculateFinalPosition } <span class="hljs-keyword">from</span> <span class="hljs-string">"./calculateFinalPosition"</span>;
<span class="hljs-keyword">import</span> { minTileIndex, maxTileIndex } <span class="hljs-keyword">from</span> <span class="hljs-string">"../constants"</span>;
<span class="hljs-keyword">import</span> { rows } <span class="hljs-keyword">from</span> <span class="hljs-string">"../metadata"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">endsUpInValidPosition</span>(<span class="hljs-params">currentPosition, moves</span>) </span>{
  <span class="hljs-comment">// Calculate where the player would end up after the move</span>
  <span class="hljs-keyword">const</span> finalPosition = calculateFinalPosition(
    currentPosition,
    moves
  );

  <span class="hljs-comment">// Detect if we hit the edge of the board</span>
  <span class="hljs-keyword">if</span> (
    finalPosition.rowIndex === <span class="hljs-number">-1</span> ||
    finalPosition.tileIndex === minTileIndex - <span class="hljs-number">1</span> ||
    finalPosition.tileIndex === maxTileIndex + <span class="hljs-number">1</span>
  ) {
    <span class="hljs-comment">// Invalid move, ignore move command</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
  }

  <span class="hljs-comment">// Detect if we hit a tree</span>
  <span class="hljs-keyword">const</span> finalRow = rows[finalPosition.rowIndex - <span class="hljs-number">1</span>];
  <span class="hljs-keyword">if</span> (
    finalRow &amp;&amp;
    finalRow.type === <span class="hljs-string">"forest"</span> &amp;&amp;
    finalRow.trees.some(
      <span class="hljs-function">(<span class="hljs-params">tree</span>) =&gt;</span> tree.tileIndex === finalPosition.tileIndex
    )
  ) {
    <span class="hljs-comment">// Invalid move, ignore move command</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
  }

  <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
}
</code></pre>
<p>Finally, let's extend the player's <strong>queueMove</strong> function with the <strong>endsUpInValidPosition</strong> function to check if a move is valid. If the <strong>endsUpInValidPosition</strong> function returns false, we cannot take this step. In this case, we return early from the function before the move is added to the <strong>movesQueue</strong> array. So we are ignoring the move.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { endsUpInValidPosition } <span class="hljs-keyword">from</span> <span class="hljs-string">"../utilities/endsUpInValidPosition"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> state = {
  <span class="hljs-attr">currentRow</span>: <span class="hljs-number">0</span>,
  <span class="hljs-attr">currentTile</span>: <span class="hljs-number">0</span>,
  <span class="hljs-attr">movesQueue</span>: [],
};

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">queueMove</span>(<span class="hljs-params">direction</span>) </span>{
  <span class="hljs-keyword">const</span> isValidMove = endsUpInValidPosition(
    { <span class="hljs-attr">rowIndex</span>: state.currentRow, <span class="hljs-attr">tileIndex</span>: state.currentTile },
    [...state.movesQueue, direction]
  );

  <span class="hljs-keyword">if</span> (!isValidMove) <span class="hljs-keyword">return</span>; <span class="hljs-comment">// Ignore move</span>

  state.movesQueue.push(direction);
}

. . .
</code></pre>
<p>This way, as you can see, you can move around the map – but you can never move before the first row, you can't go too far to the left or too far to the right, and you also can’t go through a tree anymore.</p>
<h2 id="heading-hit-detection"><strong>Hit Detection</strong></h2>
<p>To finish the game, let's add hit detection. We check if the player gets hit by a vehicle, and if so, we show an alert popup.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739826639155/1497d588-bf60-4fdc-a185-a01a781beafb.png" alt="Calculating bounding boxes for hit detection" width="1846" height="1146" loading="lazy"></p>
<p>We add a new hook that checks from the vehicles’ perspective if they hit the player. So far, the player and the vehicles have handled their own movement independently. They have no notion of each other. To handle hit detection, either the player needs to know about the vehicles or the vehicles need to know about the player.</p>
<p>We’ll choose the former approach because this way, we only need to store one reference to the player in the store, and all the vehicles can check against this reference. Let’s extend the player store with a <code>ref</code> property to store the player object’s reference. We also expose a <code>setRef</code> method that sets this reference.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { endsUpInValidPosition } <span class="hljs-keyword">from</span> <span class="hljs-string">"../utilities/endsUpInValidPosition"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> state = {
  <span class="hljs-attr">currentRow</span>: <span class="hljs-number">0</span>,
  <span class="hljs-attr">currentTile</span>: <span class="hljs-number">0</span>,
  <span class="hljs-attr">movesQueue</span>: [],
  <span class="hljs-attr">ref</span>: <span class="hljs-literal">null</span>,
};

. . .

export <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setRef</span>(<span class="hljs-params">ref</span>) </span>{
  state.ref = ref;
}
</code></pre>
<p>Then, we call the <code>setRef</code> method in the <code>Player</code> component to set the reference to the player object. We already have the <code>player</code> reference, so we can pass its value to the <code>setRef</code> method in the <code>useEffect</code> hook once it is set.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useRef, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { useThree } <span class="hljs-keyword">from</span> <span class="hljs-string">"@react-three/fiber"</span>;
<span class="hljs-keyword">import</span> usePlayerAnimation <span class="hljs-keyword">from</span> <span class="hljs-string">"../hooks/usePlayerAnimation"</span>;
<span class="hljs-keyword">import</span> { setRef } <span class="hljs-keyword">from</span> <span class="hljs-string">"../stores/player"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Player</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> player = useRef(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> camera = useThree(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> state.camera);

  usePlayerAnimation(player);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (!player.current) <span class="hljs-keyword">return</span>;

    <span class="hljs-comment">// Attach the camera to the player</span>
    player.current.add(camera);

    <span class="hljs-comment">// Set the player reference in the store</span>
    setRef(player.current);
  });

  <span class="hljs-keyword">return</span> (
    . . .
  );
}
</code></pre>
<p>Then, let’s define another hook to handle hit detection. We check if the player intersects with any of the vehicles. If they do, we end the game.</p>
<p>This hook is from the perspective of a vehicle. It receives the <code>vehicle</code> reference and the <code>rowIndex</code>. We check if the vehicle intersects with the player if the player is in the same row, the row before, or the row after the vehicle. We use the <code>useFrame</code> hook to run the hit detection logic on every frame.</p>
<p>Then we create bounding boxes for the player and the vehicle to check for an intersection. This might be a bit overkill, as the shape of our objects is known, but it is a nice generic way to handle hit detection.</p>
<p>If the bounding boxes intersect, we show an alert. Once the user clicks OK on the alert, we reload the page.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { useFrame } <span class="hljs-keyword">from</span> <span class="hljs-string">"@react-three/fiber"</span>;
<span class="hljs-keyword">import</span> { state <span class="hljs-keyword">as</span> player } <span class="hljs-keyword">from</span> <span class="hljs-string">"../stores/player"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useHitDetection</span>(<span class="hljs-params">vehicle, rowIndex</span>) </span>{
  useFrame(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (!vehicle.current) <span class="hljs-keyword">return</span>;
    <span class="hljs-keyword">if</span> (!player.ref) <span class="hljs-keyword">return</span>;

    <span class="hljs-keyword">if</span> (
      rowIndex === player.currentRow ||
      rowIndex === player.currentRow + <span class="hljs-number">1</span> ||
      rowIndex === player.currentRow - <span class="hljs-number">1</span>
    ) {
      <span class="hljs-keyword">const</span> vehicleBoundingBox = <span class="hljs-keyword">new</span> THREE.Box3();
      vehicleBoundingBox.setFromObject(vehicle.current);

      <span class="hljs-keyword">const</span> playerBoundingBox = <span class="hljs-keyword">new</span> THREE.Box3();
      playerBoundingBox.setFromObject(player.ref);

      <span class="hljs-keyword">if</span> (playerBoundingBox.intersectsBox(vehicleBoundingBox)) {
        <span class="hljs-built_in">window</span>.alert(<span class="hljs-string">"Game over!"</span>);
        <span class="hljs-built_in">window</span>.location.reload();
      }
    }
  });
}
</code></pre>
<p>Finally, we call this hook in the vehicle components. In the <code>Car</code> component, we pass the <code>car</code> reference and the <code>rowIndex</code> to the <code>useHitDetection</code> hook.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useRef } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { tileSize } <span class="hljs-keyword">from</span> <span class="hljs-string">"../constants"</span>;
<span class="hljs-keyword">import</span> useVehicleAnimation <span class="hljs-keyword">from</span> <span class="hljs-string">"../hooks/useVehicleAnimation"</span>;
<span class="hljs-keyword">import</span> useHitDetection <span class="hljs-keyword">from</span> <span class="hljs-string">"../hooks/useHitDetection"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Car</span>(<span class="hljs-params">{
  rowIndex,
  initialTileIndex,
  direction,
  speed,
  color,
}</span>) </span>{
  <span class="hljs-keyword">const</span> car = useRef(<span class="hljs-literal">null</span>);
  useVehicleAnimation(car, direction, speed);
  useHitDetection(car, rowIndex);

  <span class="hljs-keyword">return</span> (
    . . .
  );
}
</code></pre>
<h2 id="heading-next-steps"><strong>Next Steps</strong></h2>
<p>Congratulations, you’ve reached the end of this tutorial, and we’ve covered all the main features of the game. We rendered a map, animated the vehicles, added event handling for the player, and added hit detection.</p>
<p>I hope you had great fun creating this game. This game, of course, is far from perfect, and there are various improvements you can make if you’d like to keep working on it.</p>
<p>You can find the extended tutorial with interactive demos on <a target="_blank" href="http://javascriptgametutorials.com/">JavaScriptGameTutorials.com</a>. There, we also cover how to add shadows and truck lanes and how to generate an infinite number of rows as the player moves forward. We also add UI elements for the controls and the score indicator, and we add a result screen with a button to reset the game.</p>
<p>Alternatively, you can find the extended tutorial on <a target="_blank" href="https://www.youtube.com/watch?v=ccYrSACDNsw&amp;ab_channel=HunorM%C3%A1rtonBorb%C3%A9ly">YouTube</a>.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/ccYrSACDNsw" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Code a Crossy Road Game Clone with Three.js ]]>
                </title>
                <description>
                    <![CDATA[ In this tutorial, you’ll learn how to create a clone of the mobile game Crossy Road with Three.js. The goal of this game is to move a character through an endless path of static and moving obstacles. You have to go around trees and avoid getting hit ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-code-a-crossy-road-game-clone-with-threejs/</link>
                <guid isPermaLink="false">67b7bad6bcd9ef47aba6001d</guid>
                
                    <category>
                        <![CDATA[ ThreeJS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Game Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ javascript game ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Hunor Márton Borbély ]]>
                </dc:creator>
                <pubDate>Thu, 20 Feb 2025 23:29:26 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1740094139765/282432bf-1a5b-40e4-8b74-7aa854fd20ac.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this tutorial, you’ll learn how to create a clone of the mobile game Crossy Road with Three.js. The goal of this game is to move a character through an endless path of static and moving obstacles. You have to go around trees and avoid getting hit by cars.</p>
<p>There's a lot to cover in this tutorial: we will start with setting up the scene, the camera, and the lights. Then you’ll learn how to draw the player and the map with the trees and the cars. We’ll also cover how to animate the vehicles, and we’ll add event handlers to move the player through the map. Finally, we’ll add hit detection between the cars and the player.</p>
<p>This article is a shortened version of the Crossy Road tutorial from my site <a target="_blank" href="https://javascriptgametutorials.com/">JavaScriptGameTutorials.com</a>. The extended tutorial is also available as a video on <a target="_blank" href="https://www.youtube.com/watch?v=vNr3_hQ3Bws&amp;ab_channel=HunorM%C3%A1rtonBorb%C3%A9ly">YouTube</a>.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-the-game">How to Set Up the Game</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-render-a-map">How to Render a Map</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-animate-the-cars">How to Animate the Cars</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-move-the-player">How to Move the Player</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-hit-detection">Hit Detection</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-next-steps">Next Steps</a></p>
</li>
</ol>
<h2 id="heading-how-to-set-up-the-game">How to Set Up the Game</h2>
<p>In this chapter, we’ll set up the drawing canvas, camera, and lights and render a box representing our player.</p>
<h3 id="heading-initializing-the-project">Initializing the Project</h3>
<p>I recommend using Vite to initialize the project. To do so, go to your terminal and type <code>npm create vite</code>, which will create an initial project for you.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Create app</span>
npm create vite my-crossy-road-game

<span class="hljs-comment"># Navigate to the project</span>
<span class="hljs-built_in">cd</span> my-crossy-road-game

<span class="hljs-comment"># Install dependencies</span>
npm install three

<span class="hljs-comment"># Start development server</span>
npm run dev
</code></pre>
<p>When generating the project, select <strong>Vanilla</strong> because we won't use any front-end framework for this project. Then navigate to the project folder Vite just created for you and install Three.js with <code>npm install three</code>. Finally, you can go to the terminal and type <code>npm run dev</code> to start a development server. This way, you can see live the result of your coding in the browser.</p>
<h3 id="heading-the-drawing-canvas">The Drawing Canvas</h3>
<p>Now, let's look into this project. The entry point of this project is the <strong>index.html</strong> file in the root folder. Let's replace the div element with a canvas element with the ID <strong>game</strong>. This is the drawing canvas that Three.js will use to render the scene. This file also has a script tag that points to the main JavaScript file.</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"icon"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"image/svg+xml"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/vite.svg"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Vite App<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">canvas</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"game"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">canvas</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/src/main.ts"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<h3 id="heading-the-mainjs-file">The main.js file</h3>
<p>The <strong>main.js</strong> file is the root of our game. Let's replace its content. We’ll define a Three.js scene containing all the 3D elements, including the player, that we will soon define. The scene also includes a camera that we’ll use together with the renderer to render a static frame of it. We’ll define these in the following steps.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { Renderer } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Renderer"</span>;
<span class="hljs-keyword">import</span> { Camera } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Camera"</span>;
<span class="hljs-keyword">import</span> { player } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Player"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./style.css"</span>;

<span class="hljs-keyword">const</span> scene = <span class="hljs-keyword">new</span> THREE.Scene();
scene.add(player);

<span class="hljs-keyword">const</span> camera = Camera();
player.add(camera);

<span class="hljs-keyword">const</span> renderer = Renderer();
renderer.render(scene, camera);
</code></pre>
<h3 id="heading-the-player">The Player</h3>
<p>Let's start adding the necessary objects to render the first scene. Let's add a simple box to represent the player. We already added the player to the scene in the main file, so let's see how to define this player.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739804077435/9667f94f-5005-41f8-a6ed-faa6706f0be1.png" alt="The player" class="image--center mx-auto" width="3840" height="2160" loading="lazy"></p>
<p>In this file, we write a function that creates a 3D object and exports a property containing the player instance. The player is a singleton. There is only one player object in the game, and every other file can access it through this export.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> player = Player();

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Player</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> player = <span class="hljs-keyword">new</span> THREE.Group();

  <span class="hljs-keyword">const</span> body = <span class="hljs-keyword">new</span> THREE.Mesh(
    <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">15</span>, <span class="hljs-number">15</span>, <span class="hljs-number">20</span>),
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-string">"white"</span> })
  );
  body.position.z = <span class="hljs-number">10</span>;
  player.add(body);

  <span class="hljs-keyword">return</span> player;
}
</code></pre>
<p>Initially, the player will be a simple box. To draw a 3D object, we’ll define a geometry and a material. The geometry defines the object's shape, and the material defines its appearance. Here, we’re using box geometry to define a box. The box geometry takes three arguments: the width, depth, and height of the box along the x, y, and z axes.</p>
<p>We have different options for the material. The main difference between them is how they react to light, if at all. Here, we're using <strong>MeshLambertMaterial</strong>, a simple material that responds to light. We set the color property to white.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739804142917/7b6a49e2-30df-40d8-bd4c-7ac1a2725931.png" alt="Different light options" class="image--center mx-auto" width="3840" height="2160" loading="lazy"></p>
<p>Then, we wrap the geometry and the material into a mesh, which we can add to the scene. We can also position this mesh by setting its X, Y, and Z positions. In the case of a box, these set the center position. By setting the Z position of this box, we’re elevating it above the ground by half its height. As a result, the bottom of the box will be standing on the ground.</p>
<p>We also wrap the mesh into a group element. This is not necessary at this point, but having this structure will be handy when animating the player. When it comes to player animation, we want to separate the horizontal and vertical movement. We want this to be able to follow the player with the camera as it moves but not to move the camera up and down when the player is jumping. We will move the group horizontally along the XY plane together with the camera and move the mesh vertically.</p>
<h3 id="heading-the-camera">The Camera</h3>
<p>Now, let's look into different camera options. There are two main camera options: the perspective camera, as you can see on the left in the image below, and the orthographic camera, which you can see on the right.</p>
<p>The perspective camera is the default camera in Three.js and is the most common camera type across all video games. It creates a perspective projection, which makes things further away appear smaller and things right in front of the camera appear bigger.</p>
<p>On the other hand, the orthographic camera creates parallel projections, which means that objects are the same size regardless of their distance from the camera. We’ll use an orthographic camera here to give our game more of an arcade look.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739800656673/d2643544-b44c-418e-a475-2844e6626dbb.png" alt="Perspective vs orthographic camera" class="image--center mx-auto" width="2420" height="1224" loading="lazy"></p>
<p>In Three.js, we place the 3D objects along the X, Y, and Z axes. We define the coordinate system in a way where the ground is on the XY plane so the player can move left and right along the x-axis, forward and backward along the y-axis, and when the player is jumping, it will go up along the z-axis.</p>
<p>We place the camera in this coordinate system to the right along the x-axis, behind the player along the y-axis, and above the ground. Then, the camera will look back at the origin of the coordinate system to the 0,0,0 coordinate, where the player will be placed initially.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739803742848/d202d09f-1c1b-4246-be23-2a6f6af52423.png" alt="The coordinate system" class="image--center mx-auto" width="1576" height="892" loading="lazy"></p>
<p>With all this theory in mind, let's define our camera. We create a new file for the camera and export the camera function, which returns an orthographic camera that we'll use to render the scene.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Camera</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> size = <span class="hljs-number">300</span>;
  <span class="hljs-keyword">const</span> viewRatio = <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight;
  <span class="hljs-keyword">const</span> width = viewRatio &lt; <span class="hljs-number">1</span> ? size : size * viewRatio;
  <span class="hljs-keyword">const</span> height = viewRatio &lt; <span class="hljs-number">1</span> ? size / viewRatio : size;

  <span class="hljs-keyword">const</span> camera = <span class="hljs-keyword">new</span> THREE.OrthographicCamera(
    width / <span class="hljs-number">-2</span>, <span class="hljs-comment">// left</span>
    width / <span class="hljs-number">2</span>, <span class="hljs-comment">// right</span>
    height / <span class="hljs-number">2</span>, <span class="hljs-comment">// top</span>
    height / <span class="hljs-number">-2</span>, <span class="hljs-comment">// bottom</span>
    <span class="hljs-number">100</span>, <span class="hljs-comment">// near</span>
    <span class="hljs-number">900</span> <span class="hljs-comment">// far</span>
  );

  camera.up.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>);
  camera.position.set(<span class="hljs-number">300</span>, <span class="hljs-number">-300</span>, <span class="hljs-number">300</span>);
  camera.lookAt(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);

  <span class="hljs-keyword">return</span> camera;
}
</code></pre>
<p>To define a camera, we need to define a camera frustum. This will determine how to project the 3D elements onto the screen. In the case of an orthographic camera, we define a box. Everything in the scene within this box will be projected onto the screen. In the image below, the green dot represents the camera position and the gray box around the scene represents the camera frustum.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739804878454/3af6d9ba-1d35-4b98-8731-95af5889a1ed.png" alt="The camera frustum" class="image--center mx-auto" width="1720" height="1292" loading="lazy"></p>
<p>In this function, we set up the camera frustum to fill the browser window, and the width or height will be 300 units, depending on the aspect ratio. The smaller value between width and height will be 300 units, and the other one will fill the available space. If the width is larger than the height, then the height is 300 units. If the height is larger, then the width is 300 units.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739805040485/c7b9aa02-3c5d-4a2e-a1ff-e9941ce83c39.png" alt="Sizing the scene" class="image--center mx-auto" width="3840" height="2160" loading="lazy"></p>
<p>Then, we set the camera's position. We move the camera to the right along the x-axis with 300 units, then behind the player along the y-axis with -300 units, and finally above the ground. We also look back to the origin of the coordinate system, to the 0,0,0 coordinate, where the player is positioned initially. Finally, we set which axis is pointing upwards. Here, we set the z-axis to point upwards.</p>
<h3 id="heading-the-lights">The Lights</h3>
<p>After setting up the camera, let's set up the lights. There are many types of lights in Three.js. Here, we're going to use an ambient light and a directional light.</p>
<p>You can see the result of ambient light only on the left side of the below image. The ambient light brightens the entire scene. It doesn't have a specific position or direction. You can think of it like the light on a cloudy day when it's bright, but there are no shadows. The ambient light is used to simulate indirect light.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739800781538/9e140ef9-4133-4151-9fc5-b629504259a8.png" alt="Ambient vs directional light" class="image--center mx-auto" width="2420" height="1224" loading="lazy"></p>
<p>Now, let's look at the directional light that you can see on the right of the image above. A directional light has a position and a target. It shines light in a specific direction with parallel light rays. Even though it has a position, you can rather think of it as the sun that is shining from very far away. The position here is more to define the direction of the light, but then all the other light rays are also parallel with this light ray. So you can think of it like the sun.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739805160031/4742e1de-5dc0-4443-9716-af18581bfa1e.png" alt="The directional light shines with parallel light rays" class="image--center mx-auto" width="3840" height="2160" loading="lazy"></p>
<p>That's why we're combining an ambient light (so that we have a base brightness all around the scene) with a directional light (to illuminate specific sides of our objects with a brighter color).</p>
<p>After seeing what the lights look like, let's add an ambient and directional light to the scene in our main file. We also position the directional light to the left along the x-axis, behind the player along the y-axis, and above the ground. By default, the target of the directional light is going to be the 0,0,0 coordinate. We don't have to set that.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { Renderer } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Renderer"</span>;
<span class="hljs-keyword">import</span> { Camera } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Camera"</span>;
<span class="hljs-keyword">import</span> { player } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Player"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./style.css"</span>;

<span class="hljs-keyword">const</span> scene = <span class="hljs-keyword">new</span> THREE.Scene();
scene.add(player);

<span class="hljs-keyword">const</span> ambientLight = <span class="hljs-keyword">new</span> THREE.AmbientLight();
scene.add(ambientLight);

<span class="hljs-keyword">const</span> dirLight = <span class="hljs-keyword">new</span> THREE.DirectionalLight();
dirLight.position.set(<span class="hljs-number">-100</span>, <span class="hljs-number">-100</span>, <span class="hljs-number">200</span>);
scene.add(dirLight);

<span class="hljs-keyword">const</span> camera = Camera();
player.add(camera);

<span class="hljs-keyword">const</span> renderer = Renderer();
renderer.render(scene, camera);
</code></pre>
<p>Note that we add the lights to the scene, but we add the camera to the player. This way, when we animate the player, the camera will follow the player.</p>
<h3 id="heading-the-renderer">The Renderer</h3>
<p>We have defined many things, but we still don’t see anything on the screen. As a final piece, we need to have a renderer to render the scene. A renderer renders the 3D scene into a canvas element.</p>
<p>In this function, we get the canvas element we defined in the HTML and set it as the drawing context. We also set a couple more parameters. We make the background of the 3D scene transparent with the alpha flag, set the pixel ratio, and set the size of the canvas to fill the entire screen.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Renderer</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"canvas.game"</span>);
  <span class="hljs-keyword">if</span> (!canvas) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Canvas not found"</span>);

  <span class="hljs-keyword">const</span> renderer = <span class="hljs-keyword">new</span> THREE.WebGLRenderer({
    <span class="hljs-attr">alpha</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">antialias</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">canvas</span>: canvas,
  });
  renderer.setPixelRatio(<span class="hljs-built_in">window</span>.devicePixelRatio);
  renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight);

  <span class="hljs-keyword">return</span> renderer;
}
</code></pre>
<p>This is how our first scene comes together. We rendered a simple box.</p>
<h2 id="heading-how-to-render-a-map">How to Render a Map</h2>
<p>Now, let's add all the other objects to the scene. In this chapter, we’ll define the map. The map will consist of multiple rows, each described by metadata. Each row can be a forest, a car, or a truck lane. We’ll go through each type and define the 3D objects representing them.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739803813951/3796e0c0-6f02-4c82-979b-a5192d8c3b8c.png" alt="The different row types" class="image--center mx-auto" width="2560" height="1442" loading="lazy"></p>
<p>The map can be broken down into rows, and each row can be broken down into multiple tiles. The player will move from tile to tile. Trees are also placed on a distinct tile. Cars, on the other hand, do not relate to tiles. They move freely through the lane.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739803829719/38318821-8496-43d1-9b1c-a5b36d78af14.png" alt="A row can be broken down into a tile" class="image--center mx-auto" width="2560" height="1442" loading="lazy"></p>
<p>We define a file for the constants. Here, we define the number of tiles in each row. In this case, there are 17 ties per row, going from -8 to +8. The player will start in the middle at tile zero.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> minTileIndex = <span class="hljs-number">-8</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> maxTileIndex = <span class="hljs-number">8</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> tilesPerRow = maxTileIndex - minTileIndex + <span class="hljs-number">1</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> tileSize = <span class="hljs-number">42</span>;
</code></pre>
<h3 id="heading-the-starting-row">The Starting Row</h3>
<p>First, let's add the starting row. We’ll define a couple of components that we’re going to use to render the map, and we’ll render the initial row.</p>
<p>Let's create a new component called Map. This file will expose the map's metadata and the 3D objects representing it. Let's export a group called map. This container will contain all the 3D objects for each row. Soon, we will add this group to the scene.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { Grass } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Grass"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> map = <span class="hljs-keyword">new</span> THREE.Group();

<span class="hljs-keyword">const</span> grass = Grass(<span class="hljs-number">0</span>);
map.add(grass);
</code></pre>
<p>Then, we set the map's content. Later, we will generate the 3D objects based on the metadata and use it to render the map. For now, let's just call the Grass function, which will return another Three.js group. We call the grass function with the row index, so the grass component will position itself based on this row index. Then, we add the returned group to the map.</p>
<p>Now, let's define the Grass component. The Grass function returns the foundation and container of the forest rows and is also used for the starting row. It returns a group containing a flat, wide, green box. The dimensions of this box are determined by the constants <strong>tileSize</strong> and <strong>tilesPerRow</strong>. The box also has some height, so it sticks out compared to the road, which will be completely flat.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { tilesPerRow, tileSize } <span class="hljs-keyword">from</span> <span class="hljs-string">"./constants"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Grass</span>(<span class="hljs-params">rowIndex</span>) </span>{
  <span class="hljs-keyword">const</span> grass = <span class="hljs-keyword">new</span> THREE.Group();
  grass.position.y = rowIndex * tileSize;

  <span class="hljs-keyword">const</span> foundation = <span class="hljs-keyword">new</span> THREE.Mesh(
    <span class="hljs-keyword">new</span> THREE.BoxGeometry(tilesPerRow * tileSize, tileSize, <span class="hljs-number">3</span>),
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0xbaf455</span> })
  );
  foundation.position.z = <span class="hljs-number">1.5</span>;
  grass.add(foundation);

  <span class="hljs-keyword">return</span> grass;
}
</code></pre>
<p>The grass can serve as a container for the trees in the row. That's why we wrap the green box into a group so that later, we can also add children to this group. We position the group along the y-axis based on the row index that we received from the Map component. For the initial lane, this is zero, but as we're going to have multiple lanes, we need to place them according to this position.</p>
<p>Now that we have the map container and the grass component, we can finally add the map to the scene.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { Renderer } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Renderer"</span>;
<span class="hljs-keyword">import</span> { Camera } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Camera"</span>;
<span class="hljs-keyword">import</span> { player } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Player"</span>;
<span class="hljs-keyword">import</span> { map } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Map"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./style.css"</span>;

<span class="hljs-keyword">const</span> scene = <span class="hljs-keyword">new</span> THREE.Scene();
scene.add(player);
scene.add(map);

<span class="hljs-keyword">const</span> ambientLight = <span class="hljs-keyword">new</span> THREE.AmbientLight();
scene.add(ambientLight);

<span class="hljs-keyword">const</span> dirLight = <span class="hljs-keyword">new</span> THREE.DirectionalLight();
dirLight.position.set(<span class="hljs-number">-100</span>, <span class="hljs-number">-100</span>, <span class="hljs-number">200</span>);
scene.add(dirLight);

<span class="hljs-keyword">const</span> camera = Camera();
scene.add(camera);

<span class="hljs-keyword">const</span> renderer = Renderer();
renderer.render(scene, camera);
</code></pre>
<h3 id="heading-how-to-add-a-forest-row">How to Add a Forest Row</h3>
<p>Now that we have an empty forest, let's add another row containing trees. We define the map's metadata and render the rows based on this metadata.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739806992172/139197f0-f2c1-4f11-aacf-f1fedeeaf9b0.png" alt="A forest row" class="image--center mx-auto" width="2560" height="1442" loading="lazy"></p>
<p>Back in the Map component, let's define the map's metadata. The metadata is an array of objects that contain information about each row. Each row will contain a type that will determine the kind of the row and the rest of the properties depending on the row type.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { Grass } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Grass"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> metadata = [
  {
    <span class="hljs-attr">type</span>: <span class="hljs-string">"forest"</span>,
    <span class="hljs-attr">trees</span>: [
      { <span class="hljs-attr">tileIndex</span>: <span class="hljs-number">-3</span>, <span class="hljs-attr">height</span>: <span class="hljs-number">50</span> },
      { <span class="hljs-attr">tileIndex</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">height</span>: <span class="hljs-number">30</span> },
      { <span class="hljs-attr">tileIndex</span>: <span class="hljs-number">5</span>, <span class="hljs-attr">height</span>: <span class="hljs-number">50</span> },
    ],
  },
];

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> map = <span class="hljs-keyword">new</span> THREE.Group();

<span class="hljs-keyword">const</span> grass = Grass(<span class="hljs-number">0</span>);
map.add(grass);
</code></pre>
<p>The metadata for a forest includes the type of “forest” and a list of trees. Each tree has a tile index, which represents which tile it is standing on. In this case, we have 17 tiles per row, going from -8 to +8. The trees also have a height, which is actually the height of the crown.</p>
<p>To render the rows, we loop over this array and generate 3D objects for each row based on the row type. For the forest type, it calls the Grass function again, which will return a Three.js group. We call this Grass function with the row index so the Grass function can position itself along the y-axis. The row index is off by one compared to the array index because the first item in the metadata will become the second row right after the starting row, which is not part of the metadata.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { Grass } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Grass"</span>;
<span class="hljs-keyword">import</span> { Tree } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Tree"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> metadata = [
  {
    <span class="hljs-attr">type</span>: <span class="hljs-string">"forest"</span>,
    <span class="hljs-attr">trees</span>: [
      { <span class="hljs-attr">tileIndex</span>: <span class="hljs-number">-3</span>, <span class="hljs-attr">height</span>: <span class="hljs-number">50</span> },
      { <span class="hljs-attr">tileIndex</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">height</span>: <span class="hljs-number">30</span> },
      { <span class="hljs-attr">tileIndex</span>: <span class="hljs-number">5</span>, <span class="hljs-attr">height</span>: <span class="hljs-number">50</span> },
    ],
  },
];

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> map = <span class="hljs-keyword">new</span> THREE.Group();

<span class="hljs-keyword">const</span> grass = Grass(<span class="hljs-number">0</span>);
map.add(grass);

metadata.forEach(<span class="hljs-function">(<span class="hljs-params">rowData, index</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> rowIndex = index + <span class="hljs-number">1</span>;

  <span class="hljs-keyword">if</span> (rowData.type === <span class="hljs-string">"forest"</span>) {
    <span class="hljs-keyword">const</span> row = Grass(rowIndex);

    rowData.trees.forEach(<span class="hljs-function">(<span class="hljs-params">{ tileIndex, height }</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> three = Tree(tileIndex, height);
      row.add(three);
    });

    map.add(row);
  }
});
</code></pre>
<p>Forest rows also have trees. For each item in the trees array, we render a tree. The Tree function will return a 3D object representing the tree.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739807310182/40cbb90b-0356-4db5-97bb-cdbe0c8e8cb8.png" alt="A tree" class="image--center mx-auto" width="2560" height="1442" loading="lazy"></p>
<p>We pass on to this function the tile index that we will use to position the tree within the row and the height. We add the trees to the group the Grass function returned, and then we add the whole group returned by the Grass function to the map.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { tileSize } <span class="hljs-keyword">from</span> <span class="hljs-string">"../constants"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Tree</span>(<span class="hljs-params">tileIndex, height</span>) </span>{
  <span class="hljs-keyword">const</span> tree = <span class="hljs-keyword">new</span> THREE.Group();
  tree.position.x = tileIndex * tileSize;

  <span class="hljs-keyword">const</span> trunk = <span class="hljs-keyword">new</span> THREE.Mesh(
    <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">15</span>, <span class="hljs-number">15</span>, <span class="hljs-number">20</span>),
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0x4d2926</span> })
  );
  trunk.position.z = <span class="hljs-number">10</span>;
  tree.add(trunk);

  <span class="hljs-keyword">const</span> crown = <span class="hljs-keyword">new</span> THREE.Mesh(
    <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">30</span>, <span class="hljs-number">30</span>, height),
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0x7aa21d</span> })
  );
  crown.position.z = height / <span class="hljs-number">2</span> + <span class="hljs-number">20</span>;
  tree.add(crown);

  <span class="hljs-keyword">return</span> tree;
}
</code></pre>
<p>Since we’ve already added the map to the scene, the forest will appear on the screen. But first, we need to define how to render a tree. We are going to represent a tree with two boxes. We're going to have a box for the trunk and one for the crown.</p>
<p>These are both simple boxes, just like we had before with the player and also in the Grass component. The trunk is placed on top of the ground. We lift it along the Z-axis by half of its height, and the crown is placed on top of the trunk. The crown's height is also based on the height property. These two meshes are wrapped together into a group, and then we position this group along the X-axis based on the tile index property.</p>
<h3 id="heading-car-lanes">Car Lanes</h3>
<p>Now, let's add another row type: car lanes. The process of adding car lanes will follow a similar structure. We define the lanes' metadata, including the vehicles, and then map them into 3D objects.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739807532566/1c9cfdc9-2c45-476c-9ada-09f7abf79328.png" alt="The car lane" class="image--center mx-auto" width="2560" height="1442" loading="lazy"></p>
<p>In the Map component in the metadata, let's replace the first row with a car lane. The car lane will contain a single red car moving to the left. We have a direction property, which is a boolean flag. If this is true, that means the cars are moving to the right in the lane, and if it's false, then the vehicles are moving to the left. We also have a speed property, which defines how many units each vehicle takes every second.</p>
<p>Finally, we have an array of vehicles. Each car will have an initial tile index, which represents only its initial position because the cars will move later. Each car will also have a color property, which is a hexadecimal color value.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { Grass } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Grass"</span>;
<span class="hljs-keyword">import</span> { Tree } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Tree"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> metadata = [
  {
    <span class="hljs-attr">type</span>: <span class="hljs-string">"car"</span>,
    <span class="hljs-attr">direction</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">speed</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">vehicles</span>: [{ <span class="hljs-attr">initialTileIndex</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">color</span>: <span class="hljs-number">0xff0000</span> }],
  },
];

. . .
</code></pre>
<p>Now, to render this lane type, we have to extend our logic to support car lanes. We add another if block that is very similar to the rendering of the forest. In the case of a car type, we call the Road function, which will also return a Three.js group. We also call this function with the row index to position the group according to the lane.</p>
<p>Then, for each item in the vehicles array, we create a 3D object representing the car with the Car function. We add the cars to the group returned by the Road function, and we add the whole group to the map. For the car function, we also pass on the initial tile index that we will use to position the car within the row, the direction, and the color.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { Grass } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Grass"</span>;
<span class="hljs-keyword">import</span> { Road } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Road"</span>;
<span class="hljs-keyword">import</span> { Tree } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Tree"</span>;
<span class="hljs-keyword">import</span> { Car } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Car"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> metadata = [
  {
    <span class="hljs-attr">type</span>: <span class="hljs-string">"car"</span>,
    <span class="hljs-attr">direction</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">speed</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">vehicles</span>: [{ <span class="hljs-attr">initialTileIndex</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">color</span>: <span class="hljs-number">0xff0000</span> }],
  },
];

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> map = <span class="hljs-keyword">new</span> THREE.Group();

<span class="hljs-keyword">const</span> grass = Grass(<span class="hljs-number">0</span>);
map.add(grass);

metadata.forEach(<span class="hljs-function">(<span class="hljs-params">rowData, index</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> rowIndex = index + <span class="hljs-number">1</span>;

  <span class="hljs-keyword">if</span> (rowData.type === <span class="hljs-string">"forest"</span>) {
    <span class="hljs-keyword">const</span> row = Grass(rowIndex);

    rowData.trees.forEach(<span class="hljs-function">(<span class="hljs-params">{ tileIndex, height }</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> three = Tree(tileIndex, height);
      row.add(three);
    });

    map.add(row);
  }

  <span class="hljs-keyword">if</span> (rowData.type === <span class="hljs-string">"car"</span>) {
    <span class="hljs-keyword">const</span> row = Road(rowIndex);

    rowData.vehicles.forEach(<span class="hljs-function">(<span class="hljs-params">vehicle</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> car = Car(
        vehicle.initialTileIndex,
        rowData.direction,
        vehicle.color
      );
      row.add(car);
    });

    map.add(row);
  }
});
</code></pre>
<p>The Road and Car functions are new here, so let's examine them next. The Road function returns the foundation and container of the car and truck lanes. Similar to the Grass function, it also returns a group containing a gray plane.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739807755293/e731a758-ac20-40f5-819b-82a5ae81e65f.png" alt="The Road component" class="image--center mx-auto" width="2560" height="1442" loading="lazy"></p>
<p>The size of the plane is also determined by the constants <strong>tileSize</strong> and <strong>tilesPerRow</strong>. Unlike the grass function, though, it doesn't have any height. It's completely flat. The road will also serve as a container for the cars and trucks in the row, so that's why we wrap the plane into a group – so that we can add children to it.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { tilesPerRow, tileSize } <span class="hljs-keyword">from</span> <span class="hljs-string">"../constants"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Road</span>(<span class="hljs-params">rowIndex</span>) </span>{
  <span class="hljs-keyword">const</span> road = <span class="hljs-keyword">new</span> THREE.Group();
  road.position.y = rowIndex * tileSize;

  <span class="hljs-keyword">const</span> foundation = <span class="hljs-keyword">new</span> THREE.Mesh(
    <span class="hljs-keyword">new</span> THREE.PlaneGeometry(tilesPerRow * tileSize, tileSize),
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0x454a59</span> })
  );
  road.add(foundation);

  <span class="hljs-keyword">return</span> road;
}
</code></pre>
<p>Now, let's look at the Car. The Car function returns a very simple 3D car model.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739807975022/dc04c3c1-de94-49fd-ad85-ade999c8862a.png" alt="A car" class="image--center mx-auto" width="2560" height="1442" loading="lazy"></p>
<p>It contains a box for the body and a smaller box for the top part. We also have two wheel meshes. Because we never see the cars from underneath, we don't need to separate the wheels into left and right. We can just use one long box for the front wheels and another one for the back wheels.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { tileSize } <span class="hljs-keyword">from</span> <span class="hljs-string">"./constants"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Car</span>(<span class="hljs-params">initialTileIndex, direction, color</span>) </span>{
  <span class="hljs-keyword">const</span> car = <span class="hljs-keyword">new</span> THREE.Group();
  car.position.x = initialTileIndex * tileSize;
  <span class="hljs-keyword">if</span> (!direction) car.rotation.z = <span class="hljs-built_in">Math</span>.PI;

  <span class="hljs-keyword">const</span> main = <span class="hljs-keyword">new</span> THREE.Mesh(
    <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">60</span>, <span class="hljs-number">30</span>, <span class="hljs-number">15</span>),
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ color })
  );
  main.position.z = <span class="hljs-number">12</span>;
  car.add(main);

  <span class="hljs-keyword">const</span> cabin = <span class="hljs-keyword">new</span> THREE.Mesh(
    <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">33</span>, <span class="hljs-number">24</span>, <span class="hljs-number">12</span>),
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-string">"white"</span> })
  );
  cabin.position.x = <span class="hljs-number">-6</span>;
  cabin.position.z = <span class="hljs-number">25.5</span>;
  car.add(cabin);

  <span class="hljs-keyword">const</span> frontWheel = <span class="hljs-keyword">new</span> THREE.Mesh(
    <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">12</span>, <span class="hljs-number">33</span>, <span class="hljs-number">12</span>),
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0x333333</span> })
  );
  frontWheel.position.x = <span class="hljs-number">18</span>;
  frontWheel.position.z = <span class="hljs-number">6</span>;
  car.add(frontWheel);

  <span class="hljs-keyword">const</span> backWheel = <span class="hljs-keyword">new</span> THREE.Mesh(
    <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">12</span>, <span class="hljs-number">33</span>, <span class="hljs-number">12</span>),
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0x333333</span> })
  );
  backWheel.position.x = <span class="hljs-number">-18</span>;
  backWheel.position.z = <span class="hljs-number">6</span>;
  car.add(backWheel);

  <span class="hljs-keyword">return</span> car;
}
</code></pre>
<p>We group all these elements, position them based on the <strong>initialTileIndex</strong> property, and turn them based on the <strong>direction</strong> property. If the car goes to the left, we rotate it by 180°. When we set rotation values in Three.js. We have to set them in radians, so that's why we set it to Math.Pi, which is equivalent to 180°.</p>
<p>You can also find a more extended version of how to draw this car with textures <a target="_blank" href="https://www.freecodecamp.org/news/three-js-tutorial/">in this article</a>.</p>
<p>Based on the metadata, we can now render a map with several rows. Here’s an example with a few more lanes. Of course, feel free to define your own map.</p>
<pre><code class="lang-javascript">. . .

export <span class="hljs-keyword">const</span> metadata = [
  {
    <span class="hljs-attr">type</span>: <span class="hljs-string">"car"</span>,
    <span class="hljs-attr">direction</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">speed</span>: <span class="hljs-number">188</span>,
    <span class="hljs-attr">vehicles</span>: [
      { <span class="hljs-attr">initialTileIndex</span>: <span class="hljs-number">-4</span>, <span class="hljs-attr">color</span>: <span class="hljs-number">0xbdb638</span> },
      { <span class="hljs-attr">initialTileIndex</span>: <span class="hljs-number">-1</span>, <span class="hljs-attr">color</span>: <span class="hljs-number">0x78b14b</span> },
      { <span class="hljs-attr">initialTileIndex</span>: <span class="hljs-number">4</span>, <span class="hljs-attr">color</span>: <span class="hljs-number">0xa52523</span> },
    ],
  },
  {
    <span class="hljs-attr">type</span>: <span class="hljs-string">"forest"</span>,
    <span class="hljs-attr">trees</span>: [
      { <span class="hljs-attr">tileIndex</span>: <span class="hljs-number">-5</span>, <span class="hljs-attr">height</span>: <span class="hljs-number">50</span> },
      { <span class="hljs-attr">tileIndex</span>: <span class="hljs-number">0</span>, <span class="hljs-attr">height</span>: <span class="hljs-number">30</span> },
      { <span class="hljs-attr">tileIndex</span>: <span class="hljs-number">3</span>, <span class="hljs-attr">height</span>: <span class="hljs-number">50</span> },
    ],
  },
  {
    <span class="hljs-attr">type</span>: <span class="hljs-string">"car"</span>,
    <span class="hljs-attr">direction</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">speed</span>: <span class="hljs-number">125</span>,
    <span class="hljs-attr">vehicles</span>: [
      { <span class="hljs-attr">initialTileIndex</span>: <span class="hljs-number">-4</span>, <span class="hljs-attr">color</span>: <span class="hljs-number">0x78b14b</span> },
      { <span class="hljs-attr">initialTileIndex</span>: <span class="hljs-number">0</span>, <span class="hljs-attr">color</span>: <span class="hljs-number">0xbdb638</span> },
      { <span class="hljs-attr">initialTileIndex</span>: <span class="hljs-number">5</span>, <span class="hljs-attr">color</span>: <span class="hljs-number">0xbdb638</span> },
    ],
  },
  {
    <span class="hljs-attr">type</span>: <span class="hljs-string">"forest"</span>,
    <span class="hljs-attr">trees</span>: [
      { <span class="hljs-attr">tileIndex</span>: <span class="hljs-number">-8</span>, <span class="hljs-attr">height</span>: <span class="hljs-number">30</span> },
      { <span class="hljs-attr">tileIndex</span>: <span class="hljs-number">-3</span>, <span class="hljs-attr">height</span>: <span class="hljs-number">50</span> },
      { <span class="hljs-attr">tileIndex</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">height</span>: <span class="hljs-number">30</span> },
    ],
  },
];

. . .
</code></pre>
<p>This article does not cover truck lanes, but they follow a similar structure. The code for it can be found at <a target="_blank" href="http://JavaScriptGameTutorials.com">JavaScriptGameTutorials.com</a>.</p>
<h2 id="heading-how-to-animate-the-cars">How to Animate the Cars</h2>
<p>Let's move on and animate the cars in their lanes according to their speed and direction. To move the vehicles, we first need to be able to access them. So far, we have added them to the scene, and theoretically, we could traverse the scene and figure out which object represents a vehicle. But it's much easier to collect their references in our metadata and access them through these references.</p>
<p>Let's modify the Map generation. After generating a car, we not only add them to the container group but also save the reference together with their metadata. After this, we can go to the metadata and access each vehicle in the scene.</p>
<pre><code class="lang-javascript">. . .

metadata.forEach(<span class="hljs-function">(<span class="hljs-params">rowData, index</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> rowIndex = index + <span class="hljs-number">1</span>;

  <span class="hljs-keyword">if</span> (rowData.type === <span class="hljs-string">"forest"</span>) {
    <span class="hljs-keyword">const</span> row = Grass(rowIndex);

    rowData.trees.forEach(<span class="hljs-function">(<span class="hljs-params">{ tileIndex, height }</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> three = Tree(tileIndex, height);
      row.add(three);
    });

    map.add(row);
  }

  <span class="hljs-keyword">if</span> (rowData.type === <span class="hljs-string">"car"</span>) {
    <span class="hljs-keyword">const</span> row = Road(rowIndex);

    rowData.vehicles.forEach(<span class="hljs-function">(<span class="hljs-params">vehicle</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> car = Car(
        vehicle.initialTileIndex,
        rowData.direction,
        vehicle.color
      );
      vehicle.ref = car; <span class="hljs-comment">// Add a reference to the car object in metadata</span>
      row.add(car);
    });

    map.add(row);
  }
});
</code></pre>
<p>Next, let's go to the main file and define an animate function that will be called on every animation frame. For now, we only call the <strong>animateVehicles</strong> function, which we will define next. Later, we will extend this function with logic to animate the player and to have hit detection.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { Renderer } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Renderer"</span>;
<span class="hljs-keyword">import</span> { Camera } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Camera"</span>;
<span class="hljs-keyword">import</span> { player } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Player"</span>;
<span class="hljs-keyword">import</span> { map } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Map"</span>;
<span class="hljs-keyword">import</span> { animateVehicles } <span class="hljs-keyword">from</span> <span class="hljs-string">"./animateVehicles"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./style.css"</span>;

<span class="hljs-keyword">const</span> scene = <span class="hljs-keyword">new</span> THREE.Scene();
scene.add(player);
scene.add(map);

<span class="hljs-keyword">const</span> ambientLight = <span class="hljs-keyword">new</span> THREE.AmbientLight();
scene.add(ambientLight);

<span class="hljs-keyword">const</span> dirLight = <span class="hljs-keyword">new</span> THREE.DirectionalLight();
dirLight.position.set(<span class="hljs-number">-100</span>, <span class="hljs-number">-100</span>, <span class="hljs-number">200</span>);
scene.add(dirLight);

<span class="hljs-keyword">const</span> camera = Camera();
scene.add(camera);

<span class="hljs-keyword">const</span> renderer = Renderer();
renderer.setAnimationLoop(animate);

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params"></span>) </span>{
  animateVehicles();

  renderer.render(scene, camera);
}
</code></pre>
<p>We also move the renderer render call here to render the scene on every animation loop. To call this function on every frame, we pass it on to the renderer’s <strong>setAnimationLoop</strong> function. This is similar to <strong>requestAnimationFrame</strong> in plain JavaScript, except that it calls itself at the end of the function, so we don't have to call it again.</p>
<p>Now, let's implement the <strong>animateVehicles</strong> function. As this function is part of the animate function, this function is called on every animation frame. Here, we use a Three.js clock to calculate how much time passed between the animation frames. Then, we loop over the metadata, take every vehicle from every car or truck lane, and move them along the x-axis based on their speed, direction, and the time passed.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { metadata <span class="hljs-keyword">as</span> rows } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Map"</span>;
<span class="hljs-keyword">import</span> { minTileIndex, maxTileIndex, tileSize } <span class="hljs-keyword">from</span> <span class="hljs-string">"./constants"</span>;

<span class="hljs-keyword">const</span> clock = <span class="hljs-keyword">new</span> THREE.Clock();

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animateVehicles</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> delta = clock.getDelta();

  <span class="hljs-comment">// Animate cars and trucks</span>
  rows.forEach(<span class="hljs-function">(<span class="hljs-params">rowData</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (rowData.type === <span class="hljs-string">"car"</span> || rowData.type === <span class="hljs-string">"truck"</span>) {
      <span class="hljs-keyword">const</span> beginningOfRow = (minTileIndex - <span class="hljs-number">2</span>) * tileSize;
      <span class="hljs-keyword">const</span> endOfRow = (maxTileIndex + <span class="hljs-number">2</span>) * tileSize;

      rowData.vehicles.forEach(<span class="hljs-function">(<span class="hljs-params">{ ref }</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (!ref) <span class="hljs-keyword">throw</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Vehicle reference is missing"</span>);

        <span class="hljs-keyword">if</span> (rowData.direction) {
          ref.position.x =
            ref.position.x &gt; endOfRow
              ? beginningOfRow
              : ref.position.x + rowData.speed * delta;
        } <span class="hljs-keyword">else</span> {
          ref.position.x =
            ref.position.x &lt; beginningOfRow
              ? endOfRow
              : ref.position.x - rowData.speed * delta;
        }
      });
    }
  });
}
</code></pre>
<p>If a car reaches the end of the lane, we respawn it at the other end, depending on its direction. This creates an infinite loop in which cars go from left to right or right to left, depending on their direction. Once they reach the end of the lane, they start over from the beginning. With this function, we should have a scene where all the cars are moving in their lanes.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739808359961/eca84295-09d7-463a-8d29-e5f8069bb673.png" alt="The cars move in an infinite loop" class="image--center mx-auto" width="3840" height="2160" loading="lazy"></p>
<h2 id="heading-how-to-move-the-player">How to Move the Player</h2>
<p>Now, let's move on to animating the player. Moving the player on the map is more complex than moving the vehicles. The player can move in all directions, bump into trees, or get hit by cars, and it shouldn't be able to move outside the map.</p>
<p>In this chapter, we are focusing on two parts: collecting user inputs and executing the movement commands. Player movement is not instant – we need to collect the movement commands into a queue and execute them one by one. We are going to collect user inputs and put them into a queue.</p>
<h3 id="heading-collecting-user-inputs">Collecting User Inputs</h3>
<p>To check the movement commands, let's extend the player component with state. We keep track of the player's position and the movement queue. The player starts at the middle of the first row, and the move queue is initially empty.</p>
<p>We will also export two functions: <strong>queueMove</strong> adds the movement command to the end of the move queue, and the <strong>stepCompleted</strong> function removes the first movement command from the queue and updates the player's position accordingly.</p>
<pre><code class="lang-javascript">. . .

export <span class="hljs-keyword">const</span> position = {
  <span class="hljs-attr">currentRow</span>: <span class="hljs-number">0</span>,
  <span class="hljs-attr">currentTile</span>: <span class="hljs-number">0</span>,
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> movesQueue = [];

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">queueMove</span>(<span class="hljs-params">direction</span>) </span>{
  movesQueue.push(direction);
}

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">stepCompleted</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> direction = movesQueue.shift();

  <span class="hljs-keyword">if</span> (direction === <span class="hljs-string">"forward"</span>) position.currentRow += <span class="hljs-number">1</span>;
  <span class="hljs-keyword">if</span> (direction === <span class="hljs-string">"backward"</span>) position.currentRow -= <span class="hljs-number">1</span>;
  <span class="hljs-keyword">if</span> (direction === <span class="hljs-string">"left"</span>) position.currentTile -= <span class="hljs-number">1</span>;
  <span class="hljs-keyword">if</span> (direction === <span class="hljs-string">"right"</span>) position.currentTile += <span class="hljs-number">1</span>;
}
</code></pre>
<p>Now, we can add event listeners for keyboard events to listen to the arrow keys. They all call the player's <strong>queueMove</strong> function, which we just defined for the player with the corresponding direction.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { queueMove } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Player"</span>;

<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"keydown"</span>, <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (event.key === <span class="hljs-string">"ArrowUp"</span>) {
    queueMove(<span class="hljs-string">"forward"</span>);
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (event.key === <span class="hljs-string">"ArrowDown"</span>) {
    queueMove(<span class="hljs-string">"backward"</span>);
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (event.key === <span class="hljs-string">"ArrowLeft"</span>) {
    queueMove(<span class="hljs-string">"left"</span>);
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (event.key === <span class="hljs-string">"ArrowRight"</span>) {
    queueMove(<span class="hljs-string">"right"</span>);
  }
});
</code></pre>
<p>After defining the event listeners, we also have to import them into the main file so that they work.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { Renderer } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Renderer"</span>;
<span class="hljs-keyword">import</span> { Camera } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Camera"</span>;
<span class="hljs-keyword">import</span> { player } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Player"</span>;
<span class="hljs-keyword">import</span> { map } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Map"</span>;
<span class="hljs-keyword">import</span> { animateVehicles } <span class="hljs-keyword">from</span> <span class="hljs-string">"./animateVehicles"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./style.css"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./collectUserInput"</span>; <span class="hljs-comment">// Import event listeners</span>

. . .
</code></pre>
<h3 id="heading-executing-movement-commands">Executing Movement Commands</h3>
<p>So far, we have collected user inputs and put each command into the <strong>movesQueue</strong> array in the player component. Now, it's time to execute these commands one by one and animate the player.</p>
<p>Let's create a new function called <strong>animatePlayer</strong>. Its main goal is to take each move command from the <strong>moveQueue</strong> one by one, calculate the player's progress toward executing a step, and position the player accordingly.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739808997988/8adb70d2-bf5d-4e65-8ed9-29e8f297b487.png" alt="The player movement" class="image--center mx-auto" width="1590" height="892" loading="lazy"></p>
<p>This function animates the player frame by frame. It will also be part of the animate function. We also use a separate move clock that measures each step individually. We pass on false to the clock constructor so it doesn't start automatically. The clock only starts at the beginning of a step. At each animation frame, first, we check if there are any more steps to take, and if there are and we don't currently process a step, then we can start the clock. Once the clock is ticking we animate the player from tile to tile with each step.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { movesQueue, stepCompleted } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Player"</span>;

<span class="hljs-keyword">const</span> moveClock = <span class="hljs-keyword">new</span> THREE.Clock(<span class="hljs-literal">false</span>);

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animatePlayer</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">if</span> (!movesQueue.length) <span class="hljs-keyword">return</span>;

  <span class="hljs-keyword">if</span> (!moveClock.running) moveClock.start();

  <span class="hljs-keyword">const</span> stepTime = <span class="hljs-number">0.2</span>; <span class="hljs-comment">// Seconds it takes to take a step</span>
  <span class="hljs-keyword">const</span> progress = <span class="hljs-built_in">Math</span>.min(<span class="hljs-number">1</span>, moveClock.getElapsedTime() / stepTime);

  setPosition(progress);

  <span class="hljs-comment">// Once a step has ended</span>
  <span class="hljs-keyword">if</span> (progress &gt;= <span class="hljs-number">1</span>) {
    stepCompleted();
    moveClock.stop();
  }
}

. . .
</code></pre>
<p>We use the move clock to calculate the progress between the two tiles. The progress indicator can be a number between zero and one. Zero means that the player is still at the beginning of the step, and one means that it’s arrived at its new position.</p>
<p>At each animation frame, we call the <strong>setPosition</strong> function to set the player’s position according to the progress. Once we finish a step, we call the <strong>stepCompleted</strong> function to update the player's position and stop the clock. If there are any more move commands in the <strong>movesQueue</strong>, the clock will restart in the following animation frame.</p>
<p>Now that we know how to calculate the progress for each step, let's look into how to set the player's position based on the progress. The player will jump from tile to tile. Let's break this down into two parts: the movement's horizontal and vertical components.</p>
<p>The player moves from the current tile to the next tile in the direction of the move command. We calculate the player's start and end position based on the current tile and the direction of the move command. Then, we use linear interpolation with a utility function that Three.js provides. This will interpolate between the start and end positions based on the progress.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> {
  player,
  position,
  movesQueue,
  stepCompleted,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Player"</span>;
<span class="hljs-keyword">import</span> { tileSize } <span class="hljs-keyword">from</span> <span class="hljs-string">"./constants"</span>;

. . .

function setPosition(progress) {
  <span class="hljs-keyword">const</span> startX = position.currentTile * tileSize;
  <span class="hljs-keyword">const</span> startY = position.currentRow * tileSize;
  <span class="hljs-keyword">let</span> endX = startX;
  <span class="hljs-keyword">let</span> endY = startY;

  <span class="hljs-keyword">if</span> (movesQueue[<span class="hljs-number">0</span>] === <span class="hljs-string">"left"</span>) endX -= tileSize;
  <span class="hljs-keyword">if</span> (movesQueue[<span class="hljs-number">0</span>] === <span class="hljs-string">"right"</span>) endX += tileSize;
  <span class="hljs-keyword">if</span> (movesQueue[<span class="hljs-number">0</span>] === <span class="hljs-string">"forward"</span>) endY += tileSize;
  <span class="hljs-keyword">if</span> (movesQueue[<span class="hljs-number">0</span>] === <span class="hljs-string">"backward"</span>) endY -= tileSize;

  player.position.x = THREE.MathUtils.lerp(startX, endX, progress);
  player.position.y = THREE.MathUtils.lerp(startY, endY, progress);
  player.children[<span class="hljs-number">0</span>].position.z = <span class="hljs-built_in">Math</span>.sin(progress * <span class="hljs-built_in">Math</span>.PI) * <span class="hljs-number">8</span> + <span class="hljs-number">10</span>;
}
</code></pre>
<p>For the vertical component, we use a sine function to make it look like jumping. We are basically mapping the progress to the first part of a sine wave.</p>
<p>Below you can see what a sine wave looks like. It goes from 0 to 2 Pi. So if you multiply the progress value, which is going from 0 to 1 with Pi, then the progress will map into the first half of this sine wave. The sign function then will give us a value between zero and one.</p>
<p>To make the jump look higher, we can multiply this with a value. In this case, we multiply the result of the sine function by eight, so as a result, the player will have a jump where the maximum height of the jump will be eight units.</p>
<p>We also need to add the original Z position to the value – otherwise, the player will sink halfway into the ground after the first step.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739810397620/89119f6e-ced5-4cdb-8254-96fbf66479ed.png" alt="For the vertical movement we use a sine wave" class="image--center mx-auto" width="2032" height="1146" loading="lazy"></p>
<p>Now that we’ve defined the <strong>animatePlayer</strong> function, let's add it to the animate loop.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { Renderer } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Renderer"</span>;
<span class="hljs-keyword">import</span> { Camera } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Camera"</span>;
<span class="hljs-keyword">import</span> { DirectionalLight } <span class="hljs-keyword">from</span> <span class="hljs-string">"./DirectionalLight"</span>;
<span class="hljs-keyword">import</span> { player } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Player"</span>;
<span class="hljs-keyword">import</span> { map, initializeMap } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Map"</span>;
<span class="hljs-keyword">import</span> { animateVehicles } <span class="hljs-keyword">from</span> <span class="hljs-string">"./animateVehicles"</span>;
<span class="hljs-keyword">import</span> { animatePlayer } <span class="hljs-keyword">from</span> <span class="hljs-string">"./animatePlayer"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./style.css"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./collectUserInput"</span>;

. . .

function animate() {
  animateVehicles();
  animatePlayer();

  renderer.render(scene, camera);
}
</code></pre>
<p>If you did everything right, the player should be able to move around the game board, moving forward, backward, left, and right. But we haven't added any hit detection. So far, the player can move through trees and vehicles and even get off the game board. Let's fix these issues in the following steps.</p>
<h3 id="heading-restricting-player-movement">Restricting Player Movement</h3>
<p>Let’s make sure that the player can’t end up in a position that’s invalid. We will check if a move is valid by calculating where it will take the player. If the player would end up in a position outside of the map or in a tile occupied by a tree, we will ignore that move command.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739810555283/310afd32-6e5a-4143-bbe2-e0da558bd6d7.png" alt="Calculating where the player will end up" class="image--center mx-auto" width="1662" height="1146" loading="lazy"></p>
<p>First, we need to calculate where the player would end up if they made a particular move. Whenever we add a new move to the queue, we need to calculate where the player would end up if they made all the moves in the queue and take the current move command. We create a utility function that takes the player's current position and an array of moves and returns the player's final position.</p>
<p>For instance, if the player's current position is 0,0, staying in the middle of the first row, and the moves are forward and left, then the final position will be row 1 tile -1.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">calculateFinalPosition</span>(<span class="hljs-params">currentPosition, moves</span>) </span>{
  <span class="hljs-keyword">return</span> moves.reduce(<span class="hljs-function">(<span class="hljs-params">position, direction</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (direction === <span class="hljs-string">"forward"</span>)
      <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">rowIndex</span>: position.rowIndex + <span class="hljs-number">1</span>,
        <span class="hljs-attr">tileIndex</span>: position.tileIndex,
      };
    <span class="hljs-keyword">if</span> (direction === <span class="hljs-string">"backward"</span>)
      <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">rowIndex</span>: position.rowIndex - <span class="hljs-number">1</span>,
        <span class="hljs-attr">tileIndex</span>: position.tileIndex,
      };
    <span class="hljs-keyword">if</span> (direction === <span class="hljs-string">"left"</span>)
      <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">rowIndex</span>: position.rowIndex,
        <span class="hljs-attr">tileIndex</span>: position.tileIndex - <span class="hljs-number">1</span>,
      };
    <span class="hljs-keyword">if</span> (direction === <span class="hljs-string">"right"</span>)
      <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">rowIndex</span>: position.rowIndex,
        <span class="hljs-attr">tileIndex</span>: position.tileIndex + <span class="hljs-number">1</span>,
      };
    <span class="hljs-keyword">return</span> position;
  }, currentPosition);
}
</code></pre>
<p>Now that we have this utility function to calculate where the player will end up after taking a move, let's create another utility function to calculate whether the player would end up in a valid or invalid position. In this function, we use the <strong>calculateFinalPosition</strong> function that we just created. Then, we’ll check if the player would end up outside the map or on a tile occupied by a tree.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739810684106/71c45122-b10c-4623-b575-184c1cf1fecb.png" alt="Check if the player bumps into a tree" class="image--center mx-auto" width="1846" height="1146" loading="lazy"></p>
<p>If the move is invalid, we return false. First, we check if the final position is before the starting row or if the tile number is outside the range of the tiles. Then, we check the metadata of the row the player will end up in. Here, the index is off by one because the row metadata doesn't include the starting row. If we end up in a forest row, we check whether a tree occupies the tile we move to. If any of this is true, we return false.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { calculateFinalPosition } <span class="hljs-keyword">from</span> <span class="hljs-string">"./calculateFinalPosition"</span>;
<span class="hljs-keyword">import</span> { minTileIndex, maxTileIndex } <span class="hljs-keyword">from</span> <span class="hljs-string">"./constants"</span>;
<span class="hljs-keyword">import</span> { metadata <span class="hljs-keyword">as</span> rows } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Map"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">endsUpInValidPosition</span>(<span class="hljs-params">currentPosition, moves</span>) </span>{
  <span class="hljs-comment">// Calculate where the player would end up after the move</span>
  <span class="hljs-keyword">const</span> finalPosition = calculateFinalPosition(
    currentPosition,
    moves
  );

  <span class="hljs-comment">// Detect if we hit the edge of the board</span>
  <span class="hljs-keyword">if</span> (
    finalPosition.rowIndex === <span class="hljs-number">-1</span> ||
    finalPosition.tileIndex === minTileIndex - <span class="hljs-number">1</span> ||
    finalPosition.tileIndex === maxTileIndex + <span class="hljs-number">1</span>
  ) {
    <span class="hljs-comment">// Invalid move, ignore move command</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
  }

  <span class="hljs-comment">// Detect if we hit a tree</span>
  <span class="hljs-keyword">const</span> finalRow = rows[finalPosition.rowIndex - <span class="hljs-number">1</span>];
  <span class="hljs-keyword">if</span> (
    finalRow &amp;&amp;
    finalRow.type === <span class="hljs-string">"forest"</span> &amp;&amp;
    finalRow.trees.some(
      <span class="hljs-function">(<span class="hljs-params">tree</span>) =&gt;</span> tree.tileIndex === finalPosition.tileIndex
    )
  ) {
    <span class="hljs-comment">// Invalid move, ignore move command</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
  }

  <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
}
</code></pre>
<p>Finally, let's extend the player's <strong>queueMove</strong> function with the <strong>endsUpInValidPosition</strong> function to check if a move is valid. If the <strong>endsUpInValidPosition</strong> function returns false, we cannot take this step. In this case, we return early from the function before the move is added to the <strong>movesQueue</strong> array. So we are ignoring the move.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { endsUpInValidPosition } <span class="hljs-keyword">from</span> <span class="hljs-string">"./endsUpInValidPosition"</span>;

. . .

export <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">queueMove</span>(<span class="hljs-params">direction</span>) </span>{
  <span class="hljs-keyword">const</span> isValidMove = endsUpInValidPosition(
    {
      <span class="hljs-attr">rowIndex</span>: position.currentRow,
      <span class="hljs-attr">tileIndex</span>: position.currentTile,
    },
    [...movesQueue, direction]
  );

  <span class="hljs-keyword">if</span> (!isValidMove) <span class="hljs-keyword">return</span>; <span class="hljs-comment">// Return if the move is invalid</span>

  movesQueue.push(direction);
}

. . .
</code></pre>
<p>This way, as you can see, you can move around the map – but you can never move before the first row, you can't go too far to the left or too far to the right, and you also can’t go through a tree anymore.</p>
<h2 id="heading-hit-detection">Hit Detection</h2>
<p>To finish the game, let's add hit detection. We check if the player gets hit by a vehicle, and if so, we show an alert popup.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739826639155/1497d588-bf60-4fdc-a185-a01a781beafb.png" alt="Calculating bounding boxes for hit detection" class="image--center mx-auto" width="1846" height="1146" loading="lazy"></p>
<p>Let's define another function to define hit detection. We check if the player intersects with any of the vehicles. In this function, we check which row the player is currently in. The index is off by one because the row metadata does not include the starting row. If the player is in the starting row, we get undefined. We ignore that case. If the player is in a car or truck lane, we loop over the vehicles in the row and check if they intersect with the player. We create bounding boxes for the player and the vehicle to check for intersections.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { metadata <span class="hljs-keyword">as</span> rows } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Map"</span>;
<span class="hljs-keyword">import</span> { player, position } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Player"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hitTest</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> row = rows[position.currentRow - <span class="hljs-number">1</span>];
  <span class="hljs-keyword">if</span> (!row) <span class="hljs-keyword">return</span>;

  <span class="hljs-keyword">if</span> (row.type === <span class="hljs-string">"car"</span> || row.type === <span class="hljs-string">"truck"</span>) {
    <span class="hljs-keyword">const</span> playerBoundingBox = <span class="hljs-keyword">new</span> THREE.Box3();
    playerBoundingBox.setFromObject(player);

    row.vehicles.forEach(<span class="hljs-function">(<span class="hljs-params">{ ref }</span>) =&gt;</span> {
      <span class="hljs-keyword">if</span> (!ref) <span class="hljs-keyword">throw</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Vehicle reference is missing"</span>);

      <span class="hljs-keyword">const</span> vehicleBoundingBox = <span class="hljs-keyword">new</span> THREE.Box3();
      vehicleBoundingBox.setFromObject(ref);

      <span class="hljs-keyword">if</span> (playerBoundingBox.intersectsBox(vehicleBoundingBox)) {
        <span class="hljs-built_in">window</span>.alert(<span class="hljs-string">"Game over!"</span>);
        <span class="hljs-built_in">window</span>.location.reload();
      }
    });
  }
}
</code></pre>
<p>If the bounding boxes intersect, we show an alert. Once the user clicks OK on the alert, we reload the page. We call this function in the <strong>animate</strong> function, which will run it on every frame.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { Renderer } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Renderer"</span>;
<span class="hljs-keyword">import</span> { Camera } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Camera"</span>;
<span class="hljs-keyword">import</span> { player } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Player"</span>;
<span class="hljs-keyword">import</span> { map } <span class="hljs-keyword">from</span> <span class="hljs-string">"./Map"</span>;
<span class="hljs-keyword">import</span> { animateVehicles } <span class="hljs-keyword">from</span> <span class="hljs-string">"./animateVehicles"</span>;
<span class="hljs-keyword">import</span> { animatePlayer } <span class="hljs-keyword">from</span> <span class="hljs-string">"./animatePlayer"</span>;
<span class="hljs-keyword">import</span> { hitTest } <span class="hljs-keyword">from</span> <span class="hljs-string">"./hitTest"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./style.css"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./collectUserInput"</span>;

. . .

function animate() {
  animateVehicles();
  animatePlayer();
  hitTest(); <span class="hljs-comment">// Add hit detection</span>

  renderer.render(scene, camera);
}
</code></pre>
<h2 id="heading-next-steps">Next Steps</h2>
<p>Congratulations, you’ve reached the end of this tutorial, and we’ve covered all the main features of the game. We rendered a map, animated the vehicles, added event handling for the player, and added hit detection.</p>
<p>I hope you had great fun creating this game. This game, of course, is far from perfect, and there are various improvements you can make if you’d like to keep working on it.</p>
<p>You can find the extended tutorial with interactive demos on <a target="_blank" href="http://JavaScriptGameTutorials.com">JavaScriptGameTutorials.com</a>. There, we also cover how to add shadows and truck lanes and how to generate an infinite number of rows as the player moves forward. We also add UI elements for the controls and the score indicator, and we add a result screen with a button to reset the game.</p>
<p>Alternatively, you can find the extended tutorial on <a target="_blank" href="https://www.youtube.com/watch?v=vNr3_hQ3Bws&amp;ab_channel=HunorM%C3%A1rtonBorb%C3%A9ly">YouTube</a>.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/vNr3_hQ3Bws" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ JavaScript Tutorial – How to Set Up a Front End Development Project ]]>
                </title>
                <description>
                    <![CDATA[ Let’s say you plan to build a website. Before you start, you want to set up a few tools to make your life easier. But which tools should you have? The JavaScript ecosystem is changing so fast that it can be overwhelming to pick the best tools to use.... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-set-up-a-front-end-development-project/</link>
                <guid isPermaLink="false">66c4c80d6e4b60b5b844529e</guid>
                
                    <category>
                        <![CDATA[ eslint ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Front-end Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ node ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Prettier ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Hunor Márton Borbély ]]>
                </dc:creator>
                <pubDate>Wed, 12 Feb 2025 21:44:03 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1739318785959/23632d35-1d5a-4797-8c7d-fbad6c80a879.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Let’s say you plan to build a website. Before you start, you want to set up a few tools to make your life easier. But which tools should you have?</p>
<p>The JavaScript ecosystem is changing so fast that it can be overwhelming to pick the best tools to use. To solve this problem, in this article, I’m going to walk you through how to set up a front-end project from scratch.</p>
<p>We'll cover things like must-have editor extensions, adding JavaScript libraries to your project, why you'll use Node.js even if you want to do front-end development, and setting up an application bundler that will generate a live preview as you code in your browser.</p>
<p>You can also <a target="_blank" href="https://www.youtube.com/watch?v=BiBjuphZQxA">watch this article as a video</a> on YouTube. Let's dive in.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-how-to-choose-a-code-editor">How to Choose a Code Editor</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-auto-format-your-code-in-vs-code">How to Auto-format Your Code in VS Code</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-do-you-need-node-for-a-front-end-project">Why Do You Need Node for a Front-End Project?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-run-your-project">How to Run Your Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-add-libraries-to-your-javascript-project">How to Add Libraries to Your JavaScript Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-get-coding-tips-while-you-code">How to Get Coding Tips While You Code</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-initialize-a-project-with-vite">Initialize a Project with Vite</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-summary">Summary</a></p>
</li>
</ol>
<h2 id="heading-how-to-choose-a-code-editor"><strong>How to Choose a Code Editor</strong></h2>
<p>Let’s start with the foundations. As a web developer, you mostly edit text, so you need a good editor. So which one should you use?</p>
<p>Picking an editor is highly based on personal preference, as most editors have very similar features.</p>
<p>If you don’t have a personal preference, I highly recommend <a target="_blank" href="https://vscode.dev/">VS Code</a>. Lately, it has become the de facto standard editor for web development.</p>
<p><a target="_blank" href="https://survey.stackoverflow.co/2024/technology#1-integrated-development-environment"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738945424232/9f41d802-e672-4cd7-ada3-5e8d54000446.png" alt="VS Code is by far the most used editor" class="image--center mx-auto" width="3840" height="2160" loading="lazy"></a></p>
<p>One of the greatest features of all the mainstream editors is that you can add extensions to them. Let’s walk through two extensions that are must-haves.</p>
<h2 id="heading-how-to-auto-format-your-code-in-vs-code"><strong>How to Auto-format Your Code in VS Code</strong></h2>
<p>Prettier is an extension that makes your code more readable and more consistent.</p>
<p>Let’s say you copy-pasted some code, and it’s hard to read. The tabulation is off, a line is too long, and so on. Then you just save the file, and magically, everything looks as it should be.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738948709710/c64a427d-b868-4704-87db-338ebf079c67.png" alt="Prettier formats the code based on best practices" class="image--center mx-auto" width="2280" height="1280" loading="lazy"></p>
<p>This is what Prettier does. It formats the code based on best practices. It doesn't just fix tabulation and wrap the lines. It also adds parentheses to improve code readability, makes sure you are consistent with quotation marks, and many more.</p>
<p>To make it work in VS Code, we must first install the Prettier extension. To do so, go to the extensions panel in VS Code, search for Prettier, and then install it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738955063143/7e8378e5-cf70-4527-9951-b822a745f4bf.png" alt="To install Prettier, go to the Extensions panel, search for Prettier, and install it" class="image--center mx-auto" width="2260" height="1280" loading="lazy"></p>
<p>Installing this extension doesn't format your files automatically on save by default. The default behavior is that once you install this extension, you can right-click within a file and select <strong>Format Document</strong>. You can also select part of a file and choose <strong>Format Selection</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738955102891/2e55eaa7-ec2a-4aca-ad79-b1df82d31e5c.png" alt="Right-click within a file and select Format Document to format it" class="image--center mx-auto" width="2260" height="1280" loading="lazy"></p>
<p>The first time you do this, you need to select the default formatter. VS Code already has a formatter, but it isn’t as powerful as Prettier. Now that you have two formatters, you have to let VS Code know that you want to use Prettier for formatting in the future.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738955119781/d0ef8bc3-54d0-4428-9731-c8857171f3e7.png" alt="The first time, you need to select the default formatter" class="image--center mx-auto" width="2260" height="1280" loading="lazy"></p>
<p>If you wish to auto-format your files when you save them, you need to change the settings. Go to Settings in your VS Code preferences and search for the <strong>Format on Save</strong> option. By default, this is false, so make sure that you tick this checkbox. With this, Prettier formats your files every time you save them. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738955201893/fd3fbec6-5265-4c9b-adab-c1a9d524a635.png" alt="Set the Format On Save option in Settings" class="image--center mx-auto" width="2260" height="1280" loading="lazy"></p>
<p>Formatting can be controversial, though. I highly recommend the default settings, especially for beginners. But if you prefer a different style, you can customize things.</p>
<p>You can indicate with comments to <a target="_blank" href="https://prettier.io/docs/en/ignore.html">ignore specific lines</a> and create a config file to list your preferences.</p>
<p>In the root folder of your project, you can create a file called <strong>.prettierrc</strong> and add a few options. A typical option could be if you prefer single quotes instead of double quotes in your files. Or if you don't want to have semi-colons at the end of your lines.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738955259640/d430386f-de2a-49f3-b252-aee4a9bc1089.png" alt="Adding a custom Prettier configuration" class="image--center mx-auto" width="2260" height="1280" loading="lazy"></p>
<p>With this configuration, you will have a different format once you save your files.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"singleQuote"</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">"semi"</span>: <span class="hljs-literal">false</span>
}
</code></pre>
<p>There are many more options, of course. If you want to dig deeper, check out <a target="_blank" href="https://prettier.io/docs/en/configuration.html">Prettier's documentation</a><a target="_blank" href="https://prettier.io/docs/en/configuration.html">.</a></p>
<h2 id="heading-why-do-you-need-node-for-a-front-end-project"><strong>Why Do You Need Node for a Front-End Project?</strong></h2>
<p>Before we get to the second must-have extension, we need to set up a few other things. First, we need to talk about Node.js. What is Node, and why do you need it even if you work as a front-end developer?</p>
<p>Node is often associated with backend development, but that's not its only job. Node is a JavaScript runtime – this means it runs JavaScript files outside of the browser.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738949060189/11f39633-76a1-4c38-92ef-88846ffdeb8f.png" alt="You can run JavaScript as part of a website in your browser" class="image--center mx-auto" width="2200" height="1220" loading="lazy"></p>
<p>There are two ways of running JavaScript code. You can either have it as part of a website and run the entire website in a browser, or run only the Javascript file with a runtime like Node.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738949071655/68552f47-63ba-4cfc-9ba0-abb9218a4857.png" alt="You can run JavaScript on its own with Node" class="image--center mx-auto" width="2200" height="1220" loading="lazy"></p>
<p>In the example below, we have a very simple Javascript file that prints "Hello World" to the console. If we have Node installed, we can go to the terminal, navigate to the folder where this file is, and then run it with Node like this. You can see that the file was executed, and the result is in the console.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738947073105/9b56af05-e925-4cd1-9475-b3e49898dd39.png" alt="Node can run JavaScript files on their own" class="image--center mx-auto" width="1280" height="720" loading="lazy"></p>
<p>That's what Node really is: a tool that runs JavaScript files on their own.</p>
<p>JavaScript mostly behaves the same way in both environments. But there are also differences in what JavaScript can do in a browser vs when it runs with Node.</p>
<p>For instance, when running in the browser, JavaScript can access and modify HTML elements. That's the main point of having JavaScript in the first place.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738949133299/b1c6d0a6-ba11-4659-a51e-b72e4bbfde68.png" alt="In the browser, JavaScript can access and modify your HTML elements" class="image--center mx-auto" width="2200" height="1220" loading="lazy"></p>
<p>In Node, there's no HTML file. JavaScript runs on its own. On the other hand, in Node, JavaScript has access to your file system and can read and write your files.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738949113586/e8702a9c-91bf-4096-8768-cf06e4e41082.png" alt="With Node, JavaScript can access and modify your File system" class="image--center mx-auto" width="2200" height="1220" loading="lazy"></p>
<p>For instance, you can run scripts on your machine to initialize a project. We are going to do that. You can run checks on your files and automatically correct the mistakes. Or you can run your test files.</p>
<p>In short, Node lets you run some tools that make your life much easier as a developer.</p>
<p>To install Node, go to <a target="_blank" href="http://nodejs.org">nodejs.org</a> and install it. If you are unsure if you already have Node, you can also go to your terminal and run <strong>node -v</strong> to check. If you get a version number, you have Node.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738949153110/c9e63db3-d52b-4122-a31c-6b871ed2e4b5.png" alt="The website of Node.js" class="image--center mx-auto" width="2200" height="1220" loading="lazy"></p>
<p>So, why do people associate Node primarily with backend development? If the backend code is in JavaScript, the servers must run it somehow without a browser. So yes, if you are a backend developer using JavaScript, then you're most probably going to use Node. But Node is much more than that.</p>
<h2 id="heading-how-to-run-your-project"><strong>How to Run Your Project</strong></h2>
<p>Now that we have Node, we can use a live server to see our site live in the browser as we develop it. Without this, you need to manually refresh the browser window every time you make a change.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738949223269/97bc1d15-4ab6-4204-88d6-257fa471f95d.png" alt="A bundler creates a package that you can run in the browser" class="image--center mx-auto" width="2248" height="1220" loading="lazy"></p>
<p>These tools are called bundlers because they take all your files and turn them into a neat package you can run in the browser. So why do you need them?</p>
<ul>
<li><p>They update your site live in the browser with hot reloading. When you save a file, you immediately see the result in your browser.</p>
</li>
<li><p>As web development tools have evolved, the browser won't understand your files when you use anything more advanced. For instance, are you using React? Then, you're using the JSX syntax – the one that looks like HTML. The JSX syntax is not part of JavaScript. You need a tool to convert it into plain JavaScript. Otherwise, it won't run in your browser. Or are you using TypeScript? You also need to turn that into JavaScript. Or, if you're using SCSS or any other CSS dialect, you need to convert it to plain CSS.</p>
</li>
<li><p>If you import libraries using the JavaScript module system, you need a live server to avoid CORS issues in your browser.</p>
</li>
</ul>
<p>This is what bundlers do. They make sure that you can use modern-day tooling while you're developing your application, and they can also create a final production build that you can publish on the internet.</p>
<p>How do you pick a bundler? There are several options, and essentially, they all do the same thing. The difference between them is in their performance, configuration options, and ease of use.</p>
<p>The most used bundler is still <a target="_blank" href="https://webpack.js.org/">webpack</a>, one of the earliest bundlers in the field. But the one that seems to have taken over the throne and gained more and more popularity is <a target="_blank" href="https://vite.dev/">Vite</a>. Here's a chart from the latest edition of the State of JavaScript survey.</p>
<p><a target="_blank" href="https://2024.stateofjs.com/en-US/libraries/#all_tools_experience"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738947476843/195adbd5-5aba-427e-9b00-c507542235d2.png" alt="The sentiment towards WebPack and Vite" class="image--center mx-auto" width="1540" height="900" loading="lazy"></a></p>
<p>This chart shows that while most developers have used Webpack, they don’t necessarily love it. At the same time, Vite's popularity is rising while still maintaining a positive sentiment.</p>
<p>If you haven't checked out the <a target="_blank" href="https://stateofjs.com/en-US">State of JavaScript</a> survey before, I highly recommend going through it. It gives you an excellent overview of the latest trends with JavaScript. You can learn which tools and libraries people love to use and which they will abandon soon. If you feel overwhelmed by all the changes in the JavaScript ecosystem, the results of this survey can be a great guide.</p>
<p>Once we have a folder for our project, let's navigate to it using our terminal. The easiest way to do this is to open the folder in VS Code and then use the built-in terminal. VS Code will open the terminal with the correct folder.</p>
<p>Then, you can run the project in the terminal with the following command. npx is a command line tool that comes with Node. This is one of the reasons we installed Node: to be able to run commands like this.</p>
<pre><code class="lang-bash">npx vite
</code></pre>
<p>The first time you run this script, it will ask you to install Vite. Say yes. Then, it will show you the URL of a local server, which you can open in a browser to view your project.</p>
<pre><code class="lang-bash">  VITE v6.1.0  ready <span class="hljs-keyword">in</span> 162 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show <span class="hljs-built_in">help</span>
</code></pre>
<p>Now, if you update a file and save the changes, the new version appears in the browser immediately. It generates a live preview of your site until you stop the script or close the terminal. You can keep it running while you're developing your site.</p>
<p>Once you have finished, you can press <strong>Ctrl+C</strong> to stop the script. If it gets desynchronized or you break it with an error, restart it by pressing <strong>Ctrl+C</strong> to stop it and rerunning the same script. So that's how you run a project with Vite.</p>
<h2 id="heading-how-to-add-libraries-to-your-javascript-project"><strong>How to Add Libraries to Your JavaScript Project</strong></h2>
<p>Now that we have Node, we can also use npm or Note Package Manager to add libraries to our project. npm is another tool included with Node. So how does it work?</p>
<p>First, I will walk you through setting things up step by step the manual way so it's clear how the different parts come together. Then, I will show you how to automate most of these steps.</p>
<p>Navigate to your current folder in the terminal and run the following command to initialize the project. This command initializes a package.json file with some metadata.</p>
<pre><code class="lang-bash">npm init --yes
</code></pre>
<p>At this point, this file is not very interesting. It contains the project name, description, version number, and so on. You can change these values.</p>
<p>Now, we can add libraries to our package with the npm install command. In a <a target="_blank" href="https://www.freecodecamp.org/news/render-3d-objects-in-browser-drawing-a-box-with-threejs/">previous article</a>, we used Three.js to render 3D boxes in the browser.</p>
<p>So, as an example, let's install <a target="_blank" href="https://threejs.org/">Three.js</a>. Go to your terminal again, make sure you are in the correct folder, and run the following command:</p>
<pre><code class="lang-bash">npm install three
</code></pre>
<p>This command will install Three.js. But how do you know that the keyword is three here, not Three.js?</p>
<p>When you don’t know the package name, you can just google npm and the name of the library you need. Or, if you don't even know the library name, you can also just search for an npm 3D library and see what Google comes up with.</p>
<p>We can go through each package one by one and pick one based on their capabilities and other info. These packages mostly come with descriptions and quick examples to give you an idea of what the library can do for you.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738949563130/ce677d97-20c4-48ed-a168-b99eb01c84d4.png" alt="How to pick a library to use" class="image--center mx-auto" width="2260" height="1260" loading="lazy"></p>
<p>Another indicator you might want to look for is the weekly downloads and the date of the last update to ensure you select an actively maintained library that people still use.</p>
<p>Once you find the package you are looking for, you can see the command to install it at the top right corner: <code>npm i three</code>. The <code>i</code> here is just shorthand for install. Another way to learn how to install Three.js is to go to its official documentation and check the <a target="_blank" href="https://threejs.org/docs/index.html#manual/en/introduction/Installation">installation guide</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738952468171/7a54e0bb-d88d-428b-911c-cf7a71676562.png" alt="The package.json file after initializing the project and installing Three.js" class="image--center mx-auto" width="2260" height="1260" loading="lazy"></p>
<p>When we install a package, three things happen:</p>
<ul>
<li><p>It adds the latest version of Three.js in our package.json file as a project dependency.</p>
</li>
<li><p>It also creates a package-lock file, which NPM uses to keep track of the dependencies. You should never edit the dependency section of your package.json file or the package-lock file manually. Instead, you should always use commands like npm install and uninstall to add, remove, or update packages.</p>
</li>
<li><p>Finally, the node_modules folder gets created. This folder contains the source code of Three.js. When we import Three.js in our project, it looks for it in this folder. The content of this folder is also something that you should never change. You can look into it if you're interested in the source code of the library that you're using, but you shouldn't change it.</p>
</li>
</ul>
<p>If something goes wrong and you have an error with your dependencies that you can't figure out, then you can always safely delete the node_modules folder and the package-lock file and reinstall your dependencies based on the package.json file. This is not something that you should do, but you can always go back to having a clean slate.</p>
<p>Now that we have installed Three.js, we can create a simple website that displays a 3D box. It's a simple HTML file and a JavaScript file with the code for the 3D box. The key here is that we import Three.js with the import statement in the JavaScript file. This import will use the package that we just installed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738952825715/9828ea05-e75a-4fdf-b1a3-39bd3aa8e380.png" alt="Using Three.js in a sample project" class="image--center mx-auto" width="2546" height="1444" loading="lazy"></p>
<p>Then, we can run the project with Vite. Using imports means that we use the module system now. Running a project with the module syntax can be a bit tricky, as the browser gives you CORS errors by default. However, as we are using Vite to run our project, it works seamlessly without any questions. That’s one of the reasons we use Vite.</p>
<p>If you want to learn more about building 3D games with Three.js, check out my <a target="_blank" href="https://www.freecodecamp.org/news/three-js-tutorial/">earlier article</a> on building a minimalistic car in the browser.</p>
<h2 id="heading-how-to-get-coding-tips-while-you-code"><strong>How to Get Coding Tips While You Code</strong></h2>
<p>The second must-have editor extension is ESLint. While Prettier formatted the code, ESLint gives you coding tips.</p>
<p>It helps you catch basic mistakes and avoid patterns that can cause bugs or be misleading when you try to understand the code.</p>
<p>Here’s a simple example where you declare a variable, but then you have a typo, and you try to use another variable that doesn't exist. ESLint will highlight this for you. It will give you an error both at the variable declaration, saying that you created a variable you don't use, and add the usage, saying that you're trying to use a variable that is not declared. With ESLint, it's easy to spot that you made a typo.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738953388793/c081f406-f964-4c4c-95ef-83cdc8e403df.png" alt="c081f406-f964-4c4c-95ef-83cdc8e403df" class="image--center mx-auto" width="1928" height="1244" loading="lazy"></p>
<p>ESLint, of course, is much more complex than just being able to catch simple errors. There are also less obvious use cases where you might not understand why ESLint is complaining. Then, you can always click the link in the error popup for more details explaining why this pattern is harmful and what you can do to avoid it.</p>
<p>So how can we use ESLint in our projects? This time, we need to have an extension and a configuration. First, as we did with Prettier, we must install the ESLint extension. Go to your extensions, search for ESLint, and install it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738953594259/a6f02e96-069d-4463-8322-86a23c523df3.png" alt="To install ESLint go to the Extensions panel, search for ESLint, and install it" class="image--center mx-auto" width="2260" height="1280" loading="lazy"></p>
<p>We also need to set up ESLint for our project. Before we do that, we need to make sure that the project already has a package.json file. If you don't already have a package.json file, we first have to run <code>npm init --yes</code> to initialize the project. Then, we can generate an ESLint config with the following command:</p>
<pre><code class="lang-bash">npm init @eslint/config@latest
</code></pre>
<p>This script will ask you a few questions. Based on your answers, it will customize the configuration and the rules to check. For most cases, you can use the default option.</p>
<ul>
<li><p>The first time, it will ask for permission to install ESLint. Say yes.</p>
</li>
<li><p>Then, it will ask you whether to use ESLint only for syntax checks or to find problems as well. Choose the second option to get the most help from ESLint.</p>
</li>
<li><p>Then select that you’ll use it with JavaScript modules. Modern web development projects use the JavaScript module system. We use JavaScript modules if we have imports and exports in our code.</p>
</li>
<li><p>Then, it will ask what framework we are using. If we select a framework, it will add framework-specific rules to our project. For instance, using it with React will force us to define the prop types. If we don't use a front-end framework, just vanilla JavaScript, then select "None of these".</p>
</li>
<li><p>Then, it will ask if the project is using TypeScript. Choose based on your preference.</p>
</li>
<li><p>It asks where you run the code. As we have a front-end project, select "Browser".</p>
</li>
<li><p>Then, it will ask if you want to install the additional dependencies that are required for the rules based on your selections. Select yes.</p>
</li>
<li><p>It will also ask what package manager we’re using. We haven't talked a lot about this, but there are multiple package managers that we can use. Select the default: npm.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738953735596/a69f62ac-b5b4-459e-8e78-6956442adfe4.png" alt="Installing ESLint" class="image--center mx-auto" width="2640" height="1624" loading="lazy"></p>
<p>These were a lot of questions. Let's see what happened after we ran this command.</p>
<p>After this step, we have ESLint and some other dependencies based on the answers in the package.json file as development dependencies. Development dependency means that ESLint won't be part of your website's final code, but we need it during development.</p>
<p>ESLint also became part of our node_modules folder, and there are many more packages here now. This is because a dependency can have other dependencies, and the dependencies of the dependencies will also be part of the node_modules folder.</p>
<p>ESLint also created a config file that sets up the rules based on your answers. We will see how to customize the rules.</p>
<p>Now that ESLint is working, you should also see errors in the code once something is off. If you go to your JavaScript file and try to use an undeclared variable, ESLint will highlight the issue.</p>
<p>ESLint is also highly customizable. For instance, we might not want to mark an unused variable as an error. First, we go to the error popup and select the identifier of this type of error. Then, we go to the ESLint config and override this error as follows. Here, we can reduce the severity to a warning or completely turn off this rule.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738953834462/387e36f3-81ee-461c-ae02-3ca614c7b765.png" alt="Turning off ESLint rules in the config" class="image--center mx-auto" width="2640" height="1624" loading="lazy"></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> globals <span class="hljs-keyword">from</span> <span class="hljs-string">"globals"</span>;
<span class="hljs-keyword">import</span> pluginJs <span class="hljs-keyword">from</span> <span class="hljs-string">"@eslint/js"</span>;


<span class="hljs-comment">/** @type {import('eslint').Linter.Config[]} */</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> [
  {<span class="hljs-attr">languageOptions</span>: { <span class="hljs-attr">globals</span>: globals.browser }},
  pluginJs.configs.recommended,
  {
    <span class="hljs-attr">rules</span>: {
      <span class="hljs-string">"no-unused-vars"</span>: <span class="hljs-string">"off"</span>, <span class="hljs-comment">// Turn off the No Unused Variables rule</span>
    }
  }
];
</code></pre>
<p>But if you're a beginner, I recommend following the rules that ESLint has by default. Sometimes, it might be annoying to fix all the seemingly harmless issues, but all these rules are based on industry best practices, so it's good to follow them. For more details, check out <a target="_blank" href="https://eslint.org/docs/latest/use/configure/rules">ESLint's documentation</a>.</p>
<h2 id="heading-initialize-a-project-with-vite">Initialize a Project with Vite</h2>
<p>We walked through the step-by-step process of setting up a project. We used <strong>npm init</strong> to initialize the project, manually set up ESLint, and ran our project with Vite. Vite can also initialize the project with a sample application and all the necessary files, which is especially handy when we set up a React project. </p>
<p>Let's see how to set up a vanilla JavaScript and a React project. Let's navigate to a folder in the terminal that will contain our project. We don't need to create a project folder this time because the script will make it for us. Then, run the following command to initialize a project:</p>
<pre><code class="lang-bash">npm create vite@latest
</code></pre>
<p>This command asks you a few questions.</p>
<ul>
<li><p>First, it will ask you for the project name, which will also be the name of the folder created as the project root.</p>
</li>
<li><p>Then, it will ask what framework you use. If you don't use any framework and want plain old JavaScript, choose "Vanilla." If you use React, choose React.</p>
</li>
<li><p>Then, it will ask you if you want to use TypeScript or JavaScript. Here, the default is TypeScript. If you're a beginner in web development, choose JavaScript. If you are more confident with your skills, then go with TypeScript. TypeScript is more complicated, but it has become the industry standard in web development, and most jobs require you to know it. As a beginner, you can go with JavaScript.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738953943915/884e22b0-f096-4338-a392-588b032834f4.png" alt="Creating a project with Vite" class="image--center mx-auto" width="1364" height="966" loading="lazy"></p>
<p>Now, we can navigate to the new folder created and check out what we have here. If you choose a vanilla JavaScript project, you can see it generated a simple application with HTML, CSS, and some JavaScript files. You can change or even delete these. You'll need an HTML file as an entry point, but you can replace the rest.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738954107608/0234f409-6f78-4039-b5d1-133c8cd09530.png" alt="Vite can create a sample application with an HTML, CSS, and JavaScript file" class="image--center mx-auto" width="2624" height="1822" loading="lazy"></p>
<p>We can run this project with <strong>npx vite</strong> as we did before, but there’s a better way. For a real project, we want to add Vite as a development dependency to ensure consistency with the version we are using.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"my-project"</span>,
  <span class="hljs-attr">"private"</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"0.0.0"</span>,
  <span class="hljs-attr">"type"</span>: <span class="hljs-string">"module"</span>,
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"vite"</span>,
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"vite build"</span>,
    <span class="hljs-attr">"preview"</span>: <span class="hljs-string">"vite preview"</span>
  },
  <span class="hljs-attr">"devDependencies"</span>: {
    <span class="hljs-attr">"vite"</span>: <span class="hljs-string">"^6.1.0"</span>
  }
}
</code></pre>
<p>The package.json file shows that Vite has been added as a development dependency. To use this hardcoded version, we first have to install it via <code>npm install</code>. This command installs all the dependencies listed in the package.json file.</p>
<pre><code class="lang-bash">npm install
</code></pre>
<p>This package.json file now also has a scripts section. This section can define scripts to run your app locally, create a production build, or test your application. You can run them with <strong>npm run</strong>. So, for instance, to run the application, you can open the terminal with the correct folder and run the following command.</p>
<pre><code class="lang-bash">npm run dev
</code></pre>
<p>This runs the script labeled as “dev” in the scripts section, which will run the Vite version we just installed with <strong>npm install</strong>.</p>
<p>Vite does not install ESLint when you create a vanilla project, but you can always install it manually, as we did before with <strong>npm init @eslint/config@latest</strong>.</p>
<p>If you choose React as a framework when we initialize the project, we will have a couple more files. For instance, we have an ESLint config with the recommended React settings. We also have a Vite config that enables us to use React and a sample application that we can run.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738954174340/26c5a9a5-b983-4ed7-8e43-fbab4400e08b.png" alt="Vite can also create a React project for you" class="image--center mx-auto" width="2624" height="1822" loading="lazy"></p>
<p>To run this app, we need to install the dependencies. So, let's go to the terminal and run <code>npm install</code>. This will install all the dependencies, including React. Then, we can run this app with <code>npm run dev</code>, and we will have a working React application.</p>
<h2 id="heading-summary">Summary</h2>
<p>In this article, we set up and run a front-end project with Vite. We also covered how to find and add dependencies, how to have consistent and automatic formatting with Prettier, and how to avoid bugs with ESLint.</p>
<p>What happens once you finish developing your app? How do you upload it to the web and share it with the world? That's the topic of a future article.</p>
<h3 id="heading-subscribe-for-more-tutorials-on-web-development"><strong>Subscribe for more tutorials on Web Development:</strong></h3>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/BiBjuphZQxA" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ SVG Toggle Button Tutorial – How to Handle Dark Mode with CSS and JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ How can you detect dark mode in CSS and JavaScript? How can you manually override it with a toggle button? And how can you create a sun and moon icon with SVG? In this tutorial, you will learn how to detect dark mode in CSS and JavaScript, and you wi... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-handle-dark-mode-with-css-and-javascript/</link>
                <guid isPermaLink="false">66c4c80b6e4b60b5b844529c</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SVG ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Hunor Márton Borbély ]]>
                </dc:creator>
                <pubDate>Fri, 08 Mar 2024 13:46:26 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/03/thumbnail.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>How can you detect dark mode in CSS and JavaScript? How can you manually override it with a toggle button? And how can you create a sun and moon icon with SVG?</p>
<p>In this tutorial, you will learn how to detect dark mode in CSS and JavaScript, and you will create a toggle button with SVG to override the default behavior. You will use plain HTML, CSS, and JavaScript, so you don't need any preliminary requirements before starting.</p>
<p>You can also <a target="_blank" href="https://youtu.be/GUSUA72t7p0">watch this article as a video</a> on YouTube.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-how-to-handle-dark-mode-with-css">How to Handle Dark Mode with CSS</a></li>
<li><a class="post-section-overview" href="#heading-how-to-code-a-sun-icon-with-svg">How to Code a Sun Icon with SVG</a></li>
<li><a class="post-section-overview" href="#heading-how-to-detect-dark-mode-in-javascript">How to Detect Dark Mode in JavaScript</a></li>
<li><a class="post-section-overview" href="#heading-how-to-code-a-moon-icon-with-svg">How to Code a Moon Icon with SVG</a></li>
<li><a class="post-section-overview" href="#heading-how-to-toggle-dark-mode-with-javascript">How to Toggle Dark Mode with JavaScript</a></li>
<li><a class="post-section-overview" href="#next-step">Next Steps</a></li>
</ul>
<h2 id="heading-how-to-handle-dark-mode-with-css">How to Handle Dark Mode with CSS</h2>
<p>Let's say you have a simple website with some text. By default, you set the text color to be black and the background color to be white. Implementing dark mode for this site with CSS is very simple:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Dark Mode<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"index.css"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"index.js"</span> <span class="hljs-attr">defer</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
      How to detect dark mode in CSS and in JavaScript? How can we override it
      manually with a toggle button? In this quick tutorial, we look into
      detecting dark mode in CSS and JavaScript, and then we create a toggle
      button with SVG to override the default behavior.
    <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Screenshot-2024-03-07-at-11.33.28.png" alt="Image" width="600" height="400" loading="lazy">
<em>A simple website with some text in dark mode</em></p>
<p>All you need to do is add a media query and set a condition. With this condition, you set the following CSS statements to be only valid if the preferred color scheme is dark. </p>
<p>Inside this media query, you can define the colors for dark mode. In this case, you flip the colors and set the text color to white and the background color to black:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">font-family</span>: Montserrat;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">50px</span>;
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">500px</span>;
}

<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">prefers-color-scheme:</span> dark) {
  <span class="hljs-selector-tag">body</span> {
    <span class="hljs-attribute">background-color</span>: black;
    <span class="hljs-attribute">color</span>: white;
  }
}
</code></pre>
<p>This will take the setting from your OS or browser setting. By default, it comes from the Operating System, but the browser can decide to override it. In Google Chrome, you can find this setting under 'Appearance'. By default, it follows the device setting.</p>
<p>What's great about the CSS solution is that if you change this setting while visiting the website, the styling will update automatically.</p>
<p>This way, you can set a custom style for the body element and any other elements as well. </p>
<p>It doesn't work in one case, though. You cannot style what's inside an HTML Canvas element with CSS. If you built up a <a target="_blank" href="https://www.freecodecamp.org/news/gorillas-game-in-javascript/">game entirely from JavaScript</a> using the Canvas API or Three.js, you must also set the colors for dark mode in JavaScript.</p>
<p>In the next steps, we will cover this and look into how to create an SVG toggle button to switch between light and dark modes.</p>
<h2 id="heading-how-to-code-a-sun-icon-with-svg">How to Code a Sun Icon with SVG</h2>
<p>Before you learn how to handle dark mode in JavaScript, let's take a quick detour and see how to code a dark mode toggle button with SVG. Detecting dark mode is one thing, but you should allow the user to manually toggle between light and dark modes.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Slides-1.022.png" alt="Image" width="600" height="400" loading="lazy">
<em>Sun Icon</em></p>
<p>Check out my previous tutorial if you need a quick introduction to <a target="_blank" href="https://www.freecodecamp.org/news/svg-tutorial-learn-to-code-images/">coding SVG icons</a>. It contains many great examples from beginner to advanced levels. And if you are new to SVGs, don't worry. These are very simple examples.</p>
<p>So, let's start with the <code>svg</code> element. This will serve as a container for all the image elements. Set its size to 30 times 30:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Slides-1.004.png" alt="Image" width="600" height="400" loading="lazy">
<em>The <code>svg</code> element</em></p>
<p>Then, add a circle. For a <code>circle</code> element, you have to set the center coordinates of the circle and its radius. The center coordinates are both 15, and the radius is 6. Then, set a color with the <code>fill</code> property:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"30"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">cx</span>=<span class="hljs-string">"15"</span> <span class="hljs-attr">cy</span>=<span class="hljs-string">"15"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"6"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Slides-1.006.png" alt="Image" width="600" height="400" loading="lazy">
<em>We add a <code>circle</code> as the core of the sun</em></p>
<p>To set the color, you can use the <code>currentColor</code> property that takes over the current <code>color</code> setting from CSS. This will come in handy later when you toggle dark and light modes. The icon will switch colors automatically.</p>
<p>Then, add the sun rays. You need to use the <code>line</code> element for this, where you have to set the starting and end coordinates. You can also set the stroke color with the <code>stroke</code> property, the <code>stroke-width</code> to add thickness, and the <code>stroke-linecap</code> property to make the ends of the lines rounded:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"30"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">cx</span>=<span class="hljs-string">"15"</span> <span class="hljs-attr">cy</span>=<span class="hljs-string">"15"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"6"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span> /&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">line</span>
    <span class="hljs-attr">id</span>=<span class="hljs-string">"ray"</span>
    <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span>
    <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"2"</span>
    <span class="hljs-attr">stroke-linecap</span>=<span class="hljs-string">"round"</span>
    <span class="hljs-attr">x1</span>=<span class="hljs-string">"15"</span>
    <span class="hljs-attr">y1</span>=<span class="hljs-string">"1"</span>
    <span class="hljs-attr">x2</span>=<span class="hljs-string">"15"</span>
    <span class="hljs-attr">y2</span>=<span class="hljs-string">"4"</span>
  &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">line</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Slides-1.010.png" alt="Image" width="600" height="400" loading="lazy">
<em>We add a <code>line</code> element as a sunray</em></p>
<p>Now, once you have one ray, you can reuse the same ray to draw the others. </p>
<p>You can give this ray an <code>id</code> and reuse it with the <code>use</code> element. For the reused elements, you can set a rotation. Set the rotation angle and the center of rotation. You want to rotate the rays around the center of the sun, so set it to <code>15,15</code>. Then, increment the rotation by 45 degrees for each ray:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"30"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">cx</span>=<span class="hljs-string">"15"</span> <span class="hljs-attr">cy</span>=<span class="hljs-string">"15"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"6"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span> /&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">line</span>
    <span class="hljs-attr">id</span>=<span class="hljs-string">"ray"</span>
    <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span>
    <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"2"</span>
    <span class="hljs-attr">stroke-linecap</span>=<span class="hljs-string">"round"</span>
    <span class="hljs-attr">x1</span>=<span class="hljs-string">"15"</span>
    <span class="hljs-attr">y1</span>=<span class="hljs-string">"1"</span>
    <span class="hljs-attr">x2</span>=<span class="hljs-string">"15"</span>
    <span class="hljs-attr">y2</span>=<span class="hljs-string">"4"</span>
  &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">line</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#ray"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"rotate(45 15 15)"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#ray"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"rotate(90 15 15)"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#ray"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"rotate(135 15 15)"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#ray"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"rotate(180 15 15)"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#ray"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"rotate(225 15 15)"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#ray"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"rotate(270 15 15)"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#ray"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"rotate(315 15 15)"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Slides-1.016.png" alt="Image" width="600" height="400" loading="lazy">
<em>The finished sun icon</em></p>
<h2 id="heading-how-to-detect-dark-mode-in-javascript">How to Detect Dark Mode in JavaScript</h2>
<p>Before we get to the moon icon, let's see how to detect dark mode in JavaScript. This can be useful when building a game, like we did a couple weeks ago in the <a target="_blank" href="https://www.freecodecamp.org/news/gorillas-game-in-javascript/">Gorillas JavaScript game tutorial</a>. </p>
<p>In that game, we were drawing on an HTML Canvas element with JavaScript. We set all the colors with JavaScript. If we want to support dark mode, we can set the colors based on a <code>darkMode</code> variable. But how do we detect if we are in dark mode? How do we set the value of this variable?</p>
<p>The following code is an example snippet from the game tutorial above. Here we set the fill color before we draw a rectangle on the canvas. To learn more about drawing on a <code>canvas</code> element, check out <a target="_blank" href="https://www.freecodecamp.org/news/gorillas-game-in-javascript/">this tutorial</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Slides-1.018.png" alt="Image" width="600" height="400" loading="lazy">
<em>When drawing on a <code>canvas</code> element we set the colors from JavaScript. But how we detect dark mode?</em></p>
<p>Detecting dark mode in JavaScript is also very simple. Interestingly enough, this solution also depends on the CSS query selectors you used before.</p>
<p>You can create a <code>matchMedia</code> object with the same condition we used in CSS. This method can check if the document matches a media query. Pass on <code>prefers-color-scheme: dark</code> as an argument:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> darkModeMediaQuery = <span class="hljs-built_in">window</span>.matchMedia(<span class="hljs-string">"(prefers-color-scheme: dark)"</span>);

<span class="hljs-keyword">let</span> darkMode = darkModeMediaQuery.matches;

. . .

function drawBuildings() {
  state.buildings.forEach(<span class="hljs-function">(<span class="hljs-params">building</span>) =&gt;</span> {
    ctx.fillStyle = darkMode ? <span class="hljs-string">"#254D7E"</span> : <span class="hljs-string">"#947285"</span>;
    ctx.fillRect(building.x, <span class="hljs-number">0</span>, building.width, building.height);
  });
}
</code></pre>
<p>Then, can check the <code>matches</code> property of this object. If it is true, then you are in dark mode. You can save this into a variable, and later, you can use this variable to decide what colors you should use when painting on the canvas element.</p>
<p>This variable, however, doesn't get refreshed automatically when you switch between light mode and dark mode. You need to add an event listener that detects if the settings change. </p>
<p>Here, we define a function that checks the <code>matches</code> property of the incoming object to decide if you just switched to bright or dark mode:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> darkModeMediaQuery = <span class="hljs-built_in">window</span>.matchMedia(<span class="hljs-string">"(prefers-color-scheme: dark)"</span>);

<span class="hljs-keyword">let</span> darkMode = darkModeMediaQuery.matches;

darkModeMediaQuery.addEventListener(<span class="hljs-string">"change"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (e.matches) {
    darkMode = <span class="hljs-literal">true</span>;
  } <span class="hljs-keyword">else</span> {
    darkMode = <span class="hljs-literal">false</span>;
  }
});

. . .

function drawBuildings() {
  state.buildings.forEach(<span class="hljs-function">(<span class="hljs-params">building</span>) =&gt;</span> {
    ctx.fillStyle = darkMode ? <span class="hljs-string">"#254D7E"</span> : <span class="hljs-string">"#947285"</span>;
    ctx.fillRect(building.x, <span class="hljs-number">0</span>, building.width, building.height);
  });
}
</code></pre>
<p>Now, if you set the colors based on this <code>darkMode</code> variable, you should see that the game's appearance changes once you switch between light and dark mode in the OS settings. Check out this <a target="_blank" href="https://codepen.io/HunorMarton/pen/jOJZqvp">demo</a> to see it in action.</p>
<h2 id="heading-how-to-code-a-moon-icon-with-svg">How to Code a Moon Icon with SVG</h2>
<p>Before we discuss overriding the default OS setting with a toggle button, let's examine the other half of our toggle icon: Let's draw a moon.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Slides-1.023.png" alt="Image" width="600" height="400" loading="lazy">
<em>The Sun and Moon icons</em></p>
<p>Start with an <code>svg</code> element of the same size and define a path inside it. You can define a <code>path</code> element by setting its <code>d</code> attribute. In this attribute, you build a path from a series of commands:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Slides-1.026.png" alt="Image" width="600" height="400" loading="lazy">
<em>We define a <code>path</code> with a series of commands</em></p>
<p>You start with the move-to command to go to the initial position. This command consists of the letter <code>M</code> and the starting coordinate: </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Slides-1.027.png" alt="Image" width="600" height="400" loading="lazy">
<em>Using the move-to command within a path</em></p>
<p>Then, use an arc command to draw the outer arc of the moon. This command might look a bit scary because it has several properties. Let's see what we have:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Slides-1.028.png" alt="Image" width="600" height="400" loading="lazy">
<em>The arc command and it's several properties</em></p>
<p>A command always continues the previous command, so this arc will draw the arc from the coordinates of the move-to command. Commands also end with the coordinates of the endpoint. </p>
<p>Here, you set where the arc ends. The rest of the properties are about how to draw an arc from the starting point to the endpoint:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Slides-1.029.png" alt="Image" width="600" height="400" loading="lazy">
<em>The last two properties of the arc command show the endpoint of the arc</em></p>
<p>The first two properties are the horizontal and vertical radius of our arc. In our case, we want to have the arc of a circle, so we set the same value for both. With the third argument, you can set a rotation. When both radiuses are the same, this property makes no difference. You can leave it at zero:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Slides-1.030.png" alt="Image" width="600" height="400" loading="lazy">
<em>Horiyontal and vertical radius of the arc</em></p>
<p>Then, we have the large arc flag property. With this, you can decide whether to go the long or short way to our end coordinate. You can see that you can reach the endpoint in multiple ways, even with the same radiuses. </p>
<p>There are two arcs – in the case of the first one, you go the long way and in the case of the second one, you will go the short way. This is a flag, so the value here can be 0 or 1:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Slides-1.032.png" alt="Image" width="600" height="400" loading="lazy">
<em>The large arc flag decides if we should reach the endpoint the short way or the long way</em></p>
<p>Finally, there is the sweep flag. This basically sets whether you should draw the arc clockwise or counterclockwise. The two options mirror each other. In the first case, you set this to zero – in the second, you set it to one:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Slides-1.035.png" alt="Image" width="600" height="400" loading="lazy">
<em>The sweep flag decides if we should go clockwise or counterclockwise</em></p>
<p>Now that you have one arc, you set up the other one. Here, you set the endpoint to the beginning. To the same coordinates as you used for the move-to command.</p>
<p>Then you can use the same radiuses, but you have to change the large arc flag and the sweep flag to end up with a moon:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"30"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
    <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span>
    <span class="hljs-attr">d</span>=<span class="hljs-string">"
      M 23, 5
      A 12 12 0 1 0 23, 25
      A 12 12 0 0 1 23, 5"</span>
  /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Slides-1.039.png" alt="Image" width="600" height="400" loading="lazy">
<em>The finished moon icon</em></p>
<p>How can you use these two icons in a button to toggle light mode and dark mode in JavaScript?</p>
<h2 id="heading-how-to-toggle-dark-mode-with-javascript">How to Toggle Dark Mode with JavaScript</h2>
<p>If you want to override the system or browser settings for dark mode with a manual switch, you can't rely on the CSS media query anymore. This works for rendering the UI based on the settings, but you can't override it from JavaScript.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Slides-1.041.png" alt="Image" width="600" height="400" loading="lazy">
<em>You can't override a CSS media query</em></p>
<p>Instead, you can define a <code>dark-mode</code> class and toggle it from JavaScript. </p>
<p>In CSS, define a class that will change the same settings the media query did before. Then, in JavaScript, you can use the same logic you had before to get the default setting and then add or remove this class. </p>
<p>You can set this class on our initial page load and toggle it if you click a button:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">font-family</span>: Montserrat;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">50px</span>;
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">500px</span>;
}

<span class="hljs-selector-class">.dark-mode</span> {
  <span class="hljs-attribute">background-color</span>: black;
  <span class="hljs-attribute">color</span>: white;
}
</code></pre>
<p>Now, how do you toggle this with a button? In your HTML file, add a button element with an event handler. Then, move both SVGs inside this button element and assign IDs for them. You will toggle the visibility of these icons from JavaScript:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Dark Mode<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"index.css"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"index.js"</span> <span class="hljs-attr">defer</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
      How to detect dark mode in CSS and in JavaScript? How can we override it
      manually with a toggle button? In this quick tutorial, we look into
      detecting dark mode in CSS and JavaScript, and then we create a toggle
      button with SVG to override the default behavior.
    <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"toggleDarkMode()"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"light-icon"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">cx</span>=<span class="hljs-string">"15"</span> <span class="hljs-attr">cy</span>=<span class="hljs-string">"15"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"6"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span> /&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">line</span>
          <span class="hljs-attr">id</span>=<span class="hljs-string">"ray"</span>
          <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span>
          <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"2"</span>
          <span class="hljs-attr">stroke-linecap</span>=<span class="hljs-string">"round"</span>
          <span class="hljs-attr">x1</span>=<span class="hljs-string">"15"</span>
          <span class="hljs-attr">y1</span>=<span class="hljs-string">"1"</span>
          <span class="hljs-attr">x2</span>=<span class="hljs-string">"15"</span>
          <span class="hljs-attr">y2</span>=<span class="hljs-string">"4"</span>
        &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">line</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#ray"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"rotate(45 15 15)"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#ray"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"rotate(90 15 15)"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#ray"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"rotate(135 15 15)"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#ray"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"rotate(180 15 15)"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#ray"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"rotate(225 15 15)"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#ray"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"rotate(270 15 15)"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#ray"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"rotate(315 15 15)"</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"dark-icon"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
          <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span>
          <span class="hljs-attr">d</span>=<span class="hljs-string">"
          M 23, 5
          A 12 12 0 1 0 23, 25
          A 12 12 0 0 1 23, 5"</span>
        /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>You can also unset the default appearance of the button element in CSS, except the cursor property. You should have that as pointer:</p>
<pre><code class="lang-css">. . .

<span class="hljs-selector-tag">button</span> {
  <span class="hljs-attribute">all</span>: unset;
  <span class="hljs-attribute">cursor</span>: pointer;
}

. . .
</code></pre>
<p>Now, let's implement the event handler in JavaScript. First you need to access the SVG icons by ID:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> lightIcon = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"light-icon"</span>);
<span class="hljs-keyword">const</span> darkIcon = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"dark-icon"</span>);

. . .
</code></pre>
<p>Then, add the <code>dark-mode</code> class to the <code>body</code> element in case you are in dark mode and hide one of the SVG icons based on the <code>darkMode</code> variable. You detect dark mode as you did before:</p>
<pre><code class="lang-js">. . .

<span class="hljs-comment">// Check if dark mode is preferred</span>
<span class="hljs-keyword">const</span> darkModeMediaQuery = <span class="hljs-built_in">window</span>.matchMedia(<span class="hljs-string">"(prefers-color-scheme: dark)"</span>);
<span class="hljs-keyword">let</span> darkMode = darkModeMediaQuery.matches;

<span class="hljs-comment">// Set dark-mode class on body if darkMode is true and pick icon</span>
<span class="hljs-keyword">if</span> (darkMode) {
  <span class="hljs-built_in">document</span>.body.classList.add(<span class="hljs-string">"dark-mode"</span>);
  darkIcon.setAttribute(<span class="hljs-string">"display"</span>, <span class="hljs-string">"none"</span>);
} <span class="hljs-keyword">else</span> {
  lightIcon.setAttribute(<span class="hljs-string">"display"</span>, <span class="hljs-string">"none"</span>);
}

. . .
</code></pre>
<p>And finally,  can implement the function that flips the <code>darkMode</code> property. This function toggles the <code>dark-mode</code> class on the body element, and toggles the SVG icons:</p>
<pre><code class="lang-js">. . .

<span class="hljs-comment">// Toggle dark mode on button click</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">toggleDarkMode</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Toggle darkMode variable</span>
  darkMode = !darkMode;

  <span class="hljs-comment">// Toggle dark-mode class on body</span>
  <span class="hljs-built_in">document</span>.body.classList.toggle(<span class="hljs-string">"dark-mode"</span>);

  <span class="hljs-comment">// Toggle light and dark icons</span>
  <span class="hljs-keyword">if</span> (darkMode) {
    lightIcon.setAttribute(<span class="hljs-string">"display"</span>, <span class="hljs-string">"block"</span>);
    darkIcon.setAttribute(<span class="hljs-string">"display"</span>, <span class="hljs-string">"none"</span>);
  } <span class="hljs-keyword">else</span> {
    lightIcon.setAttribute(<span class="hljs-string">"display"</span>, <span class="hljs-string">"none"</span>);
    darkIcon.setAttribute(<span class="hljs-string">"display"</span>, <span class="hljs-string">"block"</span>);
  }
}
</code></pre>
<p>Now, this works: by default, you still have the setting from the OS or browser. But once you click this button, it will override this manually.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Screenshot-2024-03-07-at-12.31.42.png" alt="Image" width="600" height="400" loading="lazy">
<em>Final look in light mode</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/Screenshot-2024-03-07-at-12.31.27.png" alt="Image" width="600" height="400" loading="lazy">
<em>Final look in dark mode</em></p>
<h2 id="heading-next-steps">Next Steps</h2>
<p>With all this in place, you have a functionality that takes the dark mode setting from the browser or the OS by default, and you can override it with a nice-looking toggle button. In the <a target="_blank" href="https://youtu.be/GUSUA72t7p0">YouTube version of this tutorial</a>, you can also learn how to use <code>localStorage</code> to save this setting for the next session.</p>
<p>If you want to learn more about SVGs, check out <a target="_blank" href="https://svg-tutorial.com/">SVG-tutorial.com</a>, where you can learn more about SVGs from beginner to advanced levels with many great examples.</p>
<p>If you want to use this behavior in a game, check out the <a target="_blank" href="https://www.freecodecamp.org/news/gorillas-game-in-javascript/">Gorillas JavaScript game tutorial</a>, where we build the entire game from scratch. It's a massive two-hour tutorial that covers drawing on a Canvas element with JavaScript and the whole game logic with plain JavaScript.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/2q5EufbUEQk" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>Subscribe to my channel for more JavaScript game development tutorials:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/undefined" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to use the Fullscreen API in JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ How do you run a game created for the web in fullscreen? In this quick tutorial, you'll see how to display a game or any other HTML element in fullscreen, how to exit fullscreen, and how to make a nice fullscreen toggle button in SVG. Recently I publ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-use-full-screen-api-in-js/</link>
                <guid isPermaLink="false">66c4c81129f446a67a4197ee</guid>
                
                    <category>
                        <![CDATA[ Fullscreen API ]]>
                    </category>
                
                    <category>
                        <![CDATA[ canvas ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SVG ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Hunor Márton Borbély ]]>
                </dc:creator>
                <pubDate>Thu, 22 Feb 2024 14:17:32 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/02/Untitled.022.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>How do you run a game created for the web in fullscreen? In this quick tutorial, you'll see how to display a game or any other HTML element in fullscreen, how to exit fullscreen, and how to make a nice fullscreen toggle button in SVG.</p>
<p>Recently I published a long <a target="_blank" href="https://www.freecodecamp.org/news/how-to-draw-a-gorilla-with-javascript-on-html-canvas/">JavaScript game tutorial</a>. While it was a very packed guide, there were still a few things we could not cover in it: how to display the game in fullscreen.</p>
<p>When you watch a video on YouTube, you have the option to also watch it on fullscreen. But did you know that the fullscreen feature isn't only for video elements?</p>
<p>In JavaScript, there’s a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API">Fullscreen API</a>. And it’s surprisingly simple to use. Here's a quick demo of what we're about to implement. Let's see how it works.</p>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/HunorMarton/embed/QWoRLXM" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<p>You can also <a target="_blank" href="https://www.youtube.com/watch?v=jX3mIQdQQ2w&amp;t=15s">watch this article as a video</a> on YouTube.</p>
<h2 id="heading-table-of-contents">Table of Contents:</h2>
<ul>
<li><a class="post-section-overview" href="#heading-how-to-enter-fullscreen-mode">How to Enter Fullscreen Mode</a></li>
<li><a class="post-section-overview" href="#heading-how-to-style-the-fullscreen">How to Style the Fullscreen</a></li>
<li><a class="post-section-overview" href="#heading-how-to-display-games-with-the-canvas-element-in-fullscreen">How to Display Games with the Canvas Element in Fullscreen</a></li>
<li><a class="post-section-overview" href="#heading-how-to-exit-fullscreen">How to Exit Fullscreen</a></li>
<li><a class="post-section-overview" href="#heading-how-to-code-a-fullscreen-icon-with-svg">How to Code a Fullscreen Icon with SVG</a></li>
<li><a class="post-section-overview" href="#heading-learn-more">Learn More</a></li>
</ul>
<h2 id="heading-how-to-enter-fullscreen-mode">How to Enter Fullscreen Mode</h2>
<p>Let’s say we have a simple website with some text. And at the bottom, we have a button that will display the text in full screen. We are going to refine the look of this button, but first, let’s get work on the main logic.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/Screenshot-2024-02-21-at-18.20.51.png" alt="Image" width="600" height="400" loading="lazy">
<em>A simple website with some text and a Toggle Fullscreen button</em></p>
<pre><code class="lang-css"><span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">font-family</span>: Montserrat;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">50px</span>;
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">500px</span>;
}
</code></pre>
<p>In the code above, we attached an event handler to the button in HTML. We can then implement the <code>toggleFullscreen</code> function logic in JavaScript.</p>
<p>In this function, all we have to do is call the <code>requestFullScreen</code> method on the <code>document</code>’s <code>documentElement</code> property. And that’s it:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">toggleFullscreen</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-built_in">document</span>.documentElement.requestFullscreen();
}
</code></pre>
<p>If you click the button, your website will pop into fullscreen.</p>
<h2 id="heading-how-to-style-the-fullscreen">How to Style the Fullscreen</h2>
<p>Before we cover how to exit full screen and create a nice-looking toggle button, let’s see a few other things.</p>
<p>What you might notice right away is that, with more space, your content might get a bit lost on a full screen. Make sure you have a responsive styling that looks good on every screen size.</p>
<p>You can even style the layout specifically for fullscreen. In CSS you can set a media query that only applies the styling in case the <code>display-mode</code> is <code>fullscreen</code>. </p>
<p>For instance, you can change the font-size, or change the <code>background-color</code> to have a distinct look on full screen.</p>
<pre><code class="lang-css"><span class="hljs-keyword">@media</span> (<span class="hljs-attribute">display-mode:</span> fullscreen) {
  <span class="hljs-selector-tag">body</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f9bb86</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.2em</span>;
  }
}
</code></pre>
<h2 id="heading-how-to-display-games-with-the-canvas-element-in-fullscreen">How to Display Games with the Canvas Element in Fullscreen</h2>
<p>In this case, we want to make a game that uses the <code>canvas</code> element to be fullscreen – like the <a target="_blank" href="https://www.freecodecamp.org/news/how-to-draw-a-gorilla-with-javascript-on-html-canvas/">Gorillas</a> game – we also need to resize the <code>canvas</code> element to fit the whole screen.</p>
<p>In this case, we can use the <code>windows</code>’s <code>resize</code> event. The event is triggered both when we simply resize the browser window, and when we enter or exit fullscreen mode. </p>
<p>With the <code>resize</code> event, we can resize the <code>canvas</code> element to fit the whole screen, update the scaling, adjust any other properties we need to change on resize and redraw the whole scene.</p>
<pre><code class="lang-js"><span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"resize"</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-comment">// Resize canvas element</span>
  canvas.width = <span class="hljs-built_in">window</span>.innerWidth;
  canvas.height = <span class="hljs-built_in">window</span>.innerHeight;

  <span class="hljs-comment">// Update scaling</span>
  <span class="hljs-comment">// . . .</span>

  <span class="hljs-comment">// Adjust size dependent properties</span>
  <span class="hljs-comment">// . . .</span>

  <span class="hljs-comment">// Redraw canvas</span>
  draw();
});
</code></pre>
<p>If you check the source code of the <a target="_blank" href="https://codepen.io/HunorMarton/pen/jOJZqvp">Gorillas game on CodePen</a>, you'll find similar steps.</p>
<h2 id="heading-how-to-exit-fullscreen">How to Exit Fullscreen</h2>
<p>Now that we know how to enter full screen, how do we exit from it?</p>
<p>By default, if you press the <code>Escape</code> key, the browser switches back to the normal view. In Google Chrome, you even get a notification at the top of the screen about this when you enter fullscreen mode.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/Screenshot-2024-02-21-at-17.47.03-2.png" alt="Image" width="600" height="400" loading="lazy">
<em>Google Chrome shows a notification on top of the screen once you enter full screen</em></p>
<p>What if you want to exit fullscreen mode when you click the HTML button? Let’s change our button’s behavior to toggle fullscreen on or off.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">toggleFullscreen</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">document</span>.fullscreenElement) {
    <span class="hljs-built_in">document</span>.documentElement.requestFullscreen();
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-built_in">document</span>.exitFullscreen();
  }
}
</code></pre>
<p>First, we start by checking if we are in fullscreen mode already. We can do this by checking the <code>document</code>’s <code>fullscreenElement</code> property. If it is undefined, then we enter fullscreen mode the same way we did before. And if we are already in fullscreen mode, then we can exit by calling the document’s <code>exitFullscreen</code> method. </p>
<p>It is really that simple. With a few lines of code, we can implement the logic for a fullscreen toggle button.</p>
<h2 id="heading-how-to-code-a-fullscreen-icon-with-svg">How to Code a Fullscreen Icon with SVG</h2>
<p>If you follow <a target="_blank" href="https://www.freecodecamp.org/news/author/hunor/">my tutorials</a>, you know I love creative coding, and <a target="_blank" href="https://www.freecodecamp.org/news/svg-tutorial-learn-to-code-images/">drawing from code</a>. So let’s update the look of our button, to make it look similar to what we have on YouTube.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/Screenshot-2024-02-21-at-18.06.07.png" alt="Image" width="600" height="400" loading="lazy">
<em>Fullscreen icon</em></p>
<p>Let’s create an SVG image within our button. If you check the source code of YouTube, you will see that they also use an SVG.</p>
<p>Let’s define an SVG element within HTML. We'll set its size to 30 x 30 and define a <code>path</code> element:</p>
<pre><code class="lang-html">. . .

<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"toggleFullscreen()"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"30"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
      <span class="hljs-attr">stroke</span>=<span class="hljs-string">"black"</span>
      <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"3"</span>
      <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span>
      <span class="hljs-attr">d</span>=<span class="hljs-string">"
        M 10, 2 L 2,2 L 2, 10
        M 20, 2 L 28,2 L 28, 10
        M 28, 20 L 28,28 L 20, 28
        M 10, 28 L 2,28 L 2, 20"</span>
    /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

. . .
</code></pre>
<p>To style the path, we set its color with the <code>stroke</code> property, set its <code>stroke-width</code>, and made sure that we didn't end up with a filled shape. SVG paths by default are filled, so we need to set explicitly that we don’t want to <code>fill</code> this shape.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/Screenshot-2024-02-21-at-18.02.10.png" alt="Image" width="600" height="400" loading="lazy">
<em>Using the move-to and line-to commands within an SVG path</em></p>
<p>Then we defined the path with a few move-to and line-to commands. We can set these commands as a string in the <code>d</code> attribute of the path element.</p>
<p>We started with a move-to command: <code>M 10, 2</code>. The letter <code>M</code> signifies that we have a move-to command, and the 10 and 2 are the <code>x</code> and <code>y</code> coordinates of this command. We moved to the start of one of the four lines.</p>
<p>Then we continued the path with a line-to command that moves to the corner, and then with another line-to command. The line-to command works in a similar way. It starts with the letter <code>L</code>, then we set an <code>x, y</code> coordinates where the line should go to.</p>
<p>Then we did the same with the other corners. We move to the next line segment with another move-to command and draw a line with two more line-to commands.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/Untitled.021.png" alt="Image" width="600" height="400" loading="lazy">
<em>Drawing a path on a Canvas element with JavaScript has some similarities to defining a path in SVG</em></p>
<p><strong>Note</strong>: If you read my <a target="_blank" href="https://www.freecodecamp.org/news/how-to-draw-a-gorilla-with-javascript-on-html-canvas/">previous tutorial</a> on how to make the gorillas game, then you might have noticed that we had something similar there. We also drew paths with move to and line to. Except that there we were drawn on a <code>canvas</code> element with JavaScript, and now we have the commands as a string within the HTML file.</p>
<h3 id="heading-how-to-toggle-the-icons-appearance">How to Toggle the Icon's Appearance</h3>
<p>Now the SVG is looking great, but what if we want to have a different look when we are in fullscreen mode? On YouTube, when we enter fullscreen mode, the button switches to a different icon.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/Screenshot-2024-02-21-at-18.06.33.png" alt="Image" width="600" height="400" loading="lazy">
<em>The two faces of the fullscreen icon</em></p>
<p>You can do this in different ways. The easiest way is probably to define another path, within the same SVG element with a different look. Then make this path transparent by default. We are going to toggle the visibility of these two paths in JavaScript.</p>
<pre><code class="lang-html">. . .

<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"toggleFullscreen()"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"30"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
      <span class="hljs-attr">id</span>=<span class="hljs-string">"enter-fullscreen"</span>
      <span class="hljs-attr">stroke</span>=<span class="hljs-string">"black"</span>
      <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"3"</span>
      <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span>
      <span class="hljs-attr">d</span>=<span class="hljs-string">"
        M 10, 2 L 2,2 L 2, 10
        M 20, 2 L 28,2 L 28, 10
        M 28, 20 L 28,28 L 20, 28
        M 10, 28 L 2,28 L 2, 20"</span>
    /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
      <span class="hljs-attr">id</span>=<span class="hljs-string">"exit-fullscreen"</span>
      <span class="hljs-attr">stroke</span>=<span class="hljs-string">"transparent"</span>
      <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"3"</span>
      <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span>
      <span class="hljs-attr">d</span>=<span class="hljs-string">"
        M 10, 2 L 10,10 L 2, 10
        M 20, 2 L 20,10 L 28, 10
        M 28, 20 L 20,20 L 20, 28
        M 10, 28 L 10,20 L 2, 20"</span>
    /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

. . .
</code></pre>
<p>This second path is very similar to the previous one. Except that we used different coordinates for some of the line-to commands.</p>
<p>Then we set unique IDs for both of these paths, and we update our toggle function in JavaScript. In JavaScript, we get a reference to these paths by ID, and then in the toggle button’s event handler, we can switch the visibility of these elements back and forth.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> enterFullscreen = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"enter-fullscreen"</span>);
<span class="hljs-keyword">const</span> exitFullscreen = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"exit-fullscreen"</span>);

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">toggleFullscreen</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">document</span>.fullscreenElement) {
    <span class="hljs-built_in">document</span>.documentElement.requestFullscreen();
    enterFullscreen.setAttribute(<span class="hljs-string">"stroke"</span>, <span class="hljs-string">"transparent"</span>);
    exitFullscreen.setAttribute(<span class="hljs-string">"stroke"</span>, <span class="hljs-string">"black"</span>);
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-built_in">document</span>.exitFullscreen();
    enterFullscreen.setAttribute(<span class="hljs-string">"stroke"</span>, <span class="hljs-string">"black"</span>);
    exitFullscreen.setAttribute(<span class="hljs-string">"stroke"</span>, <span class="hljs-string">"transparent"</span>);
  }
}
</code></pre>
<p>Now if you click this button, it toggles the fullscreen mode and changes its own appearance.</p>
<h2 id="heading-learn-more">Learn More</h2>
<p>If you want to learn more about SVGs, check out <a target="_blank" href="http://SVG-Tutorial.com">SVG-Tutorial.com</a> where you can find a lot of examples from the basics to more advanced levels. It’s a free site and you can also find the example that we discussed in this article.</p>
<p>To use the button to run a JavaScript game in full screen, check out the whole JavaScript Game Tutorial on how to remake the classic Gorillas game here on <a target="_blank" href="https://www.freecodecamp.org/news/how-to-draw-a-gorilla-with-javascript-on-html-canvas/">freeCodeCamp</a> or on <a target="_blank" href="https://www.youtube.com/watch?v=2q5EufbUEQk&amp;t=2337s">YouTube</a>. It’s a massive tutorial that covers things from drawing on an HTML Canvas element, to the entire game logic, from event handling, through the animation loop, hit detection, and even AI logic, for the enemy gorilla.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/2q5EufbUEQk" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>You can subscribe to my channel for more JavaScript game development tutorials:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/undefined" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Draw with JavaScript on an HTML Canvas Element – Gorilla Example ]]>
                </title>
                <description>
                    <![CDATA[ Drawing from code can be fun for many reasons. You can generate art that follows a certain logic. You can create animations by moving only parts of an image. And you can even build up a whole game as I covered in this tutorial. In my last article, we... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-draw-a-gorilla-with-javascript-on-html-canvas/</link>
                <guid isPermaLink="false">66c4c80829f446a67a4197ec</guid>
                
                    <category>
                        <![CDATA[ canvas ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Hunor Márton Borbély ]]>
                </dc:creator>
                <pubDate>Thu, 15 Feb 2024 16:30:12 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/02/Thumbnail.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Drawing from code can be fun for many reasons. You can generate art that follows a certain logic. You can create animations by moving only parts of an image. And you can even build up a whole game as I covered in <a target="_blank" href="https://www.freecodecamp.org/news/gorillas-game-in-javascript/">this tutorial</a>.</p>
<p>In my last article, we focused on the <a target="_blank" href="https://www.freecodecamp.org/news/drawing-on-a-canvas-element-with-javascript/">basics of drawing</a>. Now, let's see a concrete example and explore how to use JavaScript to draw a Gorilla. </p>
<p>You don't need to have any prerequisites for this tutorial. Even if you missed the basics, you can start right away. We are only going to have some simple HTML and a plain JavaScript file that you can run directly in the browser.</p>
<p>By the end, you'll know how to draw a gorilla with JS.‌</p>
<h2 id="heading-table-of-contents">Table of Contents:</h2>
<ol>
<li><a class="post-section-overview" href="#heading-how-to-define-a-canvas">How to Define a Canvas</a></li>
<li><a class="post-section-overview" href="#heading-how-to-turn-the-coordinate-system-upside-down">How to Turn the Coordinate System Upside Down</a></li>
<li><a class="post-section-overview" href="#heading-how-to-draw-the-legs-of-the-gorilla">How to Draw the Body of the Gorilla</a></li>
<li><a class="post-section-overview" href="#heading-how-to-draw-the-arms-of-the-gorilla">How to Draw the Legs of the gorilla</a></li>
<li><a class="post-section-overview" href="#heading-how-to-draw-the-face-of-the-gorilla">How to Draw the Arms of the Gorilla</a></li>
<li><a class="post-section-overview" href="#heading-how-to-draw-the-face-of-the-gorilla">How to Draw the Face of the Gorilla</a></li>
<li><a class="post-section-overview" href="#heading-next-steps">Next Steps</a></li>
</ol>
<h2 id="heading-how-to-define-a-canvas">How to Define a Canvas</h2>
<p>To draw our gorilla, first let's define a simple HTML file with a Canvas element. Then we'll see how to access it from JavaScript. </p>
<p>In the HTML file, in the header, we'll add our JavaScript file. Note that I’m using the <code>defer</code> keyword to make sure the script only executes once the rest of the document is parsed.</p>
<p>In the body, we'll add a <code>canvas</code> element. We set its size to 500 x 500 and set an ID.</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Gorilla<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"index.js"</span> <span class="hljs-attr">defer</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">canvas</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"gorilla"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"500"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"500"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">canvas</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Then, let's create a JavaScript file. In this file, we'll first get the canvas element by ID. Then, we'll get the rendering context of the canvas element. This is a built-in API with many methods and properties that we can use to draw on the canvas.</p>
<pre><code class="lang-js"><span class="hljs-comment">// The canvas element and its drawing context </span>
<span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"gorilla"</span>); 
<span class="hljs-keyword">const</span> ctx = canvas.getContext(<span class="hljs-string">"2d"</span>);

. . .
</code></pre>
<p>Before we get to drawing, first let's make our life easier by turning the coordinate system upside down.</p>
<h2 id="heading-how-to-turn-the-coordinate-system-upside-down">How to Turn the Coordinate System Upside Down</h2>
<p>When we use canvas, we have a coordinate system with the origin at the top-left corner of the canvas that grows to the right and downwards. This is aligned with how websites work in general. Things go from left to right and top to bottom. </p>
<p>This is the default, but we can change it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/gorilla-with-and-without-transforming-and-scaling.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>The gorilla with and without transforming and scaling the coordinate system</em></p>
<p>In our case, it is more convenient to go from the bottom to the top. Then the gorilla can stand at the bottom, and we don’t have to figure out where the bottom of the canvas is.</p>
<p>We can use the <code>translate</code> method to shift the entire coordinate system to the bottom-middle of the canvas. We'll move the coordinate system down along the Y-axis by the size of the canvas, and to the right along the X-axis by half the size of the canvas.</p>
<p>Once we do this, the Y-coordinate is still growing downwards. We can flip it using the <code>scale</code> method. Setting a negative number for the vertical direction will flip the entire coordinate system upside down.</p>
<pre><code class="lang-js"><span class="hljs-comment">// The canvas element and its drawing context </span>
<span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"gorilla"</span>); 
<span class="hljs-keyword">const</span> ctx = canvas.getContext(<span class="hljs-string">"2d"</span>);

ctx.translate(<span class="hljs-number">250</span>, <span class="hljs-number">500</span>);
ctx.scale(<span class="hljs-number">1</span>, <span class="hljs-number">-1</span>);

. . .
</code></pre>
<p>‌We have to do this before we paint anything on the canvas because the <code>translate</code> and <code>scale</code> methods do not actually move anything that's already on the canvas. But anything we paint after these method calls will be painted according to this new coordinate system.</p>
<h2 id="heading-how-to-draw-the-gorilla">How to Draw the Gorilla</h2>
<p>Now let's look into drawing the gorilla. We'll break this down into multiple steps. First, we'll draw the body, then the legs, the arms, and finally the face of the gorilla.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/drawing-gorilla-steps.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>We'll draw the gorilla in multiple steps</em></p>
<h3 id="heading-how-to-draw-the-body-of-the-gorilla">How to draw the body of the gorilla</h3>
<p>We'll draw the body of the gorilla as a path. Paths start with the <code>beginPath</code> method and end with either calling the <code>fill</code> or the <code>stroke</code> method – or both. </p>
<p>In between, we'll build the path by calling path-building methods. In this case, to build up the body of the gorilla we'll use the <code>moveTo</code> and a bunch of <code>lineTo</code> methods.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/Screenshot-2024-02-12-at-23.53.57.png" alt="Image" width="600" height="400" loading="lazy">
<em>The body of the gorilla. The image shows the path we're filling.</em></p>
<p>We'll set the fill style to black, and then we'll begin a path. Move to a starting position and then draw straight lines to draw the silhouette of the gorilla. Once we're finished, we'll fill the shape with the <code>fill</code> method.</p>
<pre><code class="lang-js">. . .

ctx.fillStyle = <span class="hljs-string">"black"</span>;

<span class="hljs-comment">// Draw the Body of the Gorilla</span>
ctx.beginPath();
ctx.moveTo(<span class="hljs-number">-68</span>, <span class="hljs-number">72</span>);
ctx.lineTo(<span class="hljs-number">-80</span>, <span class="hljs-number">176</span>);

ctx.lineTo(<span class="hljs-number">-44</span>, <span class="hljs-number">308</span>);
ctx.lineTo(<span class="hljs-number">0</span>, <span class="hljs-number">336</span>);
ctx.lineTo(+<span class="hljs-number">44</span>, <span class="hljs-number">308</span>);

ctx.lineTo(+<span class="hljs-number">80</span>, <span class="hljs-number">176</span>);
ctx.lineTo(+<span class="hljs-number">68</span>, <span class="hljs-number">72</span>);
ctx.fill();

. . .
</code></pre>
<p>In case you are wondering how I came up with these coordinates, I actually started with an initial sketch with pen and paper. I tried to estimate the coordinates, tried them with code, and then adjusted them until they started getting the right shape. Of course, you might have other methods as well.</p>
<h3 id="heading-how-to-draw-the-legs-of-the-gorilla">How to draw the legs of the gorilla</h3>
<p>We'll draw the legs the same way. We could even continue the same path we used before, but it might be easier to see what's happening if we break this down into separate paths.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/Screenshot-2024-02-12-at-23.09.59.png" alt="Image" width="600" height="400" loading="lazy">
<em>Drawing the legs as two separate paths</em></p>
<pre><code class="lang-js">. . .

<span class="hljs-comment">// Draw the Left Leg</span>
ctx.beginPath();
ctx.moveTo(<span class="hljs-number">0</span>, <span class="hljs-number">72</span>);
ctx.lineTo(<span class="hljs-number">-28</span>, <span class="hljs-number">0</span>);
ctx.lineTo(<span class="hljs-number">-80</span>, <span class="hljs-number">0</span>);
ctx.lineTo(<span class="hljs-number">-68</span>, <span class="hljs-number">72</span>);
ctx.fill();

<span class="hljs-comment">// Draw the Right Leg</span>
ctx.beginPath();
ctx.moveTo(<span class="hljs-number">0</span>, <span class="hljs-number">72</span>);
ctx.lineTo(+<span class="hljs-number">28</span>, <span class="hljs-number">0</span>);
ctx.lineTo(+<span class="hljs-number">80</span>, <span class="hljs-number">0</span>);
ctx.lineTo(+<span class="hljs-number">68</span>, <span class="hljs-number">72</span>);
ctx.fill();

. . .
</code></pre>
<p>The fill color in this case is also going to be black. Why? Because that's what we set the <code>fillStyle</code> property to, the last time we set its value. Every path that follows this statement will use this color until we change its value.</p>
<h3 id="heading-how-to-draw-the-arms-of-the-gorilla">How to draw the arms of the gorilla</h3>
<p>While the body and the legs are relatively simple parts of the gorilla, the arms are a bit more complicated. We'll draw them as a curve.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/Screenshot-2024-02-12-at-23.12.03.png" alt="Image" width="600" height="400" loading="lazy">
<em>Drawing the arms as a curve</em></p>
<p>Let’s start with the left arm. The main part of this is actually only two lines of code. We'll use the <code>moveTo</code> method to move to the shoulder of the gorilla, then from there, we'll draw the arm as a quadratic curve with the <code>quadraticCurveTo</code> method.</p>
<p>A quadratic curve is a simple curve with one control point. As the curve goes from the starting point (which we'll set with <code>moveTo</code>), the curve bends towards this control point (set as the first two arguments of the <code>quadraticCurveTo</code> method) as it reaches its end position (set as the last two arguments).</p>
<pre><code class="lang-js">. . .

ctx.strokeStyle = <span class="hljs-string">"black"</span>;
ctx.lineWidth = <span class="hljs-number">70</span>;

<span class="hljs-comment">// Draw the Left Arm</span>
ctx.beginPath();
ctx.moveTo(<span class="hljs-number">-56</span>, <span class="hljs-number">200</span>);
ctx.quadraticCurveTo(<span class="hljs-number">-176</span>, <span class="hljs-number">180</span>, <span class="hljs-number">-112</span>, <span class="hljs-number">48</span>);
ctx.stroke();

<span class="hljs-comment">// Draw the Right Arm</span>
ctx.beginPath();
ctx.moveTo(+<span class="hljs-number">56</span>, <span class="hljs-number">200</span>);
ctx.quadraticCurveTo(+<span class="hljs-number">176</span>, <span class="hljs-number">180</span>, +<span class="hljs-number">112</span>, <span class="hljs-number">48</span>);
ctx.stroke();

. . .
</code></pre>
<p>We'll draw the hands as strokes. Instead of ending the path with the <code>fill</code> method, we'll use the <code>stroke</code> method.</p>
<p>We'll also set up the styling differently. Instead of using the <code>fillStyle</code> property, here we'll set the color with <code>strokeStyle</code> and give thickness to the arms with the <code>lineWidth</code> property.</p>
<p>Drawing the right arm is the same, except the horizontal coordinates are flipped. The negative numbers have a positive sign now.</p>
<p>As a result, our gorillas should start to gain shape. They still don’t have a face, but we have the whole silhouette now.</p>
<h3 id="heading-how-to-draw-the-face-of-the-gorilla">How to draw the face of the gorilla</h3>
<p>The face of the gorilla comes together from multiple different parts. First, we'll draw the facial mask with three circles.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/gorilla-facial-mask.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>We draw the facial mask as three circles</em></p>
<p>Unfortunately, we don’t have a simple fill circle method, as we have in the case of rectangles. We have to draw an <code>arc</code> instead.</p>
<p>An <code>arc</code> method can be called as part of a path. We'll start each circle with the <code>beginPath</code> method and end with the <code>fill</code> method.</p>
<pre><code class="lang-js">. . .

ctx.fillStyle = <span class="hljs-string">"lightgray"</span>;

<span class="hljs-comment">// Draw the Facial Mask</span>
ctx.beginPath();
ctx.arc(<span class="hljs-number">0</span>, <span class="hljs-number">252</span>, <span class="hljs-number">36</span>, <span class="hljs-number">0</span>, <span class="hljs-number">2</span> * <span class="hljs-built_in">Math</span>.PI);
ctx.fill();

ctx.beginPath();
ctx.arc(<span class="hljs-number">-14</span>, <span class="hljs-number">280</span>, <span class="hljs-number">16</span>, <span class="hljs-number">0</span>, <span class="hljs-number">2</span> * <span class="hljs-built_in">Math</span>.PI);
ctx.fill();

ctx.beginPath();
ctx.arc(+<span class="hljs-number">14</span>, <span class="hljs-number">280</span>, <span class="hljs-number">16</span>, <span class="hljs-number">0</span>, <span class="hljs-number">2</span> * <span class="hljs-built_in">Math</span>.PI);
ctx.fill();

. . .
</code></pre>
<p>The <code>arc</code> method has a lot of properties. This might look a bit scary, but we only need to focus on the first 3 when drawing circles:</p>
<ul>
<li>The first two arguments are <code>x</code> and <code>y</code>, the center coordinates of the arc.</li>
<li>The third argument is the <code>radius</code>.</li>
<li>Then the last two arguments are the <code>startAngle</code> and the <code>endAngle</code> of the arc in radians. Because here we want to have a full circle and not an arc, we'll start with 0 and end at a full circle. A full circle in radians is two times Pi.</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/Screenshot-2024-02-12-at-23.17.08.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If these last two properties are confusing, don't worry about it. What's important is that when we draw circles, they are always <code>0</code> and <code>2 * Math.Pi</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/gorilla-final-steps.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>We draw the eyes, the nostrils, and the mouth</em></p>
<p>Then we draw the eyes of the gorilla as two other circles. Here the center coordinates of the circles are the same as the bigger gray circles around them. Only their radius and their fill color are different.</p>
<pre><code class="lang-js">. . .

ctx.fillStyle = <span class="hljs-string">"black"</span>;

<span class="hljs-comment">// Draw the Left Eye</span>
ctx.beginPath();
ctx.arc(<span class="hljs-number">-14</span>, <span class="hljs-number">280</span>, <span class="hljs-number">6</span>, <span class="hljs-number">0</span>, <span class="hljs-number">2</span> * <span class="hljs-built_in">Math</span>.PI);
ctx.fill();

<span class="hljs-comment">// Draw the Right Eye</span>
ctx.beginPath();
ctx.arc(+<span class="hljs-number">14</span>, <span class="hljs-number">280</span>, <span class="hljs-number">6</span>, <span class="hljs-number">0</span>, <span class="hljs-number">2</span> * <span class="hljs-built_in">Math</span>.PI);
ctx.fill();

. . .
</code></pre>
<p>Then for the nose, we'll draw two short lines as nostrils. They are part of the same path, and we'll call the <code>moveTo</code> method in between to get from one side to the other. Before calling stroke at the end of this path, we'll update the <code>lineWidth</code> property. </p>
<pre><code class="lang-js">. . .

ctx.lineWidth = <span class="hljs-number">6</span>;

<span class="hljs-comment">// Draw the Nostrils</span>
ctx.beginPath();
ctx.moveTo(<span class="hljs-number">-14</span>, <span class="hljs-number">266</span>);
ctx.lineTo(<span class="hljs-number">-6</span>, <span class="hljs-number">260</span>);

ctx.moveTo(<span class="hljs-number">14</span>, <span class="hljs-number">266</span>);
ctx.lineTo(+<span class="hljs-number">6</span>, <span class="hljs-number">260</span>);
ctx.stroke();

. . .
</code></pre>
<p>And finally, we'll also add a path for the mouth. This could be part of the same path as the nose, because it has the same line width and color, but might be clearer to have them separate.</p>
<pre><code class="lang-js">. . .

<span class="hljs-comment">// Draw the Mouth</span>
ctx.beginPath();
ctx.moveTo(<span class="hljs-number">-20</span>, <span class="hljs-number">230</span>);
ctx.quadraticCurveTo(<span class="hljs-number">0</span>, <span class="hljs-number">245</span>, <span class="hljs-number">20</span>, <span class="hljs-number">230</span>);
ctx.stroke();
</code></pre>
<p>Let's draw another quadratic curve. This is similar to the one we used for the arms. The start and endpoint of this curve are on the same level, but the control point is a bit higher so the middle of the mouth is higher than the two sides.</p>
<h2 id="heading-next-steps">Next Steps</h2>
<p>Now that we have a basic gorilla, what can we do with it? We can build a whole game around it. In this <a target="_blank" href="https://www.freecodecamp.org/news/gorillas-game-in-javascript/">JavaScript Game Tutorial</a> we rebuild the 1991 classic game, Gorillas.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/Screenshot-2024-01-19-at-23.49.34.png" alt="Image" width="600" height="400" loading="lazy">
<em>Screenshot from the JavaScript Game Tutorial</em></p>
<p>For a deep dive, read the <a target="_blank" href="https://www.freecodecamp.org/news/gorillas-game-in-javascript/">full tutorial</a> where we build up a complete game with plain JavaScript. In this tutorial, we not only cover how to draw the gorillas and the city skyline but also implement the whole game logic. From event handling, through the animation loop, to hit detection.</p>
<p>For even more, you can also watch the <a target="_blank" href="https://www.youtube.com/watch?v=2q5EufbUEQk">extended tutorial on YouTube</a>. In the YouTube version, we also cover how to make the buildings destructible, how to animate the hand of the gorilla to follow the drag movement while aiming, have nicer graphics, and we add AI logic, so you can play against the computer.</p>
<p>Check it out to learn more:</p>
<p><a target="_blank" href="https://www.youtube.com/embed/2q5EufbUEQk?feature=oembed">Embedded content</a></p>
<p>You can subscribe to my channel for more JavaScript game development tutorials:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/undefined" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Draw with JavaScript on an HTML Canvas Element – Beginner's Guide ]]>
                </title>
                <description>
                    <![CDATA[ There are many ways to code graphics for the web. You can create art with CSS. You can code an SVG image as part of an HTML file. Or you can generate graphics from JavaScript using the Canvas API. In this article, we'll explore how to use JavaScript ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/drawing-on-a-canvas-element-with-javascript/</link>
                <guid isPermaLink="false">66c4c80129f446a67a4197ea</guid>
                
                    <category>
                        <![CDATA[ HTML ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Hunor Márton Borbély ]]>
                </dc:creator>
                <pubDate>Thu, 08 Feb 2024 00:08:11 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/02/Gorillas-Keynote.001-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>There are many ways to code graphics for the web. You can create <a target="_blank" href="https://codepen.io/search/pens?q=css+art">art with CSS</a>. You can <a target="_blank" href="https://www.freecodecamp.org/news/svg-tutorial-learn-to-code-images/">code an SVG</a> image as part of an HTML file. Or you can generate graphics from JavaScript using the Canvas API.</p>
<p>In this article, we'll explore how to use JavaScript to draw some basic shapes. You don't need to have any prerequisites for this tutorial. We are only going to have a simple HTML and a plain JavaScript file that you can run directly in the browser.</p>
<p>You might be wondering – would anyone code an image from JavaScript? For one, you can generate graphics dynamically based on some variables. For instance, you can create a diagram. Or even better, we can create an entire game with JavaScript as we cover it in <a target="_blank" href="https://www.freecodecamp.org/news/gorillas-game-in-javascript/">this tutorial</a>.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.freecodecamp.org/news/gorillas-game-in-javascript/">https://www.freecodecamp.org/news/gorillas-game-in-javascript/</a></div>
<p>The following article focuses on part of the tutorial above, and teaches you the basics of drawing with JS.</p>
<h2 id="heading-the-canvas-element">The Canvas Element</h2>
<p>To draw on the screen, first we need to define a canvas element in HTML.</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Canvas<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"index.js"</span> <span class="hljs-attr">defer</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">canvas</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"myCanvas"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"1000"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"1000"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">canvas</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>This element has an ID, so that we can access it in JavaScript. Here we also set its size. If the size is dynamic, we can also set this size in JavaScript as we do in the tutorial above.</p>
<p>We defined a <code>&lt;canvas&gt;</code> element in HTML. How do we paint things on it?</p>
<p>Let's create a separate JavaScript file, where we'll add the rest of the code. We'll load this file with the script element above. Then in <code>index.js</code>, first, we'll get the canvas element by ID and get its rendering context.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"myCanvas"</span>); 
<span class="hljs-keyword">const</span> ctx = canvas.getContext(<span class="hljs-string">"2d"</span>); 

. . .
</code></pre>
<p>This is a built-in API with many methods and properties that we can use to draw on the canvas. In the next few sections, we'll continue with this file and see a few examples of how to use this API.</p>
<h3 id="heading-how-to-draw-a-rectangle"><strong>How to Draw a Rectangle</strong></h3>
<p>Let’s look at a few quick examples. The most basic thing we can do is to fill a rectangle.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-21-at-23.00.23-1.png" alt="Screenshot-2024-01-21-at-23.00.23-1" width="600" height="400" loading="lazy">
<em>Using the <code>fillRect` method to fill a rectangle</code></em></p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"myCanvas"</span>);
<span class="hljs-keyword">const</span> ctx = canvas.getContext(<span class="hljs-string">"2d"</span>);

ctx.fillStyle = <span class="hljs-string">"#58A8D8"</span>;

ctx.fillRect(<span class="hljs-number">200</span>, <span class="hljs-number">200</span>, <span class="hljs-number">440</span>, <span class="hljs-number">320</span>);
</code></pre>
<p>With the <code>fillRect</code> method, we specify the top left coordinate of our rectangle (200, 200), and we set its width and height (440, 320).</p>
<p>By default, the fill color is going to be black. We can change it by setting the <code>fillStyle</code> property.</p>
<p>The way canvas works is that we have to set up drawing parameters before we paint, and not the other way around. It’s not like we paint a rectangle, and then we can change its color. Once something is on the canvas, it stays as it is.</p>
<p>You can think of it like a real canvas, where you also pick the color with your brush before you start painting with it. Then once you've painted something you can either cover it, by painting something over it, or you can try to clear the canvas. But you can’t change existing parts, really. That’s why we set the color here up front and not afterward.</p>
<h3 id="heading-how-to-fill-a-path"><strong>How to Fill a Path</strong></h3>
<p>We can of course draw more complicated shapes as well. We can define a path, like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-21-at-23.01.23-1.png" alt="Screenshot-2024-01-21-at-23.01.23-1" width="600" height="400" loading="lazy">
<em>Filling a path</em></p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"myCanvas"</span>);
<span class="hljs-keyword">const</span> ctx = canvas.getContext(<span class="hljs-string">"2d"</span>);

ctx.fillStyle = <span class="hljs-string">"#58A8D8"</span>;

ctx.beginPath();
ctx.moveTo(<span class="hljs-number">200</span>, <span class="hljs-number">200</span>);
ctx.lineTo(<span class="hljs-number">500</span>, <span class="hljs-number">350</span>);
ctx.lineTo(<span class="hljs-number">200</span>, <span class="hljs-number">500</span>);
ctx.fill();
</code></pre>
<p>Paths start with the <code>beginPath</code> method and end with either calling the <code>fill</code> or the <code>stroke</code> method – or both. In between, we build the path by calling path-building methods.</p>
<p>In this example, we draw a triangle. We move to the <code>200,200</code> coordinate with the <code>moveTo</code> method. Then we call the <code>lineTo</code> method to move to the right side of our shape. And then we continue the path, by calling the <code>lineTo</code> method again to <code>200,500</code>.</p>
<p>None of this would be visible if we didn’t end with the <code>fill</code> method to fill the path we just built.</p>
<h3 id="heading-how-to-draw-a-stroke"><strong>How to Draw a Stroke</strong></h3>
<p>In a very similar way, we can also draw a line. Here, we'll start with the <code>beginPath</code> method again. We'll also build up the shape with a <code>moveTo</code> and two <code>lineTo</code> methods. The coordinates here are the same. But in the end, we don’t call the <code>fill</code> but the <code>stroke</code> method. This, instead of filling the shape, will draw the line we built.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-21-at-23.02.34-1.png" alt="Screenshot-2024-01-21-at-23.02.34-1" width="600" height="400" loading="lazy">
<em>Drawing a stroke</em></p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"myCanvas"</span>);
<span class="hljs-keyword">const</span> ctx = canvas.getContext(<span class="hljs-string">"2d"</span>);

ctx.strokeStyle = <span class="hljs-string">"#58A8D8"</span>;
ctx.lineWidth = <span class="hljs-number">30</span>;

ctx.beginPath();
ctx.moveTo(<span class="hljs-number">200</span>, <span class="hljs-number">200</span>);
ctx.lineTo(<span class="hljs-number">500</span>, <span class="hljs-number">350</span>);
ctx.lineTo(<span class="hljs-number">200</span>, <span class="hljs-number">500</span>);
ctx.stroke();
</code></pre>
<p>Strokes have different styling properties. Instead of the <code>fillStyle</code> property, we set <code>strokeStyle</code>. To this property – and also to <code>fillStyle</code> – we can assign any color value that is valid in CSS. To set the line width we use the <code>lineWidth</code> property.</p>
<p>We can also build more complex paths. In the example below, we draw a curve.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-21-at-23.05.04-1.png" alt="Screenshot-2024-01-21-at-23.05.04-1" width="600" height="400" loading="lazy">
<em>Drawing a curve</em></p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"myCanvas"</span>);
<span class="hljs-keyword">const</span> ctx = canvas.getContext(<span class="hljs-string">"2d"</span>);

ctx.strokeStyle = <span class="hljs-string">"#58A8D8"</span>;
ctx.lineWidth = <span class="hljs-number">30</span>;

ctx.beginPath();
ctx.moveTo(<span class="hljs-number">200</span>, <span class="hljs-number">300</span>);
ctx.quadraticCurveTo(<span class="hljs-number">500</span>, <span class="hljs-number">400</span>, <span class="hljs-number">800</span>, <span class="hljs-number">300</span>);
ctx.stroke();
</code></pre>
<p>A quadratic curve is a simple curve with one control point. As the curve goes from the starting point (which we set with <code>moveTo</code>), the curve bends towards this control point (set as the first two arguments of the <code>quadraticCurveTo</code> method) as it reaches its end position (set as the last two arguments).</p>
<h2 id="heading-next-steps">Next Steps</h2>
<p>With these very basic shapes, we can already get quite far. We use these methods to draw the gorillas in the <a target="_blank" href="https://www.freecodecamp.org/news/gorillas-game-in-javascript/">Gorillas - JavaScript Game Tutorial</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/gorilla-drawings.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>We fill a path to draw the body and the legs of the gorillas, and use strokes to draw the arms and the face</em></p>
<p>For a deep dive, read the <a target="_blank" href="https://www.freecodecamp.org/news/gorillas-game-in-javascript/">full tutorial</a> where we build up a complete game with plain JavaScript. In this tutorial, we not only cover how to draw the gorillas and the city skyline but also implement the whole game logic. From event handling, through the animation loop, to hit detection.</p>
<p>For even more, you can also watch the <a target="_blank" href="https://www.youtube.com/watch?v=2q5EufbUEQk">extended tutorial on YouTube</a>. In the YouTube version, we also cover how to make the buildings destructible, how to animate the hand of the gorilla to follow the drag movement while aiming, have nicer graphics, and we add AI logic, so you can play against the computer.</p>
<p>Check it out to learn more:</p>
<p><a target="_blank" href="https://www.youtube.com/embed/2q5EufbUEQk?feature=oembed">Embedded content</a></p>
<p>You can subscribe to my channel for more JavaScript game development tutorials:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/undefined" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ JavaScript Game Dev Tutorial – Build Gorillas with HTML Canvas + JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ In this JavaScript game tutorial, you'll learn how to create a modern version of the 1991 classic game Gorillas using plain JavaScript and the HTML canvas element. In this game, two gorillas throw explosive bananas at each other, and the first one to... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/gorillas-game-in-javascript/</link>
                <guid isPermaLink="false">66c4c805e7521bfd6862b3b0</guid>
                
                    <category>
                        <![CDATA[ Game Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Hunor Márton Borbély ]]>
                </dc:creator>
                <pubDate>Tue, 30 Jan 2024 18:18:55 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-21-at-16.02.31-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this JavaScript game tutorial, you'll learn how to create a modern version of the 1991 classic game Gorillas using plain JavaScript and the HTML canvas element.</p>
<p>In this game, two gorillas throw explosive bananas at each other, and the first one to hit the other one wins the game.</p>
<p>We'll build out the whole game from scratch here. First, you'll learn how to draw on a canvas element with JavaScript. You'll see how to draw the background, the buildings, the gorillas, and the bomb. We won't use any images here – we'll draw everything using code.</p>
<p>Then we'll add some interactions and add event handlers. We'll also cover how to aim, how to animate the bomb across the sky, and how to detect if the bomb hit the other gorilla, or a building.</p>
<p>Throughout the tutorial, we'll be using plain JavaScript. To get the most out of this tutorial, you should have a basic understanding of JavaScript. But even if you are a beginner, you can still follow along and learn as you go.</p>
<p>In this article, we'll simplify a couple of steps. For more detail, you can also watch the <a target="_blank" href="https://www.youtube.com/watch?v=2q5EufbUEQk">extended tutorial on YouTube</a>. In the YouTube version, we also cover how to make the buildings destructible, how to animate the hand of the gorilla to follow the drag movement while aiming, have nicer graphics, and we add AI logic, so you can play against the computer.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/2q5EufbUEQk" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>If you got stuck, you can also find the final source code of the game we are about to create on <a target="_blank" href="https://github.com/HunorMarton/gorillas">GitHub</a>.</p>
<h2 id="heading-table-of-contents">Table of Contents:</h2>
<ol>
<li><a class="post-section-overview" href="#heading-background-on-the-game">Background on the Game</a></li>
<li><a class="post-section-overview" href="#heading-project-setup">Project Setup</a></li>
<li><a class="post-section-overview" href="#heading-overview-of-the-game-logic">Overview of the Game Logic</a></li>
<li><a class="post-section-overview" href="#heading-how-to-draw-the-scene">How to Draw the Scene</a></li>
<li><a class="post-section-overview" href="#heading-how-to-turn-the-coordinate-system-upside-down">How to Turn the Coordinate System Upside Down</a></li>
<li><a class="post-section-overview" href="#heading-how-to-draw-the-game-elements">How to Draw the Game Elements</a></li>
<li><a class="post-section-overview" href="#heading-how-to-fit-the-size-of-the-city-to-the-browser-window">How to Fit the Size of the City to the Browser Window</a></li>
<li><a class="post-section-overview" href="#heading-how-the-gorilla-can-throw-the-bomb">How the Gorilla Can Throw the Bomb</a></li>
<li><a class="post-section-overview" href="#heading-how-the-gorilla-can-throw-the-bomb">How to Animate the Incoming Bomb</a></li>
<li><a class="post-section-overview" href="#heading-next-steps">Next Steps</a></li>
</ol>
<h2 id="heading-background-on-the-game">Background on the Game</h2>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/Gorillas_%28video_game%29">Gorillas</a> is a game from 1991. In this game, two gorillas are standing on the top of randomly generated buildings and take turns throwing explosive bananas at each other.</p>
<p>In each round, the players set the angle and velocity of the throw, and keep refining it, until they hit the other gorilla. The flying banana bomb is affected by gravity and the wind.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-07-at-22.58.23-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>The original Gorillas game from 1991 (source: retrogames.cz)</em></p>
<p>We are going to implement a modern version of this game. The original game did not support a mouse. Every round, the players had to type in the angle and the velocity with a keyboard. We are going to implement it with mouse support, and nicer graphics.</p>
<p>You can try out the extended version of the game on <a target="_blank" href="https://codepen.io/HunorMarton/pen/jOJZqvp">CodePen</a>. Give it a try before we get into it.</p>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/HunorMarton/embed/jOJZqvp" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<h2 id="heading-project-setup">Project Setup</h2>
<p>To implement this game, we are going to have a simple HTML, CSS, and JavaScript file. You can break down the JavaScript logic into multiple files if you want, but for the sake of simplicity, we have everything in one place.</p>
<p>Because we're using plain JavaScript and don’t use any libraries and third-party tools, we don’t need any compilers or builders. We can run things directly in the browser.</p>
<p>To simplify the process, I recommend installing the <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer">Live Server</a> VS Code extension. With this extension installed, you can simply right-click the HTML file and select ‘Open with Live Server’. This will run a live version of the game in the browser.</p>
<p>This means we don’t have to hit refresh in the browser every time we make a change in the code. It’s enough that we save the changes in the file and the browser will refresh automatically.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-18-at-21.29.22-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Running the game with the Live Server VS Code extension</em></p>
<h2 id="heading-overview-of-the-game-logic">Overview of the Game Logic</h2>
<p>Before we get to the details, let's walk through the main parts of the game.</p>
<p>The game is driven by the game <code>state</code>. This is a JavaScript object that serves as metadata for the game. From the size of buildings to the current position of the bomb, it includes many things.</p>
<p>The game state includes things like whose turn it is, are we aiming now, or is the bomb already flying in the air? And so on. These are all variables that we need to keep track of. When we draw the game scene, we'll draw it based on the game state.</p>
<p>Then we have the <code>draw</code> function. This function will draw almost everything we have on the screen. It paints the background, the buildings, the gorillas, and the banana bombs. This function paints the entire screen from top to bottom every time we call it.</p>
<p>We are going to add event handling to aim the bombs and we'll implement the <code>throwBomb</code> function that kicks off the animation loop. The <code>animate</code> function moves the bombs across the sky.</p>
<p>This function will be responsible for calculating the exact position of the bomb as it flies through the air at every animation cycle. On top of that, it also has to figure out when the movement ends. With every movement, we check if we hit a building or an enemy, or if the bomb got off-screen. We’ll add hit detection as well.</p>
<p>Now let's walk through our initial files.</p>
<h3 id="heading-the-initial-html-file">The initial HTML file</h3>
<p>Our initial HTML file will be very simple. In the header, we'll add a link to our stylesheet and our JavaScript file. Note that I’m using the <code>defer</code> keyword to make sure the script only executes once the rest of the document is parsed.</p>
<p>In the body, we'll add a <code>canvas</code> element. We are going to paint on this element with JavaScript. Almost everything we can see on the screen will be in this canvas element. Here's the code:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Gorillas<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"index.css"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"index.js"</span> <span class="hljs-attr">defer</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">canvas</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"game"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">canvas</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Later we are going to add more things into this file. We'll add info panels to show the angle and velocity of the current throw. But for now, that’s all we have.</p>
<h3 id="heading-the-initial-css-file">The initial CSS file</h3>
<p>Initially, our CSS is also very simple. We can’t style anything inside the canvas element, so here we only style the other elements we have.</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
}
</code></pre>
<p>‌Later, when we add more elements in HTML, we will also update this file. For now, let’s make sure that our canvas can fit the whole screen. By default, browsers tend to add a little margin or padding around the body. Let’s remove that.</p>
<h3 id="heading-the-main-parts-of-our-javascript-file">The Main Parts of Our JavaScript File</h3>
<p>Most of the logic will be in our JavaScript file. Let's walk through the main parts of this file, and define a few placeholder functions:</p>
<ul>
<li>We declare the game <code>state</code> object and a bunch of utility variables. This will contain the metadata of our game. For now, it's an empty object. We will initialize its value when we get to the <code>newGame</code> function.</li>
<li>Then we have references to every HTML element that we need to access from JavaScript. For now, we only have a reference to the <code>&lt;canvas&gt;</code> element. We access this element by ID.</li>
<li>We initialize the game state and paint the scene by calling the <code>newGame</code> function. This is the only top-level function call. This function is responsible for both initializing the game and resetting it.</li>
<li>We define the <code>draw</code> function that draws the whole scene on the canvas element based on the game state. We will draw the background, the buildings, the gorillas, and the bomb.</li>
<li>We set up event handlers for the <code>mousedown</code>, <code>mousemove</code>, and <code>mouseup</code> events. We are going to use these for aiming.</li>
<li>The <code>mouseup</code> event will trigger the <code>throwBomb</code> function that kicks off the main animation loop. The <code>animate</code> function will manipulate the state in every animation cycle and call the <code>draw</code> function to update the screen.</li>
</ul>
<pre><code class="lang-js"><span class="hljs-comment">// The state of the game</span>
<span class="hljs-keyword">let</span> state = {};
<span class="hljs-comment">// ...</span>

<span class="hljs-comment">// References to HTML elements</span>
<span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"game"</span>);
<span class="hljs-comment">// ...</span>

newGame();

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">newGame</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Initialize game state</span>
  state = {
    <span class="hljs-comment">// ...</span>
  };

  <span class="hljs-comment">// ...</span>

  draw();
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">draw</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// ...</span>
}

<span class="hljs-comment">// Event handlers</span>
<span class="hljs-comment">// ...</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">throwBomb</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// ...</span>
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params">timestamp</span>) </span>{
  <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>‌We will have a couple more utility functions, but they are less important. We'll discuss them as we go.</p>
<h3 id="heading-game-phases">Game Phases</h3>
<p>In the next step, we'll set up our initial game <code>state</code>. Before we get to the different parts of the state, let's talk about one of its most important properties: the game <code>phase</code> property.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/game-phases.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>The three game phases: the <code>aiming</code> phase, the <code>in flight</code> phase, and the <code>celebrating</code> phase</em></p>
<p>The game has three different phases. The game starts in the <code>aiming</code> phase, when the bomb is in the hand of a gorilla and the event handlers are active. ‌‌‌‌Then once you throw the bomb, the game moves on to the <code>in flight</code> phase. In this phase, the event handlers are deactivated and the <code>animate</code> function moves the bomb across the sky. We also add hit detection to know when should we stop the animation.</p>
<p>These two game phases repeat each other over and over again, until one of the gorillas hits the other. Once we hit the enemy, the game moves on to the <code>celebrating</code> phase. We draw a winning gorilla, show the congratulations screen, and a button to restart the game.</p>
<h3 id="heading-how-to-initialize-the-game">How to initialize the Game</h3>
<p>The game is initialized by the <code>newGame</code> function. This resets the game <code>state</code>, generates a new level, and calls the <code>draw</code> function to draw the whole scene.</p>
<p>Let’s walk through what we have initially in the <code>state</code> object:</p>
<ul>
<li>First, we have the game <code>phase</code> property that can be either <code>aiming</code>, <code>in flight</code>, or <code>celebrating</code>.</li>
<li>Then, the <code>currentPlayer</code> property tells us whose turn it is – the player on the left or the player on the right.</li>
<li>The <code>bomb</code> object describes the bomb’s current position and its velocity. Its initial position has to be aligned with the second building so we only set it once the level is generated.</li>
<li>The <code>buildings</code> array defines the position and size of the buildings that appear on the screen. We generate the metadata of the buildings with a utility function that we'll discuss later.</li>
</ul>
<pre><code class="lang-js"><span class="hljs-comment">// The state of the game</span>
<span class="hljs-keyword">let</span> state = {};

. . .

newGame();

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">newGame</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Initialize game state</span>
  state = {
    <span class="hljs-attr">phase</span>: <span class="hljs-string">"aiming"</span>, <span class="hljs-comment">// aiming | in flight | celebrating</span>
    <span class="hljs-attr">currentPlayer</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">bomb</span>: {
      <span class="hljs-attr">x</span>: <span class="hljs-literal">undefined</span>,
      <span class="hljs-attr">y</span>: <span class="hljs-literal">undefined</span>,
      <span class="hljs-attr">velocity</span>: { <span class="hljs-attr">x</span>: <span class="hljs-number">0</span>, <span class="hljs-attr">y</span>: <span class="hljs-number">0</span> },
    },
    <span class="hljs-attr">buildings</span>: generateBuildings(),
  };

  initializeBombPosition();

  draw();
}
</code></pre>
<p>‌We'll discuss the utility functions used above (<code>generateBuildings</code> and <code>initializeBombPosition</code>) in the next chapter as we draw the buildings and the bomb. For now, let's just add some placeholder functions to make sure we don't get an error from JavaScript.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">generateBuildings</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// ...</span>
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initializeBombPosition</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>Now that we have a skeleton of our application and we've initialized some of the states, let’s switch gears and start drawing on the canvas as we fill in the missing pieces of the state.</p>
<h2 id="heading-how-to-draw-the-scene">How to Draw the Scene</h2>
<p>The <code>draw</code> function paints the whole canvas based on the state. It draws the background and the buildings, draws the gorillas, and draws the bomb. The function can also draw different gorilla variations depending on the state. The gorilla looks different while aiming, celebrating, or waiting for impact.</p>
<p>We will use this function both for painting the initial scene and throughout our main animation loop.</p>
<p>For the initial paint, some of the features we cover here won't be necessary. For instance, we'll also cover how to draw the celebrating gorilla, while we'll only see that once the game is over. But we'll still cover it because this way we won’t have to get back to this function once we start animating the state.</p>
<p>Everything we draw in this function is based on the state, and it doesn't matter to the function if the game is in the initial state, or if we are further into the game.</p>
<p>We defined a <code>&lt;canvas&gt;</code> element in HTML. How do we paint things on it?</p>
<pre><code class="lang-html">. . .

<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">canvas</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"game"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">canvas</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>

. . .
</code></pre>
<p>In JavaScript first, we get the canvas element by ID. Then we set the size of the canvas to fill the whole browser window. And finally, we get its drawing context.</p>
<p>This is a built-in API with many methods and properties that we can use to draw on the canvas. Let's see a few examples of how to use this API.</p>
<pre><code class="lang-js">. . . 

<span class="hljs-comment">// The canvas element and its drawing context </span>
<span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"game"</span>); 
canvas.width = <span class="hljs-built_in">window</span>.innerWidth; 
canvas.height = <span class="hljs-built_in">window</span>.innerHeight; 
<span class="hljs-keyword">const</span> ctx = canvas.getContext(<span class="hljs-string">"2d"</span>); 

. . . 

function draw() {
  <span class="hljs-comment">// ... </span>
} 

. . .
</code></pre>
<h3 id="heading-example-drawing-a-rectangle">Example: Drawing a Rectangle</h3>
<p>Let’s look at a few quick examples. These are not part of our game yet, they'll just serve as an introduction.</p>
<p>The most basic thing we can do is to fill a rectangle.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-21-at-23.00.23-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Using the <code>fillRect</code> method to fill a rectangle</em></p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"game"</span>);
canvas.width = <span class="hljs-built_in">window</span>.innerWidth;
canvas.height = <span class="hljs-built_in">window</span>.innerHeight;
<span class="hljs-keyword">const</span> ctx = canvas.getContext(<span class="hljs-string">"2d"</span>);

ctx.fillStyle = <span class="hljs-string">"#58A8D8"</span>;

ctx.fillRect(<span class="hljs-number">200</span>, <span class="hljs-number">200</span>, <span class="hljs-number">440</span>, <span class="hljs-number">320</span>);
</code></pre>
<p>With the <code>fillRect</code> method, we specify the top left coordinate of our rectangle (200, 200), and we set its width and height (440, 320).</p>
<p>By default, the fill color is going to be black. We can change it by setting the <code>fillStyle</code> property.</p>
<p>The way canvas works is that we have to set up drawing parameters before we paint, and not the other way around. It’s not like we paint a rectangle, and then we can change its color. Once something is on the canvas, it stays as it is.</p>
<p>You can think of it like a real canvas, where you also pick the color with your brush before you start painting with it. Then once you've painted something you can either cover it, by painting something over it, or you can try to clear the canvas. But you can’t change existing parts, really. That’s why we set the color here up front and not afterward.</p>
<p>We are going to draw rectangles to fill the background and to show the buildings.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-21-at-19.30.36.png" alt="Image" width="600" height="400" loading="lazy">
<em>The background and the buildings are simple rectangles</em></p>
<h3 id="heading-example-filling-a-path">Example: Filling a Path</h3>
<p>We can of course draw more complicated shapes as well. We can define a path, like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-21-at-23.01.23-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Filling a path</em></p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"game"</span>);
canvas.width = <span class="hljs-built_in">window</span>.innerWidth;
canvas.height = <span class="hljs-built_in">window</span>.innerHeight;
<span class="hljs-keyword">const</span> ctx = canvas.getContext(<span class="hljs-string">"2d"</span>);

ctx.fillStyle = <span class="hljs-string">"#58A8D8"</span>;

ctx.beginPath();
ctx.moveTo(<span class="hljs-number">200</span>, <span class="hljs-number">200</span>);
ctx.lineTo(<span class="hljs-number">500</span>, <span class="hljs-number">350</span>);
ctx.lineTo(<span class="hljs-number">200</span>, <span class="hljs-number">500</span>);
ctx.fill();
</code></pre>
<p>Paths start with the <code>beginPath</code> method and end with either calling the <code>fill</code> or the <code>stroke</code> method – or both. In between, we build the path by calling path-building methods.</p>
<p>In this example, we draw a triangle. We move to the <code>300,300</code> coordinate with the <code>moveTo</code> method. Then we call the <code>lineTo</code> method to move to the right side of our shape. And then we continue the path, by calling the <code>lineTo</code> method again to <code>300,400</code>.</p>
<p>None of this would be visible if we didn’t end with the <code>fill</code> method to fill the path we just built.</p>
<p>We are going to fill paths to draw our gorilla and our bomb.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/paths-gorilla-drawing.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>We will use paths to draw the main body of the gorilla</em></p>
<h3 id="heading-example-drawing-a-stroke">‌Example: Drawing a Stroke</h3>
<p>In a very similar way, we can also draw a line. Here, we'll start with the <code>beginPath</code> method again. We'll also build up the shape with a <code>moveTo</code> and two <code>lineTo</code> methods. The coordinates here are the same. But in the end, we don’t call the <code>fill</code> but the <code>stroke</code> method. This, instead of filling the shape, will draw the line we built.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-21-at-23.02.34-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Drawing a stroke</em></p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"game"</span>);
canvas.width = <span class="hljs-built_in">window</span>.innerWidth;
canvas.height = <span class="hljs-built_in">window</span>.innerHeight;
<span class="hljs-keyword">const</span> ctx = canvas.getContext(<span class="hljs-string">"2d"</span>);

ctx.strokeStyle = <span class="hljs-string">"#58A8D8"</span>;
ctx.lineWidth = <span class="hljs-number">30</span>;

ctx.beginPath();
ctx.moveTo(<span class="hljs-number">200</span>, <span class="hljs-number">200</span>);
ctx.lineTo(<span class="hljs-number">500</span>, <span class="hljs-number">350</span>);
ctx.lineTo(<span class="hljs-number">200</span>, <span class="hljs-number">500</span>);
ctx.stroke();
</code></pre>
<p>Strokes have different styling properties. Instead of the <code>fillStyle</code> property, we set <code>strokeStyle</code>. To this property – and also to <code>fillStyle</code> – we can assign any color value that is valid in CSS. To set the line width we use the <code>lineWidth</code> property.</p>
<p>We can also build more complex paths. In the example below, we draw a curve. We are going to cover this in a bit more detail when we draw the arms of the gorillas.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-21-at-23.05.04-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Drawing a curve</em></p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"game"</span>);
canvas.width = <span class="hljs-built_in">window</span>.innerWidth;
canvas.height = <span class="hljs-built_in">window</span>.innerHeight;
<span class="hljs-keyword">const</span> ctx = canvas.getContext(<span class="hljs-string">"2d"</span>);

ctx.strokeStyle = <span class="hljs-string">"#58A8D8"</span>;
ctx.lineWidth = <span class="hljs-number">30</span>;

ctx.beginPath();
ctx.moveTo(<span class="hljs-number">200</span>, <span class="hljs-number">300</span>);
ctx.quadraticCurveTo(<span class="hljs-number">500</span>, <span class="hljs-number">400</span>, <span class="hljs-number">800</span>, <span class="hljs-number">300</span>);
ctx.stroke();
</code></pre>
<p>We are going to use the <code>stroke</code> method to draw the arms and the face of the gorilla.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/gorilla-stroke-drawing.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>We use the <code>stroke</code> method to draw the arms and the face of the gorilla</em></p>
<p>‌Now that we're finished with this introduction, let’s go back to our game and see what’s inside the <code>draw</code> function.</p>
<h2 id="heading-how-to-turn-the-coordinate-system-upside-down">How to Turn the Coordinate System Upside Down</h2>
<p>When we use canvas, we have a coordinate system with the origin at the top-left corner of the browser window that grows to the right and downwards. This is aligned with how websites work in general. Things go from left to right and top to bottom. This is the default, but we can change this.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-21-at-23.09.35-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>The default coordinate system</em></p>
<p>When we talk about games, it is more convenient to go from the bottom to the top. For instance, when we draw buildings, they can start at the bottom, and we don’t have to figure out where the bottom of the window is.</p>
<p>We can use the <code>translate</code> method to shift the entire coordinate system to the bottom-left corner. We just need to move the coordinate system down along the Y-axis by the size of the browser window.</p>
<p>Once we do this, the Y-coordinate is still growing downwards. We can flip it using the <code>scale</code> method. Setting a negative number for the vertical direction will flip the entire coordinate system upside down.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-21-at-23.09.32-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>The coordinate system upside down</em></p>
<pre><code class="lang-js"><span class="hljs-comment">// The canvas element and its drawing context </span>
<span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"game"</span>); 
canvas.width = <span class="hljs-built_in">window</span>.innerWidth; 
canvas.height = <span class="hljs-built_in">window</span>.innerHeight; 
<span class="hljs-keyword">const</span> ctx = canvas.getContext(<span class="hljs-string">"2d"</span>); 

. . . 

function draw() { 
  ctx.save(); 
  <span class="hljs-comment">// Flip coordinate system upside down </span>
  ctx.translate(<span class="hljs-number">0</span>, <span class="hljs-built_in">window</span>.innerHeight); 
  ctx.scale(<span class="hljs-number">1</span>, <span class="hljs-number">-1</span>); 

  <span class="hljs-comment">// Draw scene </span>
  drawBackground(); 
  drawBuildings();
  drawGorilla(<span class="hljs-number">1</span>);
  drawGorilla(<span class="hljs-number">2</span>);
  drawBomb(); 

  <span class="hljs-comment">// Restore transformation </span>
  ctx.restore(); 
}
</code></pre>
<p>‌Now let's implement the <code>draw</code> function. One of the first things we do is calling the <code>translate</code> and the <code>scale</code> methods to flip the coordinate system upside down.</p>
<p>We have to do this before we paint anything on the canvas because the <code>translate</code> and <code>scale</code> methods do not actually move anything on the canvas. If we painted anything on the canvas before, it would stay as it was.</p>
<p>Technically these methods change the transformation matrix. You can think of it as changing the coordinate system. Anything we paint after these methods will be painted according to this new coordinate system.</p>
<p>We also need to restore these transformations once we draw by calling the <code>restore</code> method. The <code>restore</code> method comes in a pair with the <code>save</code> method. <code>save</code> serves as a checkpoint that the <code>restore</code> method can get back to.</p>
<p>It's a common practice to start a drawing block by calling the <code>save</code> method and ending it with the <code>restore</code> method when we use transformations in between.</p>
<p>We need to call these two functions because the <code>translate</code> and <code>scale</code> methods accumulate. We are going to call the <code>draw</code> function multiple times. Without the <code>save</code> and <code>restore</code> methods, the coordinate system would keep on moving downwards each time we call the <code>draw</code> function, and it would eventually get completely off-screen.</p>
<p>Drawing the entire scene includes many parts. We'll break it down into separate draw functions. Now let’s start drawing by implementing these functions:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">drawBackground</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// ...</span>
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">drawBuildings</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// ...</span>
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">drawGorilla</span>(<span class="hljs-params">player</span>) </span>{
  <span class="hljs-comment">// ...</span>
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">drawBomb</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// ...</span>
}
</code></pre>
<h2 id="heading-how-to-draw-the-game-elements">How to Draw the Game Elements</h2>
<h3 id="heading-how-to-draw-the-background">How to draw the background</h3>
<p>When we draw on a canvas, the order matters. We'll start with the background, then we'll go layer by layer. In our case, the background is a simple rectangle.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-30-at-16.00.14.png" alt="Image" width="600" height="400" loading="lazy">
<em>The background sky</em></p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">drawBackground</span>(<span class="hljs-params"></span>) </span>{
  ctx.fillStyle = <span class="hljs-string">"#58A8D8"</span>;
  ctx.fillRect(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight);
}
</code></pre>
<p>We draw this rectangle the same way we did it in our introduction. First, we set the fill style, then we draw a rectangle with the <code>fillRect</code> method. Here we set the starting coordinates in the corner and set the size to fill the whole browser window.</p>
<p>We could also add a moon to the sky. Unfortunately, there's no fill circle method that would easily do this, so we'll skip it for now. On <a target="_blank" href="https://codepen.io/HunorMarton/full/jOJZqvp">CodePen</a> you can find a version that also draws a moon in the <code>drawBackground</code> function.</p>
<h3 id="heading-how-to-draw-the-buildings">How to draw the buildings</h3>
<p>Drawing the buildings has two parts. First, we need to generate metadata for the buildings when we initialize the level. Then we implement the <code>drawBuildings</code> function that paints the buildings based on this metadata.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-19-at-17.02.52-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>The buildings and their metadata</em></p>
<h3 id="heading-buildings-metadata">Buildings Metadata</h3>
<p>In the <code>newGame</code> function, we called the <code>generateBuildings</code> function to initialize the <code>buildings</code> property in our game <code>state</code>. We haven't implemented this function yet.</p>
<pre><code class="lang-js">  . . .

  state = {
    <span class="hljs-attr">phase</span>: <span class="hljs-string">"aiming"</span>, <span class="hljs-comment">// aiming | in flight | celebrating</span>
    <span class="hljs-attr">currentPlayer</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">bomb</span>: {
      <span class="hljs-attr">x</span>: <span class="hljs-literal">undefined</span>,
      <span class="hljs-attr">y</span>: <span class="hljs-literal">undefined</span>,
      <span class="hljs-attr">velocity</span>: { <span class="hljs-attr">x</span>: <span class="hljs-number">0</span>, <span class="hljs-attr">y</span>: <span class="hljs-number">0</span> },
    },
    <span class="hljs-attr">buildings</span>: generateBuildings(),
  };

  . . .
</code></pre>
<p>‌Let's see how this function works. Each building is defined by its <code>x</code> position, its <code>width</code>, and its <code>height</code>.</p>
<p>The <code>x</code> coordinate is always aligned to the previous building. We check where the previous building ends, and we add a little gap. In case there is no previous building – because we're adding the first one – then we start at the beginning of the screen at 0.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">generateBuildings</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> buildings = [];
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> index = <span class="hljs-number">0</span>; index &lt; <span class="hljs-number">8</span>; index++) {
    <span class="hljs-keyword">const</span> previousBuilding = buildings[index - <span class="hljs-number">1</span>];

    <span class="hljs-keyword">const</span> x = previousBuilding
      ? previousBuilding.x + previousBuilding.width + <span class="hljs-number">4</span>
      : <span class="hljs-number">0</span>;

    <span class="hljs-keyword">const</span> minWidth = <span class="hljs-number">80</span>;
    <span class="hljs-keyword">const</span> maxWidth = <span class="hljs-number">130</span>;
    <span class="hljs-keyword">const</span> width = minWidth + <span class="hljs-built_in">Math</span>.random() * (maxWidth - minWidth);

    <span class="hljs-keyword">const</span> platformWithGorilla = index === <span class="hljs-number">1</span> || index === <span class="hljs-number">6</span>;

    <span class="hljs-keyword">const</span> minHeight = <span class="hljs-number">40</span>;
    <span class="hljs-keyword">const</span> maxHeight = <span class="hljs-number">300</span>;
    <span class="hljs-keyword">const</span> minHeightGorilla = <span class="hljs-number">30</span>;
    <span class="hljs-keyword">const</span> maxHeightGorilla = <span class="hljs-number">150</span>;

    <span class="hljs-keyword">const</span> height = platformWithGorilla
      ? minHeightGorilla + <span class="hljs-built_in">Math</span>.random() * (maxHeightGorilla - minHeightGorilla)
      : minHeight + <span class="hljs-built_in">Math</span>.random() * (maxHeight - minHeight);

    buildings.push({ x, width, height });
  }
  <span class="hljs-keyword">return</span> buildings;
}
</code></pre>
<p>Then the function generates a random building <code>width</code> within a predefined range. We set the minimum and maximum width, and pick a random number in between.</p>
<p>We generate a random building <code>height</code> in a similar way with one difference: the <code>height</code> of a building also depends on whether a gorilla is standing on top of it or not.</p>
<p>If a gorilla is standing on top of the building, then the height range is smaller. We want to have relatively higher buildings in between the two gorillas so that they can’t just see each other in a straight line.</p>
<p>We'll know if a gorilla is standing on the building because they always stand on top of the same buildings. The second one from the left, and the second to last. If the building index matches these positions, we set the height based on a different range.</p>
<p>Then we push these three values as an object into the <code>buildings</code> array, and at the final line we return this array from the function. This will set be <code>buildings</code> array in our game <code>state</code>.</p>
<h3 id="heading-how-to-draw-the-the-buildings">How to draw the the buildings</h3>
<p>Now that we have the metadata for the buildings, we can draw them on the screen.</p>
<p>The <code>drawBuildings</code> function is a very simple one. We iterate over the array we just generated and draw a simple rectangle for each. We use the same <code>fillRect</code> method we used to draw the sky. We call this function with the attributes of the building (the Y position is 0 because the building starts at the bottom of the screen).</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">drawBuildings</span>(<span class="hljs-params"></span>) </span>{
  state.buildings.forEach(<span class="hljs-function">(<span class="hljs-params">building</span>) =&gt;</span> {
    ctx.fillStyle = <span class="hljs-string">"#152A47"</span>;
    ctx.fillRect(building.x, <span class="hljs-number">0</span>, building.width, building.height);
  });
}
</code></pre>
<p>Once we are done with this, we should see a line of buildings. The metadata is regenerated every time we start the game. Every time we refresh the browser window, we see a different background.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-19-at-16.57.16-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Randomly generated buildings</em></p>
<h3 id="heading-how-to-draw-the-gorillas">‌How to draw the gorillas</h3>
<p>Drawing the gorillas is one of the most complicated and most fun parts of this game. Finally, we are not just drawing rectangles – we're drawing paths.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/gorilla-variations.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>The different variations of the gorillas</em></p>
<p>Gorillas also have different variations depending on the game state. The gorilla looks different while aiming, waiting for the incoming bomb, and celebrating after a successful hit.</p>
<p>In the <code>draw</code> function, we call the <code>drawGorilla</code> function twice. We draw two gorillas: one on the second rooftop and one on the second to last rooftop. They are mostly identical, but they mirror each other while aiming. When the left one is aiming, it raises its left hand, and when the right one is aiming, it raises its right hand.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">draw</span>(<span class="hljs-params"></span>) </span>{
  ctx.save();
  <span class="hljs-comment">// Flip coordinate system upside down</span>
  ctx.translate(<span class="hljs-number">0</span>, <span class="hljs-built_in">window</span>.innerHeight);
  ctx.scale(<span class="hljs-number">1</span>, <span class="hljs-number">-1</span>);

  <span class="hljs-comment">// Draw scene</span>
  drawBackground();
  drawBuildings();
  drawGorilla(<span class="hljs-number">1</span>);
  drawGorilla(<span class="hljs-number">2</span>);
  drawBomb();

  <span class="hljs-comment">// Restore transformation</span>
  ctx.restore();
}
</code></pre>
<p>‌We'll break down drawing the gorilla into multiple steps as well. We'll use different functions to draw the main body, to draw the arms, and to draw the face of the gorilla.</p>
<p>To make things easier, we'll <code>translate</code> our coordinate system again. We <code>translate</code> the coordinate system to the middle of the rooftop the gorilla stands on. This way we can draw both gorillas the same way. We only need to translate the origin of our coordinate system to a different building.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-30-at-16.58.41.png" alt="Image" width="600" height="400" loading="lazy">
<em>Moving the origin of the coordinate system to the top of the building the gorilla stands on</em></p>
<p>‌As an argument, the <code>drawGorilla</code> function receives which <code>player</code> are we currently drawing. To draw the gorilla one on the left, we translate to the top of the second building, and to draw the one on the right we translate to the top of the second to last building.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">drawGorilla</span>(<span class="hljs-params">player</span>) </span>{
  ctx.save();
  <span class="hljs-keyword">const</span> building =
    player === <span class="hljs-number">1</span>
      ? state.buildings.at(<span class="hljs-number">1</span>) <span class="hljs-comment">// Second building</span>
      : state.buildings.at(<span class="hljs-number">-2</span>); <span class="hljs-comment">// Second last building</span>

  ctx.translate(building.x + building.width / <span class="hljs-number">2</span>, building.height);

  drawGorillaBody();
  drawGorillaLeftArm(player);
  drawGorillaRightArm(player);
  drawGorillaFace();

  ctx.restore();
}
</code></pre>
<p>‌Because we're using the <code>translate</code> method, this function starts with saving the current coordinate system and ends with restoring it.</p>
<p>Now let's look into the functions that draw the different parts of a gorilla.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">drawGorillaBody</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// ...</span>
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">drawGorillaLeftArm</span>(<span class="hljs-params">player</span>) </span>{
  <span class="hljs-comment">// ...</span>
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">drawGorillaRightArm</span>(<span class="hljs-params">player</span>) </span>{
  <span class="hljs-comment">// ...</span>
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">drawGorillaFace</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// ...</span>
}
</code></pre>
<h4 id="heading-how-to-draw-the-body-of-the-gorilla">How to draw the body of the gorilla</h4>
<p>We draw the body of the gorilla as a path. We drew a path in our introduction to drawing on a canvas. We use the <code>moveTo</code> and a bunch of <code>lineTo</code> methods to draw the main part of the gorilla.</p>
<p>We set the fill style to black, and then we begin a path. We move to a coordinate in the middle and then we draw straight lines to draw the silhouette of the gorilla. Once we're finished, we fill the shape with the <code>fill</code> method.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/gorilla-path-fill.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>The body, the head, and the legs of the gorilla. The image on the right shows the path we're filling.</em></p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">drawGorillaBody</span>(<span class="hljs-params"></span>) </span>{
  ctx.fillStyle = <span class="hljs-string">"black"</span>;

  ctx.beginPath(); 

  <span class="hljs-comment">// Starting Position</span>
  ctx.moveTo(<span class="hljs-number">0</span>, <span class="hljs-number">15</span>); 

  <span class="hljs-comment">// Left Leg</span>
  ctx.lineTo(<span class="hljs-number">-7</span>, <span class="hljs-number">0</span>);
  ctx.lineTo(<span class="hljs-number">-20</span>, <span class="hljs-number">0</span>); 

  <span class="hljs-comment">// Main Body</span>
  ctx.lineTo(<span class="hljs-number">-13</span>, <span class="hljs-number">77</span>);
  ctx.lineTo(<span class="hljs-number">0</span>, <span class="hljs-number">84</span>);
  ctx.lineTo(<span class="hljs-number">13</span>, <span class="hljs-number">77</span>); 

  <span class="hljs-comment">// Right Leg</span>
  ctx.lineTo(<span class="hljs-number">20</span>, <span class="hljs-number">0</span>);
  ctx.lineTo(<span class="hljs-number">7</span>, <span class="hljs-number">0</span>);

  ctx.fill();
}
</code></pre>
<p>In case you are wondering how I came up with these coordinates, I actually started with an initial sketch with pen and paper. I tried to estimate the coordinates, tried them with code, and then adjusted them until they started getting the right shape. Of course, you might have other methods as well.</p>
<h4 id="heading-how-to-draw-the-arms-of-the-gorilla">How to draw the arms of the gorilla</h4>
<p>While the body was a relatively simple part of the gorilla, the hands are a bit more complicated. They come in different variations and we draw them as a curve.</p>
<p>Let’s start with the left arm. The main part of this is actually only two lines. We'll use the <code>moveTo</code> method to move to the shoulder of the gorilla, then from there, we'll draw the arm as a quadratic curve with the <code>quadraticCurveTo</code> method.</p>
<p>A quadratic curve is a simple curve with one control point. As the curve goes from the starting point (which we'll set with <code>moveTo</code>), the curve bends towards this control point (set as the first two arguments of the <code>quadraticCurveTo</code> method) as it reaches its end position (set as the last two arguments).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-19-at-17.33.01-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>The different coordaintes of a curve</em></p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">drawGorillaLeftArm</span>(<span class="hljs-params">player</span>) </span>{
  ctx.strokeStyle = <span class="hljs-string">"black"</span>;
  ctx.lineWidth = <span class="hljs-number">18</span>;

  ctx.beginPath();
  ctx.moveTo(<span class="hljs-number">-13</span>, <span class="hljs-number">50</span>);

  <span class="hljs-keyword">if</span> (
    (state.phase === <span class="hljs-string">"aiming"</span> &amp;&amp; state.currentPlayer === <span class="hljs-number">1</span> &amp;&amp; player === <span class="hljs-number">1</span>) ||
    (state.phase === <span class="hljs-string">"celebrating"</span> &amp;&amp; state.currentPlayer === player)
  ) {
    ctx.quadraticCurveTo(<span class="hljs-number">-44</span>, <span class="hljs-number">63</span>, <span class="hljs-number">-28</span>, <span class="hljs-number">107</span>);
  } <span class="hljs-keyword">else</span> {
    ctx.quadraticCurveTo(<span class="hljs-number">-44</span>, <span class="hljs-number">45</span>, <span class="hljs-number">-28</span>, <span class="hljs-number">12</span>);
  }

  ctx.stroke();
}
</code></pre>
<p>What makes this function complicated is that it has two variations of the same curve. By default, the hands go down next to the body (second case above).</p>
<p>If we are in the <code>aiming</code> phase, the <code>currentPlayer</code> is player number 1, and we are drawing <code>player</code> 1, then the left hand goes up (first case above). The left hand also goes up in case the we draw the <code>celebrating</code> gorilla (also first case above).</p>
<p>In these cases, we start the from the same point (the curve starts with the same <code>moveTo</code> method), but we set different coordinates for the control point and end point of the curve.</p>
<p>We draw the hands as strokes. So instead of ending the path with the <code>fill</code> method, we use the <code>stroke</code> method instead.</p>
<p>We also set it up differently. Instead of using the <code>fillStyle</code> property, here we set the color with <code>strokeStyle</code> and give thickness to the arm with the <code>lineWidth</code> property.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/gorilla-arm-variations.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>The different arm variations of the gorillas in <code>aiming</code>, <code>in flight</code>, and <code>celebrating</code> phase</em></p>
<p>Drawing the right arm is the same, except the horizontal coordinates and some conditions are flipped. We could merge these two functions, but for clarity, I kept them separate.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">drawGorillaRightArm</span>(<span class="hljs-params">player</span>) </span>{
  ctx.strokeStyle = <span class="hljs-string">"black"</span>;
  ctx.lineWidth = <span class="hljs-number">18</span>;

  ctx.beginPath();
  ctx.moveTo(+<span class="hljs-number">13</span>, <span class="hljs-number">50</span>);

  <span class="hljs-keyword">if</span> (
    (state.phase === <span class="hljs-string">"aiming"</span> &amp;&amp; state.currentPlayer === <span class="hljs-number">2</span> &amp;&amp; player === <span class="hljs-number">2</span>) ||
    (state.phase === <span class="hljs-string">"celebrating"</span> &amp;&amp; state.currentPlayer === player)
  ) {
    ctx.quadraticCurveTo(+<span class="hljs-number">44</span>, <span class="hljs-number">63</span>, +<span class="hljs-number">28</span>, <span class="hljs-number">107</span>);
  } <span class="hljs-keyword">else</span> {
    ctx.quadraticCurveTo(+<span class="hljs-number">44</span>, <span class="hljs-number">45</span>, +<span class="hljs-number">28</span>, <span class="hljs-number">12</span>);
  }

  ctx.stroke();
}
</code></pre>
<p>As a result, our gorillas should start to gain shape. They still don’t have a face, but they have hands now. And to reflect our game state, the one on the left has his hands up, preparing to throw the bomb.</p>
<p>You can test our solution by changing the game state. You can change the <code>currentPlayer</code> and the game <code>phase</code> properties to see the different variations.</p>
<h4 id="heading-how-to-draw-the-face-of-the-gorilla">How to draw the face of the gorilla</h4>
<p>The face of the gorilla comes together from a few straight lines. We'll draw the two eyes and the mouth as a straight line. For each, we'll use pair of <code>moveTo</code> and a <code>lineTo</code> method. Because each line segment uses the same <code>strokeStyle</code> and <code>lineWidth</code>, we can draw them as one single path.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-19-at-17.08.58.png" alt="Image" width="600" height="400" loading="lazy">
<em>The finished gorillas</em></p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">drawGorillaFace</span>(<span class="hljs-params"></span>) </span>{
  ctx.strokeStyle = <span class="hljs-string">"lightgray"</span>;
  ctx.lineWidth = <span class="hljs-number">3</span>;

  ctx.beginPath();

  <span class="hljs-comment">// Left Eye</span>
  ctx.moveTo(<span class="hljs-number">-5</span>, <span class="hljs-number">70</span>);
  ctx.lineTo(<span class="hljs-number">-2</span>, <span class="hljs-number">70</span>);

  <span class="hljs-comment">// Right Eye</span>
  ctx.moveTo(<span class="hljs-number">2</span>, <span class="hljs-number">70</span>);
  ctx.lineTo(<span class="hljs-number">5</span>, <span class="hljs-number">70</span>);

  <span class="hljs-comment">// Mouth</span>
  ctx.moveTo(<span class="hljs-number">-5</span>, <span class="hljs-number">62</span>);
  ctx.lineTo(<span class="hljs-number">5</span>, <span class="hljs-number">62</span>);

  ctx.stroke();
}
</code></pre>
<p>With this, we have our finished gorillas with all the variations. There’s only one thing that’s missing from the screen: the banana bomb.</p>
<h3 id="heading-how-to-draw-the-bomb">How to draw the bomb</h3>
<p>Now we'll draw the bomb. The bomb is going to be a simple circle. But before we get to draw it, first we have to figure out where it is.</p>
<h4 id="heading-how-to-initialize-the-bombs-position">How to initialize the bomb’s position</h4>
<p>If we look back at our <code>newGame</code> function, we can see that the metadata of the bomb has a position and a velocity. The position so far is still <code>undefined</code>. Before we get to drawing the bomb, first let’s figure out its position.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">newGame</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Initialize game state</span>
  state = {
    <span class="hljs-attr">phase</span>: <span class="hljs-string">"aiming"</span>, <span class="hljs-comment">// aiming | in flight | celebrating</span>
    <span class="hljs-attr">currentPlayer</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">bomb</span>: {
      <span class="hljs-attr">x</span>: <span class="hljs-literal">undefined</span>,
      <span class="hljs-attr">y</span>: <span class="hljs-literal">undefined</span>,
      <span class="hljs-attr">velocity</span>: { <span class="hljs-attr">x</span>: <span class="hljs-number">0</span>, <span class="hljs-attr">y</span>: <span class="hljs-number">0</span> },
    },
    <span class="hljs-attr">buildings</span>: generateBuildings(),
  };

  initializeBombPosition();

  draw();
}
</code></pre>
<p>‌At the end of the <code>newGame</code> function, we also call the <code>initializeBombPosition</code> function before drawing the scene. Let’s implement this function.</p>
<p>The <code>initializeBombPosition</code> places the bomb in the hand of the gorilla that throws the bomb this turn. We have to call this function after we generate our building metadata because the bomb’s position depends on the position of the gorilla, and that depends on the building it stands on.</p>
<p>First, we look up which <code>building</code> we need to align with. If it’s the first player’s turn, then the bomb has to be in the left hand of the gorilla on the left. And if it’s the second player’s turn, then it has to be in the right hand of the gorilla on the right.</p>
<p>First, we'll calculate the midpoint of the rooftop we need (<code>gorillaX</code> and <code>gorillaY</code>), then offset the position to match the left or right hand of the gorilla.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-30-at-17.00.32.png" alt="Image" width="600" height="400" loading="lazy">
<em>Calculating the position of the bomb</em></p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initializeBombPosition</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> building =
    state.currentPlayer === <span class="hljs-number">1</span>
      ? state.buildings.at(<span class="hljs-number">1</span>) <span class="hljs-comment">// Second building</span>
      : state.buildings.at(<span class="hljs-number">-2</span>); <span class="hljs-comment">// Second last building</span>

  <span class="hljs-keyword">const</span> gorillaX = building.x + building.width / <span class="hljs-number">2</span>;
  <span class="hljs-keyword">const</span> gorillaY = building.height;

  <span class="hljs-keyword">const</span> gorillaHandOffsetX = state.currentPlayer === <span class="hljs-number">1</span> ? <span class="hljs-number">-28</span> : <span class="hljs-number">28</span>;
  <span class="hljs-keyword">const</span> gorillaHandOffsetY = <span class="hljs-number">107</span>;

  state.bomb.x = gorillaX + gorillaHandOffsetX;
  state.bomb.y = gorillaY + gorillaHandOffsetY;
  state.bomb.velocity.x = <span class="hljs-number">0</span>;
  state.bomb.velocity.y = <span class="hljs-number">0</span>;
}
</code></pre>
<p>We'll also initialize the velocity again here. Later we are going to call this function at the beginning of every turn and then we need it to initialize these values.</p>
<h4 id="heading-how-to-draw-the-bomb-1">How to draw the bomb</h4>
<p>Now that the bomb is in the right place, let’s draw it. Unfortunately, we don’t have a simple fill circle method, as we have in the case of rectangles. We have to draw an <code>arc</code> instead.</p>
<p>An <code>arc</code> method can be called as part of a path. We'll start with the <code>beginPath</code> method and end with the <code>fill</code> method.</p>
<p>The <code>arc</code> method has a lot of properties. This might look a bit scary, but we only need to focus on the first 3 when drawing circles:</p>
<ul>
<li>The first two arguments are <code>x</code> and <code>y</code>, the center coordinates of the arc. We'll set them to match the position of the bomb that we know from the <code>state</code>.</li>
<li>The third argument is the <code>radius</code>. Here we'll set it to 6.</li>
<li>Then the last two arguments are the <code>startAngle</code> and the <code>endAngle</code> of the arc in radians. Because here we want to have a full circle and not an arc, we'll start with 0 and end at a full circle. A full circle in radians is two times Pi.</li>
</ul>
<p>If these last two properties are confusing, don't worry about it. What's important is that when we draw circles, they are always <code>0</code> and <code>2 * Math.Pi</code>.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">drawBomb</span>(<span class="hljs-params"></span>) </span>{
  ctx.fillStyle = <span class="hljs-string">"white"</span>;
  ctx.beginPath();
  ctx.arc(state.bomb.x, state.bomb.y, <span class="hljs-number">6</span>, <span class="hljs-number">0</span>, <span class="hljs-number">2</span> * <span class="hljs-built_in">Math</span>.PI);
  ctx.fill();
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-19-at-17.08.58-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>The final scene with the bomb in place</em></p>
<p>Now we have everything on the screen. We drew the background, the buildings, the gorillas, and the bomb. But things are not centered on the screen. Let’s fix that.</p>
<h2 id="heading-how-to-fit-the-size-of-the-city-to-the-browser-window">How to Fit the Size of the City to the Browser Window</h2>
<p>So far, we've aligned everything to the left side of the screen. Because the size of the buildings is random, the entire size of the city might be shorter or wider than the size of the browser window. It could even happen that we don’t see the second gorilla because it’s entirely off-screen. Or if the browser window is too wide, then we have a huge gap on the right side of the window.</p>
<p>To fix this, let’s match the size of the city with the width of the browser window.</p>
<p>To do this, let’s add a <code>scale</code> property to our state. In the <code>newGame</code> function, let’s add the <code>scale</code> property to the <code>state</code> object, and call a new function named <code>calculateScale</code> to set its value. This function call has to come after we generate our buildings because the scaling depends on the size of the city. It also has to come before we initialize the bomb position, because later that will depend on the scaling.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">newGame</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Initialize game state</span>
  state = {
    <span class="hljs-attr">scale</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">phase</span>: <span class="hljs-string">"aiming"</span>, <span class="hljs-comment">// aiming | in flight | celebrating</span>
    <span class="hljs-attr">currentPlayer</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">bomb</span>: {
      <span class="hljs-attr">x</span>: <span class="hljs-literal">undefined</span>,
      <span class="hljs-attr">y</span>: <span class="hljs-literal">undefined</span>,
      <span class="hljs-attr">velocity</span>: { <span class="hljs-attr">x</span>: <span class="hljs-number">0</span>, <span class="hljs-attr">y</span>: <span class="hljs-number">0</span> },
    },
    <span class="hljs-attr">buildings</span>: generateBuildings(),
  };

  calculateScale();

  initializeBombPosition();

  draw();
}
</code></pre>
<p>The <code>calculateScale</code> function is relatively simple. We calculate the total width of the city and divide the inner width of the browser window by this value. This will give us a ratio. It will tell how the width of our city relates to the width of the browser window.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-19-at-17.42.09-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Calculating the ratio between the size of the city and the size of the browser window</em></p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">calculateScale</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> lastBuilding = state.buildings.at(<span class="hljs-number">-1</span>);
  <span class="hljs-keyword">const</span> totalWidthOfTheCity = lastBuilding.x + lastBuilding.width;

  state.scale = <span class="hljs-built_in">window</span>.innerWidth / totalWidthOfTheCity;
}
</code></pre>
<p>Then we have to use this new <code>scale</code> property at a few places. The most important is to change the scaling of the entire game in the <code>draw</code> function.</p>
<p>At the beginning of this function – where we flip the coordinate system – now we have another <code>scale</code> method call that applies this scaling. Because this is at the beginning of the <code>draw</code> function, everything we draw after this will be scaled.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">draw</span>(<span class="hljs-params"></span>) </span>{
  ctx.save();
  <span class="hljs-comment">// Flip coordinate system upside down</span>
  ctx.translate(<span class="hljs-number">0</span>, <span class="hljs-built_in">window</span>.innerHeight);
  ctx.scale(<span class="hljs-number">1</span>, <span class="hljs-number">-1</span>);
  ctx.scale(state.scale, state.scale);

  <span class="hljs-comment">// Draw scene</span>
  drawBackground();
  drawBuildings();
  drawGorilla(<span class="hljs-number">1</span>);
  drawGorilla(<span class="hljs-number">2</span>);
  drawBomb();

  <span class="hljs-comment">// Restore transformation</span>
  ctx.restore();
}
</code></pre>
<p>‌And finally, we need to adjust how we draw our background. Earlier when we drew the background, we filled the entire screen by setting its width and height based on the window’s <code>innerWidth</code> and <code>innerHeight</code> properties. Now as everything is scaled, they don’t match the size of the browser window anymore. Whenever we use these properties, we need to adjust them by our scaling factor.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">drawBackground</span>(<span class="hljs-params"></span>) </span>{
  ctx.fillStyle = <span class="hljs-string">"#58A8D8"</span>;
  ctx.fillRect(
    <span class="hljs-number">0</span>,
    <span class="hljs-number">0</span>,
    <span class="hljs-built_in">window</span>.innerWidth / state.scale,
    <span class="hljs-built_in">window</span>.innerHeight / state.scale
  );
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-19-at-17.45.05-2.png" alt="Image" width="600" height="400" loading="lazy">
<em>Matching the size of the city to the size of the browser window</em></p>
<h3 id="heading-how-to-resize-the-window">How to resize the window</h3>
<p>Now the 8 buildings we drew fit nicely with the size of our screen – but what happens if we resize the window? We are going to have the same problems we had before.</p>
<p>As a finishing touch, let’s handle the window <code>resize</code> event. This event handler resizes the canvas element to fit the new window size, recalculates the scaling, readjusts the position of the bomb, and redraws the entire scene based on the new scaling.</p>
<p>Readjusting the bomb position does not make a difference yet, but later we will update this function and it will rely on the new scaling.</p>
<pre><code class="lang-js"><span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"resize"</span>, <span class="hljs-function">() =&gt;</span> {
  canvas.width = <span class="hljs-built_in">window</span>.innerWidth;
  canvas.height = <span class="hljs-built_in">window</span>.innerHeight;
  calculateScale();
  initializeBombPosition();
  draw();
});
</code></pre>
<p>So far we've just drawn a static scene. It’s time to make things interactive.</p>
<h2 id="heading-how-the-gorilla-can-throw-the-bomb">How the Gorilla Can Throw the Bomb</h2>
<p>Throwing the bomb has two different parts. First, we have to aim. We grab the bomb with the mouse and drag it to set the throwing angle and the velocity. While dragging, we need to display the angle and velocity in info panels at the top of the screen. We'll also paint the throw trajectory on the screen. This is the <code>aiming</code> phase.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-21-at-16.27.06-4.png" alt="Image" width="600" height="400" loading="lazy">
<em>The <code>aiming</code> phase</em></p>
<p>Then once we release the mouse, the bomb flies across the sky. We are going to have an animation loop that moves the bomb across the sky. This is the <code>in flight</code> phase.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-21-at-16.27.09-4.png" alt="Image" width="600" height="400" loading="lazy">
<em>The <code>in flight</code> phase</em></p>
<p>‌In this phase, this animation loop will also include hit detection. We need to know if the bomb hit the enemy, a building, or went out of the screen. If we hit a building, or the bomb goes off-screen, we switch players and get back to the <code>aiming</code> phase again. If we hit the enemy, then we get to the <code>celebrating</code> phase. We then draw the celebrating variant of the current player’s gorilla and we show the congratulations screen.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-21-at-16.27.11-4.png" alt="Image" width="600" height="400" loading="lazy">
<em>The <code>celebrating</code> phase</em></p>
<p>Now let’s go through these parts in detail.</p>
<h3 id="heading-how-to-aim">How to aim</h3>
<p>In the <code>aiming</code> phase, we can drag the bomb to set its angle and velocity – like we throw a bird in Angry Birds. We are going to set up event handlers for this.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screen-Recording-2024-01-21-at-16.52.12--3--1.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Aiming</em></p>
<p>While aiming, we are going to show the current angle and velocity at the top-left or the top-right corner of the screen (depending on the player). We are also going to draw the throw trajectory on the screen to show in which direction the bomb will fly.</p>
<h3 id="heading-info-panels-showing-the-angle-and-the-velocity">Info panels showing the angle and the velocity</h3>
<p>In the original 1991 game, we had to type in the angle and velocity with the keyboard, as it had no mouse support. Here, we are going to aim with the mouse – but we still add the same UI elements on the screen. We'll update these fields as we are moving the mouse while dragging.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-21-at-16.02.31-2.png" alt="Image" width="600" height="400" loading="lazy">
<em>Info panels in the top left and top right corner of the screen</em></p>
<p>We'll add these info panels in HTML. We could draw these text fields on the canvas as well, but it might be easier to use plain old HTML and CSS.</p>
<p>We'll add two divs, one for player 1 and one for player 2. Both include a header with the player number. We'll also add two paragraphs for the angle and the velocity. The info panels must come after the canvas element because otherwise, the canvas would cover them.</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Gorillas<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"index.css"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"index.js"</span> <span class="hljs-attr">defer</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">canvas</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"game"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">canvas</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"info-left"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Player 1<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Angle: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"angle"</span>&gt;</span>0<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>°<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Velocity: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"velocity"</span>&gt;</span>0<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"info-right"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Player 2<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Angle: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"angle"</span>&gt;</span>0<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>°<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Velocity: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"velocity"</span>&gt;</span>0<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>We'll assign two different IDs and appropriate class names. We'll use these names both for styling and for accessing these elements in JavaScript.</p>
<p>In the CSS, we'll move these panels into position and set some styling for the texts. We also want to ensure the user cannot select the text on the screen. Otherwise, you might end up accidentally selecting these text fields while aiming.</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">font-family</span>: monospace;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">14px</span>;
  <span class="hljs-attribute">color</span>: white;
  <span class="hljs-attribute">user-select</span>: none;
  <span class="hljs-attribute">-webkit-user-select</span>: none;
}
<span class="hljs-selector-id">#info-left</span> {
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">top</span>: <span class="hljs-number">20px</span>;
  <span class="hljs-attribute">left</span>: <span class="hljs-number">25px</span>;
}
<span class="hljs-selector-id">#info-right</span> {
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">top</span>: <span class="hljs-number">20px</span>;
  <span class="hljs-attribute">right</span>: <span class="hljs-number">25px</span>;
  <span class="hljs-attribute">text-align</span>: right;
}
</code></pre>
<p>Then finally, in JavaScript we'll add some references to angle and velocity fields somewhere at the beginning of our file.</p>
<pre><code class="lang-js">. . .

<span class="hljs-comment">// Left info panel</span>
<span class="hljs-keyword">const</span> angle1DOM = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#info-left .angle"</span>);
<span class="hljs-keyword">const</span> velocity1DOM = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#info-left .velocity"</span>);

<span class="hljs-comment">// Right info panel</span>
<span class="hljs-keyword">const</span> angle2DOM = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#info-right .angle"</span>);
<span class="hljs-keyword">const</span> velocity2DOM = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#info-right .velocity"</span>);

. . .
</code></pre>
<p>When we add event handling, we'll update the content of these elements with the mouse movement. For now, let's just leave it like that.</p>
<h3 id="heading-the-grab-area-of-the-bomb">The grab area of the bomb</h3>
<p>We are going to set up event handlers to drag the bomb. But what can we even drag? The whole canvas element is one single element. We could attach an event handler to it, but then we could start dragging the bomb by clicking anywhere on the screen. We don’t want that.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-30-at-17.04.21.png" alt="Image" width="600" height="400" loading="lazy">
<em>The grab area of the bomb</em></p>
<p>Instead, we define another element in HTML that will serve as the grab area. We add the <code>bomb-grab-area</code> element. This is not going to include the bomb that we see on screen – that is already part of the canvas – but it will be an invisible area around it, serving as an event target.</p>
<pre><code class="lang-html">  . . .

  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">canvas</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"game"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">canvas</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"info-left"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Player 1<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Angle: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"angle"</span>&gt;</span>0<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>°<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Velocity: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"velocity"</span>&gt;</span>0<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"info-right"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Player 2<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Angle: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"angle"</span>&gt;</span>0<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>°<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Velocity: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"velocity"</span>&gt;</span>0<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"bomb-grab-area"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>

  . . .
</code></pre>
<p>We'll also add some styling to it with CSS. We want it to have an absolute position and to be an invisible circle with a slightly bigger radius than the bomb (so it’s easier to grab it). We can set the exact position in JavaScript.</p>
<p>We'll also change the cursor once the mouse is on top of it to <code>grab</code>. While this element is invisible, you can still notice that your mouse is at the right place by the changing cursor.</p>
<pre><code class="lang-css">. . .

<span class="hljs-selector-id">#bomb-grab-area</span> {
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">30px</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">30px</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50%</span>;
  <span class="hljs-attribute">background-color</span>: transparent;
  <span class="hljs-attribute">cursor</span>: grab;
}
</code></pre>
<p>Then finally in JavaScript, we need to do two things before we set up event handling. First, get a reference to it somewhere at the beginning of the file.</p>
<pre><code class="lang-js">. . .

<span class="hljs-comment">// The bomb's grab area </span>
<span class="hljs-keyword">const</span> bombGrabAreaDOM = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"bomb-grab-area"</span>);

. . .
</code></pre>
<p>Then we need to move it to the correct position. We already have a function that initializes the bomb position on the canvas. We can extend the <code>initializeBombPosition</code> function to position this HTML element as well.</p>
<p>We'll move the grab area to the same place as the bomb on the screen, but its coordinates are a bit different. The content of the canvas is scaled, but any other HTML element is not. We need to adjust the coordinates by the scaling. Also, note that we'll set the <code>grabAreaRadius</code> variable to be half of the size we defined for this element in CSS.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initializeBombPosition</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> building =
    state.currentPlayer === <span class="hljs-number">1</span>
      ? state.buildings.at(<span class="hljs-number">1</span>) <span class="hljs-comment">// Second building</span>
      : state.buildings.at(<span class="hljs-number">-2</span>); <span class="hljs-comment">// Second last building</span>

  <span class="hljs-keyword">const</span> gorillaX = building.x + building.width / <span class="hljs-number">2</span>;
  <span class="hljs-keyword">const</span> gorillaY = building.height;

  <span class="hljs-keyword">const</span> gorillaHandOffsetX = state.currentPlayer === <span class="hljs-number">1</span> ? <span class="hljs-number">-28</span> : <span class="hljs-number">28</span>;
  <span class="hljs-keyword">const</span> gorillaHandOffsetY = <span class="hljs-number">107</span>;

  state.bomb.x = gorillaX + gorillaHandOffsetX;
  state.bomb.y = gorillaY + gorillaHandOffsetY;
  state.bomb.velocity.x = <span class="hljs-number">0</span>;
  state.bomb.velocity.y = <span class="hljs-number">0</span>;

  <span class="hljs-comment">// Initialize the position of the grab area in HTML</span>
  <span class="hljs-keyword">const</span> grabAreaRadius = <span class="hljs-number">15</span>;
  <span class="hljs-keyword">const</span> left = state.bomb.x * state.scale - grabAreaRadius;
  <span class="hljs-keyword">const</span> bottom = state.bomb.y * state.scale - grabAreaRadius;
  bombGrabAreaDOM.style.left = <span class="hljs-string">`<span class="hljs-subst">${left}</span>px`</span>;
  bombGrabAreaDOM.style.bottom = <span class="hljs-string">`<span class="hljs-subst">${bottom}</span>px`</span>;
}
</code></pre>
<p>‌Once we've added this, we won’t see any difference on the screen, because this item is invisible. But once we hover over the bomb, we'll see that the cursor changes to grab.</p>
<h3 id="heading-event-handling">Event handling</h3>
<p>Now with everything set up, we can finally set up the event handling. This is going to be a simple drag-and-drop implementation where we listen to the <code>mousedown</code>, <code>mousemove</code>, and <code>mouseup</code> events.</p>
<p>First, we'll set up some top-level variables somewhere at the beginning of the file. We have a boolean variable that tells us if we are currently dragging or not, and two variables that tell us where the drag started, in case we are dragging.</p>
<pre><code class="lang-js">. . .

let isDragging = <span class="hljs-literal">false</span>;
<span class="hljs-keyword">let</span> dragStartX = <span class="hljs-literal">undefined</span>;
<span class="hljs-keyword">let</span> dragStartY = <span class="hljs-literal">undefined</span>;

. . .
</code></pre>
<p>We add the first event handler for the <code>mousedown</code> event on the grab area of the bomb. Being able to set this event handler is the reason we added the <code>bomb-grab-area</code> element earlier.</p>
<p>This event handler only does anything if we are in the <code>aiming</code> phase. If that is true, we set the <code>isDragging</code> variable to true, save the current mouse position, and set the mouse cursor permanently to <code>grabbing</code> (so that the cursor stays as grabbing even if we leave the grab area).</p>
<pre><code class="lang-js">bombGrabAreaDOM.addEventListener(<span class="hljs-string">"mousedown"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>) </span>{
  <span class="hljs-keyword">if</span> (state.phase === <span class="hljs-string">"aiming"</span>) {
    isDragging = <span class="hljs-literal">true</span>;

    dragStartX = e.clientX;
    dragStartY = e.clientY;

    <span class="hljs-built_in">document</span>.body.style.cursor = <span class="hljs-string">"grabbing"</span>;
  }
});
</code></pre>
<p>Then we'll add an event handler for the <code>mousemove</code> event. Note that now the event target is not the grab area of the bomb, but the <code>window</code> object. This is because while we are dragging we can easily move the mouse outside the grab area – or even outside the browser window – and we still want this event handler to work.</p>
<p>This event handler first checks if we are currently dragging. If not, then it doesn’t do anything. If we are dragging, then it calculates the delta of the mouse position since the <code>mousedown</code> event and sets it as the velocity of the bomb.</p>
<pre><code class="lang-js"><span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"mousemove"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>) </span>{
  <span class="hljs-keyword">if</span> (isDragging) {
    <span class="hljs-keyword">let</span> deltaX = e.clientX - dragStartX;
    <span class="hljs-keyword">let</span> deltaY = e.clientY - dragStartY;

    state.bomb.velocity.x = -deltaX;
    state.bomb.velocity.y = +deltaY;
    setInfo(deltaX, deltaY);

    draw();
  }
});
</code></pre>
<p>When we release the mouse, we want the bomb to move the opposite way as we drag the mouse, so we need to set the horizontal and vertical velocity with a negative sign. But then with a double twist, we switch back the vertical velocity (the Y coordinate), to have a positive sign because we flipped the coordinate system.</p>
<p>Event handlers still assume that the coordinate system grows downwards. Within the canvas, it goes in the opposite direction.</p>
<p>This event handler also calls a utility function to show the angle and the velocity at the info panels we just added in HTML. Then we call the <code>draw</code> function again. For now, calling the <code>draw</code> function here does not change anything on the screen, but soon we will update the <code>drawBomb</code> function to draw the throwing trajectory.</p>
<p>The <code>setInfo</code> button updates the UI elements we defined in HTML. We already have references to these elements at the top of our file, so here we only need to update their content as we drag the bomb.</p>
<pre><code class="lang-js">. . .

<span class="hljs-comment">// Left info panel</span>
<span class="hljs-keyword">const</span> angle1DOM = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#info-left .angle"</span>);
<span class="hljs-keyword">const</span> velocity1DOM = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#info-left .velocity"</span>);

<span class="hljs-comment">// Right info panel</span>
<span class="hljs-keyword">const</span> angle2DOM = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#info-right .angle"</span>);
<span class="hljs-keyword">const</span> velocity2DOM = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#info-right .velocity"</span>);

. . .
</code></pre>
<p>‌But there is a bit of a complication. From the <code>mousemove</code> event, we got the vertical and horizontal components of the velocity. But on the UI we want to display the angle and the total velocity of the throw. We need to use some trigonometry to calculate these values.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-19-at-20.56.06-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Calculating the total <code>velocity</code> and the <code>angle</code> from the horizontal and verical movement of the mouse</em></p>
<p>‌For the velocity, we'll calculate the <code>hypotenuse</code> of an imaginary triangle made up of the horizontal and vertical components of the velocity. For the <code>angle</code>, we'll use the arc sine function (the inverse of the sine function). We'll also make sure to update the correct info panel depending on whose turn it is, and we'll round the values.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setInfo</span>(<span class="hljs-params">deltaX, deltaY</span>) </span>{
  <span class="hljs-keyword">const</span> hypotenuse = <span class="hljs-built_in">Math</span>.sqrt(deltaX ** <span class="hljs-number">2</span> + deltaY ** <span class="hljs-number">2</span>);
  <span class="hljs-keyword">const</span> angleInRadians = <span class="hljs-built_in">Math</span>.asin(deltaY / hypotenuse);
  <span class="hljs-keyword">const</span> angleInDegrees = (angleInRadians / <span class="hljs-built_in">Math</span>.PI) * <span class="hljs-number">180</span>;

  <span class="hljs-keyword">if</span> (state.currentPlayer === <span class="hljs-number">1</span>) {
    angle1DOM.innerText = <span class="hljs-built_in">Math</span>.round(angleInDegrees);
    velocity1DOM.innerText = <span class="hljs-built_in">Math</span>.round(hypotenuse);
  } <span class="hljs-keyword">else</span> {
    angle2DOM.innerText = <span class="hljs-built_in">Math</span>.round(angleInDegrees);
    velocity2DOM.innerText = <span class="hljs-built_in">Math</span>.round(hypotenuse);
  }
}
</code></pre>
<p>At this point, while the scene should stay the same as it is, the values in the left info panel should update as we drag.</p>
<p>Finally, we'll add the event handler for the <code>mouseup</code> event, again on the <code>window</code> object. It only does anything if we are currently dragging. Then it notes that we are not dragging anymore, changes the cursor back to default, and calls the <code>throwBomb</code> function.</p>
<pre><code class="lang-js"><span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"mouseup"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">if</span> (isDragging) {
    isDragging = <span class="hljs-literal">false</span>;

    <span class="hljs-built_in">document</span>.body.style.cursor = <span class="hljs-string">"default"</span>;

    throwBomb();
  }
});
</code></pre>
<p>The <code>throwBomb</code> function switches the game phase to <code>in flight</code> and kicks off the animation of the bomb. We are going to cover this function in the next chapter, but before we get there, let's update one more thing.</p>
<h3 id="heading-how-to-draw-the-throw-trajectory">How to draw the throw trajectory</h3>
<p>While we are aiming, we can also draw the throwing trajectory. The throwing trajectory is the visualized form of the velocity and the angle.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-21-at-16.27.06-5.png" alt="Image" width="600" height="400" loading="lazy">
<em>The throwing trajectory shows the angle and velocity in a visual form</em></p>
<p>‌To do this, let’s go back to the <code>drawBomb</code> function and make some changes. If we are in the <code>aiming</code> phase, we'll draw a straight line from the center of the bomb to the velocity.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">drawBomb</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Draw throwing trajectory</span>
  <span class="hljs-keyword">if</span> (state.phase === <span class="hljs-string">"aiming"</span>) {
    ctx.strokeStyle = <span class="hljs-string">"rgba(255, 255, 255, 0.7)"</span>;
    ctx.setLineDash([<span class="hljs-number">3</span>, <span class="hljs-number">8</span>]);
    ctx.lineWidth = <span class="hljs-number">3</span>;

    ctx.beginPath();
    ctx.moveTo(state.bomb.x, state.bomb.y);
    ctx.lineTo(
      state.bomb.x + state.bomb.velocity.x,
      state.bomb.y + state.bomb.velocity.y
    );
    ctx.stroke();
  }

  <span class="hljs-comment">// Draw circle</span>
  ctx.fillStyle = <span class="hljs-string">"white"</span>;
  ctx.beginPath();
  ctx.arc(state.bomb.x, state.bomb.y, <span class="hljs-number">6</span>, <span class="hljs-number">0</span>, <span class="hljs-number">2</span> * <span class="hljs-built_in">Math</span>.PI);
  ctx.fill();
}
</code></pre>
<p>We'll draw this line as a path, as we did before. We'll start it with the <code>beginPath</code> method and end it with the <code>stroke</code> method. In between we'll use the <code>moveTo</code> and the <code>lineTo</code> method.</p>
<p>There’s one new thing here though: the <code>setLineDash</code> method. With this setting, we can draw a dotted line. We'll set that after every 3 pixels of line, and we want to have 8 pixels of gap. The 3-pixel dash matches the <code>lineWidth</code>, so the dashes will look like dots.</p>
<p>Now we've finished everything about the <code>aiming</code> phase. It’s time to throw the bomb.</p>
<h2 id="heading-how-to-animate-the-incoming-bomb">How to Animate the Incoming Bomb</h2>
<p>Once we release the mouse after the aim, the bomb flies across the sky. We'll add an animation loop that moves the bomb, calculates its position with every animation cycle, and checks if we hit something.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screen-Recording-2024-01-21-at-17.06.18-1.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Animating the bomb</em></p>
<p>Earlier, we saw that the event handler of the <code>mouseup</code> event ends with calling the <code>throwBomb</code> function. Let's implement this function.</p>
<p>This function first kicks off the <code>in flight</code> phase. Then it resets the <code>previousAnimationTimestamp</code> variable. This is a new utility variable needed for the animation loop. We'll cover it in a second. Then we start the animation loop, by calling <code>requestAnimationFrame</code> with the <code>animate</code> function as an argument. Let’s dig deeper into this animate function.</p>
<pre><code class="lang-js">. . . 

let previousAnimationTimestamp = <span class="hljs-literal">undefined</span>; 

. . .

function throwBomb() {
  state.phase = <span class="hljs-string">"in flight"</span>;
  previousAnimationTimestamp = <span class="hljs-literal">undefined</span>;
  requestAnimationFrame(animate);
}
</code></pre>
<h3 id="heading-the-animation-loop">The animation loop</h3>
<p>The <code>animate</code> function moves the bomb and calls the <code>draw</code> function to repaint the screen over and over again until we hit the enemy, a building, or the bomb goes off the screen.</p>
<p>By calling this function with <code>requestAnimationFrame</code> the way you see below, the animate function will run around 60 times every second. The constant repainting of the screen will appear as a continuous animation. Because this function is running so frequently, we're only moving the bomb little by little each time.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Gorillas-Keynote.006.png" alt="Image" width="600" height="400" loading="lazy">
<em>The animation loop</em></p>
<p>‌This function keeps track of how much time has passed since its last call. We are going to use this information to precisely calculate how much should we move the bomb.</p>
<p>Functions invoked with the <code>requestAnimationFrame</code> function receive the current <code>timestamp</code> as an attribute. At the end of every cycle, we save this <code>timestamp</code> value into the <code>previousAnimationTimestamp</code> variable, so that in the next cycle we can calculate how much time has passed between two cycles. In the code below, this is the <code>elapsedTime</code> variable.</p>
<p>The first cycle is an exception because, at that point, we didn’t have a previous cycle yet. At the beginning of each throw, the value of <code>previousAnimationTimestamp</code> is <code>undefined</code> (we made sure it's <code>undefined</code> in the <code>throwBomb</code> function). In this case, we skip a render and we'll only render the scene on the second cycle, where we already have all the values we need. This is the part at the very beginning of the <code>animate</code> function.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params">timestamp</span>) </span>{
  <span class="hljs-keyword">if</span> (previousAnimationTimestamp === <span class="hljs-literal">undefined</span>) {
    previousAnimationTimestamp = timestamp;
    requestAnimationFrame(animate);
    <span class="hljs-keyword">return</span>;
  }
  <span class="hljs-keyword">const</span> elapsedTime = timestamp - previousAnimationTimestamp;

  moveBomb(elapsedTime); 

  <span class="hljs-comment">// Hit detection</span>
  <span class="hljs-keyword">let</span> miss = <span class="hljs-literal">false</span>; <span class="hljs-comment">// Bomb hit a building or got out of the screen</span>
  <span class="hljs-keyword">let</span> hit = <span class="hljs-literal">false</span>; <span class="hljs-comment">// Bomb hit the enemy</span>

  <span class="hljs-comment">// Handle the case when we hit a building or the bomb got off-screen</span>
  <span class="hljs-keyword">if</span> (miss) {
    <span class="hljs-comment">// ...</span>
    <span class="hljs-keyword">return</span>;
  } <span class="hljs-comment">// Handle the case when we hit the enemy</span>
  <span class="hljs-keyword">if</span> (hit) {
    <span class="hljs-comment">// ... </span>
    <span class="hljs-keyword">return</span>;
  }

  draw();

  <span class="hljs-comment">// Continue the animation loop</span>
  previousAnimationTimestamp = timestamp;
  requestAnimationFrame(animate);
}
</code></pre>
<p>‌Inside the function, we are going to move the bomb every cycle by calling the <code>moveBomb</code> function. We'll pass on the <code>elapsedTime</code> variable so it can precisely calculate by how much should it move the bomb.</p>
<p>In every cycle, we also detect if we hit an enemy, or if we missed and the bomb hit a building or got off screen. If none of that happens, then we need to repaint the scene and request another animation frame to continue the animation loop. But if we missed the enemy or got a hit, then we'll stop the animation loop by returning from the function. By returning early from the function, we never reach the last line, which would trigger the next animation cycle. The loop stops.</p>
<h3 id="heading-how-to-move-the-bomb">How to move the bomb</h3>
<p>We move the bomb by calling the <code>moveBomb</code> function in the animation loop. This function calculates the new <code>x</code> and <code>y</code> position of the bomb in every cycle.</p>
<p>The new <code>x</code> and <code>y</code> values are calculated based on the velocity. But the vertical and horizontal velocity can be a relatively high number. </p>
<p>We don’t want the bomb to cross the screen with lightning speed, so to slow down the movement we'll multiply the values by a very small number. This <code>multiplier</code> also takes into consideration the elapsed time, so the animation will look consistent even if the animation cycles are not triggered at equal intervals.</p>
<p>With every cycle, the velocity of the bomb is also adjusted by gravity. We'll gradually increase the motion of the bomb towards the ground. We'll also adjust the vertical velocity by a small constant that also depends on the time passed.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">moveBomb</span>(<span class="hljs-params">elapsedTime</span>) </span>{
  <span class="hljs-keyword">const</span> multiplier = elapsedTime / <span class="hljs-number">200</span>; <span class="hljs-comment">// Adjust trajectory by gravity</span>

  state.bomb.velocity.y -= <span class="hljs-number">20</span> * multiplier; <span class="hljs-comment">// Calculate new position</span>

  state.bomb.x += state.bomb.velocity.x * multiplier;
  state.bomb.y += state.bomb.velocity.y * multiplier;
}
</code></pre>
<h3 id="heading-hit-detection">Hit detection</h3>
<p>Our animation loop keeps moving the bomb to infinity. But once we hit a building, the enemy, or the bomb got off-screen, we should stop this motion.</p>
<p>We need to detect these cases and handle them appropriately. If the bomb gets off-screen or we hit a building we should move on to the next player and get back to the <code>aiming</code> phase. In case we hit the enemy, we should move on to the <code>celebrating</code> phase and announce the winner.</p>
<p>In all these cases, we can stop the loop by returning early from the <code>animate</code> function. In these cases, the animate function does not reach its last line which would trigger another animation cycle.</p>
<h3 id="heading-how-to-improve-hit-detection-precision">How to improve hit detection precision</h3>
<p>While we already slowed down the movement of the bomb to have a nice animation on screen, the bomb can still be a bit too fast for hit detection. </p>
<p>When the bomb is in flight, it can move by more than 10 pixels at a time. If we do hit detections only once per animation cycle, then we are completely blind to what happens during these 10-pixel movements. The bomb can easily go through parts of the gorilla without us noticing that we should have had a hit, or go through the corner of a building without any impact.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-19-at-23.19.47-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>We need to break down each animation cycle into multiple segments to improve hit detection</em></p>
<p>To solve this, we are not only going to do hit detection once per every animation cycle, but multiple times. We'll break down every movement into smaller segments, and with every tiny movement, we need to check if we have a hit. </p>
<p>We'll still render the scene once per animation cycle, but before calling the <code>draw</code> function, we'll break down the movement into 10 segments with a loop.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-30-at-17.10.02.png" alt="Image" width="600" height="400" loading="lazy">
<em>Break down movement and hit detection to 10 segments in each animation cycle</em></p>
<p>If we break down each animation cycle into ten segments, that also means that now we call the <code>moveBomb</code> function ten times more. We need to slow it down even more. Because this function moves the bomb according to the time passed, it’s enough if we divide its time attribute also by ten.</p>
<p>This way the bomb moves across the sky with the same speed, but we have ten times more precision with hit detection. In the example below, we wrap calling the <code>moveBomb</code> function and the hit detection logic into a for loop.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params">timestamp</span>) </span>{
  <span class="hljs-keyword">if</span> (!previousAnimationTimestamp) {
    previousAnimationTimestamp = timestamp;
    requestAnimationFrame(animate);
    <span class="hljs-keyword">return</span>;
  }

  <span class="hljs-keyword">const</span> elapsedTime = timestamp - previousAnimationTimestamp;

  <span class="hljs-keyword">const</span> hitDetectionPrecision = <span class="hljs-number">10</span>;
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; hitDetectionPrecision; i++) {
    moveBomb(elapsedTime / hitDetectionPrecision);

    <span class="hljs-comment">// Hit detection</span>
    <span class="hljs-keyword">const</span> miss = checkFrameHit() || checkBuildingHit();
    <span class="hljs-keyword">const</span> hit = checkGorillaHit();

    <span class="hljs-comment">// Handle the case when we hit a building or the bomb got off-screen</span>
    <span class="hljs-keyword">if</span> (miss) {
      <span class="hljs-comment">// ...</span>
      <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-comment">// Handle the case when we hit the enemy</span>
    <span class="hljs-keyword">if</span> (hit) {
      <span class="hljs-comment">// ...</span>
      <span class="hljs-keyword">return</span>;
    }
  }

  draw();

  <span class="hljs-comment">// Continue the animation loop</span>
  previousAnimationTimestamp = timestamp;
  requestAnimationFrame(animate);
}
</code></pre>
<p>In the example above we've also introduced some utility functions for hit detection. In the next steps let’s implement these functions.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">checkFrameHit</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// ...</span>
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">checkBuildingHit</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// ...</span>
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">checkGorillaHit</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// ...</span>
}
</code></pre>
<h3 id="heading-how-to-detect-if-the-bomb-is-off-the-screen">How to detect if the bomb is off the screen</h3>
<p>We've missed the target if we reach the edge of the screen or if we hit a building. Checking if we've reached the edge of the screen is a very easy thing to do.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-30-at-17.07.56.png" alt="Image" width="600" height="400" loading="lazy">
<em>Checking if the bomb went off-screen</em></p>
<p>‌We only need to check if the bomb went out on the left side, the bottom, or the right side of the screen. If the bomb goes through the top of the screen, that’s okay. Gravity will eventually pull it back. </p>
<p>Note that because of the scaling, the right side of the screen is not the same as the window’s <code>innerWidth</code> value. We have to adjust that by the scaling factor.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">checkFrameHit</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">if</span> (
    state.bomb.y &lt; <span class="hljs-number">0</span> ||
    state.bomb.x &lt; <span class="hljs-number">0</span> ||
    state.bomb.x &gt; <span class="hljs-built_in">window</span>.innerWidth / state.scale
  ) {
    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>; <span class="hljs-comment">// The bomb is off-screen</span>
  }
}
</code></pre>
<p>If the bomb went out of the screen, we return <code>true</code> to signal to the <code>animate</code> function that we can stop the animation loop.</p>
<h3 id="heading-how-to-detect-if-the-bomb-hit-a-building">How to detect if the bomb hit a building</h3>
<p>We also miss the target if the bomb hits a building. We can iterate over the buildings array and check if any side of the bomb is within the rectangle of the building. We need to check that:</p>
<ul>
<li>The right side of the bomb is on the right of the left side of the building,</li>
<li>The left side of the bomb is on the left of the right side of the building,</li>
<li>And that the bottom of the bomb is below the top of the building.</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/detecting-bomb-building.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Detecting if the bomb hit a building</em></p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">checkBuildingHit</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; state.buildings.length; i++) {
    <span class="hljs-keyword">const</span> building = state.buildings[i];
    <span class="hljs-keyword">if</span> (
      state.bomb.x + <span class="hljs-number">4</span> &gt; building.x &amp;&amp;
      state.bomb.x - <span class="hljs-number">4</span> &lt; building.x + building.width &amp;&amp;
      state.bomb.y - <span class="hljs-number">4</span> &lt; <span class="hljs-number">0</span> + building.height
    ) {
      <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>; <span class="hljs-comment">// Building hit</span>
    }
  }
}
</code></pre>
<p>If all this is true, then we know that the bomb hit the building. If we get a hit, the function ends with returning true. This will also signal to the <code>animate</code> function that we can stop the animation loop.</p>
<h3 id="heading-how-to-detect-if-the-bomb-hit-the-enemy">How to detect if the bomb hit the enemy</h3>
<p>Now that we can detect if the bomb went off-screen and we have hit detection for buildings, it’s time to detect if we hit the enemy. </p>
<p>We are going to have a different approach this time. Detecting if we got a hit with traditional geometric calculations would be much more complicated, because the gorillas are not built with basic rectangles and circles.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-19-at-23.49.34-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Screenshot of the game once we hit the enemy</em></p>
<p>Instead, we can use the <code>isPointInPath</code> method that conveniently tells us if a point – in this case, the center of the bomb – is within a path. </p>
<p>This method tells us if a point is within the current or previous path. So before calling the method, we need to recreate the gorilla. The gorilla is not only one single path, so we have to test against the most relevant path, the main body of the gorilla.</p>
<p>Conveniently – and with some smart planning – we already have a function that paints the body of the gorilla as a single path. We call the <code>drawGorillaBody</code> function right before <code>isPointInPath</code>. But the <code>drawGorillaBody</code> function paints with coordinates relative to the rooftop of a building, so before calling it, we need to translate the coordinate system. </p>
<p>Depending on the current player, we'll calculate which building the enemy stands on, and translate the coordinate system to top of that. Because of this translation, we also use the <code>save</code> and <code>restore</code> methods.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">checkGorillaHit</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> enemyPlayer = state.currentPlayer === <span class="hljs-number">1</span> ? <span class="hljs-number">2</span> : <span class="hljs-number">1</span>;
  <span class="hljs-keyword">const</span> enemyBuilding =
    enemyPlayer === <span class="hljs-number">1</span>
      ? state.buildings.at(<span class="hljs-number">1</span>) <span class="hljs-comment">// Second building</span>
      : state.buildings.at(<span class="hljs-number">-2</span>); <span class="hljs-comment">// Second last building</span>

  ctx.save();

  ctx.translate(
    enemyBuilding.x + enemyBuilding.width / <span class="hljs-number">2</span>,
    enemyBuilding.height
  );

  drawGorillaBody();
  <span class="hljs-keyword">let</span> hit = ctx.isPointInPath(state.bomb.x, state.bomb.y);

  drawGorillaLeftArm(enemyPlayer);
  hit ||= ctx.isPointInStroke(state.bomb.x, state.bomb.y);

  drawGorillaRightArm(enemyPlayer);
  hit ||= ctx.isPointInStroke(state.bomb.x, state.bomb.y);

  ctx.restore();

  <span class="hljs-keyword">return</span> hit;
}
</code></pre>
<p>Similarly, we can also detect if we hit one of the arms of the gorilla. The arms are drawn as a stroke, so in this case instead of <code>isPointInPath</code> we'll use the <code>isPointInStroke</code> method. This detection would not have worked if we did not increase the hit detection precision earlier, because the bomb could easily jump over the arm.</p>
<p>With this function, we have every piece of the hit detection. The animation loop stops in case we hit a building, the enemy, or the bomb gets off-screen. It’s time to handle what’s next in these cases.</p>
<h3 id="heading-how-to-handle-the-result-of-hit-detection">How to handle the result of hit detection</h3>
<p>Once we have proper hit detection, it's time to finally handle the cases when we hit a building, the enemy, or the bomb goes off-screen. We'll update the <code>animate</code> function one last time. The only thing new below is the code block of the two <code>if</code> statements inside the loop.</p>
<p>If we hit the edge of the screen or a building, that means we've missed the target. In this case, we'll switch players and get back to the <code>aiming</code> phase. We'll also re-initialize the bomb position to move it to the new player’s hand.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-20-at-00.39.59-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Once we've missed the enemy, we switch players</em></p>
<p>Then we'll simply call the <code>draw</code> function to handle the rest. The beauty of this structure is that we only need to change the game <code>state</code> and the <code>draw</code> function can repaint the whole scene according to it.</p>
<p>Then we'll <code>return</code> from the animate function to stop the animation loop. The event handlers are active again (because we are back in the <code>aiming</code> phase), and the next player can take a shot.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params">timestamp</span>) </span>{
  <span class="hljs-keyword">if</span> (!previousAnimationTimestamp) {
    previousAnimationTimestamp = timestamp;
    requestAnimationFrame(animate);
    <span class="hljs-keyword">return</span>;
  }

  <span class="hljs-keyword">const</span> elapsedTime = timestamp - previousAnimationTimestamp;

  <span class="hljs-keyword">const</span> hitDetectionPrecision = <span class="hljs-number">10</span>;
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; hitDetectionPrecision; i++) {
    moveBomb(elapsedTime / hitDetectionPrecision); <span class="hljs-comment">// Hit detection</span>

    <span class="hljs-keyword">const</span> miss = checkFrameHit() || checkBuildingHit();
    <span class="hljs-keyword">const</span> hit = checkGorillaHit();

    <span class="hljs-comment">// Handle the case when we hit a building or the bomb got off-screen</span>
    <span class="hljs-keyword">if</span> (miss) {
      state.currentPlayer = state.currentPlayer === <span class="hljs-number">1</span> ? <span class="hljs-number">2</span> : <span class="hljs-number">1</span>; <span class="hljs-comment">// Switch players</span>
      state.phase = <span class="hljs-string">"aiming"</span>;
      initializeBombPosition();
      draw();
      <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-comment">// Handle the case when we hit the enemy</span>
    <span class="hljs-keyword">if</span> (hit) {
      state.phase = <span class="hljs-string">"celebrating"</span>;
      announceWinner();
      draw();
      <span class="hljs-keyword">return</span>;
    }
  }

  draw();

  <span class="hljs-comment">// Continue the animation loop</span>
  previousAnimationTimestamp = timestamp;
  requestAnimationFrame(animate);
}
</code></pre>
<p>If we hit the enemy, we need to switch to the <code>celebrating</code> phase. We'll announce the winner with a function that we are about to cover in the next section. Then we repaint the scene with a celebrating gorilla and return to stop the animation loop.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-19-at-23.49.34-2.png" alt="Image" width="600" height="400" loading="lazy">
<em>Once we hit the enemy the <code>draw</code> function repaints the scene with a celebrating gorilla</em></p>
<p>‌To make sure our code does not throw an error, let's add a placeholder for the <code>announceWinner</code> function.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">announceWinner</span>(<span class="hljs-params"></span>) </span>{ 
    <span class="hljs-comment">// ... </span>
}
</code></pre>
<h3 id="heading-how-to-announce-the-winner">How to announce the winner</h3>
<p>Once we hit the enemy, we should announce the winner.  For this, we'll add another info panel in HTML that includes who won the game and a restart button. At the bottom of the now-finished HTML file, you can find the <code>congratulations</code> panel.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/Screenshot-2024-01-21-at-16.27.11-6.png" alt="Image" width="600" height="400" loading="lazy">
<em>The <code>congratulations</code> panel announcing the winner</em></p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Gorillas<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"index.css"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"index.js"</span> <span class="hljs-attr">defer</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">canvas</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"game"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">canvas</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"info-left"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Player 1<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Angle: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"angle"</span>&gt;</span>0<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>°<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Velocity: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"velocity"</span>&gt;</span>0<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"info-right"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Player 2<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Angle: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"angle"</span>&gt;</span>0<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>°<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Velocity: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"velocity"</span>&gt;</span>0<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"bomb-grab-area"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"congratulations"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"winner"</span>&gt;</span>?<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span> won!<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"new-game"</span>&gt;</span>New Game<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>This panel is hidden by default and it only shows up at the end of the game. When it shows up, it should be in the middle of the screen. We'll update our CSS file according to this. </p>
<p>Note that we added <code>display: flex</code> to the body element with a few more properties to center everything on the screen. Then we set <code>position: absolute</code> for the <code>congratulations</code> element and hide it by default.</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">font-family</span>: monospace;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">14px</span>;
  <span class="hljs-attribute">color</span>: white;
  <span class="hljs-attribute">user-select</span>: none;
  <span class="hljs-attribute">-webkit-user-select</span>: none;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">justify-content</span>: center;
  <span class="hljs-attribute">align-items</span>: center;
}

<span class="hljs-selector-id">#info-left</span> {
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">top</span>: <span class="hljs-number">20px</span>;
  <span class="hljs-attribute">left</span>: <span class="hljs-number">25px</span>;
}

<span class="hljs-selector-id">#info-right</span> {
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">top</span>: <span class="hljs-number">20px</span>;
  <span class="hljs-attribute">right</span>: <span class="hljs-number">25px</span>;
  <span class="hljs-attribute">text-align</span>: right;
}

<span class="hljs-selector-id">#bomb-grab-area</span> {
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">30px</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">30px</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50%</span>;
  <span class="hljs-attribute">background-color</span>: transparent;
  <span class="hljs-attribute">cursor</span>: grab;
}

<span class="hljs-selector-id">#congratulations</span> {
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">visibility</span>: hidden;
}
</code></pre>
<p>Then finally we can use this panel to announce the winner once the game ends in JavaScript. We already call the <code>announceWinner</code> function in our <code>animate</code> function so it’s time to implement it.</p>
<p>First, somewhere at the beginning of the file, we'll set up some references for the <code>congratulations</code> panel itself and the <code>winner</code> field.</p>
<pre><code class="lang-js">. . .

<span class="hljs-comment">// Congratulations panel</span>
<span class="hljs-keyword">const</span> congratulationsDOM = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"congratulations"</span>);
<span class="hljs-keyword">const</span> winnerDOM = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"winner"</span>);

. . .
</code></pre>
<p>Then in the <code>announceWinner</code> function, we'll set the content of the winner field to say the current player and show the congratulations panel.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">announceWinner</span>(<span class="hljs-params"></span>) </span>{
  winnerDOM.innerText = <span class="hljs-string">`Player <span class="hljs-subst">${state.currentPlayer}</span>`</span>;
  congratulationsDOM.style.visibility = <span class="hljs-string">"visible"</span>;
}
</code></pre>
<p>With this piece, we finally can play the game until the end. We can aim, the bomb flies across the sky, and the gorillas take turns until one of them wins the game. The only missing piece is resetting the game for another round.</p>
<h3 id="heading-how-to-reset-for-another-round">How to reset for another round</h3>
<p>As a final finishing touch, let’s add an event handler for the 'New Game' button on our congratulations panel to be able to reset the game. We've already added a button with the ID <code>new-game</code> in our HTML.</p>
<p>In JavaScript first, we'll create a reference to this button and then add an event handler for it. This event handler simply calls the <code>newGame</code> function.</p>
<pre><code class="lang-js">. . .

<span class="hljs-comment">// Congratulations panel</span>
<span class="hljs-keyword">const</span> congratulationsDOM = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"congratulations"</span>);
<span class="hljs-keyword">const</span> winnerDOM = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"winner"</span>);
<span class="hljs-keyword">const</span> newGameButtonDOM = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"new-game"</span>);

. . .

newGameButtonDOM.addEventListener(<span class="hljs-string">"click"</span>, newGame);
</code></pre>
<p>The <code>newGame</code> function should reset everything and generate a new level so we can start a new game. At this point, though, our <code>newGame</code> function does not reset everything. It does not reset the HTML elements we introduced in the meantime.</p>
<p>As a very last step, we'll make sure that the <code>congratulations</code> element is hidden once we start a new game, and reset the angle and velocity values in the left and right info panels to 0.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">newGame</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Initialize game state</span>
  state = {
    <span class="hljs-attr">scale</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">phase</span>: <span class="hljs-string">"aiming"</span>, <span class="hljs-comment">// aiming | in flight | celebrating</span>
    <span class="hljs-attr">currentPlayer</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">bomb</span>: {
      <span class="hljs-attr">x</span>: <span class="hljs-literal">undefined</span>,
      <span class="hljs-attr">y</span>: <span class="hljs-literal">undefined</span>,
      <span class="hljs-attr">velocity</span>: { <span class="hljs-attr">x</span>: <span class="hljs-number">0</span>, <span class="hljs-attr">y</span>: <span class="hljs-number">0</span> },
    },
    <span class="hljs-attr">buildings</span>: generateBuildings(),
  };

  calculateScale();

  initializeBombPosition();

  <span class="hljs-comment">// Reset HTML elements</span>
  congratulationsDOM.style.visibility = <span class="hljs-string">"hidden"</span>;
  angle1DOM.innerText = <span class="hljs-number">0</span>;
  velocity1DOM.innerText = <span class="hljs-number">0</span>;
  angle2DOM.innerText = <span class="hljs-number">0</span>;
  velocity2DOM.innerText = <span class="hljs-number">0</span>;

  draw();
}
</code></pre>
<h2 id="heading-next-steps">Next Steps</h2>
<p>While we now have a full two-player game, we can do a lot more. On <a target="_blank" href="https://youtu.be/2q5EufbUEQk?si=9IlOu7Ds-UeeNbIh">YouTube</a> I also cover how to make the buildings destructible, how to animate the hand of the gorilla to follow the drag movement while aiming, and we spend more time adding detailed graphics. There's also a whole chapter on how to add computer logic to the game, so that you can play against the computer. </p>
<p>Check it out to learn more:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/2q5EufbUEQk" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h3 id="heading-subscribe-to-my-channel-for-more-javascript-game-development-tutorials">Subscribe to my channel for more JavaScript game development tutorials:</h3>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/undefined" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ SVG Tutorial – How to Code Images with 12 Examples ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever needed an icon for your website, but you couldn't quite find the right one? Or perhaps you wanted to have a simple diagram, but didn't want to learn a whole new library just for that? Well, good news – you can do all that and more witho... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/svg-tutorial-learn-to-code-images/</link>
                <guid isPermaLink="false">66c4c81d4173ed342943d0c4</guid>
                
                    <category>
                        <![CDATA[ image ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SVG ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Hunor Márton Borbély ]]>
                </dc:creator>
                <pubDate>Mon, 04 Dec 2023 12:29:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/10/Learn-SVG.001.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever needed an icon for your website, but you couldn't quite find the right one? Or perhaps you wanted to have a simple diagram, but didn't want to learn a whole new library just for that?</p>
<p>Well, good news – you can do all that and more without ever leaving your favorite code editor or using any third party tools or libraries.</p>
<p>Since HTML5, we can include the code of an SVG image inside an HTML document. We don’t even need to use the image tag that refers to a separate file. We can inline the code of an image right inside the HTML. We can do this because SVGs have a very similar syntax to HTML.</p>
<p>This opens up a lot of cool options. Suddenly we can access parts of an image from JavaScript or set the style from CSS. We can animate parts of an image from JavaScript or make it interactive. Or we can turn things around and generate graphics from code.</p>
<p>For more complicated images, you will still use a designer tool. But the next time you need a simple icon, a diagram, or animation, maybe you can code it yourself.</p>
<p>So how do SVGs look like under the surface? In this tutorial, we go through the source code of a few SVGs to cover the foundations.</p>
<p>The following examples are from <a target="_blank" href="https://svg-tutorial.com">svg-tutorial.com</a>. You can also <a target="_blank" href="https://youtu.be/kBT90nwUb_o">watch this article as a video</a> with even more fun examples.</p>
<h2 id="heading-the-svg-tag"><strong>The SVG tag</strong></h2>
<p>First, we have to talk about the <code>svg</code> tag itself. This tag contains the image elements and defines the frame of our image. It sets the inner size and the outer size of the image.</p>
<p>The <code>width</code> and <code>height</code> property define how much space the image takes up in the browser. There’s often a <code>viewBox</code> property as well. This defines a coordinate system for the elements inside the image. These two can be confusing because they both define a size.</p>
<p>You can think of the <code>width</code> and <code>height</code> of an SVG as an external size and the <code>viewBox</code> as an internal size.</p>
<p>The size defined by <code>width</code> and <code>height</code> is how the rest of HTML thinks of the image and how big it appears in the browser. The size defined by <code>viewBox</code> is how the image elements think of the image when they position themselves inside of it.</p>
<p>In the next example we have three SVGs that have the very same content. A <code>circle</code> element with the same center coordinate and same radius. They appear quite different, though.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Learn-SVG.001-1.jpeg" alt="Learn-SVG.001-1" width="600" height="400" loading="lazy">
<em>The very same circle can appear different based on the size of the image and the <code>viewBox</code> property</em></p>
<p>At the example in the middle, the size defined by <code>width</code> and <code>height</code> matches the one defined by the <code>viewbox</code>. In the first example we see what happens if the <code>width</code> and <code>height</code> are smaller. The image simply shrinks down as all the coordinates and sizes defined within the image still align to the <code>viewbox</code>.</p>
<p>In the last example we see what happens if the <code>viewbox</code> is focusing on only part of the image. Things appear bigger in this case, but the actual size of the image is still defined by the <code>width</code> and <code>height</code> property.</p>
<p>The <code>viewBox</code> also defines the center of the coordinate system in which the image items place themselves.</p>
<p>The first two numbers define which coordinate should be at the top left corner of the image. Coordinates grow to the right and downwards. In this article, we will center the coordinate systems. The 0,0 coordinate will always be in the middle of the image.</p>
<p>One note before we start: while we can inline SVG images in an HTML file, that doesn’t mean we can freely combine any SVG tag with any HTML tag.</p>
<p>The SVG tag represents the frame of the image and every SVG element has to come within an SVG tag. The same is true in the opposite direction. HTML tags can’t be within an SVG tag, so we can’t have a div or a header tag inside an SVG. But don’t worry, there are similar tags available.</p>
<h2 id="heading-how-to-make-a-christmas-ornament-with-svg"><strong>How to Make a Christmas Ornament with SVG</strong></h2>
<p>Let’s start with a simple Christmas tree ornament. Here we'll only use simple shapes. A rectangle, and two circles.</p>
<p>We'll position and style these elements with attributes. For the circle, we define the center position and for the rectangle, we define the top left corner. These positions are always related to the coordinate system defined by the viewBox.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Learn-SVG.002-1.jpeg" alt="Learn-SVG.002-1" width="600" height="400" loading="lazy">
<em>Christmas Ornament made out of circles and a rectangle. On the right you can see the coordinates we use in this example.</em></p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"-100 -100 200 200”&gt;
    &lt;circle cx="</span><span class="hljs-attr">0</span>" <span class="hljs-attr">cy</span>=<span class="hljs-string">"20"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"70"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#D1495B"</span> /&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">circle</span>
      <span class="hljs-attr">cx</span>=<span class="hljs-string">"0"</span>
      <span class="hljs-attr">cy</span>=<span class="hljs-string">"-75"</span>
      <span class="hljs-attr">r</span>=<span class="hljs-string">"12"</span>
      <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span>
      <span class="hljs-attr">stroke</span>=<span class="hljs-string">"#F79257"</span>
      <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"2"</span>
    /&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">rect</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"-17.5"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"-65"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"35"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"20"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#F79257"</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Remember, we moved the center of the coordinate system to the middle of the image and the X-axis grows to the right and the Y-axis grows towards the bottom.</p>
<p>We also have presentational attributes that style our shapes. Unlike in HTML, we do not use <code>background-color</code> to set a color for a shape but we use the <code>fill</code> attribute.</p>
<p>And to set a border for a shape we use <code>stroke</code> and <code>stroke-width</code>. Note how we use the circle element both to draw a ring and a ball with different attributes.</p>
<h2 id="heading-how-to-build-a-christmas-tree-with-svg"><strong>How to Build a Christmas Tree with SVG</strong></h2>
<p>Let’s move on to a Christmas tree. We can’t always use basic shapes to assemble our image. A polygon is the simplest way to draw a freeform shape. Here we set a list of points that are connected with straight lines.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Learn-SVG.003.jpeg" alt="Learn-SVG.003" width="600" height="400" loading="lazy">
<em>Christmas Tree made out of polygons and a rectangle</em></p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"-100 -100 200 200"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">polygon</span> <span class="hljs-attr">points</span>=<span class="hljs-string">"0,0 80,120 -80,120"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#234236"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">polygon</span> <span class="hljs-attr">points</span>=<span class="hljs-string">"0,-40 60,60 -60,60"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#0C5C4C"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">polygon</span> <span class="hljs-attr">points</span>=<span class="hljs-string">"0,-80 40,0 -40,0"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#38755B"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">rect</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"-20"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"120"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"40"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"brown"</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>You might be wondering how we know before starting to code where our coordinates should be.</p>
<p>To be honest, this requires a bit of imagination. You can start with pen and paper and draw a draft first to get an estimate. Or you can just make a guess then adjust your values until it looks good.</p>
<h2 id="heading-how-to-make-a-gingerbread-figure-with-svg"><strong>How to Make a Gingerbread Figure with SVG</strong></h2>
<p>Let’s move on with a gingerbread figure. Since our SVG is living inside an HTML file now, we can assign CSS classes to each tag and move some attributes to CSS.</p>
<p>We can only move the presentation attributes, though. Position attributes and attributes that define the shape still have to stay in the HTML. But we can move colors, stroke, and font attributes to CSS.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Learn-SVG.004.jpeg" alt="Learn-SVG.004" width="600" height="400" loading="lazy">
<em>Gingerbread figure example. On the right you can see how would it look if the <code>stroke-width</code> were one</em></p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"gingerbread"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"-100 -100 200 200"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"body"</span> <span class="hljs-attr">cx</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">cy</span>=<span class="hljs-string">"-50"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"30"</span> /&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"eye"</span> <span class="hljs-attr">cx</span>=<span class="hljs-string">"-12"</span> <span class="hljs-attr">cy</span>=<span class="hljs-string">"-55"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"3"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"eye"</span> <span class="hljs-attr">cx</span>=<span class="hljs-string">"12"</span> <span class="hljs-attr">cy</span>=<span class="hljs-string">"-55"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"3"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">rect</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mouth"</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"-10"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"-40"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"20"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"5"</span> <span class="hljs-attr">rx</span>=<span class="hljs-string">"2"</span> /&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">line</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"limb"</span> <span class="hljs-attr">x1</span>=<span class="hljs-string">"-40"</span> <span class="hljs-attr">y1</span>=<span class="hljs-string">"-10"</span> <span class="hljs-attr">x2</span>=<span class="hljs-string">"40"</span> <span class="hljs-attr">y2</span>=<span class="hljs-string">"-10"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">line</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"limb"</span> <span class="hljs-attr">x1</span>=<span class="hljs-string">"-25"</span> <span class="hljs-attr">y1</span>=<span class="hljs-string">"50"</span> <span class="hljs-attr">x2</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y2</span>=<span class="hljs-string">"-15"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">line</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"limb"</span> <span class="hljs-attr">x1</span>=<span class="hljs-string">"25"</span> <span class="hljs-attr">y1</span>=<span class="hljs-string">"50"</span> <span class="hljs-attr">x2</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y2</span>=<span class="hljs-string">"-15"</span> /&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">cx</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">cy</span>=<span class="hljs-string">"-10"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"5"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">cx</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">cy</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"5"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
</code></pre>
<pre><code class="lang-css"><span class="hljs-selector-class">.gingerbread</span> <span class="hljs-selector-class">.body</span> {
  <span class="hljs-attribute">fill</span>: <span class="hljs-number">#cd803d</span>;
}

<span class="hljs-selector-class">.gingerbread</span> <span class="hljs-selector-class">.eye</span> {
  <span class="hljs-attribute">fill</span>: white;
}

<span class="hljs-selector-class">.gingerbread</span> <span class="hljs-selector-class">.mouth</span> {
  <span class="hljs-attribute">fill</span>: none;
  <span class="hljs-attribute">stroke</span>: white;
  <span class="hljs-attribute">stroke-width</span>: <span class="hljs-number">2px</span>;
}

<span class="hljs-selector-class">.gingerbread</span> <span class="hljs-selector-class">.limb</span> {
  <span class="hljs-attribute">stroke</span>: <span class="hljs-number">#cd803d</span>;
  <span class="hljs-attribute">stroke-width</span>: <span class="hljs-number">35px</span>;
  <span class="hljs-attribute">stroke-linecap</span>: round;
}
</code></pre>
<p>We already saw the fill and some of the stroke properties, but here’s another one – the <code>stroke-linecap</code>. This can make our line cap round.</p>
<p>Note that the legs and the arms are simple lines here. If we remove the line cap and set a smaller <code>stroke-width</code>, then we can see that these are simple lines. But by setting a thick stroke width and a round line cap we can shape legs and arms for our figure.</p>
<p>Also note the <code>rx</code> property at the rectangle defining the mouth. This will make the edges round. You can think of it as <code>border-radius</code> if you like.</p>
<h2 id="heading-how-to-make-a-star-with-svg"><strong>How to Make a Star with SVG</strong></h2>
<p>Let’s move on to a star. A star is a simple shape, so we can define it as a bunch of polygons and set each point individually. But then we would need to know each coordinate.</p>
<p>Instead of that, we can just define one wing as a group, then repeat it five times with a rotation to get the star's shape. We use the <code>transform</code> attribute to set a rotation.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Learn-SVG.005.jpeg" alt="Learn-SVG.005" width="600" height="400" loading="lazy">
<em>Star shape made out of transformed polygons. On the right we can see the coordianates of one arm of the star</em></p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"-100 -100 200 200"</span>&gt;</span>      
  <span class="hljs-tag">&lt;<span class="hljs-name">g</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"translate(0 5)"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">g</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">polygon</span> <span class="hljs-attr">points</span>=<span class="hljs-string">"0,0 36,-50 0,-100"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#EDD8B7"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">polygon</span> <span class="hljs-attr">points</span>=<span class="hljs-string">"0,0 -36,-50 0,-100"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#E5C39C"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">g</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">g</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"rotate(72)"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">polygon</span> <span class="hljs-attr">points</span>=<span class="hljs-string">"0,0 36,-50 0,-100"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#EDD8B7"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">polygon</span> <span class="hljs-attr">points</span>=<span class="hljs-string">"0,0 -36,-50 0,-100"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#E5C39C"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">g</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">g</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"rotate(-72)"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">polygon</span> <span class="hljs-attr">points</span>=<span class="hljs-string">"0,0 36,-50 0,-100"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#EDD8B7"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">polygon</span> <span class="hljs-attr">points</span>=<span class="hljs-string">"0,0 -36,-50 0,-100"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#E5C39C"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">g</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">g</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"rotate(144)"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">polygon</span> <span class="hljs-attr">points</span>=<span class="hljs-string">"0,0 36,-50 0,-100"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#EDD8B7"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">polygon</span> <span class="hljs-attr">points</span>=<span class="hljs-string">"0,0 -36,-50 0,-100"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#E5C39C"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">g</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">g</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"rotate(-144)"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">polygon</span> <span class="hljs-attr">points</span>=<span class="hljs-string">"0,0 36,-50 0,-100"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#EDD8B7"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">polygon</span> <span class="hljs-attr">points</span>=<span class="hljs-string">"0,0 -36,-50 0,-100"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#E5C39C"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">g</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">g</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
</code></pre>
<p>In this example, each wing consists of two polygons. They need to be rotated the same way, so we can group them with a <code>g</code> tag and <code>rotate</code> them together.</p>
<p>You can think of the <code>g</code> tag as the <code>div</code> tag in HTML. On its own, it does not represent anything. But it can contain other elements and attributes defined on the group tag apply to its children.</p>
<p>Groups can be embedded. With the outer group we <code>translate</code> the whole star downwards by 5 units.</p>
<h2 id="heading-how-to-make-a-snowflake-with-svg"><strong>How to Make a Snowflake with SVG</strong></h2>
<p>Grouping elements is a nice trick, but we had to repeat the same code for each wing five times.</p>
<p>Instead of repeating the same code over and over again, we can also create a definition for a shape and reuse it by <code>id</code>. Here we define a branch of a snowflake then use it six times with different rotations.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Learn-SVG.006.jpeg" alt="Learn-SVG.006" width="600" height="400" loading="lazy">
<em>Snowflake made out of reused image elements. On the right we can see the coordainates use for an arm</em></p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"-100 -100 200 200"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">defs</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
      <span class="hljs-attr">id</span>=<span class="hljs-string">"branch"</span>
      <span class="hljs-attr">d</span>=<span class="hljs-string">"
        M 0 0 L 0 -90
        M 0 -20 L 20 -34
        M 0 -20 L -20 -34
        M 0 -40 L 20 -54
        M 0 -40 L -20 -54
        M 0 -60 L 20 -74
        M 0 -60 L -20 -74"</span>
      <span class="hljs-attr">stroke</span>=<span class="hljs-string">"#E5C39C"</span>
      <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"5"</span>
    /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">defs</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#branch"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#branch"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"rotate(60)"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#branch"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"rotate(120)"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#branch"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"rotate(180)"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#branch"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"rotate(240)"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#branch"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"rotate(300)"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
</code></pre>
<p>The branch is defined as a <code>path</code>. The <code>path</code> is the most powerful SVG tag. We can define pretty much anything with paths, and if you open any SVG file, you will see mostly paths.</p>
<p>The shape of the path is defined by the <code>d</code> attribute. Here we define several drawing commands. A command always starts with a letter defining the command type and ends with a coordinate.</p>
<p>Here we only have the two most simple commands, move to (<code>M</code>) and line to (<code>L</code>). The <code>move to</code> command moves the cursor to a point without drawing a line and the <code>line to</code> command draws a straight line from the previous point.</p>
<p>A command always continues the previous command, so when we draw a line we only define the endpoint. The starting point will be the previous command’s endpoint.</p>
<p>This path is a bit unusual because there are several move to commands in it to draw the main branch and each side branch with the same path.</p>
<h2 id="heading-how-to-draw-a-forest-with-svg">How to Draw a Forest with SVG</h2>
<p>Rotation is not the only way we can generate images from simple shapes. In this example, we define a tree shape and then place it at various positions in different sizes to draw a forest.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/Screenshot-2023-11-30-at-21.21.23.png" alt="Image" width="600" height="400" loading="lazy">
<em>Forest made out of reused image elements</em></p>
<p>First, we create a background out of a rectangle and a circle. Then we define a tree shape from a simple polygon and a line.</p>
<p>Then we can reuse it in a similar way as we did in the snowflake example. We move it to the <code>defs</code> section, wrap it into a group element, set an ID for it, and then reuse it with the <code>use</code> element.</p>
<p>Here we also position the reused elements by setting an <code>x</code> and <code>y</code> coordinate and to add some perspective to the image we use the <code>scale</code> transformation.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"-100 -100 200 200"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">defs</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">g</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"tree"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">polygon</span> <span class="hljs-attr">points</span>=<span class="hljs-string">"-10,0 10,0 0 -50"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#38755b"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">line</span> <span class="hljs-attr">x1</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y1</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">x2</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y2</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">stroke</span>=<span class="hljs-string">"#778074"</span> <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"2"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">g</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">defs</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">rect</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"-100"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"-100"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#F1DBC3"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">cx</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">cy</span>=<span class="hljs-string">"380"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"350"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#F8F4E8"</span> /&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#tree"</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"-30"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"25"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"scale(2)"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#tree"</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"-20"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"40"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"scale(1.2)"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#tree"</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"40"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"40"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#tree"</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"50"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"scale(1.5)"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
</code></pre>
<h2 id="heading-how-to-make-a-curvy-tree-with-svg"><strong>How to Make a Curvy Tree with SVG</strong></h2>
<p>The path element becomes really powerful when we start using curves. One of them is the quadratic Bézier curve that not only defines an endpoint for a segment but also has a control point. The control point is an invisible coordinate towards which the line is bending, but not touching it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Learn-SVG.007.jpeg" alt="Learn-SVG.007" width="600" height="400" loading="lazy">
<em>Christmas Tree made using Quadratic Bézier curves</em></p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"400"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"-100 -200 200 400"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
    <span class="hljs-attr">d</span>=<span class="hljs-string">"
      M 0 -80
      Q 5 -75 0 -70
      Q -10 -65 0 -60
      Q 15 -55 0 -50
      Q -20 -45 0 -40
      Q 25 -35 0 -30
      Q -30 -25 0 -20
      Q 35 -15 0 -10
      Q -40 -5 0 0
      Q 45 5 0 10
      Q -50 15 0 20
      Q 55 25 0 30
      Q -60 35 0 40
      Q 65 45 0 50
      Q -70 55 0 60
      Q 75 65 0 70
      Q -80 75 0 80
      Q 85 85 0 90
      Q -90 95 0 100
      Q 95 105 0 110
      Q -100 115 0 120
      L 0 140
      L 20 140
      L -20 140"</span>
    <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span>
    <span class="hljs-attr">stroke</span>=<span class="hljs-string">"#0C5C4C"</span>
    <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"5"</span>
  /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
</code></pre>
<p>Here we have a series of quadratic Béziers curves (<code>Q</code>) where the control points get further and further away from the center of the tree as the path goes down.</p>
<h2 id="heading-how-to-make-a-bell-with-svg"><strong>How to Make a Bell with SVG</strong></h2>
<p>While the quadratic bezier curve (<code>Q</code>) is great when we want to bend a line, often it’s not flexible enough.</p>
<p>With a cubic Bezier (<code>C</code>), we not only one have one control point but two. The first control point sets the initial direction of the curve and the second one defines from which direction the curve should arrive to its endpoint.</p>
<p>If these directions match the directions of the line before and the line after the curve, then we have a smooth transition between the path segments.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Learn-SVG.008.jpeg" alt="Learn-SVG.008" width="600" height="400" loading="lazy">
<em>With Cubic Bézier curves we can set two control points</em></p>
<p>The next example uses both quadratic and cubic Béziers to form a bell. Here the bottom of this bell is defined with straight lines. Then a quadratic Béziers starts the bell cloak. Next a cubic Bezier smoothly continues the quadratic bezier as it forms the top of the bell. Then we reach the bottom part with another quadratic bezier.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Learn-SVG.001-2.jpeg" alt="Learn-SVG.001-2" width="600" height="400" loading="lazy">
<em>Bell example made out of different curves and straight lines</em></p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"-100 -100 200 200"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">g</span> <span class="hljs-attr">stroke</span>=<span class="hljs-string">"black"</span> <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"2"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">cx</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">cy</span>=<span class="hljs-string">"-45"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"7"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#4F6D7A"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">cx</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">cy</span>=<span class="hljs-string">"50"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#F79257"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
      <span class="hljs-attr">d</span>=<span class="hljs-string">"
        M -50 40
        L -50 50
        L 50 50
        L 50 40
        Q 40 40 40 10
        C 40 -60 -40 -60 -40 10   
        Q -40 40 -50 40"</span>
      <span class="hljs-attr">fill</span>=<span class="hljs-string">"#FDEA96"</span>
    /&gt;</span>
 <span class="hljs-tag">&lt;/<span class="hljs-name">g</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
</code></pre>
<h2 id="heading-how-to-write-text-along-a-path">How to Write Text Along a Path</h2>
<p>Drawing shapes is not the only use case for paths. We can also use them to render text along an invisible path. We can define a path in the definitions section and use it in a <code>textPath</code> element to make the text go around the circle. Here we use arc again, but you can use any other path and the text will follow the stroke.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/Screenshot-2023-11-30-at-21.21.27.png" alt="Image" width="600" height="400" loading="lazy">
<em>With the <code>text-path</code> property we can make a text follow a path</em></p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"-100 -100 200 200"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">defs</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"text-arc"</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M 0, 50 A 50 50 0 1 1 1,50"</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">defs</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">text</span>
    <span class="hljs-attr">fill</span>=<span class="hljs-string">"#0c5c4c"</span>
    <span class="hljs-attr">font-family</span>=<span class="hljs-string">"Tahoma"</span>
    <span class="hljs-attr">font-size</span>=<span class="hljs-string">"0.77em"</span>
    <span class="hljs-attr">font-weight</span>=<span class="hljs-string">"bold"</span>
  &gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">textPath</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#text-arc"</span>&gt;</span>
      Happy Holidays! Happy Holidays! Happy Holidays!
    <span class="hljs-tag">&lt;/<span class="hljs-name">textPath</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">text</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
</code></pre>
<h2 id="heading-how-to-animate-an-svg-with-css">How to Animate an SVG with CSS</h2>
<p>To continue our forest example, we can add a snowing effect with a similar animation. We can animate the <code>transform</code> property from CSS.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/Learn-SVG.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Animation effect made with SVG and CSS</em></p>
<p>We extend our forest example with simple reusable snowflakes and add a bunch of them to the scene with various CSS classes to set some variation in speed and appearance. Then we add animation in CSS to make them look like falling snow. It’s a bit glitchy and not the most sophisticated animation, but you get the idea.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"-100 -100 200 200"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">defs</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">g</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"tree"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">polygon</span> <span class="hljs-attr">points</span>=<span class="hljs-string">"-10,0 10,0 0 -50"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#38755b"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">line</span> <span class="hljs-attr">x2</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y2</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">stroke</span>=<span class="hljs-string">"#778074"</span> <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"2"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">g</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"big"</span> <span class="hljs-attr">cx</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">cy</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"5"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"white"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"small"</span> <span class="hljs-attr">cx</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">cy</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"3"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"white"</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">defs</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">rect</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"-100"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"-100"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#F1DBC3"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">cx</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">cy</span>=<span class="hljs-string">"380"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"350"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#F8F4E8"</span> /&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#tree"</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"-30"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"25"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"scale(2)"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#tree"</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"-20"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"40"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"scale(1.2)"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#tree"</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"40"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"40"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#tree"</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"50"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">transform</span>=<span class="hljs-string">"scale(1.5)"</span> /&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#big"</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flake fast"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#big"</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"-50"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"-20"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flake fast opaque"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#big"</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"-40"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flake fast"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#big"</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"50"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"-20"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flake fast opaque"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#big"</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"50"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flake slow"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#big"</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"-70"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"-80"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flake slow opaque"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#big"</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"50"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flake slow"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#big"</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"90"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"-80"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flake slow opaque"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#small"</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"-50"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flake slow"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#small"</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"-50"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"-60"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flake slow opaque"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#small"</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"70"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flake slow"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">use</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#small"</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"-80"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flake slow opaque"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
</code></pre>
<pre><code class="lang-css"><span class="hljs-selector-class">.flake</span> {
  <span class="hljs-attribute">animation-duration</span>: inherit;
  <span class="hljs-attribute">animation-name</span>: snowing;
  <span class="hljs-attribute">animation-iteration-count</span>: infinite;
  <span class="hljs-attribute">animation-timing-function</span>: linear;
}

<span class="hljs-selector-class">.flake</span><span class="hljs-selector-class">.opaque</span> {
  <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0.7</span>;
}

<span class="hljs-selector-class">.flake</span><span class="hljs-selector-class">.slow</span> {
  <span class="hljs-attribute">animation-duration</span>: <span class="hljs-number">5s</span>;
}

<span class="hljs-selector-class">.flake</span><span class="hljs-selector-class">.fast</span> {
  <span class="hljs-attribute">animation-duration</span>: <span class="hljs-number">3s</span>;
}

<span class="hljs-keyword">@keyframes</span> snowing {
  <span class="hljs-selector-tag">from</span> {
    <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translate</span>(<span class="hljs-number">0</span>, -<span class="hljs-number">100px</span>);
  }
  <span class="hljs-selector-tag">to</span> {
    <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translate</span>(<span class="hljs-number">0</span>, <span class="hljs-number">100px</span>);
  }
}
</code></pre>
<h2 id="heading-how-to-make-a-clock-that-shows-the-actual-time">How to Make a Clock That Shows the Actual Time</h2>
<p>SVG elements can be manipulated from JavaScript the same way as any other HTML tag. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/Screenshot-2023-11-30-at-21.21.16.png" alt="Image" width="600" height="400" loading="lazy">
<em>Clock example made with SVG and JavaScript</em></p>
<p>In this example, we are using a short code snipped to show the actual time on a clock. We access the hour and minute hands in JavaScript with <code>getElementById</code> then set their <code>transform</code> attribute with a rotation that reflects the current time. Below you see the actual SVG showing the current time.</p>
<div>
    

  

  

  

  
    
    
  

</div>

<p>For a more detailed tutorial on how to make a clock with SVG and JavaScript, check out <a target="_blank" href="https://www.freecodecamp.org/news/svg-javascript-tutorial/">How to Code an Animated Watch</a>.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"-100 -100 200 200"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">rect</span> <span class="hljs-attr">x</span>=<span class="hljs-string">"-100"</span> <span class="hljs-attr">y</span>=<span class="hljs-string">"-100"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"#CD803D"</span> /&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"55"</span> <span class="hljs-attr">stroke</span>=<span class="hljs-string">"#FCCE7B"</span> <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"white"</span> /&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">circle</span>
    <span class="hljs-attr">r</span>=<span class="hljs-string">"45"</span>
    <span class="hljs-attr">stroke</span>=<span class="hljs-string">"#B6705F"</span>
    <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"6"</span>
    <span class="hljs-attr">stroke-dasharray</span>=<span class="hljs-string">"6 17.56194490192345"</span>
    <span class="hljs-attr">stroke-dashoffset</span>=<span class="hljs-string">"3"</span>
    <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span>
  /&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">g</span> <span class="hljs-attr">stroke</span>=<span class="hljs-string">"#5f4c6c"</span> <span class="hljs-attr">stroke-linecap</span>=<span class="hljs-string">"round"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">line</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"hours"</span> <span class="hljs-attr">y2</span>=<span class="hljs-string">"-20"</span> <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">line</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"minutes"</span> <span class="hljs-attr">y2</span>=<span class="hljs-string">"-35"</span> <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"6"</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">g</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
</code></pre>
<pre><code class="lang-javascript"><span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"DOMContentLoaded"</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> hoursElement = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"hours"</span>);
  <span class="hljs-keyword">const</span> minutesElement = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"minutes"</span>);

  <span class="hljs-keyword">const</span> hour = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getHours() % <span class="hljs-number">12</span>;
  <span class="hljs-keyword">const</span> minute = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getMinutes();

  hoursElement.setAttribute(<span class="hljs-string">"transform"</span>, <span class="hljs-string">`rotate(<span class="hljs-subst">${(<span class="hljs-number">360</span> / <span class="hljs-number">12</span>) * hour}</span>)`</span>);
  minutesElement.setAttribute(<span class="hljs-string">"transform"</span>, <span class="hljs-string">`rotate(<span class="hljs-subst">${(<span class="hljs-number">360</span> / <span class="hljs-number">60</span>) * minute}</span>)`</span>);
});
</code></pre>
<h2 id="heading-how-to-make-a-data-driven-diagram-with-svg-and-react">How to make a Data-driven Diagram with SVG and React</h2>
<p>SVGs also work well with frontend libraries. Here’s an example of a React component that generates a data-driven diagram. </p>
<p>In this example we have two things. We are generating a list of rectangles to create a column diagram based on some arbitrary data. And we also generate a series of coordinates for a polyline.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/Screenshot-2023-11-30-at-21.21.32.png" alt="Image" width="600" height="400" loading="lazy">
<em>We can use JavaScript to generate a Data-Driven Diagram</em></p>
<p>For simple use cases, you can code your own diagram like this. But if you need more complex diagrams then check out the <a target="_blank" href="https://d3js.org/">D3 library</a>. The D3 library uses SVG under to hood to create all sorts of diagrams.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Diagram</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> dataPoints = [<span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">7</span>, <span class="hljs-number">5</span>, <span class="hljs-number">3</span>, <span class="hljs-number">6</span>];
  <span class="hljs-keyword">const</span> sineWave = <span class="hljs-built_in">Array</span>.from({ <span class="hljs-attr">length</span>: <span class="hljs-number">115</span> })
    .map(<span class="hljs-function">(<span class="hljs-params">item, index</span>) =&gt;</span> <span class="hljs-string">`<span class="hljs-subst">${index - <span class="hljs-number">55</span>}</span>,<span class="hljs-subst">${<span class="hljs-built_in">Math</span>.sin(index / <span class="hljs-number">20</span>) * <span class="hljs-number">20</span> + <span class="hljs-number">10</span>}</span>`</span>)
    .join(<span class="hljs-string">" "</span>);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"-100 -100 200 200"</span>&gt;</span>
      {dataPoints.map((dataPoint, index) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">rect</span>
          <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span>
          <span class="hljs-attr">x</span>=<span class="hljs-string">{index</span> * <span class="hljs-attr">20</span> <span class="hljs-attr">-</span> <span class="hljs-attr">55</span>}
          <span class="hljs-attr">y</span>=<span class="hljs-string">{50</span> <span class="hljs-attr">-</span> <span class="hljs-attr">dataPoint</span> * <span class="hljs-attr">10</span>}
          <span class="hljs-attr">width</span>=<span class="hljs-string">"15"</span>
          <span class="hljs-attr">height</span>=<span class="hljs-string">{dataPoint</span> * <span class="hljs-attr">10</span>}
          <span class="hljs-attr">fill</span>=<span class="hljs-string">"#CD803D"</span>
        /&gt;</span>
      ))}

      <span class="hljs-tag">&lt;<span class="hljs-name">polyline</span> <span class="hljs-attr">points</span>=<span class="hljs-string">{sineWave}</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"none"</span> <span class="hljs-attr">stroke</span>=<span class="hljs-string">"black"</span> <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"5"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span></span>
  );
}
</code></pre>
<h2 id="heading-next-steps-making-svgs-interactive-with-javascript"><strong>Next Steps – Making SVGs Interactive with JavaScript</strong></h2>
<p>Under the hood, SVGs can be quite confusing at first. Lots of coordinates, letters and strange parameters. Once you understand their foundations, though, you can use them to your advantage and start coding images.</p>
<p>And we are just getting started. Adding JavaScript to the mix will introduce a whole new level.</p>
<p>For more examples check out <a target="_blank" href="https://svg-tutorial.com">svg-tutorial.com</a> or my <a target="_blank" href="https://www.youtube.com/watch?v=kBT90nwUb_o">YouTube tutorial</a> with 12 more examples on how to use SVGs for your next project!</p>
<p><a target="_blank" href="http://svg-tutorial.com">Embedded content</a></p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/kBT90nwUb_o" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h3 id="heading-subscribe-for-more-tutorials-on-web-development"><strong>Subscribe for more tutorials on Web Development:</strong></h3>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/undefined" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ JavaScript Game Tutorial – Build a Stick Hero Clone with HTML Canvas + JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ In this tutorial, you'll learn how to create a game that's inspired by Stick Hero – using plain JavaScript and HTML canvas. We are going to recreate Stick Hero, a mobile game published by KetchApp. We'll go through how the game works in general, how ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/javascript-game-tutorial-stick-hero-with-html-canvas/</link>
                <guid isPermaLink="false">66c4c813e7521bfd6862b3b2</guid>
                
                    <category>
                        <![CDATA[ Game Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Hunor Márton Borbély ]]>
                </dc:creator>
                <pubDate>Thu, 16 Nov 2023 19:00:55 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/11/_Users_hunormartonborbely_Documents_Courses_Youtube_Sticky-20Hero_index.html-High-Res-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this tutorial, you'll learn how to create a game that's inspired by Stick Hero – using plain JavaScript and HTML canvas.</p>
<p>We are going to recreate <a target="_blank" href="https://apps.apple.com/us/app/stick-hero/id918338898">Stick Hero</a>, a mobile game published by KetchApp. We'll go through how the game works in general, how to use JavaScript to draw on a <code>&lt;canvas&gt;</code> element, how to add game logic and animate the game, and how event handling works. </p>
<p>By the end of this guide, you will have built the entire game using plain JavaScript.</p>
<p>Throughout the tutorial, we will be using JavaScript to manipulate the game state and the HTML canvas element to render the game scene. To get the most out of this tutorial, you should have a basic understanding of JavaScript. But even if you are a beginner, you can still follow along and learn as we go.</p>
<p>Let's get started and build our own Stick Hero game using JavaScript and HTML canvas!</p>
<p>If you prefer video format, you can also <a target="_blank" href="https://youtu.be/eue3UdFvwPo?si=U0QItUV2sRkqVCer">watch this tutorial on YouTube</a>.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><a class="post-section-overview" href="#heading-the-stick-hero-game">The Stick Hero Game</a></li>
<li><a class="post-section-overview" href="#heading-game-phases">Game Phases</a></li>
<li><a class="post-section-overview" href="#heading-the-main-parts-of-the-game">The Main Parts of the Game</a></li>
<li><a class="post-section-overview" href="#heading-how-to-initialize-the-game">How to Initialize the Game</a></li>
<li><a class="post-section-overview" href="#heading-the-draw-function">The Draw Function</a></li>
<li><a class="post-section-overview" href="#heading-event-handling">Event Handling</a></li>
<li><a class="post-section-overview" href="#heading-the-main-animation-loop">The Main Animation Loop</a></li>
<li><a class="post-section-overview" href="#heading-summary">Summary</a></li>
</ol>
<h1 id="heading-the-stick-hero-game">The Stick Hero Game</h1>
<p>In this game, you control a hero who walks from platform to platform by stretching a stick that serves as a bridge. If the stick is the right size, then the hero can safely cross to the next platform. But if the stick is too short or too long, then the hero will fall down.</p>
<p>You can find a playable version of the game we are about to create on <a target="_blank" href="https://codepen.io/HunorMarton/pen/xxOMQKg">CodePen</a> where you can also see the final source code. Give it a try before we get into the details. </p>
<p>You can also check out the original game both on <a target="_blank" href="https://apps.apple.com/us/app/stick-hero/id918338898">iOS</a> and <a target="_blank" href="https://play.google.com/store/apps/details?id=com.ketchapp.stickhero&amp;hl=en&amp;gl=US">Android</a>.</p>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codepen.io/HunorMarton/embed/xxOMQKg" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodePen embed" scrolling="no" allowtransparency="true" allowfullscreen="true" loading="lazy"></iframe></div>
<h1 id="heading-game-phases">Game Phases</h1>
<p>The game has five different phases that loop over and over again untilo the hero falls.</p>
<ol>
<li>Initially, the game is <strong>waiting</strong> for user input, and nothing is happening.</li>
<li>Then once the player holds the mouse down, the game is <strong>stretching</strong> a stick upwards until the mouse is released.</li>
<li>Then once the mouse is released the stick starts <strong>turning</strong> and it falls down, hopefully on the next platform.</li>
<li>If that’s the case, then the hero walks along the stick to the next platform.</li>
<li>Finally, once the hero reaches the next platform, the whole scene <strong>transitions</strong> to the left to center the hero and the next platform ahead. Then the whole loop restarts from the beginning. The game waits for user input, and once the player holds down the mouse a new stick is drawn.</li>
</ol>
<p>In a less favorable scenario, the same phases follow each other, but in the walking phase if the other end of the stick doesn’t fall on the next platform, then the hero will only walk until the edge of the stick, and then fall down.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/Stick-Hero.gif" alt="Image" width="600" height="400" loading="lazy">
<em>The phases of the game</em></p>
<h1 id="heading-the-main-parts-of-the-game">The Main Parts of the Game</h1>
<p>How do we realize this in code? This game has essentially three parts. Game state, the <code>draw</code> function, and the <code>animate</code> function.</p>
<p>We have a game state that is a collection of variables defining every bit of the game. It includes the current phase, the position of the hero, the coordinates of the platforms, the size and rotation of the sticks, and so on.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> phase = <span class="hljs-string">"waiting"</span>; <span class="hljs-comment">// waiting | stretching | turning | walking | transitioning | falling</span>
<span class="hljs-keyword">let</span> lastTimestamp; <span class="hljs-comment">// The timestamp of the previous animation cycle</span>

<span class="hljs-keyword">let</span> heroX; <span class="hljs-comment">// Changes when moving forward</span>
<span class="hljs-keyword">let</span> heroY; <span class="hljs-comment">// Only changes when falling</span>
<span class="hljs-keyword">let</span> sceneOffset; <span class="hljs-comment">// Moves the whole game</span>

<span class="hljs-keyword">let</span> platforms = [];
<span class="hljs-keyword">let</span> sticks = [];

<span class="hljs-keyword">let</span> score = <span class="hljs-number">0</span>;

...
</code></pre>
<p>Then we will have two main functions: one that paints the scene on the screen based on this state (this will be the <code>draw</code> function), and one that will change this state gradually so that it looks like an animation (this will be the <code>animate</code> function). Finally, we're also going to have event handling that will kick off the animation loop.</p>
<h1 id="heading-how-to-initialize-the-game">How to Initialize the Game</h1>
<p>To start, let’s initialize the project with a simple HTML, CSS, and JavaScript file. We'll establish the outline of the code and then initialize the state of the game.</p>
<h2 id="heading-the-html">The HTML</h2>
<p>The HTML part of this game is very simple. Most of the game will live inside the <code>&lt;canvas&gt;</code> element. We are going to use JavaScript to draw on this canvas. We also have a div element that will display the score and a restart button.</p>
<p>In the header, we also load our CSS and JavaScript files. Note the <code>defer</code> tag when loading the script. This will execute the script only after the rest of the HTML is loaded, so we can access parts of the HTML (like the canvas element) right away in our script.</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Stick Hero<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"index.css"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"index.js"</span> <span class="hljs-attr">defer</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">canvas</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"game"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"375"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"375"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">canvas</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"score"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"restart"</span>&gt;</span>RESTART<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<h2 id="heading-the-css">The CSS</h2>
<p>The CSS won’t contain too many things either. We paint the game on the canvas element and the content of the canvas element cannot be styled with CSS. Here we only style the position of our canvas, our score element and the reset button. </p>
<p>Note, that the reset button by default is invisible. We are going to make it visible using JavaScript once the game ends.</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">html</span>,
<span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">height</span>: <span class="hljs-number">100%</span>;
}

<span class="hljs-selector-tag">body</span>,
<span class="hljs-selector-class">.container</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">justify-content</span>: center;
  <span class="hljs-attribute">align-items</span>: center;
}

<span class="hljs-selector-class">.container</span> {
  <span class="hljs-attribute">position</span>: relative;
  <span class="hljs-attribute">font-family</span>: Helvetica;
}

<span class="hljs-selector-tag">canvas</span> {
  <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid;
}

<span class="hljs-selector-id">#score</span> {
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">top</span>: <span class="hljs-number">30px</span>;
  <span class="hljs-attribute">right</span>: <span class="hljs-number">30px</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2em</span>;
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
}

<span class="hljs-selector-id">#restart</span> {
  <span class="hljs-attribute">position</span>: absolute;
  <span class="hljs-attribute">display</span>: none;
}
</code></pre>
<h2 id="heading-the-outline-of-our-javascript-file">The Outline of Our JavaScript File</h2>
<p>And finally, the JavaScript part is where all the magic lives. For simplicity, I have put everything in one file, but feel free to break it down into multiple files.</p>
<p>We are going to introduce a few more variables and a few more functions, but this is the outline of this file. The following things are included:</p>
<ul>
<li>We define various variables that together make up the <code>game state</code>. More on their values in the section on how to initialize state.</li>
<li>We are going to define a few variables as <code>configuration</code>, like the size of the platforms and how fast the hero should move. We cover them in the drawing section and at the main loop.</li>
<li>A reference to the <code>&lt;canvas&gt;</code> element in HTML, and getting the drawing context of it. This will be used by the <code>draw</code> function.</li>
<li>A reference to the <code>score</code> element and the <code>restart</code> button in HTML. We will update the score each time the hero traverses to a new platform. And we show the reset button once the game is over.</li>
<li>We initialize the game state and paint the scene by calling the <code>resetGame</code> function. This is the only top-level function call.</li>
<li>We define the <code>draw</code> function that will draw the scene on the canvas element based on the state.</li>
<li>We set up event handlers for the <code>mousedown</code> and <code>mouseup</code> events.</li>
<li>We define the <code>animate</code> function that will manipulate the state.</li>
<li>And we will have a few utility functions that we will discuss later.</li>
</ul>
<pre><code class="lang-javascript"><span class="hljs-comment">// Game state</span>
<span class="hljs-keyword">let</span> phase = <span class="hljs-string">"waiting"</span>; <span class="hljs-comment">// waiting | stretching | turning | walking | transitioning | falling</span>
<span class="hljs-keyword">let</span> lastTimestamp; <span class="hljs-comment">// The timestamp of the previous animation cycle</span>

<span class="hljs-keyword">let</span> heroX; <span class="hljs-comment">// Changes when moving forward</span>
<span class="hljs-keyword">let</span> heroY; <span class="hljs-comment">// Only changes when falling</span>
<span class="hljs-keyword">let</span> sceneOffset; <span class="hljs-comment">// Moves the whole game</span>

<span class="hljs-keyword">let</span> platforms = [];
<span class="hljs-keyword">let</span> sticks = [];

<span class="hljs-keyword">let</span> score = <span class="hljs-number">0</span>;

<span class="hljs-comment">// Configuration</span>
...

<span class="hljs-comment">// Getting the canvas element</span>
<span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"game"</span>);

<span class="hljs-comment">// Getting the drawing context</span>
<span class="hljs-keyword">const</span> ctx = canvas.getContext(<span class="hljs-string">"2d"</span>);

<span class="hljs-comment">// Further UI elements</span>
<span class="hljs-keyword">const</span> scoreElement = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"score"</span>);
<span class="hljs-keyword">const</span> restartButton = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"restart"</span>);

<span class="hljs-comment">// Start game</span>
resetGame();

<span class="hljs-comment">// Resets game state and layout</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">resetGame</span>(<span class="hljs-params"></span>) </span>{
  ...

  draw();
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">draw</span>(<span class="hljs-params"></span>) </span>{
  ...
}

<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"mousedown"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">event</span>) </span>{
  ...
});

<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"mouseup"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">event</span>) </span>{
  ...
});

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params">timestamp</span>) </span>{
  ...
}

...
</code></pre>
<h2 id="heading-how-to-initialize-the-state">How to Initialize the State</h2>
<p>To start the game, we call the same function as we use for resetting it – the <code>resetGame</code> function. It initializes/resets the state of the game and calls the draw function to paint the scene.</p>
<p>The game state includes the following variables:</p>
<ul>
<li><code>phase</code>: The current phase of the game. Its initial value is waiting.</li>
<li><code>lastTimestamp</code>: Used by the <code>animate</code> function to determine how much time has passed since the last animation cycle. We'll cover it later in more detail.</li>
<li><code>platforms</code>: An array containing the metadata of each platform. Each platform is represented by an object with an <code>x</code> and a <code>w</code> property representing their X position and width. The first platform is always the same – as defined here – to make sure it has a reasonable size and position. The following platforms are generated by a utility function. As the game progresses, more and more platforms are generated on the fly.</li>
<li><code>heroX</code>: The X position of the hero. By default, the hero stands close to the edge of the first platform. This value will change during the walking phase.</li>
<li><code>heroY</code>: The Y position of the hero. By default, it is zero. It only changes if the hero is falling.</li>
<li><code>sceneOffset</code>: As the hero moves forward, we need to shift the entire screen backwards to keep the hero centered on the screen. Otherwise the hero will walk off the screen. In this variable, we keep track of by how much should we shift back the entire screen. We will update this value during the transitioning phase. By default its value is 0.</li>
<li><code>sticks</code>: Metadata of the sticks. While the hero can only stretch one stick at a time, we also need to store the previous sticks in order to be able to render them. Therefore the <code>sticks</code> variable is also an array.   </li>
</ul>
<p>Each stick is represented by an object with the <code>x</code>, <code>length</code>, and <code>rotation</code> properties. The <code>x</code> property represents the starting position of the stick that always matches the corresponding platform’s top-right corner. Its <code>length</code> property will grow in the stretching phase, and its <code>rotation</code> property will go from 0 to 90 in the turning phase. Or from 90 to 180 in the falling phase.   </p>
<p>Initially, the <code>sticks</code> array has one ‘invisible’ stick with 0 length. Every time the hero reaches a new platform, a new stick is added to the array.</p>
<ul>
<li><code>score</code>: The score of the game. It shows how many platforms the hero reached. By default it is 0.</li>
</ul>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">resetGame</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Reset game state</span>
  phase = <span class="hljs-string">"waiting"</span>;
  lastTimestamp = <span class="hljs-literal">undefined</span>;

  <span class="hljs-comment">// The first platform is always the same</span>
  platforms = [{ <span class="hljs-attr">x</span>: <span class="hljs-number">50</span>, <span class="hljs-attr">w</span>: <span class="hljs-number">50</span> }];
  generatePlatform();
  generatePlatform();
  generatePlatform();
  generatePlatform();

  <span class="hljs-comment">// Initialize hero position</span>
  heroX = platforms[<span class="hljs-number">0</span>].x + platforms[<span class="hljs-number">0</span>].w - <span class="hljs-number">30</span>; <span class="hljs-comment">// Hero stands a bit before the edge</span>
  heroY = <span class="hljs-number">0</span>;

  <span class="hljs-comment">// By how much should we shift the screen back</span>
  sceneOffset = <span class="hljs-number">0</span>;

  <span class="hljs-comment">// There's always a stick, even if it appears to be invisible (length: 0)</span>
  sticks = [{ <span class="hljs-attr">x</span>: platforms[<span class="hljs-number">0</span>].x + platforms[<span class="hljs-number">0</span>].w, <span class="hljs-attr">length</span>: <span class="hljs-number">0</span>, <span class="hljs-attr">rotation</span>: <span class="hljs-number">0</span> }];

  <span class="hljs-comment">//Score</span>
  score = <span class="hljs-number">0</span>;

  <span class="hljs-comment">// Reset UI</span>
  restartButton.style.display = <span class="hljs-string">"none"</span>; <span class="hljs-comment">// Hide reset button</span>
  scoreElement.innerText = score; <span class="hljs-comment">// Reset score display</span>

  draw();
}
</code></pre>
<p>At the end of this function, we also reset the UI by making sure the reset button is hidden and the score is displayed as 0.</p>
<p>Once we've initialized the state of the game and reset the UI, the <code>resetGame</code> function calls the <code>draw</code> function to paint the screen for the first time.</p>
<p>The <code>resetGame</code> function calls a utility function that generates a random platform. In this function, we define what is the minimum distance between two platforms (<code>minumumGap</code>) and what is the maximum distance (<code>maximumGap</code>). We also define what is the minimum width of a platform and what is the maximum width. </p>
<p>Based on these ranges and the existing platformsk, we generate the metadata of a new platform.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">generatePlatform</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> minimumGap = <span class="hljs-number">40</span>;
  <span class="hljs-keyword">const</span> maximumGap = <span class="hljs-number">200</span>;
  <span class="hljs-keyword">const</span> minimumWidth = <span class="hljs-number">20</span>;
  <span class="hljs-keyword">const</span> maximumWidth = <span class="hljs-number">100</span>;

  <span class="hljs-comment">// X coordinate of the right edge of the furthest platform</span>
  <span class="hljs-keyword">const</span> lastPlatform = platforms[platforms.length - <span class="hljs-number">1</span>];
  <span class="hljs-keyword">let</span> furthestX = lastPlatform.x + lastPlatform.w;

  <span class="hljs-keyword">const</span> x =
    furthestX +
    minimumGap +
    <span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * (maximumGap - minimumGap));
  <span class="hljs-keyword">const</span> w =
    minimumWidth + <span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * (maximumWidth - minimumWidth));

  platforms.push({ x, w });
}
</code></pre>
<h1 id="heading-the-draw-function">The Draw Function</h1>
<p>The <code>draw</code> function paints the whole canvas based on the state. It shifts the whole UI by the offset, puts the hero in position, and paints the platforms and the sticks. </p>
<p>Compared to the working demo linked at the beginning of the article, here we will only go through a simplified version of the draw function. We won't cover painting a background, and we'll simplify the hero’s appearance.</p>
<p>We will use this function both for painting the initial scene and throughout our main animation loop. </p>
<p>For the initial paint, some of the features we cover here won't be necessary. For instance, we don’t have any sticks on the scene yet. We'll still cover them, because this way we won’t have to rewrite this function once we start animating the state. </p>
<p>Everything we draw in this function is based on the state, and it doesn't matter if the state is in an initial state, or if we are further into the game.</p>
<p>We defined a <code>&lt;canvas&gt;</code> element in HTML. But how do we paint things on it? In JavaScript first, we get the canvas element and then get its context somewhere at the beginning of our file. Then we can use this context to execute drawing commands.</p>
<p>We also define a few variables upfront as configuration. We do this because we need to use these values at different parts of our game and we want to maintain consistency.</p>
<ul>
<li><code>canvasWidth</code> and <code>canvasHeight</code> represent the size of the canvas element in HTML. They have to match with what we set in HTML. We use these values at various places.</li>
<li><code>platformHeight</code> represents the height of the platforms. We use these values when drawing the platforms themselves, but also when positioning the hero and the sticks.</li>
</ul>
<p>The draw function repaints the whole screen from scratch every time. First, let’s make sure that it’s empty. Calling the <code>clearRect</code> function on the drawing context with the correct arguments makes sure we erase everything from it.</p>
<pre><code class="lang-javascript">...

&lt;div <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"container"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">canvas</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"game"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"375"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"375"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">canvas</span>&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"score"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"restart"</span>&gt;</span>RESTART<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
&lt;/div&gt;

...
</code></pre>
<pre><code class="lang-javascript">...

<span class="hljs-comment">// Getting the canvas element</span>
<span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"game"</span>);

<span class="hljs-comment">// Getting the drawing context</span>
<span class="hljs-keyword">const</span> ctx = canvas.getContext(<span class="hljs-string">"2d"</span>);

...

<span class="hljs-comment">// Configuration</span>
<span class="hljs-keyword">const</span> canvasWidth = <span class="hljs-number">375</span>;
<span class="hljs-keyword">const</span> canvasHeight = <span class="hljs-number">375</span>;
<span class="hljs-keyword">const</span> platformHeight = <span class="hljs-number">100</span>;

...

function draw() {
  ctx.clearRect(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, canvasWidth, canvasHeight);

  ...
}

...
</code></pre>
<h2 id="heading-how-to-frame-the-scene">How to Frame the Scene</h2>
<p>We also want to make sure that the scene has the correct framing. When we use canvas, we have a coordinate system with the center at the top left corner of the screen that grows to the right and downwards. In HTML we set the width and height attributes both to 375 pixels.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/Stick-Hero.001.jpeg" alt="Image" width="600" height="400" loading="lazy">
<em>By default the center of the coordinate system is at the top left corner</em></p>
<p>Initially, the 0, 0 coordinate is at the top-left corner of the screen, but as the hero moves forward the whole scene should shift to the left. Otherwise, we would run out of the screen.</p>
<p>As the game progresses, we update the <code>sceneOffset</code> value to keep track of this shift in the main loop. We can use this variable to translate the whole layout. We call the <code>translate</code> command to shift the scene on the X-axis.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">draw</span>(<span class="hljs-params"></span>) </span>{
  ctx.clearRect(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, canvasWidth, canvasHeight);

  <span class="hljs-comment">// Save the current transformation</span>
  ctx.save();

  <span class="hljs-comment">// Shifting the view</span>
  ctx.translate(-sceneOffset, <span class="hljs-number">0</span>);

  <span class="hljs-comment">// Draw scene</span>
  drawPlatforms();
  drawHero();
  drawSticks();

  <span class="hljs-comment">// Restore transformation to the last save</span>
  ctx.restore();
}
</code></pre>
<p>It’s important that we do this before we paint anything on the canvas, because the <code>translate</code> command does not actually move anything on the canvas. Anything we painted before on the canvas will stay as it was. </p>
<p>Instead, the <code>translate</code> command shifts the coordinate system. The 0, 0 coordinate won’t be at the top left corner anymore, but it will be out of the screen on the left. Anything we paint after will be painted according to this new coordinate system.</p>
<p>This is exactly what we want. As we progress in the game, the hero will increase its X coordinate. By moving the coordinate system backwards, we make sure that it will be painted within the screen.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/Stick-Hero.002.jpeg" alt="Image" width="600" height="400" loading="lazy">
<em>Once we use the <code>translate</code> command, the center of the coordinate system will shift to the left</em></p>
<p>The <code>translate</code> commands accumulate. This means that if we call the <code>translate</code> command twice, the second doesn’t just override the first one, but will add a shift on top of the first command. </p>
<p>We are going to call the <code>draw</code> function in a loop, so it’s important that we reset this transformation every time we draw. Also, we always start with the 0, 0 coordinate in the top left corner. Otherwise, the coordinate system will be shifted to the left infinitely.</p>
<p>We can restore transformations by calling the <code>restore</code> command once we don’t want to be in this shifter coordinate system anymore. The <code>restore</code> command resets transitions and many other settings to the state the canvas was at the last <code>save</code> command. That’s why we often start a painting block by saving the context and end it by restoring it.</p>
<h2 id="heading-how-to-draw-the-platforms">How to Draw the Platforms</h2>
<p>So that was just the framing, but we haven’t painted anything yet. Let’s start with a simple one, drawing platforms. The metadata of the platforms is stored in the <code>platforms</code> array. It contains the starting position of the platform and its width.</p>
<p>We can iterate over this array and fill a rectangle by setting the starting position, and the width and the height of the platform. We do this by calling the <code>fillRect</code> function with the X, Y coordinates and the width and the height of the rectangle to fill. Note that the Y coordinate is upside down – it's growing from top to bottom.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/Stick-Hero.003.jpeg" alt="Image" width="600" height="400" loading="lazy">
<em>Drawing the platform</em></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Example state of platforms</span>
<span class="hljs-keyword">let</span> platforms = [
  { <span class="hljs-attr">x</span>: <span class="hljs-number">50</span>, <span class="hljs-attr">w</span>: <span class="hljs-number">50</span> },
  { <span class="hljs-attr">x</span>: <span class="hljs-number">90</span>, <span class="hljs-attr">w</span>: <span class="hljs-number">30</span> },
];

...

function drawPlatforms() {
  platforms.forEach(<span class="hljs-function">(<span class="hljs-params">{ x, w }</span>) =&gt;</span> {
    <span class="hljs-comment">// Draw platform</span>
    ctx.fillStyle = <span class="hljs-string">"black"</span>;
    ctx.fillRect(x, canvasHeight - platformHeight, w, platformHeight);
  });
}
</code></pre>
<p>What is interesting about canvas, or at least it was surprising to me, is that once you paint something on the canvas you can’t modify it. It’s not like you paint a rectangle, and then you can change its color. Once something is on the canvas it stays as it is. </p>
<p>Like with a real canvas, once you've painted something you can either cover it, by painting something over it, or you can try to clear the canvas. But you can’t change existing parts really. That’s why we set the color here up front and not afterward (with the <code>fillStyle</code> property).</p>
<h2 id="heading-how-to-draw-the-hero">How to Draw the Hero</h2>
<p>We won't cover the hero part in detail in this tutorial, but you can find the source code of the demo above on <a target="_blank" href="https://codepen.io/HunorMarton/pen/xxOMQKg">CodePen</a>. Drawing more advanced shapes is a bit more complicated with the canvas element, and I’m going to cover drawing in more detail in a future tutorial.</p>
<p>For now, let’s simply use a red rectangle as a placeholder for the hero. Again, we use the <code>fillRect</code> function and pass on a X, Y coordinate and the width and the height of the hero. </p>
<p>The X and Y positions will be based on the heroX and heroY state. The hero’s X position is relative to the coordinate system, but its Y position is relative to the top of the platform (it has a value of 0 once on top of a platform). We need to adjust the Y position to be on the top of the platform.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">drawHero</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> heroWidth = <span class="hljs-number">20</span>;
  <span class="hljs-keyword">const</span> heroHeight = <span class="hljs-number">30</span>;

  ctx.fillStyle = <span class="hljs-string">"red"</span>;
  ctx.fillRect(
    heroX,
    heroY + canvasHeight - platformHeight - heroHeight,
    heroWidth,
    heroHeight
  );
}
</code></pre>
<h2 id="heading-how-to-draw-the-sticks">How to Draw the Sticks</h2>
<p>Then let’s see how to paint the sticks. Sticks are a bit trickier because they can be rotated. </p>
<p>The sticks are stored in an array in a similar fashion as the platforms but have different attributes. They all have a starting position, a length, and a rotation. The last two changes in the main game loop, and the first one – the position – should fit the top-right corner of a platform.</p>
<p>Based on the length and rotation, we could use some trigonometry and calculate the end position of the stick. But it’s much more interesting if we transform the coordinate system again.</p>
<p>We can use the <code>translate</code> command again, to set the center of the coordinate system to the edge of the platform. Then we can use the <code>rotate</code> command to rotate the coordinate system around this new center.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/Stick-Hero.004.jpeg" alt="Image" width="600" height="400" loading="lazy">
<em>After using the <code>translate</code> and <code>rotate</code> commands, the coordinate system will be twisted around a new center</em></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Example state of sticks</span>
<span class="hljs-keyword">let</span> sticks = [
  { <span class="hljs-attr">x</span>: <span class="hljs-number">100</span>, <span class="hljs-attr">length</span>: <span class="hljs-number">50</span>, <span class="hljs-attr">rotation</span>: <span class="hljs-number">60</span> }
];

...

function drawSticks() {
  sticks.forEach(<span class="hljs-function">(<span class="hljs-params">stick</span>) =&gt;</span> {
    ctx.save();

    <span class="hljs-comment">// Move the anchor point to the start of the stick and rotate</span>
    ctx.translate(stick.x, canvasHeight - platformHeight);
    ctx.rotate((<span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">180</span>) * stick.rotation);

    <span class="hljs-comment">// Draw stick</span>
    ctx.lineWidth = <span class="hljs-number">2</span>;
    ctx.beginPath();
    ctx.moveTo(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
    ctx.lineTo(<span class="hljs-number">0</span>, -stick.length);
    ctx.stroke();

    <span class="hljs-comment">// Restore transformations</span>
    ctx.restore();
  });
}
</code></pre>
<p>After the <code>translate</code> and <code>rotate</code> commands, the starting point of the stick will be at the 0, 0 coordinate and the coordinate system will be rotated. </p>
<p>In this example, we draw a line upwards – both its beginning and its end have the same X coordinate. Only the Y coordinate is changing. Yet the line is heading to the right because the whole coordinate system has turned. Now upwards is in a diagonal direction. It’s a bit mind-twisting, but you can get used to it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/Stick-Hero.005.jpeg" alt="Image" width="600" height="400" loading="lazy">
<em>While we draw a line along the Y axis, the line will appear diagonal because of the transformed coordinate system</em></p>
<p>The actual drawing of the line is also interesting. There’s no simple line-drawing command so we have to draw a path. </p>
<p>We get a path by connecting multiple points. We can connect them with arcs, curves, and straight lines. In this case, we have a very simple one. We just begin a path (<code>beginPath</code>), move to a coordinate (<code>moveTo</code>), then draw a straight line to the next coordinate (<code>lineTo</code>). Then we finish it with the <code>stroke</code> command. </p>
<p>We can also finish the path with the fill command, but that only makes sense with shapes.</p>
<p>Note that because we shift and turn the coordinate system here again, at the end of this function we need to restore transformations (and save the transformation matrix at the beginning of this function). Otherwise, all upcoming drawing commands would be twisted like this.</p>
<h1 id="heading-event-handling">Event Handling</h1>
<p>Now that we have drawn the scene, let’s kick off the game by handling user interactions. Handling events is the easiest part of the game. We are listening to the <code>mousedown</code> and the <code>mouseup</code> event, and we handle the <code>click</code> event of the restart button.</p>
<p>Once the user holds the mouse down, we initiate the stretching phase by setting the <code>phase</code> variable to <code>stretching</code>. We reset the timestamp that the main event loop is going to use (we get back to this later), and we trigger the main event loop by requesting an animation frame for the <code>animate</code> function. </p>
<p>All these are only happening if the current state of the game is waiting. In any other case, we ignore the <code>mousedown</code> event.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> phase = <span class="hljs-string">"waiting"</span>;
<span class="hljs-keyword">let</span> lastTimestamp;

...

const restartButton = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"restart"</span>);

...

window.addEventListener(<span class="hljs-string">"mousedown"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">if</span> (phase == <span class="hljs-string">"waiting"</span>) {
    phase = <span class="hljs-string">"stretching"</span>;
    lastTimestamp = <span class="hljs-literal">undefined</span>;
    <span class="hljs-built_in">window</span>.requestAnimationFrame(animate);
  }
});

<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"mouseup"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">if</span> (phase == <span class="hljs-string">"stretching"</span>) {
    phase = <span class="hljs-string">"turning"</span>;
  }
});

restartButton.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">event</span>) </span>{
  resetGame();
  restartButton.style.display = <span class="hljs-string">"none"</span>;
});

...
</code></pre>
<p>Handling the <code>mouseup</code> event is even simpler. If we are currently stretching a stick, then we stop that and move to the next phase when the stick falls down.</p>
<p>Finally, we also add an event handler for the restart button. The reset button is hidden by default and will be only visible once the hero has fallen. But we can already define its behavior, and once it appears it will work. If we click reset, we call the <code>resetGame</code> function to reset the game and hide the button.</p>
<p>That’s all the event handling we have. The rest now depends on the main animation loop that we just invoked with a <code>requestAnimationFrame</code>.</p>
<h1 id="heading-the-main-animation-loop">The Main Animation Loop</h1>
<p>The main loop is the most complicated part of the game. This is a function that will keep changing the state of the game and calling the <code>draw</code> function to repaint the whole screen based on this state. </p>
<p>As it’s going to be called 60 times every second, the constant repainting of the screen will make it look like a continuous animation. Because this function is running so frequently, we only change the game state little by little each time.</p>
<p>This <code>animate</code> function gets triggered as a <code>requestAnimationFrame</code> call by the <code>mousedown</code> event (see above). With its last line, it keeps invoking itself until we don’t stop it by returning from the function. </p>
<p>There are only two cases when we would stop the loop: when moved to the <code>waiting</code> phase and there’s nothing to animate, or when the hero falls down and the game is over.</p>
<p>This function keeps track of how much time has passed since its last call. We are going to use this information to precisely calculate how the state should change. Like when the hero is walking, we need to calculate exactly how many pixels it moves based on its speed and the time passed since the last animation cycle.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> lastTimestamp;

...

function animate(timestamp) {
  <span class="hljs-keyword">if</span> (!lastTimestamp) {
    <span class="hljs-comment">// First cycle</span>
    lastTimestamp = timestamp;
    <span class="hljs-built_in">window</span>.requestAnimationFrame(animate);
    <span class="hljs-keyword">return</span>;
  }

  <span class="hljs-keyword">let</span> timePassed = timestamp - lastTimestamp;

  <span class="hljs-keyword">switch</span> (phase) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">"waiting"</span>:
      <span class="hljs-keyword">return</span>; <span class="hljs-comment">// Stop the loop</span>
    <span class="hljs-keyword">case</span> <span class="hljs-string">"stretching"</span>: {
      sticks[sticks.length - <span class="hljs-number">1</span>].length += timePassed / stretchingSpeed;
      <span class="hljs-keyword">break</span>;
    }
    <span class="hljs-keyword">case</span> <span class="hljs-string">"turning"</span>: {
      sticks[sticks.length - <span class="hljs-number">1</span>].rotation += timePassed / turningSpeed;
      ...
      break;
    }
    <span class="hljs-keyword">case</span> <span class="hljs-string">"walking"</span>: {
      heroX += timePassed / walkingSpeed;
      ...
      break;
    }
    <span class="hljs-keyword">case</span> <span class="hljs-string">"transitioning"</span>: {
      sceneOffset += timePassed / transitioningSpeed;
      ...
      break;
    }
    <span class="hljs-keyword">case</span> <span class="hljs-string">"falling"</span>: {
      heroY += timePassed / fallingSpeed;
      ...
      break;
    }
  }

  draw();
  lastTimestamp = timestamp;

  <span class="hljs-built_in">window</span>.requestAnimationFrame(animate);
}
</code></pre>
<h2 id="heading-how-to-calculate-the-time-passed-between-two-renders">How to Calculate the Time Passed Between Two Renders</h2>
<p>Functions invoked with the <code>requestAnimationFrame</code> function receive the current <code>timestamp</code> as an attribute. At the end of every cycle, we save this <code>timestamp</code> value into the <code>lastTimestamp</code> attribute, so that in the next cycle we can calculate how much time passed between two cycles. In the code above, this is the <code>timePassed</code> variable.</p>
<p>The first cycle is an exception because at that point, we didn’t have a previous cycle yet. Initially, the value of <code>lastTimestamp</code> is <code>undefined</code>. In this case, we skip a render and we only render the scene on the second cycle, where we already have all the values we need. This is the part at the very beginning of the <code>animate</code> function.</p>
<h2 id="heading-how-to-animate-part-of-the-state">How to Animate Part of the State</h2>
<p>In each phase, we animate a different part of the state. The only exception is the waiting phase because then we have nothing to animate. In that case, we return from the function. This will break the loop, and the animation will stop.</p>
<p>In the stretching phase – when the player is holding the mouse down – we need to grow the stick as the time passes. We calculate how much longer it should be based on the time passed and a speed value that defines how much time it takes for the stick to grow one pixel.</p>
<p>A very similar thing is happening in every other phase as well. In the turning phase, we change the rotation of the stick based on the time passed. In the walking phase, we change the horizontal position of the hero based on time. In the transitioning phase, we change the offset value of the whole scene. In the falling phase, we change the vertical position of the hero.</p>
<p>Each of these phases has its own speed configuration. These values tell how many milliseconds it takes to grow the stick by one pixel, turn the stick by one degree, walk one pixel, and so on.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Configuration</span>
<span class="hljs-keyword">const</span> stretchingSpeed = <span class="hljs-number">4</span>; <span class="hljs-comment">// Milliseconds it takes to draw a pixel</span>
<span class="hljs-keyword">const</span> turningSpeed = <span class="hljs-number">4</span>; <span class="hljs-comment">// Milliseconds it takes to turn a degree</span>
<span class="hljs-keyword">const</span> walkingSpeed = <span class="hljs-number">4</span>;
<span class="hljs-keyword">const</span> transitioningSpeed = <span class="hljs-number">2</span>;
<span class="hljs-keyword">const</span> fallingSpeed = <span class="hljs-number">2</span>;

...
</code></pre>
<h2 id="heading-how-to-move-on-to-the-next-phase">How to Move On to the Next Phase</h2>
<p>In most of these phases, we also have a threshold value that ends the phase and triggers the next one. The waiting and stretching phases are the exceptions because their end is based on user interaction. |The waiting phase ends with the <code>mousedown</code> event and the stretching phase ends with the <code>mouseup</code> event.</p>
<p>The turning phase stops when the stick falls flat and its rotation reaches 90 degrees. The walking phase ends when the hero reaches the edge of the next platform or the end of the stick. And so on. </p>
<p>If these thresholds are reached, the main game loop sets the game to the next phase and in the next loop, it will act accordingly. Let’s see these in more detail.</p>
<h3 id="heading-the-waiting-phase">The waiting phase</h3>
<p>If we are in the waiting phase and nothing is happening, we return from the function. This return statement means that we never reach the end of the function and there won’t be another request for an animation frame. The loop stops. We need the user input handler to trigger another loop.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/Stick-Hero.001-1.jpeg" alt="Image" width="600" height="400" loading="lazy">
<em>In the waiting phase nothing happens</em></p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params">timestamp</span>) </span>{
    ...

  switch (phase) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">"waiting"</span>:
      <span class="hljs-keyword">return</span>; <span class="hljs-comment">// Stop the loop</span>

    ...

  }

    ...
}
</code></pre>
<h3 id="heading-the-stretching-phase">The stretching phase</h3>
<p>In the stretching phase, we increase the length of the last stick based on the time passed and wait until the user releases the mouse. The last stick is always the one in front of the hero. After every view transition, a new stick is added to the current platform.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/Stick-Hero.002-1.jpeg" alt="Image" width="600" height="400" loading="lazy">
<em>In the stretching phase we increase the last stick's length</em></p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params">timestamp</span>) </span>{
    ...

  switch (phase) {
        ...
    case <span class="hljs-string">"stretching"</span>: {
      sticks[sticks.length - <span class="hljs-number">1</span>].length += timePassed / stretchingSpeed;
            <span class="hljs-keyword">break</span>;
    }
        ...
  }

  ...
}
</code></pre>
<h3 id="heading-the-turning-phase">The turning phase</h3>
<p>In the turning phase, we change the rotation of the last stick. We only do it until the stick reaches 90 degrees because that means the stick has reached a flat position. Then we set the phase to walking, so the next <code>requestAnimationFrame</code> will adjust the hero and not the stick.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/Stick-Hero.003-1.jpeg" alt="Image" width="600" height="400" loading="lazy">
<em>In the turning phase we increase the last stick's rotation</em></p>
<p>Once the stick reaches 90 degrees, then if the stick falls on the next platform, we also increase the score value. We increase the <code>score</code> state and update the <code>innerText</code> attribute of the <code>scoreElement</code> (see the outline of the JavaScript file chapter). Then we generate a new platform to be sure that we never run out of them.</p>
<p>If the stick didn’t fall on the next platform, we don’t increase the score and we don’t generate a new platform. We also don’t trigger the falling phase yet either, because first the hero still tries to walk along the stick.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params">timestamp</span>) </span>{
  ...

  switch (phase) {
    ...
    case <span class="hljs-string">"turning"</span>: {
      sticks[sticks.length - <span class="hljs-number">1</span>].rotation += timePassed / turningSpeed;

      <span class="hljs-keyword">if</span> (sticks[sticks.length - <span class="hljs-number">1</span>].rotation &gt;= <span class="hljs-number">90</span>) {
        sticks[sticks.length - <span class="hljs-number">1</span>].rotation = <span class="hljs-number">90</span>;

        <span class="hljs-keyword">const</span> nextPlatform = thePlatformTheStickHits();
        <span class="hljs-keyword">if</span> (nextPlatform) {
          score++;
          scoreElement.innerText = score;

          generatePlatform();
        }

        phase = <span class="hljs-string">"walking"</span>;
      }
      <span class="hljs-keyword">break</span>;
    }
    ...
  }

  ...
}
</code></pre>
<p>This phase uses a utility function to figure out whether the stick will land on the platform or not. It calculates the right end position of the last stick and checks if this position falls between the left and right edge of a platform. If it does, it returns the platform, if not then it returns undefined.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">thePlatformTheStickHits</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> lastStick = sticks[sticks.length - <span class="hljs-number">1</span>];
  <span class="hljs-keyword">const</span> stickFarX = lastStick.x + lastStick.length;

  <span class="hljs-keyword">const</span> platformTheStickHits = platforms.find(
    <span class="hljs-function">(<span class="hljs-params">platform</span>) =&gt;</span> platform.x &lt; stickFarX &amp;&amp; stickFarX &lt; platform.x + platform.w
  );

  <span class="hljs-keyword">return</span> platformTheStickHits;
}
</code></pre>
<h3 id="heading-the-walking-phase">The walking phase</h3>
<p>In the walking phase, we move the hero forward. The end of this phase depends on whether the stick reaches the next platform or not. To determine this, we use the same utility function we just defined above.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/Stick-Hero.004-1.jpeg" alt="Image" width="600" height="400" loading="lazy">
<em>In the walking phase we increase the hero's X position</em></p>
<p>If the end of the stick falls on a platform, then we limit the hero’s position to the edge of that platform. Then once it’s reached, we go to the transitioning phase. If the end of the stick didn’t fall on a platform, though, we limit the hero's forward movement until the end of the stick and then we start the falling phase.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params">timestamp</span>) </span>{
  ...

  switch (phase) {
    ...
    case <span class="hljs-string">"walking"</span>: {
      heroX += timePassed / walkingSpeed;

      <span class="hljs-keyword">const</span> nextPlatform = thePlatformTheStickHits();
      <span class="hljs-keyword">if</span> (nextPlatform) {
        <span class="hljs-comment">// If the hero will reach another platform then limit its position at its edge</span>
        <span class="hljs-keyword">const</span> maxHeroX = nextPlatform.x + nextPlatform.w - <span class="hljs-number">30</span>;
        <span class="hljs-keyword">if</span> (heroX &gt; maxHeroX) {
          heroX = maxHeroX;
          phase = <span class="hljs-string">"transitioning"</span>;
        }
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-comment">// If the hero won't reach another platform then limit its position at the end of the pole</span>
        <span class="hljs-keyword">const</span> maxHeroX =
          sticks[sticks.length - <span class="hljs-number">1</span>].x +
          sticks[sticks.length - <span class="hljs-number">1</span>].length;
        <span class="hljs-keyword">if</span> (heroX &gt; maxHeroX) {
          heroX = maxHeroX;
          phase = <span class="hljs-string">"falling"</span>;
        }
      }
      <span class="hljs-keyword">break</span>;
    }
    ...
  }

  ...
}
</code></pre>
<h3 id="heading-the-transitioning-phase">The transitioning phase</h3>
<p>In the transitioning phase, we move the whole scene. We want to hero to stand at the same position on the screen where it initially stood, but now it’s standing on a different platform. This means that we have to calculate by how much we should shift the whole scene back to achieve the same position. Then just set the phase to waiting and we wait for another mouse event.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/Stick-Hero.005-1.jpeg" alt="Image" width="600" height="400" loading="lazy">
<em>In the transitioning phase we shift the whole view</em></p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params">timestamp</span>) </span>{
    ...

  switch (phase) {
        ...
    case <span class="hljs-string">"transitioning"</span>: {
      sceneOffset += timePassed / transitioningSpeed;

      <span class="hljs-keyword">const</span> nextPlatform = thePlatformTheStickHits();
      <span class="hljs-keyword">if</span> (nextPlatform.x + nextPlatform.w - sceneOffset &lt; <span class="hljs-number">100</span>) {
        sticks.push({
          <span class="hljs-attr">x</span>: nextPlatform.x + nextPlatform.w,
          <span class="hljs-attr">length</span>: <span class="hljs-number">0</span>,
          <span class="hljs-attr">rotation</span>: <span class="hljs-number">0</span>,
        });
        phase = <span class="hljs-string">"waiting"</span>;
      }
      <span class="hljs-keyword">break</span>;
    }
        ...
  }

  ...
}
</code></pre>
<p>We know we've reached the right position when the right side of the platform – shifted by the offset – reaches the first platform's original right-side position. If we look back at initializing the platform, we see that the first platform always has an X position of 50 and its width is also always 50. This means that its right end will be at 100.</p>
<p>At the end of this phase, we also added a new stick to the sticks array with initial values.</p>
<h3 id="heading-the-falling-phase">The falling phase</h3>
<p>In the failing scenario, two things are changing: the hero’s position and the last stick’s rotation. Then once the hero has fallen out of the screen, we stop the game loop again by returning from the function.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/Stick-Hero.006.jpeg" alt="Image" width="600" height="400" loading="lazy">
<em>In the falling phase we both increase the Y position of the hero and the rotation of the last stick</em></p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params">timestamp</span>) </span>{
    ...

  switch (phase) {
        ...
    case <span class="hljs-string">"falling"</span>: {
      heroY += timePassed / fallingSpeed;

      <span class="hljs-keyword">if</span> (sticks[sticks.length - <span class="hljs-number">1</span>].rotation &lt; <span class="hljs-number">180</span>) {
        sticks[sticks.length - <span class="hljs-number">1</span>].rotation += timePassed / turningSpeed;
      }

      <span class="hljs-keyword">const</span> maxHeroY = platformHeight + <span class="hljs-number">100</span>;
      <span class="hljs-keyword">if</span> (heroY &gt; maxHeroY) {
        restartButton.style.display = <span class="hljs-string">"block"</span>;
        <span class="hljs-keyword">return</span>;
      }
      <span class="hljs-keyword">break</span>;
    }
        ...
  }

  ...
}
</code></pre>
<p>So that’s the main loop – how the game moves from phase to phase, changing a range of variables. At the end of every cycle the function calls the <code>draw</code> function to update the scene and requests another frame. If you did everything right, you should have a working game now!</p>
<h1 id="heading-summary">Summary</h1>
<p>In this tutorial, we covered a lot. We learned how to paint basic shapes on a <code>canvas</code> element with JavaScript and we implemented a whole game. </p>
<p>Despite the length of this article, there are still a few things we did not cover here. You can check out the <a target="_blank" href="https://codepen.io/HunorMarton/pen/xxOMQKg">source code</a> of this game for additional features on <a target="_blank" href="https://codepen.io/HunorMarton/pen/xxOMQKg">CodePen</a>. These include:</p>
<ul>
<li>How to make the game fit the whole browser window and translate the screen accordingly.</li>
<li>How to draw a background to the scene and how to draw a more detailed version of our hero.</li>
<li>We add a double score zone to the middle of each platform. If the end of the stick falls into this very small region, the hero scores two points.</li>
</ul>
<p>I hope you enjoyed this tutorial. Stay tuned for more here on freeCodeCamp and on my <a target="_blank" href="https://www.youtube.com/channel/UCxhgW0Q5XLvIoXHAfQXg9oQ">YouTube channel</a>.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/eue3UdFvwPo" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-subscribe-for-more-tutorials-on-web-development"><strong>Subscribe for more tutorials on</strong> Web <strong>Development:</strong></h2>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/undefined" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How TypeScript Helps You Write Better Code ]]>
                </title>
                <description>
                    <![CDATA[ TypeScript is taking over the web. In this article I'll give you a high-level overview of the benefits of TypeScript and how can it help you create websites with fewer bugs.  You'll learn how TypeScript helps with handling edge cases, catching typos,... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/benefits-of-typescript/</link>
                <guid isPermaLink="false">66c4c7fd4173ed342943d0bf</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Hunor Márton Borbély ]]>
                </dc:creator>
                <pubDate>Fri, 03 Nov 2023 15:16:26 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/11/thumbnail.001.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>TypeScript is taking over the web. In this article I'll give you a high-level overview of the benefits of TypeScript and how can it help you create websites with fewer bugs. </p>
<p>You'll learn how TypeScript helps with handling edge cases, catching typos, refactoring code, and how types make code easier to understand. </p>
<p>The latest <a target="_blank" href="https://2022.stateofjs.com/en-US/usage/">State of JavaScript survey</a> found that developers spend more time writing TypeScript than JavaScript code. <a target="_blank" href="https://octoverse.github.com/2022/top-programming-languages">GitHub’s own survey</a> states a more modest claim, saying that TypeScript is only the 4th most used language on the platform – behind JavaScript – yet its usage grew by almost 40% in a year. Why is this shift happening?</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/part0.png" alt="Image" width="600" height="400" loading="lazy">
<em>Graph showing percentage of their time devs spend writing JavaScript vs TypeScript</em></p>
<p>If you prefer video format you can also <a target="_blank" href="https://youtu.be/3nmQj450zk0?si=1HLg0pHnSL5lUe-t">watch this article as a video</a> with a bit more content.</p>
<h2 id="heading-what-are-types">What are Types?</h2>
<p>You may know that in JavaScript (as in other programming languages), there are various data types like strings, numbers, arrays, objects, functions, booleans, undefined and null values. These are all types. </p>
<p>But JavaScript is dynamically typed, which means that variables can change types. You can set the value of a variable to a string in one line and set the same variable to a number in another line, for example:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> value = <span class="hljs-string">'Hello'</span>;
value = <span class="hljs-number">3</span>;
</code></pre>
<p>On the one hand, this is a benefit of the language. It means that it's simple and very flexible. You don't have to restrict yourself by setting types. </p>
<p>On the other hand, you can easily shoot yourself in the foot if you are not careful enough. When you write JavaScript, it is your responsibility to use the right type of value in your code. </p>
<p>If you accidentally use the wrong types, you will run into errors. Have you tried getting the length of undefined? Don't try, it throws you an error. But maybe you had an edge case that changed your value and you didn't realize it:</p>
<pre><code class="lang-javascript">value = <span class="hljs-number">3</span>;
...
console.log(value.length); <span class="hljs-comment">// TypeError: Cannot read properties of undefined</span>
</code></pre>
<p>Because the variables can change type at any time, JavaScript only checks if your code will work when you run it. You don't know you have an error until you run the code. </p>
<p>And if you only have an error in a very rare edge case, maybe you don't even realize that your code might fail for a very long time. When you write the code, JavaScript doesn't warn you that you might have an issue.</p>
<p>TypeScript, on the other hand, is statically typed. This means that variables can't change type. This makes the code more predictable and it allows TypeScript to analyze the code as you write it and spot mistakes as they occur (so they don't show up as errors in your code later).</p>
<p>Alright, now that you have a basic understanding of what types are and how JavaScript and TypeScript differ in how they handle them, let's dive into the main partf of the tutorial.</p>
<h2 id="heading-youre-already-using-typescript">You're Already Using TypeScript</h2>
<p>Let’s review the basics: TypeScript gives you type information. It tells you the types of your variables, the type of the arguments you need to pass on to your functions, and what kind of data they will return. </p>
<p>But what if I tell you that you're already using TypeScript, even in plain JavaScript?</p>
<p>Let’s take this quick example. This is a plain JavaScript file in VS Code. Because VS Code and TypeScript are both made by Microsoft, VS Code already comes with built-in TypeScript features.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/part1-1.gif" alt="Screen recording of VS Code with a simple JavaScript code. In one line there is a variable called 'input' that is set to a string value. The recording shows that when you hover over the variable name, in an information popup its type is correctly revealed as a string. It also shows that another variable named 'greeting' is set to the return value of a function. When hovering over this variable name, it also shows that its type is a string. Finally, a third variable named 'greetingLength' is assigned to the length of greeting. When hovering over it is revealed that its value is a number." width="600" height="400" loading="lazy">
<em>When you hover over your variables, VS Code will tell their type whenever possible</em></p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getGreeting</span>(<span class="hljs-params">str</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">`Hello <span class="hljs-subst">${str}</span>!`</span>;
}

<span class="hljs-keyword">let</span> input = <span class="hljs-string">'Bob'</span>; <span class="hljs-comment">// Type: string</span>
<span class="hljs-keyword">let</span> greeting = getGreeting(input); <span class="hljs-comment">// Type: string</span>
<span class="hljs-keyword">let</span> greetingLength = greeting.length; <span class="hljs-comment">// Type: number</span>

<span class="hljs-built_in">console</span>.log(greeting, greetingLength);
</code></pre>
<p>If you hover over the <code>input</code> variable, VS Code will tell you that its type is a <code>string</code>. This is quite clear, as we've assigned a string to it in the very same line.</p>
<p>But if we go further and check the type of <code>greeting</code> it also says it’s a <code>string</code>. This is a bit more interesting. Greeting’s value comes from a function. How do we know that it’s a string?</p>
<p>VS Code, with the help of TypeScript, analyses the function, checks every possible return path, and concludes that the only thing that this function can return is a <code>string</code>.</p>
<p>Of course, this is a very simple example. But even if we had a more complex logic, with multiple different return statements, TypeScript would still analyze every different path and would tell you what the possible return values are.</p>
<p>To stick a little longer with this example, if we hover over the <code>length</code> variable we are going to see that its type is a <code>number</code>. This might seem obvious again, but the logic behind it is smarter than it looks. </p>
<p>The <code>length</code> property of a <code>string</code> is a number. But this is only true if we are looking at a <code>string</code>. In this case, we know that it is a <code>string</code> because TypeScript already figured out that the return type of our function is a <code>string</code>. There are multiple steps here behind the scenes.</p>
<p>So the first reason why TypeScript is awesome: you can see the types of values just by hovering over them. And this even works to some extent in plain JavaScript.</p>
<h3 id="heading-checking-the-required-arguments-for-built-in-functions">Checking the required arguments for built-in functions</h3>
<p>Let’s see another example. This code is still in JavaScript, we still don’t define types explicitly, but the TypeScript parser can still derive the types.</p>
<p>Here we have an input value, again we hardcode it to ‘bob’, and then we capitalize the string. We take the first character, make it upper case, then add the rest of the string in lower case.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/part2.gif" alt="Screen recording of VS Code with a simple JavaScript code. In the code we define an 'input' variable, capitalize it, and then get the length of the capitalized variable. The recording shows, that when you hover over the used functions like charAt and toUpperCase functions, their signature is revealed. " width="600" height="400" loading="lazy">
<em>You can check the signature of JavaScript functions in VS Code</em></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> input = <span class="hljs-string">'bob'</span>;
<span class="hljs-keyword">let</span> formattedInput = 
    input.charAt(<span class="hljs-number">0</span>).toUpperCase() + input.slice(<span class="hljs-number">1</span>).toLowerCase();
<span class="hljs-keyword">let</span> formattedLength = formattedInput.length;

<span class="hljs-built_in">console</span>.log(greeting, greetingLength);
</code></pre>
<p>We can check the function signature of these functions. We can see that <code>charAt</code> requires a number parameter, <code>toUpperCase</code> does not require any parameters, and the <code>slice</code> function has two optional parameters that can be either a <code>number</code> or <code>undefined</code>. </p>
<p>These are TypeScript annotations. The question marks mean that a value is optional, and the pipe characters mean that a type could be either this or that.</p>
<p>In summary, we know that our formatted input is of type <code>string</code>.</p>
<h3 id="heading-where-javascript-cant-figure-out-the-types-anymore">Where JavaScript can’t figure out the types anymore</h3>
<p>Let’s extract the logic that capitalizes our input into a function. The types should remain the same, right? Well, not exactly. Now our capitalized input is of type <code>any</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/part3.png" alt="VS Code with a simple JavaScript code. First, we define a simple function that capitalizes a string, then we use this function to capitalize a string. The image shows that when you hover over the returned value, now its type is of type 'any'." width="600" height="400" loading="lazy">
<em>After we extracted the logic that capitalizes the input, the return value is now of type 'any'</em></p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">capitalize</span>(<span class="hljs-params">str</span>) </span>{
    <span class="hljs-keyword">return</span> str.charAt(<span class="hljs-number">0</span>).toUpperCase() + str.slice(<span class="hljs-number">1</span>).toLowerCase();
}

<span class="hljs-keyword">let</span> input = <span class="hljs-string">'bob'</span>;
<span class="hljs-keyword">let</span> formattedInput = capitalize(input); <span class="hljs-comment">// Type: any</span>
<span class="hljs-keyword">let</span> formattedLength = formattedInput.length;
</code></pre>
<p>Interesting! What happened? Our function should always return a string, right? If you look at the entire code base in this specific case, then yes. It should return a string. </p>
<p>But we have to look at the function in isolation. We can’t assume what it will receive. We could also pass on a number to it and in that case, it would fail. You can’t read the first character of a number or turn it to lower or upper case.</p>
<p>In JavaScript we can’t state that we need a string here, so we can’t be sure that the function returns a string. In TypeScript we are going to specify the type of this parameter to avoid any confusion.</p>
<p>You might notice that the previous example with the <code>getGreeting</code> function was different. There, no matter what the input was, the function always returned a <code>string</code>. Here, however, the output depends on the input.</p>
<h2 id="heading-what-about-edge-cases-in-javascript">What About Edge Cases in JavaScript?</h2>
<p>Could we avoid getting an error in case we get the wrong input in JavaScript? Yes.</p>
<p>Checking the type early and returning before the function can fail is one way to make our function fail-safe. This is still JavaScript, and the <code>typeof</code> operator is part of JavaScript.</p>
<p>Now this function won't fail. But I introduced a bug.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/part4.png" alt="VS Code with the same simple JavaScript file as we had earlier. Except that now our capitalize function simply returns if its argument is not a string. The image shows that now the type of the returned value changed from any to string or undefined." width="600" height="400" loading="lazy">
<em>After adding an early return statement to our capitalize function, the type of the returned value changed from any to string or undefined</em></p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">capitalize</span>(<span class="hljs-params">str</span>) </span>{
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> str !== <span class="hljs-string">'string'</span>) <span class="hljs-keyword">return</span>;
    <span class="hljs-keyword">return</span> str.charAt(<span class="hljs-number">0</span>).toUpperCase() + str.slice(<span class="hljs-number">1</span>).toLowerCase();
}

<span class="hljs-keyword">let</span> input = <span class="hljs-string">'bob'</span>;
<span class="hljs-keyword">let</span> formattedInput = capitalize(input); <span class="hljs-comment">// Type: string or undefined</span>
<span class="hljs-keyword">let</span> formattedLength = formattedInput.length;
</code></pre>
<p>If we check the function signature or the type of the new value, it has changed from <code>any</code> to <code>string | undefined</code>. </p>
<p>In one way that is very neat. We limited the possible return values and we have a better understanding of what the function does, just by looking at the returned value. On the other hand, in case it returns <code>undefined</code>, the application will fail in the very next line. You can’t check the length of <code>undefined</code>.</p>
<p>Of course, we could have also returned an empty string as a fallback and then we wouldn’t have this problem. We're working with strings here. But this is a great example of a topic that is very easy to overlook in JavaScript and which can cause you a lot of headaches later: edge cases.</p>
<p>What if you didn’t write the capitalize function and you didn’t know how exactly it works? </p>
<p>Maybe it is also located in a different file and you simply assumed that it would always return a string. Maybe the function you use is much longer and much more complicated. </p>
<p>Maybe you checked the function, but only the last line of it and say ‘Okay, this function returns a string’. And you completely miss that in a different line – that can also be in the middle of the function – there is another return statement that returns a different type of value.</p>
<p>The point here is that edge cases can happen and it’s very easy to miss them. It’s a typical source of bugs when developers cover the happy path of an application, but they are less attentive when covering edge cases. </p>
<p>In JavaScript, you need to be mindful of edge cases, test different scenarios and user inputs, and write thorough unit tests to make sure your logic doesn’t fail.</p>
<p>Wouldn’t it be nice if the editor would tell you that you have a problem?</p>
<h2 id="heading-turn-the-code-into-typescript">Turn the Code into TypeScript</h2>
<p>So after all this introduction, let’s finally turn this code into TypeScript. To do so, let’s simply change the extension from <code>.js</code> to <code>.ts</code> and see what happens. </p>
<p>Right away, we'll see an error saying that our <code>formattedInput</code> might be <code>undefined</code>. And that doesn’t work well with getting the length of the value. We already caught the bug that caused trouble earlier and we didn’t even invest any time in adding types to our code.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/part-5.png" alt="VS Code with the same file as we had earlier. Except now we changed the file extension to TypeScript. The image shows that a new error appears." width="600" height="400" loading="lazy">
<em>After changing the extension to .ts an error is revealing that our capitalized value might be undefined</em></p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">capitalize</span>(<span class="hljs-params">str</span>) </span>{
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> str !== <span class="hljs-string">'string'</span>) <span class="hljs-keyword">return</span>;
    <span class="hljs-keyword">return</span> str.charAt(<span class="hljs-number">0</span>).toUpperCase() + str.slice(<span class="hljs-number">1</span>).toLowerCase();
}

<span class="hljs-keyword">let</span> input = <span class="hljs-string">'bob'</span>;
<span class="hljs-keyword">let</span> formattedInput = capitalize(input);
<span class="hljs-keyword">let</span> formattedLength = formattedInput.length; <span class="hljs-comment">// Error</span>
</code></pre>
<p>Before fixing this error, let’s turn on <code>strict</code> mode. By default, TypeScript can be very permissive, but we also get less value out of it without <code>strict</code> mode. </p>
<p>For this, we need a <code>tsconfig.json</code> file at the root of our project. This might seem a bit scary now, but this file is most probably generated for you automatically when you create a project with any of the frameworks. What’s important for now is that we have <code>strict</code> mode set to true.</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"compilerOptions"</span>: {
      <span class="hljs-attr">"target"</span>: <span class="hljs-string">"ESNext"</span>,
      <span class="hljs-attr">"module"</span>: <span class="hljs-string">"ESNext"</span>,
      <span class="hljs-attr">"strict"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">"esModuleInterop"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">"skipLibCheck"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">"forceConsistentCasingInFileNames"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">"lib"</span>: [<span class="hljs-string">"DOM"</span>, <span class="hljs-string">"ESNext"</span>],
      <span class="hljs-attr">"moduleResolution"</span>: <span class="hljs-string">"node"</span>,
      <span class="hljs-attr">"noImplicitAny"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">"allowSyntheticDefaultImports"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">"baseUrl"</span>: <span class="hljs-string">"./src"</span>,
      <span class="hljs-attr">"paths"</span>: {
        <span class="hljs-attr">"*"</span>: [<span class="hljs-string">"src/*"</span>]
      },
      <span class="hljs-attr">"outDir"</span>: <span class="hljs-string">"./dist"</span>,
      <span class="hljs-attr">"rootDir"</span>: <span class="hljs-string">"./src"</span>,
      <span class="hljs-attr">"strictPropertyInitialization"</span>: <span class="hljs-literal">false</span>,
      <span class="hljs-attr">"noEmit"</span>: <span class="hljs-literal">false</span>,
    },
    <span class="hljs-attr">"include"</span>: [<span class="hljs-string">"src/**/*.ts"</span>, <span class="hljs-string">"src/index.js"</span>],
    <span class="hljs-attr">"exclude"</span>: [<span class="hljs-string">"node_modules"</span>]
  }
</code></pre>
<p>This will show more errors because with this setting we have to define the types of our function arguments.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/part-6.png" alt="VS Code with the same TypeScript file as before. Now with strict mode, another error shows up that states that we need to set the type of our function argument." width="600" height="400" loading="lazy">
<em>After turning on strict mode, we have to set the type of function arguments</em></p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">capitalize</span>(<span class="hljs-params">str</span>) </span>{ <span class="hljs-comment">// Error</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> str !== <span class="hljs-string">'string'</span>) <span class="hljs-keyword">return</span>;
    <span class="hljs-keyword">return</span> str.charAt(<span class="hljs-number">0</span>).toUpperCase() + str.slice(<span class="hljs-number">1</span>).toLowerCase();
}

<span class="hljs-keyword">let</span> input = <span class="hljs-string">'bob'</span>;
<span class="hljs-keyword">let</span> formattedInput = capitalize(input);
<span class="hljs-keyword">let</span> formattedLength = formattedInput.length; <span class="hljs-comment">// Error</span>
</code></pre>
<p>So let’s specify that our capitalize function requires a <code>string</code> by setting the argument to <code>str: string</code>. And that’s all the types we need to add in this case because that’s the only one TypeScript couldn’t figure out itself.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/part-7.png" alt="VS Code with the same TypeScript file as before. Except we set the type of the function argument to string." width="600" height="400" loading="lazy">
<em>Setting the type of the function's argument to string</em></p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">capitalize</span>(<span class="hljs-params">str: <span class="hljs-built_in">string</span></span>) </span>{
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> str !== <span class="hljs-string">'string'</span>) <span class="hljs-keyword">return</span>;
    <span class="hljs-keyword">return</span> str.charAt(<span class="hljs-number">0</span>).toUpperCase() + str.slice(<span class="hljs-number">1</span>).toLowerCase();
}

<span class="hljs-keyword">let</span> input = <span class="hljs-string">'bob'</span>;
<span class="hljs-keyword">let</span> formattedInput = capitalize(input);
<span class="hljs-keyword">let</span> formattedLength = formattedInput.length; <span class="hljs-comment">// Error</span>
</code></pre>
<p>One misconception about TypeScript is that you have to add types to everything. While that’s not a bad practice it is not strictly necessary. TypeScript is very smart. It analyses the code and figures out the types of as many things as possible.</p>
<p>We can of course specify types at other places. We can specify that we need our <code>formattedInput</code> value to be a <code>string</code> by setting <code>let formattedInput: string</code>. This is our whole problem here. We thought it was a string, but in some cases, it wasn’t.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/part-8.png" alt="VS Code with the same TypeScript file as before. Except, that now the variable we set to the returned value of our capitalize function has to be a string. This revealed that there might be a type mismatch because the capitalize function might return undefined." width="600" height="400" loading="lazy">
<em>After setting the type of the 'formattedInput' variable, the error changed</em></p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">capitalize</span>(<span class="hljs-params">str: <span class="hljs-built_in">string</span></span>) </span>{
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> str !== <span class="hljs-string">'string'</span>) <span class="hljs-keyword">return</span>;
    <span class="hljs-keyword">return</span> str.charAt(<span class="hljs-number">0</span>).toUpperCase() + str.slice(<span class="hljs-number">1</span>).toLowerCase();
}

<span class="hljs-keyword">let</span> input = <span class="hljs-string">'bob'</span>;
<span class="hljs-keyword">let</span> formattedInput: <span class="hljs-built_in">string</span> = capitalize(input); <span class="hljs-comment">// Error</span>
<span class="hljs-keyword">let</span> formattedLength = formattedInput.length;
</code></pre>
<p>This highlights our problem right away. We want it to be a <code>string</code>, but our function might return <code>undefined</code>. We can read in the popover that <code>undefined</code> cannot be assigned to type <code>string</code>.</p>
<p>We can go further, by saying we want this function to return a <code>string</code>. This again will change the error. Now the problem is not that we can’t assign the returned value to a <code>string</code> variable, but that the function itself returns an incorrect value.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/part-9.png" alt="VS Code with the same TypeScript file as before. Except, that now we set the return type of our capitalize function to string. This changes our error because the function might return undefined." width="600" height="400" loading="lazy">
<em>After setting the return type of the capitalize function, the error changed again</em></p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">capitalize</span>(<span class="hljs-params">str: <span class="hljs-built_in">string</span></span>): <span class="hljs-title">string</span> </span>{
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> str !== <span class="hljs-string">'string'</span>) <span class="hljs-keyword">return</span>; <span class="hljs-comment">// Error</span>
    <span class="hljs-keyword">return</span> str.charAt(<span class="hljs-number">0</span>).toUpperCase() + str.slice(<span class="hljs-number">1</span>).toLowerCase();
}

<span class="hljs-keyword">let</span> input = <span class="hljs-string">'bob'</span>;
<span class="hljs-keyword">let</span> formattedInput: <span class="hljs-built_in">string</span> = capitalize(input);
<span class="hljs-keyword">let</span> formattedLength = formattedInput.length;
</code></pre>
<p>To solve this, let’s delete this whole line. Earlier we added this line for type checking, but now, we let TypeScript do the whole type checking for us. The function signature already states that this property has to be a <code>string</code>, there’s no need to check it again. Our code got simpler, and at the same time safer.</p>
<p>So another reason why TypeScript is amazing is that it forces you to think of edge cases. Not just to think of them, but also to handle them. Something that’s very easy to overlook in JavaScript.</p>
<h2 id="heading-refactoring-the-code">Refactoring the Code</h2>
<p>Now that we've got the basics, let’s go on to our third topic: refactoring. Let’s update our greeting function a bit, and let’s say it now takes two arguments: a first name and a last name. Imagine that this is a utility function that is used in many places in a huge, complex project:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/part10-1.png" alt="VS Code with a simple TypeScript file. In this file, we define a 'getGreeting' function that receives two arguments, a first name, and a last name. The type of both of these arguments is a string." width="600" height="400" loading="lazy">
<em>Our new updated 'getGreeting' function that receives a first name and a last name</em></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getGreeting</span>(<span class="hljs-params">firstName: <span class="hljs-built_in">string</span>, lastName: <span class="hljs-built_in">string</span></span>) </span>{
    <span class="hljs-keyword">const</span> formattedFirstName = capitalize(firstName);
    <span class="hljs-keyword">const</span> formattedLastName = capitalize(lastName);
    <span class="hljs-keyword">return</span> <span class="hljs-string">`Hello <span class="hljs-subst">${formattedFirstName}</span> <span class="hljs-subst">${formattedLastName}</span>!`</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">capitalize</span>(<span class="hljs-params">str: <span class="hljs-built_in">string</span></span>): <span class="hljs-title">string</span> </span>{
    <span class="hljs-keyword">return</span> str.charAt(<span class="hljs-number">0</span>).toUpperCase() + str.slice(<span class="hljs-number">1</span>).toLowerCase();
}
</code></pre>
<p>What if we decide that we need to refactor this code? Instead of passing on two strings, we want to pass in an object that has the first name and last name properties.</p>
<p>In TypeScript, we can precisely define the shape of an object. We can define that we have to pass on a <code>person</code> parameter that should be an object with a <code>firstName</code> and a <code>lastName</code> property, that both have to be <code>strings</code>.</p>
<p>We can define a type for this argument. We say that we have a <code>Person</code> type with capital P by convention. This type describes an object with the <code>firstName</code> and <code>lastName</code> properties that are both strings. </p>
<p>We can even add more things if we want, like adding a <code>birthday</code> property that has a type <code>Date</code>. But let’s make this optional because we don’t want to deal with it for now. </p>
<p>Adding a question mark here makes this property optional. We can set it, but we don’t have to. But when we try to use it, we also can’t assume that it does exist.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/part-10.png" alt="VS Code with the same TypeScript file. Except now we define a custom person type like this: `type Person = { firstName: string, lastName: string, birthday?: Date }`. Then we change the two function arguments to a single 'person' argument with type 'Person'. As a result, the editor shows errors in this file and it also highlights in the file explorer that the other files have errors as well." width="600" height="400" loading="lazy">
<em>After changing the function argument type, the editor highlights the parts we need to change</em></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> Person = {
    firstName: <span class="hljs-built_in">string</span>,
    lastName: <span class="hljs-built_in">string</span>,
    birthDay?: <span class="hljs-built_in">Date</span>
}

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getGreeting</span>(<span class="hljs-params">person: Person</span>) </span>{
    <span class="hljs-keyword">const</span> formattedFirstName = capitalize(firstName);
    <span class="hljs-keyword">const</span> formattedLastName = capitalize(lastName);
    <span class="hljs-keyword">return</span> <span class="hljs-string">`Hello <span class="hljs-subst">${formattedFirstName}</span> <span class="hljs-subst">${formattedLastName}</span>!`</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">capitalize</span>(<span class="hljs-params">str: <span class="hljs-built_in">string</span></span>): <span class="hljs-title">string</span> </span>{
    <span class="hljs-keyword">return</span> str.charAt(<span class="hljs-number">0</span>).toUpperCase() + str.slice(<span class="hljs-number">1</span>).toLowerCase();
}
</code></pre>
<p>Now we can set that our <code>person</code> argument is of type <code>Person</code>.</p>
<p>As we do this change the editor lights up in red. It says that I’m trying to use variables that don’t exist. In this function, I’m referring to <code>firstName</code> and <code>lastName</code>, and there’s only a <code>person</code> object now. </p>
<p>Even more, the other files in the file explorer light up as well, saying that I called the function with two arguments even though it expects only one.</p>
<p>Let’s fix the error in this file and replace <code>firstName</code> and <code>lastName</code> with <code>person.firstName</code> and <code>person.lastName</code>. TypeScript is very picky about using variables that don’t exist. </p>
<p>To give you an even better example: What if I made a typo here? What if I miss a letter from <code>firstName</code>? This could be a very easily overlooked problem in JavaScript. Here, though, it’s not only highlighted that there’s no such property on a <code>Person</code>, but it even suggests that you might want to use <code>firstName</code> instead.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/part-11.png" alt="VS Code with the same TypeScript file. Except now we updated the function to use the new function argument. By mistake, we made a typo. The editor highlights the typo and it makes a suggestion." width="600" height="400" loading="lazy">
<em>If we make a typo, the editor suggests corrections</em></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> Person = {
    firstName: <span class="hljs-built_in">string</span>,
    lastName: <span class="hljs-built_in">string</span>,
    birthDay?: <span class="hljs-built_in">Date</span>
}

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getGreeting</span>(<span class="hljs-params">person: Person</span>) </span>{
    <span class="hljs-keyword">const</span> formattedFirstName = capitalize(person.frstName);
    <span class="hljs-keyword">const</span> formattedLastName = capitalize(person.lastName);
    <span class="hljs-keyword">return</span> <span class="hljs-string">`Hello <span class="hljs-subst">${formattedFirstName}</span> <span class="hljs-subst">${formattedLastName}</span>!`</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">capitalize</span>(<span class="hljs-params">str: <span class="hljs-built_in">string</span></span>): <span class="hljs-title">string</span> </span>{
    <span class="hljs-keyword">return</span> str.charAt(<span class="hljs-number">0</span>).toUpperCase() + str.slice(<span class="hljs-number">1</span>).toLowerCase();
}
</code></pre>
<p>Then, let’s fix the errors in the other files. As you can see the files with an error are already highlighted in the file explorer. This is of course a very simple example, but imagine you have a huge project and this function is called in a hundred different places. Of course, you can be thorough and fix all of them one by one, but in JavaScript, it would be very easy to miss one of them.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/part-12.gif" alt="VS Code with another TypeScript file calling our previously defined function. As we update the function call from the previous two-argument signature to our new one, TypeScript gives relevant errors if we make mistakes." width="600" height="400" loading="lazy">
<em>When updating the function calls, TypeScipt gives you errors if we make mistakes</em></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { getGreeting } <span class="hljs-keyword">from</span> <span class="hljs-string">"./utils"</span>;

<span class="hljs-keyword">let</span> greeting = getGreeting({ firstName: <span class="hljs-string">'bob'</span>, lastName:  <span class="hljs-string">'marley'</span> });

<span class="hljs-built_in">console</span>.log(greeting);
</code></pre>
<p>Now here, the error says that we pass on two parameters, but only one is expected. If we simply delete the second argument it says we pass on a string, but it expected an object of type <code>Person</code>. If we pass on an object with the first name only, it will still complain that we're missing the last name. If we add the last name, but again with a typo, then again it will say that we have a wrong property and it even suggests that we might have made a typo here.</p>
<p>TypeScript is very precise about what our issue is, and we can easily figure out how to fix it.</p>
<p>Now let’s fix the other file. Instead of inlining the argument, we can also define it as a variable and TypeScript will recognize that an object of this shape fits the criteria of this function. </p>
<p>If we want to be sure that our variable is of type Person, we can also import the type and set it to this object. First, we need to export it, in the utility file, and then can import it just as our function, then assign it to our object.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/part-13.png" alt="VS Code with yet another TypeScript file calling our previously defined function. This time we don't inline the function argument, but create a new variable for it. When setting the type of this argument, we reuse the previously defined 'Person' type." width="600" height="400" loading="lazy">
<em>We can also use our Person type in other files</em></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Person, getGreeting } <span class="hljs-keyword">from</span> <span class="hljs-string">"./utils"</span>;

<span class="hljs-keyword">let</span> person: Person = {
    firstName: <span class="hljs-string">'bob'</span>,
    lastName: <span class="hljs-string">'dylan'</span>
}

<span class="hljs-keyword">let</span> greeting = getGreeting(person);

<span class="hljs-built_in">console</span>.log(greeting);
</code></pre>
<h2 id="heading-summary">Summary</h2>
<p>TypeScript can be much more complicated than this. But that’s the gist of it. For the most part you define your own types and TypeScript makes sure that you use them correctly.</p>
<p>In summary, there are three main reasons you want to consider using TypeScript:</p>
<ol>
<li>You get type information about functions</li>
<li>You know what they return</li>
<li>You know what they expect from you, even without looking at the function itself</li>
</ol>
<p>TypeScript makes sure that you wire together your application correctly. It forces you to call functions with the right arguments and it forces you to think about edge cases.</p>
<p>TypeScript helps a lot during refactoring. It doesn’t let you miss any part of your code that you need to change and it doesn’t let you get away with typos.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/3nmQj450zk0" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h3 id="heading-subscribe-for-more-tutorials-on-web-development">Subscribe for more tutorials on Web Development:</h3>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/undefined" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ SVG + JavaScript Tutorial – How to Code an Animated Watch ]]>
                </title>
                <description>
                    <![CDATA[ Since SVG images can be inlined in HTML, we can manipulate them with JavaScript. This means that we can animate parts of an image from code, make it interactive, or turn things around and generate graphics from data. In this example, we are going to ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/svg-javascript-tutorial/</link>
                <guid isPermaLink="false">66c4c81a95c356a0a664df9d</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SVG ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Hunor Márton Borbély ]]>
                </dc:creator>
                <pubDate>Thu, 23 Dec 2021 17:19:15 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/12/SVG-Watch.001.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Since SVG images can be inlined in HTML, we can manipulate them with JavaScript. This means that we can animate parts of an image from code, make it interactive, or turn things around and generate graphics from data.</p>
<p>In this example, we are going to create a watch. We will use SVG to paint the watch, and use JavaScript to animate the hands. </p>
<p>This tutorial is a bit more advanced, going in-depth with some of the less obvious SVG properties and focusing on animation with JavaScript. If you'd like to get a more general overview of SVGs, then check out my <a target="_blank" href="https://www.freecodecamp.org/news/svg-tutorial-learn-to-code-images/">earlier article</a> where we walk through the code of 7 simple SVG images. </p>
<p>You can also <a target="_blank" href="https://www.youtube.com/watch?v=ULomsOSk4JA">watch this article as a video</a> with a bit more content. In the video we also cover interaction.</p>
<h2 id="heading-svg-in-html"><strong>SVG in HTML</strong></h2>
<p>In the previous article, we learned that SVG images can be inlined in an HTML document. We talked about the SVG tag itself, which defines the size of the image, and the placement of the image elements.</p>
<p>Image elements are placed within the image by their position. The <code>viewBox</code> defines how these positions should be interpreted. </p>
<p>The first two numbers of the property set the position at the top-left corner. Together with the size defined by the last two numbers, they form a coordinate system.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/SVG-Watch.001-1.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Watch<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"./index.css"</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"-100 -100 200 200"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">circle</span>
        <span class="hljs-attr">cx</span>=<span class="hljs-string">"0"</span>
        <span class="hljs-attr">cy</span>=<span class="hljs-string">"0"</span>
        <span class="hljs-attr">r</span>=<span class="hljs-string">"90"</span>
        <span class="hljs-attr">fill</span>=<span class="hljs-string">"transparent"</span>
        <span class="hljs-attr">stroke</span>=<span class="hljs-string">"#f0f0c9"</span>
        <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"7"</span>
      /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./index.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>In this example, we center the coordinate system. The <code>0,0</code> coordinate is in the middle of the image. We set with the <code>viewBox</code> that the top-left corner should be the <code>-100,-100</code> coordinate and both the width and height should be 200 units.</p>
<p>In this example, the size defined by <code>width</code> and <code>height</code> and the size defined by <code>viewBox</code> are the same. This means that one unit within the image will be one pixel in the browser. This is not always true. If the two don't match then the image scales up or scales down. </p>
<h2 id="heading-how-to-make-the-watchs-minute-and-hour-hands"><strong>How to Make the Watch's Minute and Hour Hands</strong></h2>
<p>Now that we've established our foundations, let's start coding the watch itself. We start with the minute and hour hands. </p>
<p>There are many ways to draw these little lines. We could draw each line one by one, but probably the most effective way to draw it is to draw a circle with a special dash property.</p>
<p>The <code>circle</code> tag in our initial example has a center position, a radius for the size, a fill and border color, and a border width. </p>
<p>SVG elements often have similar styling options as HTML elements with CSS. But these options have different property names. You can think of the <code>fill</code> property as <code>background-color</code> in CSS. And the <code>stroke</code> and <code>stroke-width</code> properties are also similar to the <code>border-color</code> and <code>border-width</code> properties. Just keep in mind that they are not exactly the same. </p>
<p>We will also use the <code>fill</code> property for setting text color, and we will use the <code>stroke</code> property to set the color of a line.</p>
<p>Now how do we turn a continuous circle into minute markers? You might be familiar with the <code>border-style</code> property in CSS. Mostly you would use a solid border, but you can also have a dotted or a dashed one. These border styles are not very common, as you don’t have that many options to fine-tune them in CSS. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/SVG-Watch.001-2.jpeg" alt="Image" width="600" height="400" loading="lazy">
<em>The <code>border-style</code> property in CSS for HTML elements</em></p>
<p>In SVG we have similar possibilities with much more customization options. We can use the <code>stroke-dasharray</code>, the <code>stroke-dashoffset</code>, and the <code>pathLength</code> properties. </p>
<p>Let’s have a few examples. In the first example, we set a single number as <code>stroke-dasharray</code>. This will result in a dashed border where the line segment and the gap both have the same length. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/SVG-Watch.002.jpeg" alt="Image" width="600" height="400" loading="lazy">
<em>The <code>stroke-dasharray</code> property for SVG</em></p>
<p>This property is an array though. If we set two numbers, then the first one will be the length of the line segment, and the second will be the length of the gap. You can even set more than two numbers, and then the length of the line and the gap will always take the next number. Until it runs out of the array and then it starts at the beginning.</p>
<p>We will set two numbers. One for the length of the minute marker, and one for the gap between them. The sum of these two should be exactly the length of one minute on the circle. We know that one hour is 60 minutes. So we can calculate the circumference of the circle, then divide it by 60 to get the length of one minute. </p>
<p>But there’s a better way. Instead of calculating the circumference of the circle, we can go the other way. We can set the <code>pathLength</code> property. </p>
<p>This property is a bit tricky. It does not resize the circle but affects how the dasharray property is interpreted. The dashes will be drawn as if the circle had a circumference defined by <code>pathLength</code>.</p>
<p>So let’s set the <code>pathLength</code> to <code>60</code>, representing 60 minutes. Now the sum of the line and gap segment has to be 1 in total. I set it to <code>0.2</code> and <code>0.8</code> in this example.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/SVG-Watch.001-3.jpeg" alt="Image" width="600" height="400" loading="lazy">
<em>Using the <code>pathLength</code> property. Note that the sum of the two numbers at the <code>stroke-dasharray</code> property is one, matching the length of one minute.</em></p>
<p>Now we are almost done, but one little piece is still missing. The dashing starts at the wrong position. To fix it we have to shift it by half of the line segment’s length using the <code>stroke-dashoffset</code> property. </p>
<p>The dash offset property can be a bit counterintuitive, as a positive value here shifts the dashing backwards. You can also set it to a positive number to shift it forward.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/SVG-Watch.002-1.jpeg" alt="Image" width="600" height="400" loading="lazy">
<em>Example with and without <code>stroke-dashoffset</code></em></p>
<p>In the same way, we can set an hour marker. We add a new circle tag with almost the same properties. The only thing that is different is the color and we have longer gaps in the dash array. </p>
<pre><code class="lang-html">. . .

    <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"-100 -100 200 200"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">circle</span>
        <span class="hljs-attr">cx</span>=<span class="hljs-string">"0"</span>
        <span class="hljs-attr">cy</span>=<span class="hljs-string">"0"</span>
        <span class="hljs-attr">r</span>=<span class="hljs-string">"90"</span>
        <span class="hljs-attr">fill</span>=<span class="hljs-string">"transparent"</span>
        <span class="hljs-attr">stroke</span>=<span class="hljs-string">"#0f0e0e"</span>
        <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"7"</span>
        <span class="hljs-attr">stroke-dasharray</span>=<span class="hljs-string">"0.2 0.8"</span>
        <span class="hljs-attr">stroke-dashoffset</span>=<span class="hljs-string">"0.1"</span>
        <span class="hljs-attr">pathLength</span>=<span class="hljs-string">"60"</span>
      /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">circle</span>
        <span class="hljs-attr">cx</span>=<span class="hljs-string">"0"</span>
        <span class="hljs-attr">cy</span>=<span class="hljs-string">"0"</span>
        <span class="hljs-attr">r</span>=<span class="hljs-string">"90"</span>
        <span class="hljs-attr">fill</span>=<span class="hljs-string">"transparent"</span>
        <span class="hljs-attr">stroke</span>=<span class="hljs-string">"#f0f0c9"</span>
        <span class="hljs-attr">stroke-width</span>=<span class="hljs-string">"7"</span>
        <span class="hljs-attr">stroke-dasharray</span>=<span class="hljs-string">"0.2 4.8"</span>
        <span class="hljs-attr">stroke-dashoffset</span>=<span class="hljs-string">"0.1"</span>
        <span class="hljs-attr">pathLength</span>=<span class="hljs-string">"60"</span>
      /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>

. . .
</code></pre>
<p>It's important to note here that layering in SVG matters. Tags added later in the document will be on top of the previous ones. If we add these two circles in the opposite order, then the minutes would fully cover the hour markers. </p>
<p>As SVG lives in HTML now, we can move some of these properties from CSS. We can’t move all the properties though. There’s a difference between properties defining the style and those defining the shape of an element. </p>
<p>The radius, for instance, defines the shape of the circle, so it has to stay with the SVG code. The fill and stroke properties on the other hand we can move. </p>
<pre><code class="lang-html">. . .

    <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"-100 -100 200 200"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"minute_marker"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"90"</span> <span class="hljs-attr">pathLength</span>=<span class="hljs-string">"60"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hour_marker"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"90"</span> <span class="hljs-attr">pathLength</span>=<span class="hljs-string">"60"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>

. . .
</code></pre>
<pre><code class="lang-css"><span class="hljs-selector-class">.hour_marker</span> {
  <span class="hljs-attribute">fill</span>: transparent;
  <span class="hljs-attribute">stroke</span>: <span class="hljs-number">#f0f0c9</span>;
  <span class="hljs-attribute">stroke-width</span>: <span class="hljs-number">7</span>;
  <span class="hljs-attribute">stroke-dasharray</span>: <span class="hljs-number">0.2</span>, <span class="hljs-number">4.8</span>;
  <span class="hljs-attribute">stroke-dashoffset</span>: <span class="hljs-number">0.1</span>;
}

<span class="hljs-selector-class">.minute_marker</span> {
  <span class="hljs-attribute">fill</span>: transparent;
  <span class="hljs-attribute">stroke</span>: <span class="hljs-number">#0f0e0e</span>;
  <span class="hljs-attribute">stroke-width</span>: <span class="hljs-number">7</span>;
  <span class="hljs-attribute">stroke-dasharray</span>: <span class="hljs-number">0.2</span>, <span class="hljs-number">0.8</span>;
  <span class="hljs-attribute">stroke-dashoffset</span>: <span class="hljs-number">0.1</span>;
}
</code></pre>
<h2 id="heading-how-to-draw-the-watch-hands"><strong>How to Draw the Watch Hands</strong></h2>
<p>Let's add the hands that show the time. Initially, we draw these to point upwards, then turn them into position with JavaScript. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/SVG-Watch.001-4.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>We use the <code>line</code> element to draw the hands. To define a line element we have to set the starting and ending coordinates, along with a <code>stroke</code> color and the <code>stroke-width</code> property. </p>
<p>To make things a bit nicer, we can also add the <code>stroke-linecap</code> property to have rounded line caps. These styling properties we add with CSS. </p>
<pre><code class="lang-html">. . . 

    <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"200"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"-100 -100 200 200"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"minute_marker"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"90"</span> <span class="hljs-attr">pathLength</span>=<span class="hljs-string">"60"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hour_marker"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"90"</span> <span class="hljs-attr">pathLength</span>=<span class="hljs-string">"60"</span> /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">line</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hand"</span> <span class="hljs-attr">x1</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y1</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">x2</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y2</span>=<span class="hljs-string">"-50"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">line</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hand hand--thick"</span> <span class="hljs-attr">x1</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y1</span>=<span class="hljs-string">"-12"</span> <span class="hljs-attr">x2</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y2</span>=<span class="hljs-string">"-50"</span> /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">line</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hand"</span> <span class="hljs-attr">x1</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y1</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">x2</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y2</span>=<span class="hljs-string">"-80"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">line</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hand hand--thick"</span> <span class="hljs-attr">x1</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y1</span>=<span class="hljs-string">"-12"</span> <span class="hljs-attr">x2</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y2</span>=<span class="hljs-string">"-80"</span> /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">line</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hand hand--second"</span> <span class="hljs-attr">x1</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y1</span>=<span class="hljs-string">"12"</span> <span class="hljs-attr">x2</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y2</span>=<span class="hljs-string">"-80"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>

. . .
</code></pre>
<pre><code class="lang-css">. . .

<span class="hljs-selector-class">.hand</span> {
  <span class="hljs-attribute">stroke</span>: <span class="hljs-number">#ffffff</span>;
  <span class="hljs-attribute">stroke-width</span>: <span class="hljs-number">2</span>;
  <span class="hljs-attribute">stroke-linecap</span>: round;
}

<span class="hljs-selector-class">.hand--thick</span> {
  <span class="hljs-attribute">stroke-width</span>: <span class="hljs-number">7</span>;
}

<span class="hljs-selector-class">.hand--second</span> {
  <span class="hljs-attribute">stroke</span>: yellow;
}
</code></pre>
<h2 id="heading-how-to-point-the-watch-hands-in-the-right-direction">How to Point the Watch Hands in the Right Direction</h2>
<p>Now how do we turn these lines into position? If we assign an ID to an element we can access it and manipulate it from JavaScript. </p>
<p>Which element should we assign an ID, though? We have two elements for one hand. To solve this problem we can group these two line elements in a group tag. You can think of a group tag as the <code>div</code> element in HTML.</p>
<p>We can assign an ID to this group, then we can rotate the whole group into position from JavaScript. </p>
<pre><code class="lang-html">. . .

    <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"800"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"800"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"-100 -100 200 200"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"minute_marker"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"90"</span> <span class="hljs-attr">pathLength</span>=<span class="hljs-string">"60"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">circle</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hour_marker"</span> <span class="hljs-attr">r</span>=<span class="hljs-string">"90"</span> <span class="hljs-attr">pathLength</span>=<span class="hljs-string">"60"</span> /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">g</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"hour_hand"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">line</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hand"</span> <span class="hljs-attr">x1</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y1</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">x2</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y2</span>=<span class="hljs-string">"-50"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">line</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hand hand--thick"</span> <span class="hljs-attr">x1</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y1</span>=<span class="hljs-string">"-12"</span> <span class="hljs-attr">x2</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y2</span>=<span class="hljs-string">"-50"</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">g</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">g</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"minute_hand"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">line</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hand"</span> <span class="hljs-attr">x1</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y1</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">x2</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y2</span>=<span class="hljs-string">"-80"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">line</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hand hand--thick"</span> <span class="hljs-attr">x1</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y1</span>=<span class="hljs-string">"-12"</span> <span class="hljs-attr">x2</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y2</span>=<span class="hljs-string">"-80"</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">g</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">g</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"second_hand"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">line</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hand hand--second"</span> <span class="hljs-attr">x1</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y1</span>=<span class="hljs-string">"12"</span> <span class="hljs-attr">x2</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">y2</span>=<span class="hljs-string">"-80"</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">g</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>

. . .
</code></pre>
<p>In the JavaScript file, first, we get the hand elements by ID. Then we create a Date object and we get the current hour, minute, and second. And finally, we set the elements' <code>transform</code> attribute based on these values. </p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> hoursElement = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"hour_hand"</span>);
<span class="hljs-keyword">const</span> minutesElement = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"minute_hand"</span>);
<span class="hljs-keyword">const</span> secondsElement = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"second_hand"</span>);

<span class="hljs-keyword">const</span> date = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>();

<span class="hljs-keyword">const</span> hour = date.getHours();
<span class="hljs-keyword">const</span> minute = date.getMinutes();
<span class="hljs-keyword">const</span> second = date.getSeconds();

hoursElement.setAttribute(<span class="hljs-string">"transform"</span>, <span class="hljs-string">`rotate(<span class="hljs-subst">${(<span class="hljs-number">360</span> / <span class="hljs-number">12</span>) * hour}</span>)`</span>);
minutesElement.setAttribute(<span class="hljs-string">"transform"</span>, <span class="hljs-string">`rotate(<span class="hljs-subst">${(<span class="hljs-number">360</span> / <span class="hljs-number">60</span>) * minute}</span>)`</span>);
secondsElement.setAttribute(<span class="hljs-string">"transform"</span>, <span class="hljs-string">`rotate(<span class="hljs-subst">${(<span class="hljs-number">360</span> / <span class="hljs-number">60</span>) * second}</span>)`</span>);
</code></pre>
<p>The transform attribute can include multiple transformations like scaling, translating, or skewing. </p>
<p>We are setting the <code>rotate</code> transformation, which requires a number. This number is a rotation between 0 and 360 degrees. For the hour hand, we divide 360 by 12 to get how much rotation we need per hour and multiply it with the current hour. This should turn the hour hand towards the current hour. </p>
<p>For the minute and second hand, we do the same thing, except we divide 360 by 60, as one hour consists of 60 minutes and 1 minute is 60 seconds.</p>
<p>Luckily for us, the transformation center by default is the origin, the <code>0,0</code> coordinate. If this wouldn’t be the case we could set another transformation origin, but because of our <code>viewBox</code> settings, we don’t need that.  </p>
<h2 id="heading-how-to-animate-the-watch-hands"><strong>How to Animate </strong>the<strong> Watch H</strong>ands<em>**</em></h2>
<p>Now, this should already show the current time, but our image is static. To keep up with time we can use the <code>requestAnimationFrame</code> function to move the hands. </p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> hoursElement = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"hour_hand"</span>);
<span class="hljs-keyword">const</span> minutesElement = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"minute_hand"</span>);
<span class="hljs-keyword">const</span> secondsElement = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"second_hand"</span>);

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> date = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>();

  <span class="hljs-keyword">const</span> hour = date.getHours() % <span class="hljs-number">12</span>;
  <span class="hljs-keyword">const</span> minute = date.getMinutes();
  <span class="hljs-keyword">const</span> second = date.getSeconds();

  hoursElement.setAttribute(<span class="hljs-string">"transform"</span>, <span class="hljs-string">`rotate(<span class="hljs-subst">${(<span class="hljs-number">360</span> / <span class="hljs-number">12</span>) * hour}</span>)`</span>);
  minutesElement.setAttribute(<span class="hljs-string">"transform"</span>, <span class="hljs-string">`rotate(<span class="hljs-subst">${(<span class="hljs-number">360</span> / <span class="hljs-number">60</span>) * minute}</span>)`</span>);
  secondsElement.setAttribute(<span class="hljs-string">"transform"</span>, <span class="hljs-string">`rotate(<span class="hljs-subst">${(<span class="hljs-number">360</span> / <span class="hljs-number">60</span>) * second}</span>)`</span>);

  requestAnimationFrame(animate);
}

requestAnimationFrame(animate);
</code></pre>
<p>We move the rotation logic into an animate function, and use the requestAnimationFrame function. </p>
<p>First, we trigger it by calling requestAnimationFrame outside the animate function. Then, to keep on with the animation we also request another frame at the end of every animation cycle. </p>
<p>If you want to have a smoother animation, then you can refine the positioning. Instead of having discrete positions for the hands, we can define them in a way that they can point to split seconds, minutes, and hours. </p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> hoursElement = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"hour_hand"</span>);
<span class="hljs-keyword">const</span> minutesElement = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"minute_hand"</span>);
<span class="hljs-keyword">const</span> secondsElement = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"second_hand"</span>);

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> date = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>();

  <span class="hljs-keyword">const</span> hour = date.getHours() + date.getMinutes() / <span class="hljs-number">60</span>;
  <span class="hljs-keyword">const</span> minute = date.getMinutes() + date.getSeconds() / <span class="hljs-number">60</span>;
  <span class="hljs-keyword">const</span> second = date.getSeconds() + date.getMilliseconds() / <span class="hljs-number">1000</span>;

  hoursElement.setAttribute(<span class="hljs-string">"transform"</span>, <span class="hljs-string">`rotate(<span class="hljs-subst">${(<span class="hljs-number">360</span> / <span class="hljs-number">12</span>) * hour}</span>)`</span>);
  minutesElement.setAttribute(<span class="hljs-string">"transform"</span>, <span class="hljs-string">`rotate(<span class="hljs-subst">${(<span class="hljs-number">360</span> / <span class="hljs-number">60</span>) * minute}</span>)`</span>);
  secondsElement.setAttribute(<span class="hljs-string">"transform"</span>, <span class="hljs-string">`rotate(<span class="hljs-subst">${(<span class="hljs-number">360</span> / <span class="hljs-number">60</span>) * second}</span>)`</span>);

  requestAnimationFrame(animate);
}

requestAnimationFrame(animate);
</code></pre>
<p>The hour hand won’t get its position only based on the hour, but it will make a slight turn based on the current minutes as well. </p>
<p>The minute hand will consider the current second in its rotation. And the second hand will also consider milliseconds. This way our hands will have a continuous movement. They won’t jump from second to second, but they will animate.</p>
<h2 id="heading-next-steps-how-to-make-the-watch-interactive"><strong>Next steps – How to Make the Watch Interactive</strong></h2>
<p>Now if we check the result, we should have a smoothly animated watch. </p>
<p>To go further, you can also add a calendar window showing the current date, with the <code>text</code> element. And to take it to the next level you can even add an event handler for this element, which toggles its content between the current date and the AM/PM indicator. </p>
<p>If you are stuck, check out the video below, where we also cover this part.</p>
<p>Mixing SVG with JavaScript opens up a lot of cool options. You can animate things, add interactions, and generate graphics. Can't wait to see what you come up with.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/ULomsOSk4JA" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-subscribe-for-more-tutorials-on-web-development"><strong>Subscribe for more tutorials on Web Development:</strong></h2>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/undefined" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Three.js Tutorial - How to Build a Simple Car with Texture in 3D ]]>
                </title>
                <description>
                    <![CDATA[ Putting together a 3D scene in the browser with Three.js is like playing with Legos. We put together some boxes, add lights, define a camera, and Three.js renders the 3D image. In this tutorial, we're going to put together a minimalistic car from box... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/three-js-tutorial/</link>
                <guid isPermaLink="false">66c4c820e7521bfd6862b3b4</guid>
                
                    <category>
                        <![CDATA[ Game Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ three.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Hunor Márton Borbély ]]>
                </dc:creator>
                <pubDate>Mon, 22 Mar 2021 13:47:23 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/03/Screenshot-2021-03-19-at-11.31.27-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Putting together a 3D scene in the browser with Three.js is like playing with Legos. We put together some boxes, add lights, define a camera, and Three.js renders the 3D image.</p>
<p>In this tutorial, we're going to put together a minimalistic car from boxes and learn how to map texture onto it. </p>
<p>First, we'll set things up – we'll define the lights, the camera, and the renderer. Then we'll learn how to define geometries and materials to create 3D objects. And finally we are going to code textures with JavaScript and HTML Canvas.</p>
<h2 id="heading-how-to-setup-the-threejs-project">How to Setup the Three.js Project</h2>
<p>Three.js is an external library, so first we need to add it to our project. I used NPM to install it to my project then imported it at the beginning of the JavaScript file.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>; 

<span class="hljs-keyword">const</span> scene = <span class="hljs-keyword">new</span> THREE.Scene();

. . .
</code></pre>
<p>First, we need to define the scene. The scene is a container that contains all the 3D objects we want to display along with the lights. We are about to add a car to this scene, but first let's set up the lights, the camera, and the renderer.</p>
<h3 id="heading-how-to-set-up-the-lights">How to Set Up the Lights</h3>
<p>We'll add two lights to the scene: an ambient light and a directional light. We define both by setting a color and an intensity. </p>
<p>The color is defined as a hex value. In this case we set it to white. The intensity is a number between 0 and 1, and as both of them shine simultaneously we want these values somewhere around 0.5. </p>
<pre><code class="lang-javascript">. . . 

const ambientLight = <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.6</span>);
scene.add(ambientLight);

<span class="hljs-keyword">const</span> directionalLight = <span class="hljs-keyword">new</span> THREE.DirectionalLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.8</span>);
directionalLight.position.set(<span class="hljs-number">200</span>, <span class="hljs-number">500</span>, <span class="hljs-number">300</span>);
scene.add(directionalLight); 

. . .
</code></pre>
<p>The ambient light is shining from every direction, giving a base color for our geometry while the directional light simulates the sun. </p>
<p>The directional light shines from very far away with parallel light rays. We set a position for this light that defines the direction of these light rays. </p>
<p>This position can be a bit confusing so let me explain. Out of all the parallel rays we define one in particular. This specific light ray will shine from the position we define (200,500,300) to the 0,0,0 coordinate. The rest will be in parallel to it. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Loop-driver.015.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>As the light rays are in parallel, and they shine from very far away, the exact coordinates don't matter here – rather, their proportions do. </p>
<p>The three position-parameters are the X, Y, and Z coordinates. By default, the Y-axis points upwards, and as it has the highest value (500), that means the top of our car receives the most light. So it will be the brightest. </p>
<p>The other two values define by how much the light is bent along the X and Z axis, that is how much light the front and the side of the car will receive. </p>
<h3 id="heading-how-to-set-up-the-camera">How to Set Up the Camera</h3>
<p>Next, let's set up the camera that defines how we look at this scene. </p>
<p>There are two options here – perspective cameras and orthographic cameras. Video games mostly use perspective cameras, but we are going to use an orthographic one to have a more minimal, geometric look.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Loop-driver.018.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In <a target="_blank" href="https://www.freecodecamp.org/news/render-3d-objects-in-browser-drawing-a-box-with-threejs/">my previous article</a>, we discussed the differences between the two cameras in more detail. Therefore in this one, we'll only discuss how to set up an orthographic camera. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Loop-driver.019.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>For the camera, we need to define a view frustum. This is the region in the 3D space that is going to be projected to the screen. </p>
<p>In the case of an orthographic camera, this is a box. The camera projects the 3D objects inside this box toward one of its sides. Because each projection line is in parallel, orthographic cameras don't distort geometries.</p>
<pre><code class="lang-javascript">. . .

<span class="hljs-comment">// Setting up camera</span>
<span class="hljs-keyword">const</span> aspectRatio = <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight;
<span class="hljs-keyword">const</span> cameraWidth = <span class="hljs-number">150</span>;
<span class="hljs-keyword">const</span> cameraHeight = cameraWidth / aspectRatio;

<span class="hljs-keyword">const</span> camera = <span class="hljs-keyword">new</span> THREE.OrthographicCamera(
  cameraWidth / <span class="hljs-number">-2</span>, <span class="hljs-comment">// left</span>
  cameraWidth / <span class="hljs-number">2</span>, <span class="hljs-comment">// right</span>
  cameraHeight / <span class="hljs-number">2</span>, <span class="hljs-comment">// top</span>
  cameraHeight / <span class="hljs-number">-2</span>, <span class="hljs-comment">// bottom</span>
  <span class="hljs-number">0</span>, <span class="hljs-comment">// near plane</span>
  <span class="hljs-number">1000</span> <span class="hljs-comment">// far plane</span>
);
camera.position.set(<span class="hljs-number">200</span>, <span class="hljs-number">200</span>, <span class="hljs-number">200</span>);
camera.lookAt(<span class="hljs-number">0</span>, <span class="hljs-number">10</span>, <span class="hljs-number">0</span>);

. . .
</code></pre>
<p>To set up an orthographic camera, we have to define how far each side of the frustum is from the viewpoint. We define that the left side is 75 units away to the left, the right plane is 75 units away to the right, and so on. </p>
<p>Here these units don't represent screen pixels. The size of the rendered image will be defined at the renderer. Here these values have an arbitrary unit that we use in the 3D space. Later on, when defining 3D objects in the 3D space, we are going to use the same units to set their size and position. </p>
<p>Once we define a camera we also need to position it and turn it in a direction. We are moving the camera by 200 units in each dimension, then we set it to look back towards the 0,10,0 coordinate. This is almost at the origin. We look towards a point slightly above the ground, where our car's center will be. </p>
<h3 id="heading-how-to-set-up-the-renderer">How to Set Up the Renderer</h3>
<p>The last piece we need to set up is a renderer that renders the scene according to our camera into our browser. We define a WebGLRenderer like this:</p>
<pre><code class="lang-js">. . .

<span class="hljs-comment">// Set up renderer</span>
<span class="hljs-keyword">const</span> renderer = <span class="hljs-keyword">new</span> THREE.WebGLRenderer({ <span class="hljs-attr">antialias</span>: <span class="hljs-literal">true</span> });
renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight);
renderer.render(scene, camera);

<span class="hljs-built_in">document</span>.body.appendChild(renderer.domElement);
</code></pre>
<p>Here we also set up the size of the canvas. This is the only place where we set the size in pixels since we're setting how it should appear in the browser. If we want to fill the whole browser window, we pass on the window's size. </p>
<p>And finally, the last line adds this rendered image to our HTML document. It creates an HTML Canvas element to display the rendered image and adds it to the DOM. </p>
<h2 id="heading-how-to-build-the-car-in-threejs">How to Build the Car in Three.js</h2>
<p>Now let's see how can we can compose a car. First, we will create a car without texture. It is going to be a minimalistic design – we'll just put together four boxes.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Screenshot-2021-03-19-at-11.18.04.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-add-a-box">How to Add a Box</h3>
<p>First, we create a pair of wheels. We will define a gray box that represents both a left and a right wheel. As we never see the car from below, we won't notice that instead of having a separate left and right wheel we only have one big box. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Screenshot-2021-03-19-at-11.01.43.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>We are going to need a pair of wheels both in the front and at the back of the car, so we can create a reusable function.</p>
<pre><code class="lang-js">. . . 

function createWheels() {
  <span class="hljs-keyword">const</span> geometry = <span class="hljs-keyword">new</span> THREE.BoxBufferGeometry(<span class="hljs-number">12</span>, <span class="hljs-number">12</span>, <span class="hljs-number">33</span>);
  <span class="hljs-keyword">const</span> material = <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0x333333</span> });
  <span class="hljs-keyword">const</span> wheel = <span class="hljs-keyword">new</span> THREE.Mesh(geometry, material);
  <span class="hljs-keyword">return</span> wheel;
}

. . .
</code></pre>
<p>We define the wheel as a mesh. The mesh is a combination of a geometry and a material and it will represent our 3D object. </p>
<p>The geometry defines the shape of the object. In this case, we create a box by settings its dimensions along the X, Y, and Z-axis to be 12, 12, and 33 units. </p>
<p>Then we pass on a material that will define the appearance of our mesh. There are different material options. The main difference between them is how they react to light.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/materials.001.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In this tutorial, we'll use <code>MeshLambertMaterial</code>. The <code>MeshLambertMaterial</code> calculates the color for each vertex. In the case of drawing a box, that's basically each side. </p>
<p>We can see how that works, as each side of the box has a different shade. We defined a directional light to shine primarily from above, so the top of the box is the brightest.</p>
<p>Some other materials calculate the color, not only for each side but for each pixel within the side. They result in more realistic images for more complex shapes. But for boxes illuminated with directional light, they don't make much of a difference. </p>
<h3 id="heading-how-to-build-the-rest-of-the-car">How to Build the Rest of the Car</h3>
<p>Then in a similar way let's let's create the rest of the car. We define the <code>createCar</code> function that returns a Group. This group is another container like the scene. It can hold Three.js objects. It is convenient because if we want to move around the car, we can simply move around the Group. </p>
<pre><code class="lang-js">. . .

function createCar() {
  <span class="hljs-keyword">const</span> car = <span class="hljs-keyword">new</span> THREE.Group();

  <span class="hljs-keyword">const</span> backWheel = createWheels();
  backWheel.position.y = <span class="hljs-number">6</span>;
  backWheel.position.x = <span class="hljs-number">-18</span>;
  car.add(backWheel);

  <span class="hljs-keyword">const</span> frontWheel = createWheels();
  frontWheel.position.y = <span class="hljs-number">6</span>;  
  frontWheel.position.x = <span class="hljs-number">18</span>;
  car.add(frontWheel);

  <span class="hljs-keyword">const</span> main = <span class="hljs-keyword">new</span> THREE.Mesh(
    <span class="hljs-keyword">new</span> THREE.BoxBufferGeometry(<span class="hljs-number">60</span>, <span class="hljs-number">15</span>, <span class="hljs-number">30</span>),
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0x78b14b</span> })
  );
  main.position.y = <span class="hljs-number">12</span>;
  car.add(main);

  <span class="hljs-keyword">const</span> cabin = <span class="hljs-keyword">new</span> THREE.Mesh(
    <span class="hljs-keyword">new</span> THREE.BoxBufferGeometry(<span class="hljs-number">33</span>, <span class="hljs-number">12</span>, <span class="hljs-number">24</span>),
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0xffffff</span> })
  );
  cabin.position.x = <span class="hljs-number">-6</span>;
  cabin.position.y = <span class="hljs-number">25.5</span>;
  car.add(cabin);

  <span class="hljs-keyword">return</span> car;
}

<span class="hljs-keyword">const</span> car = createCar();
scene.add(car);

renderer.render(scene, camera);

. . .
</code></pre>
<p>We generate two pairs of wheels with our function, then define the main part of the car. Then we'll add the top of the cabin as the forth mesh. These are all just boxes with different dimensions and different colors.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/car-boxes.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>By default every geometry will be in the middle, and their centers will be at the 0,0,0 coordinate. </p>
<p>First, we raise them by adjusting their position along the Y-axis. We raise the wheels by half of their height – so instead of sinking in halfway to the ground, they lay on the ground. Then we also adjust the pieces along the X-axis to reach their final position. </p>
<p>We add these pieces to the car group, then add the whole group to the scene. It's important that we add the car to the scene before rendering the image, or we'll need to call rendering again once we've modified the scene. </p>
<h3 id="heading-how-to-add-texture-to-the-car">How to Add Texture to the Car</h3>
<p>Now that we have our very basic car model, let's add some textures to the cabin. We are going to paint the windows. We'll define a texture for the sides and one for the front and the back of the cabin. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/textures.001.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>When we set up the appearance of a mesh with a material, setting a color is not the only option. We can also map a texture. We can provide the same texture for every side or we can provide a material for each side in an array. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Loop-driver.038.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>As a texture, we could use an image. But instead of that, we are going to create textures with JavaScript. We are going to code images with HTML Canvas and JavaScript. </p>
<p>Before we continue, we need to make some distinctions between Three.js and HTML Canvas. </p>
<p>Three.js is a JavaScript library. It uses WebGL under the hood to render 3D objects into an image, and it displays the final result in a canvas element. </p>
<p>HTML Canvas, on the other hand, is an HTML element, just like the <code>div</code> element or the paragraph tag. What makes it special, though, is that we can draw shapes on this element with JavaScript. </p>
<p>This is how Three.js renders the scene in the browser, and this is how we are going to create textures. Let's see how they work. </p>
<h3 id="heading-how-to-draw-on-an-html-canvas">How to Draw on an HTML Canvas</h3>
<p>To draw on a canvas, first we need to create a canvas element. While we create an HTML element, this element will never be part of our HTML structure. On its own, it won't be displayed on the page. Instead, we will turn it into a Three.js texture. </p>
<p>Let's see how can we draw on this canvas. First, we define the width and height of the canvas. The size here doesn't define how big the canvas will appear, it's more like the resolution of the canvas. The texture will be stretched to the side of the box, regardless of its size.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getCarFrontTexture</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"canvas"</span>);
  canvas.width = <span class="hljs-number">64</span>;
  canvas.height = <span class="hljs-number">32</span>;
  <span class="hljs-keyword">const</span> context = canvas.getContext(<span class="hljs-string">"2d"</span>);

  context.fillStyle = <span class="hljs-string">"#ffffff"</span>;
  context.fillRect(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">64</span>, <span class="hljs-number">32</span>);

  context.fillStyle = <span class="hljs-string">"#666666"</span>;
  context.fillRect(<span class="hljs-number">8</span>, <span class="hljs-number">8</span>, <span class="hljs-number">48</span>, <span class="hljs-number">24</span>);

  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> THREE.CanvasTexture(canvas);
}
</code></pre>
<p>Then we get the 2D drawing context. We can use this context to execute drawing commands. </p>
<p>First, we are going to fill the whole canvas with a white rectangle. To do so, first we set the fill style to be while. Then fill a rectangle by setting its top-left position and its size. When drawing on a canvas, by default the 0,0 coordinate will be at the top-left corner. </p>
<p>Then we fill another rectangle with a gray color. This one starts at the 8,8 coordinate and it doesn't fill the canvas, it only paints the windows. </p>
<p>And that's it – the last line turns the canvas element into a texture and returns it, so we can use it for our car. </p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getCarSideTexture</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"canvas"</span>);
  canvas.width = <span class="hljs-number">128</span>;
  canvas.height = <span class="hljs-number">32</span>;
  <span class="hljs-keyword">const</span> context = canvas.getContext(<span class="hljs-string">"2d"</span>);

  context.fillStyle = <span class="hljs-string">"#ffffff"</span>;
  context.fillRect(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">128</span>, <span class="hljs-number">32</span>);

  context.fillStyle = <span class="hljs-string">"#666666"</span>;
  context.fillRect(<span class="hljs-number">10</span>, <span class="hljs-number">8</span>, <span class="hljs-number">38</span>, <span class="hljs-number">24</span>);
  context.fillRect(<span class="hljs-number">58</span>, <span class="hljs-number">8</span>, <span class="hljs-number">60</span>, <span class="hljs-number">24</span>);

  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> THREE.CanvasTexture(canvas);
}
</code></pre>
<p>In a similar way, we can define the side texture. We create a canvas element again, we get its context, then first fill the whole canvas to have a base color, and then draw the windows as rectangles. </p>
<h3 id="heading-how-to-map-textures-to-a-box">How to Map Textures to a Box</h3>
<p>Now let's see how can we use these textures for our car. When we define the mesh for the top of the cabin, instead of setting only one material, we set one for each side. We define an array of six materials. We map textures to the sides of the cabin, while the top and bottom will still have a plain color. </p>
<pre><code class="lang-javascript">. . .

function createCar() {
  <span class="hljs-keyword">const</span> car = <span class="hljs-keyword">new</span> THREE.Group();

  <span class="hljs-keyword">const</span> backWheel = createWheels();
  backWheel.position.y = <span class="hljs-number">6</span>;
  backWheel.position.x = <span class="hljs-number">-18</span>;
  car.add(backWheel);

  <span class="hljs-keyword">const</span> frontWheel = createWheels();
  frontWheel.position.y = <span class="hljs-number">6</span>;
  frontWheel.position.x = <span class="hljs-number">18</span>;
  car.add(frontWheel);

  <span class="hljs-keyword">const</span> main = <span class="hljs-keyword">new</span> THREE.Mesh(
    <span class="hljs-keyword">new</span> THREE.BoxBufferGeometry(<span class="hljs-number">60</span>, <span class="hljs-number">15</span>, <span class="hljs-number">30</span>),
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0xa52523</span> })
  );
  main.position.y = <span class="hljs-number">12</span>;
  car.add(main);

  <span class="hljs-keyword">const</span> carFrontTexture = getCarFrontTexture();

  <span class="hljs-keyword">const</span> carBackTexture = getCarFrontTexture();

  <span class="hljs-keyword">const</span> carRightSideTexture = getCarSideTexture();

  <span class="hljs-keyword">const</span> carLeftSideTexture = getCarSideTexture();
  carLeftSideTexture.center = <span class="hljs-keyword">new</span> THREE.Vector2(<span class="hljs-number">0.5</span>, <span class="hljs-number">0.5</span>);
  carLeftSideTexture.rotation = <span class="hljs-built_in">Math</span>.PI;
  carLeftSideTexture.flipY = <span class="hljs-literal">false</span>;

  <span class="hljs-keyword">const</span> cabin = <span class="hljs-keyword">new</span> THREE.Mesh(<span class="hljs-keyword">new</span> THREE.BoxBufferGeometry(<span class="hljs-number">33</span>, <span class="hljs-number">12</span>, <span class="hljs-number">24</span>), [
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">map</span>: carFrontTexture }),
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">map</span>: carBackTexture }),
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0xffffff</span> }), <span class="hljs-comment">// top</span>
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0xffffff</span> }), <span class="hljs-comment">// bottom</span>
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">map</span>: carRightSideTexture }),
    <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">map</span>: carLeftSideTexture }),
  ]);
  cabin.position.x = <span class="hljs-number">-6</span>;
  cabin.position.y = <span class="hljs-number">25.5</span>;
  car.add(cabin);

  <span class="hljs-keyword">return</span> car;
}

. . .
</code></pre>
<p>Most of these textures will be mapped correctly without any adjustments. But if we turn the car around then we can see the windows appear in the wrong order on the left side.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/fixing-texture-before-after.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Right side and left side before and after fixing the texture</em></p>
<p>This is expected as we use the texture for the right side here as well. We can define a separate texture for the left side or we can mirror the right side.</p>
<p>Unfortunately, we can't flip a texture horizontally. We can only flip a texture vertically. We can fix this in 3 steps.</p>
<p>First, we turn the texture around by 180 degrees, which equals PI in radians. Before turning it, though, we have to make sure that the texture is rotated around its center. This is not the default – we have to set that the center of rotation is halfway. We set 0.5 on both axes which basically means 50%. Then finally we flip the texture upside down to have it in the correct position.</p>
<h2 id="heading-wrap-up">Wrap-up</h2>
<p>So what did we do here? We created a scene that contains our car and the lights. We built the car from simple boxes. </p>
<p>You might think this is too basic, but if you think about it many mobile games with stylish looks are actually created using boxes. Or just think about Minecraft to see how far you can get by putting together boxes. </p>
<p>Then we created textures with HTML canvas. HTML canvas is capable of much more than what we used here. We can draw different shapes with curves and arcs, but then again sometimes a minimal design is all that we need.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/Screenshot-2021-03-19-at-11.31.27-2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>And finally, we defined a camera to establish how we look at this scene, as well as a renderer that renders the final image into the browser.</p>
<h2 id="heading-next-steps">Next Steps</h2>
<p>If you want to play around with the code, you can find the source code on <a target="_blank" href="https://codepen.io/HunorMarton/pen/qBqzQOJ">CodePen</a>. And if you want to move forward with this project, then check out my YouTube video on how to turn this into a game. </p>
<p>In this tutorial, we create a traffic run game. After defining the car we draw the race track, we add game logic, event handlers, and animation.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/JhgBwJn1bQw" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Three.js Tutorial – How to Render 3D Objects in the Browser ]]>
                </title>
                <description>
                    <![CDATA[ If you have ever wanted to build a game with JavaScript, you might have come across Three.js.  Three.js is a library that we can use to render 3D graphics in the browser. The whole thing is in JavaScript, so with some logic you can add animation, int... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/render-3d-objects-in-browser-drawing-a-box-with-threejs/</link>
                <guid isPermaLink="false">66c4c8174173ed342943d0c2</guid>
                
                    <category>
                        <![CDATA[ 3d ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #Game Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Game Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ three.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Hunor Márton Borbély ]]>
                </dc:creator>
                <pubDate>Wed, 03 Feb 2021 23:06:37 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/02/Stack.002-1.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you have ever wanted to build a game with JavaScript, you might have come across Three.js. </p>
<p>Three.js is a library that we can use to render 3D graphics in the browser. The whole thing is in JavaScript, so with some logic you can add animation, interaction, or even turn it into a game. </p>
<p>In this tutorial, we will go through a very simple example. We'll render a 3D box, and while doing so we'll learn the fundamentals of Three.js. </p>
<p>Three.js uses WebGL under the hood to render 3D graphics. We could use plain WebGL, but it's very complex and rather low level. On the other hand, Three.js is like playing with Legos. </p>
<p>In this article, we'll go through how to place a 3D object in a scene, set up the lighting and a camera, and render the scene on a canvas. So let’s see how we can do all this.</p>
<h2 id="heading-define-the-scene-object">Define the Scene Object</h2>
<p>First, we have to define a scene. This will be a container where we place our 3D objects and lights. The scene object also has some properties, like the background color. Setting that is optional though. If we don't set it, the default will be black.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;

<span class="hljs-keyword">const</span> scene = <span class="hljs-keyword">new</span> THREE.Scene();
scene.background = <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-number">0x000000</span>); <span class="hljs-comment">// Optional, black is default</span>

...
</code></pre>
<h2 id="heading-geometry-material-mesh">Geometry + Material = Mesh</h2>
<p>Then we add our 3D box to the scene as a mesh. A mesh is a combination of a geometry and a material.</p>
<pre><code class="lang-js">...

<span class="hljs-comment">// Add a cube to the scene</span>
<span class="hljs-keyword">const</span> geometry = <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">3</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>); <span class="hljs-comment">// width, height, depth</span>
<span class="hljs-keyword">const</span> material = <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0xfb8e00</span> });
<span class="hljs-keyword">const</span> mesh = <span class="hljs-keyword">new</span> THREE.Mesh(geometry, material);
mesh.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>); <span class="hljs-comment">// Optional, 0,0,0 is the default</span>
scene.add(mesh);

...
</code></pre>
<h3 id="heading-what-is-a-geometry">What is a Geometry?</h3>
<p>A geometry is a rendered shape that we’re building - like a box. A geometry can be build from vertices or we can use a predefined one. </p>
<p>The BoxGeometry is the most basic predefined option. We only have to set the width, height, and depth of the box and that’s it. </p>
<p>You might think that we can't get far by defining boxes, but many games with minimalistic design use only a combination of boxes. </p>
<p>There are other predefined geometries as well. We can easily define a plane, a cylinder, a sphere, or even an icosahedron.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/02/freecodecamp-4.001.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-work-with-material">How to Work with Material</h3>
<p>Then we define a material. A material describes the appearance of an object. Here we can define things like texture, color, or opacity. </p>
<p>In this example we are only going to set a color. There are still different options for materials. The main difference between most of them is how they react to light. </p>
<p>The simplest one is the MeshBasicMaterial. This material doesn't care about light at all, and each side will have the same color. It might not be the best option, though, as you can’t see the edges of the box.</p>
<p>The simplest material that cares about light is the MeshLambertMaterial. This will calculate the color of each vertex, which is practically each side. But it doesn't go beyond that.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/02/freecodecamp-4.002.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you need more precision, there are more advanced materials. The MeshPhongMaterial not only calculates the color by vertex but by each pixel. The color can change within a side. This can help with realism but also costs in performance. </p>
<p>It also depends on the light settings and the geometry if it has any real effect. If we render boxes and use directional light, the result won't change that much. But if we render a sphere, the difference is more obvious.</p>
<h3 id="heading-how-to-position-a-mesh">How to Position a Mesh</h3>
<p>Once we have a mesh we can also position it within the scene and set a rotation by each axis. Later if we want to animate objects in the 3D space we will mostly adjust these values. </p>
<p>For positioning we use the same units that we used for setting the size. It doesn't matter if you are using small numbers or big numbers, you just need to be consistent in your own world. </p>
<p>For the rotation we set the values in radians. So if you have your values in degrees you have to divide them by 180° then multiply by PI.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/02/freecodecamp-3.004.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-add-light">How to Add Light</h2>
<p>Then let's add lights. A mesh with basic material doesn’t need any light, as the mesh will have the set color regardless of the light settings. </p>
<p>But the Lambert material and Phong material require light. If there isn't any light, the mesh will remain in darkness.</p>
<pre><code class="lang-js">...

<span class="hljs-comment">// Set up lights</span>
<span class="hljs-keyword">const</span> ambientLight = <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.6</span>);
scene.add(ambientLight);

...
</code></pre>
<p>We'll add two lights - an ambient light and a directional light. </p>
<p>First, we add the ambient light. The ambient light is shining from every direction, giving a base color for our geometry. </p>
<p>To set an ambient light we set a color and an intensity. The color is usually white, but you can set any color. The intensity is a number between 0 and 1. The two lights we define work in an accumulative way so in this case we want the intensity to be around 0.5 for each.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/02/freecodecamp-3.003.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The directional light has a similar setup, but it also has a position. The word position here is a bit misleading, because it doesn’t mean that the light is coming from an exact position. </p>
<p>The directional light is shining from very far away with many parallel light rays all having a fixed angle. But instead of defining angles, we define the direction of a single light ray. </p>
<p>In this case, it shines from the direction of the 10,20,0 position towards the 0,0,0 coordinate. But of course, the directional light is not only one light ray, but an infinite amount of parallel rays. </p>
<p>Think of it as the sun. On a smaller scale, light rays of the sun also come down in parallel, and the sun’s position isn't what matters but rather its direction. </p>
<p>And that’s what the directional light is doing. It shines on everything with parallel light rays from very far away.</p>
<pre><code class="lang-js">...

const dirLight = <span class="hljs-keyword">new</span> THREE.DirectionalLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.6</span>);
dirLight.position.set(<span class="hljs-number">10</span>, <span class="hljs-number">20</span>, <span class="hljs-number">0</span>); <span class="hljs-comment">// x, y, z</span>
scene.add(dirLight);

...
</code></pre>
<p>Here we set the position of the light to be from above (with the Y value) and shift it a bit along the X-axis as well. The Y-axis has the highest value. This means that the top of the box receives the most light and it will be the shiniest side of the box. </p>
<p>The light is also moved a bit along the X-axis, so the right side of the box will also receive some light, but less. </p>
<p>And because we don’t move the light position along the Z-axis, the front side of the box will not receive any light from this source. If there wasn't an ambient light, the front side would remain in darkness. </p>
<p>There are other light types as well. The PointLight, for instance, can be used to simulate light bulbs. It has a fixed position and it emits light in every direction. And the SpotLight can be used to simulate the spotlight of a car. It emits light from a single point into a direction along a cone.</p>
<h2 id="heading-how-to-set-up-the-camera">How to Set up the Camera</h2>
<p>So far, we have created a mesh with geometry and material. And we have also set up lights and added to the scene. We still need a camera to define how we look at this scene. </p>
<p>There are two options here: perspective cameras and orthographic cameras.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/02/Stack.010.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Video games mostly use perspective cameras, because how they work is similar to how you see things in real life. Things that are further away appear to be smaller and things that are right in front of you appear bigger. </p>
<p>With orthographic projections, things will have the same size no matter how far they are from the camera. Orthographic cameras have a more minimal, geometric look. They don't distort the geometries - the parallel lines will appear in parallel.</p>
<p>For both cameras, we have to define a view frustum. This is the region in the 3D space that is going to be projected to the screen. Anything outside of this region won't appear on the screen. This is because it is either too close or too far away, or because the camera isn't pointed towards it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/02/freecodecamp-2.001.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>With perspective projection, everything within the view frustum is projected towards the viewpoint with a straight line. Things further away from the camera appear smaller on the screen, because from the viewpoint you can see them under a smaller angle.</p>
<pre><code class="lang-js">...

<span class="hljs-comment">// Perspective camera</span>
<span class="hljs-keyword">const</span> aspect = <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight;
<span class="hljs-keyword">const</span> camera = <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(
  <span class="hljs-number">45</span>, <span class="hljs-comment">// field of view in degrees</span>
  aspect, <span class="hljs-comment">// aspect ratio</span>
  <span class="hljs-number">1</span>, <span class="hljs-comment">// near plane</span>
  <span class="hljs-number">100</span> <span class="hljs-comment">// far plane</span>
);

...
</code></pre>
<p>To define a perspective camera, you need to set a field of view, which is the vertical angle from the viewpoint. Then you define an aspect ratio of the width and the height of the frame. If you fill the whole browser window and you want to keep its aspect ratio, then this is how you can do it. </p>
<p>Then the last two parameters define how far the near and far planes are from the viewpoint. Things that are too close to the camera will be ignored, and things that are too far away will be ignored as well.</p>
<pre><code class="lang-js">...

<span class="hljs-comment">// Orthographic camera</span>
<span class="hljs-keyword">const</span> width = <span class="hljs-number">10</span>;
<span class="hljs-keyword">const</span> height = width * (<span class="hljs-built_in">window</span>.innerHeight / <span class="hljs-built_in">window</span>.innerWidth);
<span class="hljs-keyword">const</span> camera = <span class="hljs-keyword">new</span> THREE.OrthographicCamera(
  width / <span class="hljs-number">-2</span>, <span class="hljs-comment">// left</span>
  width / <span class="hljs-number">2</span>, <span class="hljs-comment">// right</span>
  height / <span class="hljs-number">2</span>, <span class="hljs-comment">// top</span>
  height / <span class="hljs-number">-2</span>, <span class="hljs-comment">// bottom</span>
  <span class="hljs-number">1</span>, <span class="hljs-comment">// near</span>
  <span class="hljs-number">100</span> <span class="hljs-comment">// far</span>
);

...
</code></pre>
<p>Then there’s the orthographic camera. Here we are not projecting things towards a single point but towards a surface. Each projection line is in parallel. That’s why it doesn’t matter how far objects are from the camera, and that’s why it doesn’t distort geometries. </p>
<p>For orthographic cameras, we have to define how far each plane is from the viewpoint. The left plane is therefor five units to the left, and the right plane is five units to the right, and so on.</p>
<pre><code class="lang-js">...

camera.position.set(<span class="hljs-number">4</span>, <span class="hljs-number">4</span>, <span class="hljs-number">4</span>);
camera.lookAt(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);

...
</code></pre>
<p>Regardless of which camera are we using, we also need to position it and set it in a direction. If we are using an orthographic camera the actual numbers here don’t matter that much. The objects will appear the same size no matter how far away they are from the camera. What matters, though, is their proportion. </p>
<p>Through this whole tutorial, we saw all the examples through the same camera. This camera was moved by the same unit along every axis and it looks towards the 0,0,0 coordinate. Positioning an orthographic camera is like positioning a directional light. It's not the actual position that matters, but its direction.</p>
<h2 id="heading-how-to-render-the-scene">How to Render the Scene</h2>
<p>So we managed to put together the scene and a camera. Now only the final piece is missing that renders the image into our browser. </p>
<p>We need to define a WebGLRenderer. This is the piece that is capable of rendering the actual image into an HTML canvas when we provide a scene and a camera. This is also where we can set the actual size of this canvas – the width and height of the canvas in pixels as it should appear in the browser.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;

<span class="hljs-comment">// Scene</span>
<span class="hljs-keyword">const</span> scene = <span class="hljs-keyword">new</span> THREE.Scene();

<span class="hljs-comment">// Add a cube to the scene</span>
<span class="hljs-keyword">const</span> geometry = <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">3</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>); <span class="hljs-comment">// width, height, depth</span>
<span class="hljs-keyword">const</span> material = <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0xfb8e00</span> });
<span class="hljs-keyword">const</span> mesh = <span class="hljs-keyword">new</span> THREE.Mesh(geometry, material);
mesh.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
scene.add(mesh);

<span class="hljs-comment">// Set up lights</span>
<span class="hljs-keyword">const</span> ambientLight = <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.6</span>);
scene.add(ambientLight);

<span class="hljs-keyword">const</span> directionalLight = <span class="hljs-keyword">new</span> THREE.DirectionalLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.6</span>);
directionalLight.position.set(<span class="hljs-number">10</span>, <span class="hljs-number">20</span>, <span class="hljs-number">0</span>); <span class="hljs-comment">// x, y, z</span>
scene.add(directionalLight);

<span class="hljs-comment">// Camera</span>
<span class="hljs-keyword">const</span> width = <span class="hljs-number">10</span>;
<span class="hljs-keyword">const</span> height = width * (<span class="hljs-built_in">window</span>.innerHeight / <span class="hljs-built_in">window</span>.innerWidth);
<span class="hljs-keyword">const</span> camera = <span class="hljs-keyword">new</span> THREE.OrthographicCamera(
  width / <span class="hljs-number">-2</span>, <span class="hljs-comment">// left</span>
  width / <span class="hljs-number">2</span>, <span class="hljs-comment">// right</span>
  height / <span class="hljs-number">2</span>, <span class="hljs-comment">// top</span>
  height / <span class="hljs-number">-2</span>, <span class="hljs-comment">// bottom</span>
  <span class="hljs-number">1</span>, <span class="hljs-comment">// near</span>
  <span class="hljs-number">100</span> <span class="hljs-comment">// far</span>
);

camera.position.set(<span class="hljs-number">4</span>, <span class="hljs-number">4</span>, <span class="hljs-number">4</span>);
camera.lookAt(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);

<span class="hljs-comment">// Renderer</span>
<span class="hljs-keyword">const</span> renderer = <span class="hljs-keyword">new</span> THREE.WebGLRenderer({ <span class="hljs-attr">antialias</span>: <span class="hljs-literal">true</span> });
renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight);
renderer.render(scene, camera);

<span class="hljs-comment">// Add it to HTML</span>
<span class="hljs-built_in">document</span>.body.appendChild(renderer.domElement);
</code></pre>
<p>And finally, the last line here adds this rendered canvas to our HTML document. And that’s all you need to render a box. It might seem a little too much for just a single box, but most of these things we only have to set up once. </p>
<p>If you want to move forward with this project, then check out my YouTube video on how to turn this into a simple game. In the video, we create a stack building game. We add game logic, event handlers and animation, and even some physics with Cannon.js.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/hBiGFpBle7E" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>If you have any feedback or questions on this tutorial, feel free to Tweet me <a target="_blank" href="https://twitter.com/HunorBorbely">@HunorBorbely</a> or leave a comment on <a target="_blank" href="https://www.youtube.com/channel/UCxhgW0Q5XLvIoXHAfQXg9oQ">YouTube</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
