<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Pujaa M</title>
    <description>The latest articles on Forem by Pujaa M (@pujaam).</description>
    <link>https://forem.com/pujaam</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1264169%2F93e72964-927d-4c62-b595-914fe02b2a9a.JPG</url>
      <title>Forem: Pujaa M</title>
      <link>https://forem.com/pujaam</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/pujaam"/>
    <language>en</language>
    <item>
      <title>Code a cannon to shoot colored balls when clicked using Vanilla JS</title>
      <dc:creator>Pujaa M</dc:creator>
      <pubDate>Fri, 09 Feb 2024 08:47:23 +0000</pubDate>
      <link>https://forem.com/pujaam/cannon-shoot-1pe7</link>
      <guid>https://forem.com/pujaam/cannon-shoot-1pe7</guid>
      <description>&lt;p&gt;Created a shooting cannon using the trajectory of projectile motion with just a click.&lt;/p&gt;

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

&lt;p&gt;In this tutorial, we'll see how to create an engaging visual experience: a cannon launching colorful projectiles upon a click, each trajectory influenced by the user's mouse movement using CSS and JS. It's a fun and interactive way to play with angles and animation on the web!&lt;/p&gt;

&lt;p&gt;To achieve the cannon shoot, follow the below steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  1: Create a canvas
&lt;/h2&gt;

&lt;p&gt;The entire process will be working on a canvas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;HTML&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;CSS&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* {
  margin: 0;
  padding: 0;
}

html, body {
  height: 100%;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;JS&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let canvas = document.querySelector(".canvas");
let pen = canvas.getContext("2d");

function setupCanvas() {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    canvas.style.backgroundColor = "#000000";
}

setupCanvas();

window.addEventListener("resize", setupCanvas);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;We have created a canvas to work on.&lt;/p&gt;

&lt;h2&gt;
  
  
  2: Find a point from where you to launch and fix it as the origin
&lt;/h2&gt;

&lt;p&gt;In my case, I fixed the origin to somewhere in the bottom left. What I did was, I positioned my cannon image first then figured out the new origin.&lt;/p&gt;

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

&lt;p&gt;I placed the origin at the point I marked with a blue dot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;My Origin&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  x: 100,
  y: canvas.height -108
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;From now on, this point will be playing a vital role.&lt;/p&gt;

&lt;h2&gt;
  
  
  3: Rotate the Cannon and find the angle using 'mousemove' EventListener
&lt;/h2&gt;

&lt;p&gt;From the &lt;code&gt;mousemove&lt;/code&gt; EventListener we can figure out the &lt;em&gt;x&lt;/em&gt; and &lt;em&gt;y&lt;/em&gt; coordinates of the cursor pointer but how to find the angle? This is where some mathematics comes into play!&lt;/p&gt;

&lt;p&gt;The below formula is for finding the angle between two lines:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjyssm4yv0rz2us588y42.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjyssm4yv0rz2us588y42.png" alt="Angle between two lines" width="209" height="45"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Where &lt;em&gt;m1&lt;/em&gt; and &lt;em&gt;m2&lt;/em&gt; are the slopes of the given lines.&lt;/p&gt;

&lt;p&gt;Let us consider that the first line is passing through the origin fixed by us and the cursor pointer and the second line is through that origin and any one point lying on the &lt;em&gt;x-axis&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F75ppi3c70podvdkefaec.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F75ppi3c70podvdkefaec.png" alt="Angle between two lines" width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;JS&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

let cursorPointer = {
    x: 100,
    y: canvas.height - 108
}

/* ... */

function findAngle() {
    let x1 = 100;
    let y1 = canvas.height - 108;
    let x2 = cursorPointer.x;
    let y2 = cursorPointer.y;
    let tempX = 500;
    let tempY = canvas.height - 108;

    let m1 = (tempY - y1) / (tempX - x1);
    let m2 = (y2 - y1) / (x2 - x1);

    let angle = Math.atan(Math.abs((m2 - m1) / (1 + (m1 * m2))));

    return angle;
}

function rotateCannon(pointerX, pointerY) {
    cursorPointer.x = pointerX;
    cursorPointer.y = pointerY;

        // Start of code snippet for restricting the angle from 30deg to 75deg
    angle = findAngle() * (180 / Math.PI);
    if(cursorPointer.x &amp;lt; 100) {
        angle += 90;
    }

    if(cursorPointer.y &amp;gt; canvas.height - 108) {
        angle = -angle;
    }

    if(angle &amp;gt; 75) {
        angle = 75;
    }

    if(angle &amp;lt; 30) {
        angle = 30;
    }
        // End of code snippet

        // Rotating the cannon to the angle calculated
    /* let cannon = document.querySelector("#cannon");
    cannon.style.transform = "translateX(22px) rotate(-" + angle + "deg)"; */ 
}

document.addEventListener("mousemove", function(event) {
    rotateCannon(event.clientX, event.clientY);
});

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

&lt;/div&gt;



&lt;p&gt;At this moment, the angle changes according to the movement of the mouse. In my case, I rotate the cannon to the angle calculated along with the mouse movement.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd71wlo006uto4zkpben1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd71wlo006uto4zkpben1.gif" alt="Rotating cannon" width="356" height="146"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4: Find the trajectory of the ball
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Trajectory of a projectile&lt;/em&gt;&lt;/strong&gt; is the path of an object moving in the air that is affected by external force such as gravity.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcig0zlbifm195u5uqrlt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcig0zlbifm195u5uqrlt.png" alt="Trajectory of a projectile" width="350" height="144"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As we can see, the trajectory is parabolic.&lt;/p&gt;

&lt;p&gt;For simplicity, we will be considering the acceleration of the ball as &lt;em&gt;zero&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The equation for the trajectory of a projectile is:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F161vvg8dpf2ida9tclkw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F161vvg8dpf2ida9tclkw.png" alt="Equation of Trajectory of a Projectile" width="212" height="53"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;y-coordinates&lt;/em&gt;, we will be finding them using the above equation. To find them we will require:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;x-coordinates&lt;/em&gt;: Roughly consider from 1 to 200&lt;br&gt;
&lt;em&gt;g&lt;/em&gt;: Acceleration due to gravity(~10m/s²)&lt;br&gt;
&lt;em&gt;V₀&lt;/em&gt;: Initial velocity&lt;br&gt;
&lt;em&gt;θ&lt;/em&gt;: Angle at which the ball is launched&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;JS&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

function initializePathAndTrail() {
    for(let i = 0; i &amp;lt; numberOfPoints; i++) {
        path[i] = {
            x: 0,
            y: 0
        }
    }
}

initializePathAndTrail();

/* ... */

function getYTrajectoryPoint(pointerX) {
    let g = 10;
    let initialVelocity = 100;
    let radians = angle * (Math.PI / 180);

    let pointerY = (pointerX * Math.tan(radians)) - ((g * pointerX * pointerX) / (2 * initialVelocity * initialVelocity * Math.cos(radians) * Math.cos(radians)));

    return pointerY;
}

function createTrajectory() {
    let x = 10;
    for(let i = 0; i &amp;lt; numberOfPoints; i++) {
        let y = getYTrajectoryPoint(x);
        path[i].x = x;
        path[i].y = y;
        if(angle &amp;lt;= 60) { //To increase the distance between the points since the distance between the points for an angle above 60deg is greater while increasing by 15.
            x += 20;
        }
        else {
            x += 15;
        }
    }
}

/* ... */

function draw() {
    pen.fillStyle = "cyan";
    for(let i = 0; i &amp;lt; path.length - 1; i++) {
        pen.beginPath();
        pen.arc(path[i].x, path[i].y, 10, 0, Math.PI * 2);
        pen.fill();
    }
}

document.addEventListener("click", function() {
    cancelAnimationFrame(animationId);
    initializePathAndTrail();
    createTrajectory();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will get something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F892j9q6cwtnwcwtv0o12.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F892j9q6cwtnwcwtv0o12.gif" alt="Create trajectory" width="356" height="147"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the trajectory projected is based on the initial origin. So we need to translate all these points according to the new origin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;JS&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* ... */
function transformOrigin() {
    for(let i = 0; i &amp;lt; numberOfPoints; i++) {
        path[i].x = 100 + path[i].x;
        path[i].y = canvas.height - 108 - path[i].y;
    }
}

/* ... */

document.addEventListener("click", function() {

/* ... */

    transformOrigin();

/* ... */
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvvbha4bdb2e8xkw795wq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvvbha4bdb2e8xkw795wq.gif" alt="Transformed origin" width="356" height="147"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5: Animating the ball
&lt;/h2&gt;

&lt;p&gt;We have almost reached the result. We have figured out how to rotate the cannon, found the trajectory and now we will be animating the ball to travel along the trajectory whenever clicked.&lt;/p&gt;

&lt;p&gt;To achieve this, we will be using &lt;code&gt;requestAnimationFrame()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;JS&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

function draw() {
    pen.clearRect(0, 0, canvas.width, canvas.height);
    pen.beginPath();
    pen.arc(path[params.index].x, path[params.index].y, 10, 0, Math.PI * 2);
    pen.fill();
        params.index ++;
    if(params.index &amp;gt;= numberOfPoints) {
        params.index = numberOfPoints - 1;
    }

    animationId = requestAnimationFrame(draw);
}

document.addEventListener("click", function() {
    cancelAnimationFrame(animationId);
    initializePathAndTrail();
    createTrajectory();
    transformOrigin();
    params.index = 0;

    let num = Math.floor((Math.random() * 10) % 4);
    pen.fillStyle = color[num];

    draw();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F59tfkzvffymict3nymtm.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F59tfkzvffymict3nymtm.gif" alt="Animating the ball" width="356" height="144"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And done! Now just click and launch the ball in various angles and play!&lt;/p&gt;

&lt;h2&gt;
  
  
  6: Creating a trail behind the ball(optional)
&lt;/h2&gt;

&lt;p&gt;I am sharing a tutorial here on &lt;a href="https://dev.to/uuuuuulala/coding-an-interactive-and-damn-satisfying-cursor-7-simple-steps-2kb-of-code-1c8b"&gt;how to create a trail&lt;/a&gt; by &lt;a class="mentioned-user" href="https://dev.to/uuuuuulala"&gt;@uuuuuulala&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;I am also sharing the code below:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;JS&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

function initializePathAndTrail() {
    for(let i = 0; i &amp;lt; numberOfPoints; i++) {
        path[i] = {
            x: 0,
            y: 0
        }
    }

    for(let i = 0; i &amp;lt; trailNumber; i++) {
        trail[i] = {
            x: 100,
            y: canvas.height - 108,
            dx: 0,
            dy: 0
        }
    }
}

/* ... */

function draw() {
    pen.clearRect(0, 0, canvas.width, canvas.height);
    let tempPointer = {
        x: 0,
        y: 0
    };

    tempPointer.x = path[params.index].x;
    tempPointer.y = path[params.index].y;

    trail.forEach((pointer, index) =&amp;gt; {
        const previousPointer = (index === 0) ? tempPointer : trail[index - 1];

        const spring = params.spring;

        pointer.dx += (previousPointer.x - pointer.x) * spring;
        pointer.dy += (previousPointer.y - pointer.y) * spring;

        pointer.dx *= params.friction
        pointer.dy *= params.friction

        pointer.x += pointer.dx;
        pointer.y += pointer.dy;
    })

    pen.lineCap = "round";
    pen.beginPath();
    pen.moveTo(trail[0].x, trail[0].y);
    for(let i = 0; i &amp;lt; trail.length - 1; i++) {
        pen.lineWidth = trailNumber * 2 - i * 2;
        pen.globalAlpha = (i === 0) ? 1 : 0.1;
        pen.lineTo(trail[i].x, trail[i].y);
        pen.stroke();
    }
    params.index ++;
    if(params.index &amp;gt;= numberOfPoints) {
        params.index = numberOfPoints - 1;
    }

    animationId = requestAnimationFrame(draw);
}

document.addEventListener("click", function() {
    cancelAnimationFrame(animationId);
    initializePathAndTrail();
    createTrajectory();
    transformOrigin();
    params.index = 0;

    let num = Math.floor((Math.random() * 10) % 4);
    pen.strokeStyle = color[num];

    draw();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F67tfg4ib91j6osdembud.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F67tfg4ib91j6osdembud.gif" alt="Trail behind the ball" width="356" height="145"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hope you enjoyed this tutorial. Have a nice day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;SVG for the cannon and its support&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
Cannon&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkuebtk7xe71hjqukwch3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkuebtk7xe71hjqukwch3.png" alt="Cannon" width="411" height="363"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;svg id="cannon" width="200" height="129" viewBox="0 0 200 129" fill="none" xmlns="http://www.w3.org/2000/svg"&amp;gt;
        &amp;lt;rect x="14.2857" y="57.1429" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="42.8571" y="57.1429" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="28.5714" y="57.1429" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="14.2857" y="71.4286" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="42.8571" y="71.4286" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="28.5714" y="71.4286" width="14.2857" height="14.2857" fill="#F365FF"/&amp;gt;
        &amp;lt;rect x="14.2857" y="42.8571" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="42.8571" y="42.8571" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="28.5714" y="42.8571" width="14.2857" height="14.2857" fill="#F365FF"/&amp;gt;
        &amp;lt;rect y="57.1429" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect y="71.4286" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect y="42.8571" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="42.8571" y="14.2857" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="28.5714" y="14.2857" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="14.2857" y="28.5714" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="42.8571" y="28.5714" width="14.2857" height="14.2857" fill="#F7E1D4"/&amp;gt;
        &amp;lt;rect x="28.5714" y="28.5714" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="42.8571" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="42.8571" y="100" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="28.5714" y="100" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="42.8571" y="114.286" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="14.2857" y="85.7143" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="42.8571" y="85.7143" width="14.2857" height="14.2857" fill="#F365FF"/&amp;gt;
        &amp;lt;rect x="28.5714" y="85.7143" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="185.714" y="57.1429" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="185.714" y="71.4286" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="185.714" y="42.8571" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="185.714" y="28.5714" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="185.714" y="85.7143" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="57.1429" y="57.1429" width="14.2857" height="14.2857" fill="#F365FF"/&amp;gt;
        &amp;lt;rect x="85.7143" y="57.1429" width="14.2857" height="14.2857" fill="#F365FF"/&amp;gt;
        &amp;lt;rect x="71.4286" y="57.1429" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="57.1429" y="71.4286" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="85.7143" y="71.4286" width="14.2857" height="14.2857" fill="#F365FF"/&amp;gt;
        &amp;lt;rect x="71.4286" y="71.4286" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="57.1429" y="42.8571" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="85.7143" y="42.8571" width="14.2857" height="14.2857" fill="#F7E1D4"/&amp;gt;
        &amp;lt;rect x="71.4286" y="42.8571" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="142.857" y="57.1429" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="171.429" y="57.1429" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="157.143" y="57.1429" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="142.857" y="71.4286" width="14.2857" height="14.2857" fill="#F365FF"/&amp;gt;
        &amp;lt;rect x="171.429" y="71.4286" width="14.2857" height="14.2857" fill="#F365FF"/&amp;gt;
        &amp;lt;rect x="157.143" y="71.4286" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="142.857" y="42.8571" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="171.429" y="42.8571" width="14.2857" height="14.2857" fill="#F7E1D4"/&amp;gt;
        &amp;lt;rect x="157.143" y="42.8571" width="14.2857" height="14.2857" fill="#F7E1D4"/&amp;gt;
        &amp;lt;rect x="100" y="57.1429" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="128.571" y="57.1429" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="114.286" y="57.1429" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="100" y="71.4286" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="128.571" y="71.4286" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="114.286" y="71.4286" width="14.2857" height="14.2857" fill="#F365FF"/&amp;gt;
        &amp;lt;rect x="100" y="42.8571" width="14.2857" height="14.2857" fill="#F7E1D4"/&amp;gt;
        &amp;lt;rect x="128.571" y="42.8571" width="14.2857" height="14.2857" fill="#F7E1D4"/&amp;gt;
        &amp;lt;rect x="114.286" y="42.8571" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="57.1429" y="14.2857" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="85.7143" y="14.2857" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="71.4286" y="14.2857" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="57.1429" y="28.5714" width="14.2857" height="14.2857" fill="#F7E1D4"/&amp;gt;
        &amp;lt;rect x="85.7143" y="28.5714" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="71.4286" y="28.5714" width="14.2857" height="14.2857" fill="#F7E1D4"/&amp;gt;
        &amp;lt;rect x="57.1429" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="71.4286" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="171.429" y="14.2857" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="142.857" y="28.5714" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="171.429" y="28.5714" width="14.2857" height="14.2857" fill="#F7E1D4"/&amp;gt;
        &amp;lt;rect x="157.143" y="28.5714" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="100" y="14.2857" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="100" y="28.5714" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="128.571" y="28.5714" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="114.286" y="28.5714" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="57.1429" y="100" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="85.7143" y="100" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="71.4286" y="100" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="57.1429" y="114.286" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="71.4286" y="114.286" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="57.1429" y="85.7143" width="14.2857" height="14.2857" fill="#F365FF"/&amp;gt;
        &amp;lt;rect x="85.7143" y="85.7143" width="14.2857" height="14.2857" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="71.4286" y="85.7143" width="14.2857" height="14.2857" fill="#F365FF"/&amp;gt;
        &amp;lt;rect x="171.429" y="100" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="142.857" y="85.7143" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="171.429" y="85.7143" width="14.2857" height="14.2857" fill="#F365FF"/&amp;gt;
        &amp;lt;rect x="157.143" y="85.7143" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="100" y="100" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="100" y="85.7143" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="128.571" y="85.7143" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="114.286" y="85.7143" width="14.2857" height="14.2857" fill="#FF357E"/&amp;gt;
&amp;lt;/svg&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;svg id="support" width="200" height="110" viewBox="0 0 200 110" fill="none" xmlns="http://www.w3.org/2000/svg"&amp;gt;
        &amp;lt;rect x="36.3636" y="54.5455" width="18.1818" height="18.1818" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="72.7273" y="54.5455" width="18.1818" height="18.1818" fill="#F365FF"/&amp;gt;
        &amp;lt;rect x="54.5455" y="54.5455" width="18.1818" height="18.1818" fill="#F7E1D4"/&amp;gt;
        &amp;lt;rect x="36.3636" y="72.7273" width="18.1818" height="18.1818" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="72.7273" y="72.7273" width="18.1818" height="18.1818" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="54.5455" y="72.7273" width="18.1818" height="18.1818" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="72.7273" y="36.3636" width="18.1818" height="18.1818" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="54.5455" y="36.3636" width="18.1818" height="18.1818" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="145.455" y="54.5455" width="18.1818" height="18.1818" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="145.455" y="72.7273" width="18.1818" height="18.1818" fill="#F7E1D4"/&amp;gt;
        &amp;lt;rect x="163.636" y="72.7273" width="18.1818" height="18.1818" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="90.9091" y="54.5455" width="18.1818" height="18.1818" fill="#F365FF"/&amp;gt;
        &amp;lt;rect x="127.273" y="54.5455" width="18.1818" height="18.1818" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="109.091" y="54.5455" width="18.1818" height="18.1818" fill="#F365FF"/&amp;gt;
        &amp;lt;rect x="90.9091" y="72.7273" width="18.1818" height="18.1818" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="127.273" y="72.7273" width="18.1818" height="18.1818" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="109.091" y="72.7273" width="18.1818" height="18.1818" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="90.9091" y="36.3636" width="18.1818" height="18.1818" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="127.273" y="36.3636" width="18.1818" height="18.1818" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="109.091" y="36.3636" width="18.1818" height="18.1818" fill="#F7E1D4"/&amp;gt;
        &amp;lt;rect x="72.7273" y="18.1818" width="18.1818" height="18.1818" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="90.9091" width="18.1818" height="18.1818" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="90.9091" y="18.1818" width="18.1818" height="18.1818" fill="#FFA2C4"/&amp;gt;
        &amp;lt;rect x="109.091" y="18.1818" width="18.1818" height="18.1818" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="36.3636" y="90.9091" width="18.1818" height="18.1818" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="72.7273" y="90.9091" width="18.1818" height="18.1818" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="54.5455" y="90.9091" width="18.1818" height="18.1818" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="145.455" y="90.9091" width="18.1818" height="18.1818" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="181.818" y="90.9091" width="18.1818" height="18.1818" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="163.636" y="90.9091" width="18.1818" height="18.1818" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="90.9091" y="90.9091" width="18.1818" height="18.1818" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="127.273" y="90.9091" width="18.1818" height="18.1818" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="109.091" y="90.9091" width="18.1818" height="18.1818" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="18.1818" y="72.7273" width="18.1818" height="18.1818" fill="#FF357E"/&amp;gt;
        &amp;lt;rect x="18.1818" y="90.9091" width="18.1818" height="18.1818" fill="#FF357E"/&amp;gt;
        &amp;lt;rect y="90.9091" width="18.1818" height="18.1818" fill="#FF357E"/&amp;gt;
&amp;lt;/svg&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>codepen</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Eye tracking dog</title>
      <dc:creator>Pujaa M</dc:creator>
      <pubDate>Thu, 01 Feb 2024 15:32:09 +0000</pubDate>
      <link>https://forem.com/pujaam/eye-tracking-dog-nnh</link>
      <guid>https://forem.com/pujaam/eye-tracking-dog-nnh</guid>
      <description>&lt;p&gt;The eyes of the dog track the direction of the cursor when moved.&lt;/p&gt;

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

&lt;p&gt;In this tutorial, we'll explore the intricate process of synchronizing a virtual dog's eyes with the movement of your cursor.&lt;/p&gt;

&lt;h2&gt;
  
  
  1: Create a canvas in HTML
&lt;/h2&gt;

&lt;p&gt;Using the canvas tag, create a canvas and setup the width, height and color of the canvas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTML&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;CSS&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;canvas {
    background-color: #A15A22;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;JS&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const canvas = document.querySelector("canvas");
const pen = canvas.getContext("2d");
let radius = 50;
let trackerRadius = 30;

function setupCanvas() {
    canvas.width = 500;
    canvas.height = 500;
}

setupCanvas();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will achieve something like this:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  2: Draw eyeball(circle) at the center of the canvas
&lt;/h2&gt;

&lt;p&gt;We can draw a circle on a canvas using the &lt;code&gt;arc()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JS&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* ... */
let radius = 125;
/* ... */

function draw() {
    let centerX1 = canvas.width * 0.5;
    let centerY1 = canvas.height * 0.5;

    pen.beginPath();
    pen.arc(centerX1, centerY1, radius, 0, 2 * Math.PI);
    pen.fillStyle = "white";
    pen.fill();
}

draw();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;This circle that we have drawn will be the eyeball of the dog later on...&lt;/p&gt;

&lt;h2&gt;
  
  
  3: Draw iris the moves along with the cursor.
&lt;/h2&gt;

&lt;p&gt;We will be using the &lt;code&gt;arc()&lt;/code&gt; method again to draw a dot. Then we shall fetch the cursor position using &lt;code&gt;EventListeners&lt;/code&gt; to update the position of the dot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JS&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

let cursorPointer = {
    x: canvas.width * 0.5,
    y: canvas.height * 0.5
};

/* ... */

function updateCursorPointer(pointerX, pointerY) {
    let boundary = canvas.getBoundingClientRect();
    cursorPointer.x = pointerX - boundary.left;
    cursorPointer.y = pointerY - boundary.top;
}

document.addEventListener("mousemove", function(event) {
    updateCursorPointer(event.clientX, event.clientY);
});

function draw() {
    pen.clearRect(0, 0, canvas.width, canvas.height);
/* ... */

    pen.beginPath();
    pen.moveTo(cursorPointer.x, cursorPointer.y);
    pen.arc(cursorPointer.x, cursorPointer.y, trackerRadius, 0, 2 * Math.PI);
    pen.fillStyle = "#03c2fc";
    pen.fill();

/* ... */

    requestAnimationFrame(draw);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;requestAnimationFrame()&lt;/code&gt; method schedules the &lt;code&gt;draw()&lt;/code&gt; method for the next frame. The &lt;code&gt;clearRect()&lt;/code&gt; method is used so that the canvas is completely cleared before painting the next frame.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbo4431cyw391urtl8hfg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbo4431cyw391urtl8hfg.gif" alt="Iris" width="399" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We see that the dot, which will be the iris of the eyeball, moves along with the cursor all over the canvas but not bounded within the eyeball.&lt;/p&gt;

&lt;h2&gt;
  
  
  3: Bound the iris within the eyeball.
&lt;/h2&gt;

&lt;p&gt;As we can observe from the image that the eyeball is actually a circle. So, in order to bound the iris within the eyeball, we need to check whether the points read from the cursor pointer is actually present within the circle or not.&lt;/p&gt;

&lt;p&gt;Let us consider the equation of a circle:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv8vu84z9hqonu0qyu8n1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv8vu84z9hqonu0qyu8n1.png" alt="Equation of circle" width="221" height="24"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using the above equation, we can easily check whether the point lies within the cirlce or not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JS&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function updateCursorPointer(pointerX, pointerY) {
let boundary = canvas.getBoundingClientRect();
    let tempX = pointerX - boundary.left;
    let tempY = pointerY - boundary.top;

    let h = canvas.width * 0.5;
    let k = canvas.height * 0.5;

    let boundaryRadius = radius - trackerRadius;
    let u = tempX - h;
    let v = tempY - k;
    let eq = (u * u) + (v * v);

    if(eq &amp;lt;= (boundaryRadius * boundaryRadius)) {
            cursorPointer.x = tempX;
            cursorPointer.y = tempY;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw2h4lfpibg4wktjhejav.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw2h4lfpibg4wktjhejav.gif" alt="Iris bounded within Eyeball" width="415" height="353"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4: Move iris wherever the cursor is within the window
&lt;/h2&gt;

&lt;p&gt;As of now, the iris moves along to the cursor only when the cursor is inside the eyeball. In order to move the iris no matter where the cursor is on the window, we will be using the method to find the point of intersection between a line and a circle.&lt;/p&gt;

&lt;p&gt;Refer this link for detailed explanation and examples on how to find &lt;a href="https://www.superprof.co.uk/resources/academic/maths/analytical-geometry/conics/circle-line-intersection.html"&gt;points of intersection&lt;/a&gt; between a line and a circle.&lt;/p&gt;

&lt;p&gt;So, when substituting the &lt;em&gt;y&lt;/em&gt; obtained from the equation of line in the equation of circle, we get the following equation:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyttuahb9djdlyw4he5ie.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyttuahb9djdlyw4he5ie.png" alt="Equation after substituting y" width="558" height="24"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our case, the circle obviously is the eyeball and the line is obtained from two points - the center of eyeball and cursor pointer's position.&lt;/p&gt;

&lt;p&gt;We will find the slope &lt;em&gt;m&lt;/em&gt; using the two points, find c from the equation of line &lt;em&gt;y = mx + c&lt;/em&gt; and r being the radius of the eyeball.&lt;/p&gt;

&lt;p&gt;Let us solve the above quadratic equation using&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F34ntzjdebvw304qxcvi8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F34ntzjdebvw304qxcvi8.png" alt="Equation for solving a quadratic equation" width="161" height="49"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JS&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function lineEquation(x, slope, constant) {
   return (slope * x) + constant;
}

function discriminant(a, b, c) {
  return (b * b) - (4 * a * c);
}

function isPointBetween(x, y, x1, y1, x2, y2) {
  return (x &amp;gt;= Math.min(x1, x2) &amp;amp;&amp;amp; x &amp;lt;= Math.max(x1, x2) &amp;amp;&amp;amp;
          y &amp;gt;= Math.min(y1, y2) &amp;amp;&amp;amp; y &amp;lt;= Math.max(y1, y2));
}

function findIntersectionPoint(slope, h, k, point1X, point1Y, constant, circleRadius) {
    var a = 1 + (slope * slope);
      var b = -2 * (h + slope * (k - constant));
      var c = (h * h) + (k * k) - (circleRadius * circleRadius) + (constant * (constant - (2 * k)));

    var disc = discriminant(a, b, c);

    if (disc &amp;gt;= 0) {
      const x1 = (-b + Math.sqrt(disc)) / (2 * a);
      const x2 = (-b - Math.sqrt(disc)) / (2 * a);

      const y1 = lineEquation(x1, slope, constant);
      const y2 = lineEquation(x2, slope, constant);

            if(isPointBetween(x1, y1, point1X, point1Y, h, k)) {
                    cursorPointer.x = x1;
                    cursorPointer.y = y1;
            }
            else if(isPointBetween(x2, y2, point1X, point1Y, h, k)) {
                    cursorPointer.x = x2;
                    cursorPointer.y = y2;
            }
        }
}

function findCursorPointerPosition(h, k, tempX, tempY) {
    let boundaryRadius = radius - trackerRadius;
    let u = tempX - h;
    let v = tempY - k;
    let eq = (u * u) + (v * v);

    if(eq &amp;lt;= (boundaryRadius * boundaryRadius)) {
            cursorPointer.x = tempX
            cursorPointer.y = tempY;
    }
    else {
        let x1 = h;
        let y1 = k;
        let x2 = tempX;
        let y2 = tempY;

        let slope = (y2 - y1) / (x2 - x1);

        let constant = y2 - (slope * x2);

        findIntersectionPoint(slope, x1, y1, x2, y2, constant, boundaryRadius);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since the result gives out two points, we need to find the point that is between the center of the eyeball and the cursor pointer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fquqyj4nz3zd32rcf9naw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fquqyj4nz3zd32rcf9naw.gif" alt="Iris movement to cursor" width="406" height="353"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5: Create another eye
&lt;/h2&gt;

&lt;p&gt;Since our dog needs two eyes to see, we will be repeating the same process for the other eye too. But this time, we will be relocating the center of the circles for the left and the right eyes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JS&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

let radius = 50;

/* ... */

function setupCanvas() {
    canvas.width = 250;
    canvas.height = 150;
}

let cursorPointerLeft = {
    x: canvas.width * 0.25,
    y: canvas.height * 0.5
}

let cursorPointerRight = {
    x: canvas.width * 0.75,
    y: canvas.height * 0.5
}

/* ... */

function updateCursorPointer(pointerX, pointerY) {
    let boundary = canvas.getBoundingClientRect();
    let tempX = pointerX - boundary.left;
    let tempY = pointerY - boundary.top;

    let h1 = canvas.width * 0.25;
    let k1 = canvas.height * 0.5;
    let h2 = canvas.width * 0.75;
    let k2 = canvas.height * 0.5;

    findCursorPointerPosition(h1, k1, tempX, tempY, cursorPointerLeft);
    findCursorPointerPosition(h2, k2, tempX, tempY, cursorPointerRight); // Add an additional parameter, cursorPointer, in findCursorPointerPosition() and findIntersectionPoint()
}

/* ... */

function draw() {
    pen.clearRect(0, 0, canvas.width, canvas.height);
    let centerX1 = canvas.width * 0.25;
    let centerY1 = canvas.height * 0.5;
    let centerX2 = canvas.width * 0.75;
    let centerY2 = canvas.height * 0.5;

    pen.beginPath();
    pen.arc(centerX1, centerY1, radius, 0, 2 * Math.PI);
    pen.fillStyle = "white";
    pen.fill();

    pen.beginPath();
    pen.arc(centerX2, centerY2, radius, 0, 2 * Math.PI);
    pen.fillStyle = "white";
    pen.fill();

    pen.beginPath();
    pen.moveTo(cursorPointerLeft.x, cursorPointerLeft.y);
    pen.arc(cursorPointerLeft.x, cursorPointerLeft.y, trackerRadius, 0, 2 * Math.PI);
    pen.fillStyle = "#03c2fc";
    pen.fill();

    pen.beginPath();
    pen.moveTo(cursorPointerLeft.x, cursorPointerLeft.y);
    pen.arc(cursorPointerLeft.x, cursorPointerLeft.y, trackerRadius * 0.75, 0, 2 * Math.PI);
    pen.fillStyle = "black";
    pen.fill();

    pen.beginPath();
    pen.moveTo(cursorPointerRight.x, cursorPointerRight.y);
    pen.arc(cursorPointerRight.x, cursorPointerRight.y, trackerRadius, 0, 2 * Math.PI);
    pen.fillStyle = "#03c2fc";
    pen.fill();

    pen.beginPath();
    pen.moveTo(cursorPointerRight.x, cursorPointerRight.y);
    pen.arc(cursorPointerRight.x, cursorPointerRight.y, trackerRadius * 0.75, 0, 2 * Math.PI);
    pen.fillStyle = "black";
    pen.fill();
    requestAnimationFrame(draw);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6j8bich3t9vrqb3xzugn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6j8bich3t9vrqb3xzugn.gif" alt="Two Eyeballs" width="389" height="353"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have almost achieved what we require but the eyes look like the dog has got its fifth shot of Benedryl in a day &amp;gt;_&amp;lt; !&lt;/p&gt;

&lt;h2&gt;
  
  
  6: Correcting the eye movement
&lt;/h2&gt;

&lt;p&gt;So instead of having two different centers for the eye we will gonna be having one, common for both.&lt;/p&gt;

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

&lt;p&gt;Let us imagine there is a circle with the same radius as the eyeballs between the two eyeballs. We will be moving the irises according to that imaginary circle. Once we calculated the position according to that circle we will adjust the x-axes of the irises according to the eyeballs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JS&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

function findIntersectionPoint(slope, h, k, point1X, point1Y, constant, circleRadius) {
    var a = 1 + (slope * slope);
      var b = -2 * (h + slope * (k - constant));
      var c = (h * h) + (k * k) - (circleRadius * circleRadius) + (constant * (constant - (2 * k)));

    var disc = discriminant(a, b, c);

    if (disc &amp;gt;= 0) {
      const x1 = (-b + Math.sqrt(disc)) / (2 * a);
      const x2 = (-b - Math.sqrt(disc)) / (2 * a);

      const y1 = lineEquation(x1, slope, constant);
      const y2 = lineEquation(x2, slope, constant);

            if(isPointBetween(x1, y1, point1X, point1Y, h, k)) {
                    cursorPointerLeft.x = x1 - canvas.width * 0.25;
                    cursorPointerLeft.y = y1;
                    cursorPointerRight.x = x1 + canvas.width * 0.25;
                    cursorPointerRight.y = y1;
            }
            else if(isPointBetween(x2, y2, point1X, point1Y, h, k)) {
                    cursorPointerLeft.x = x2 - canvas.width * 0.25;
                    cursorPointerLeft.y = y2;
                    cursorPointerRight.x = x2 + canvas.width * 0.25;
                    cursorPointerRight.y = y2;
            }
        }
}

function updateCursorPointer(pointerX, pointerY) {
    let boundary = canvas.getBoundingClientRect();
    let tempX = pointerX - boundary.left;
    let tempY = pointerY - boundary.top;

    let h = canvas.width * 0.5;
    let k = canvas.height * 0.5;

    findCursorPointerPosition(h, k, tempX, tempY);
}

function findCursorPointerPosition(h, k, tempX, tempY) {
    let boundaryRadius = radius - trackerRadius;
    let u = tempX - h;
    let v = tempY - k;
    let eq = (u * u) + (v * v);

    if(eq &amp;lt;= (boundaryRadius * boundaryRadius)) {
            cursorPointerLeft.x = tempX - canvas.width * 0.25;
            cursorPointerLeft.y = tempY;
            cursorPointerRight.x = tempX + canvas.width * 0.25;
            cursorPointerRight.y = tempY;
    }
    else {
        let x1 = h;
        let y1 = k;
        let x2 = tempX;
        let y2 = tempY;

        let slope = (y2 - y1) / (x2 - x1);

        let constant = y2 - (slope * x2);

        findIntersectionPoint(slope, x1, y1, x2, y2, constant, boundaryRadius);
    }
}

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvr3xdgt17enqvowv4fp.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvr3xdgt17enqvowv4fp.gif" alt="Corrected eyeball movement" width="389" height="353"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To finish off, add your dog face(in my case I designed the dog face in Figma and exported the SVG). And to give a dramatic touch, I reduced the height of the canvas.&lt;/p&gt;

&lt;p&gt;Hope you enjoyed this tutorial. Have a nice day!!!&lt;/p&gt;

</description>
      <category>codepen</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
