<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Roman Guivan</title>
    <description>The latest articles on DEV Community by Roman Guivan (@roman_guivan_17680f142e28).</description>
    <link>https://dev.to/roman_guivan_17680f142e28</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%2F694485%2F284ac2f9-a5f8-4aec-be6a-ce7827dfef0f.png</url>
      <title>DEV Community: Roman Guivan</title>
      <link>https://dev.to/roman_guivan_17680f142e28</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/roman_guivan_17680f142e28"/>
    <language>en</language>
    <item>
      <title>How I've deleted my git remote branch and kind of managed to restore it</title>
      <dc:creator>Roman Guivan</dc:creator>
      <pubDate>Thu, 25 May 2023 14:43:20 +0000</pubDate>
      <link>https://dev.to/roman_guivan_17680f142e28/how-ive-deleted-my-git-remote-branch-and-kind-managed-to-restore-it-2ofk</link>
      <guid>https://dev.to/roman_guivan_17680f142e28/how-ive-deleted-my-git-remote-branch-and-kind-managed-to-restore-it-2ofk</guid>
      <description>&lt;p&gt;Restoring things (branches) from git that were deleted in git, git git git git.&lt;/p&gt;

&lt;p&gt;I'm a spaz. Last night I've felt like tidying up, so I've started dropping my branches in remote and locally. Few hours later I've realised some pull requests are gone. &lt;/p&gt;

&lt;p&gt;So my local branch is gone, my remote branch is also gone. What do i do now?&lt;/p&gt;

&lt;p&gt;Good solution would be to search for SHA that's printed every time you delete branch. With that you can &lt;code&gt;git checkout -b new-branch-name SHA&lt;/code&gt; and you'll be fine. But not me, i've deleted 30-40 branches through a shell script (told you, i'ma spaz).&lt;/p&gt;

&lt;p&gt;Next suggestion would be &lt;a href="https://git-scm.com/docs/git-reflog"&gt;git reflog&lt;/a&gt; but that didn't do it for me either. Next is "local history" plugins in your IDE yet i had none so I'll cut straight to the chase, here's what I did:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;you run &lt;code&gt;git fsck --lost-found&lt;/code&gt; - this would print all the lost boys. Out of these i suggest you filter out ones that start with "dangling commit" (that's what i did)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now you have hundreds, maybe thousands of commit hashes without a message that you can theoretically look into. Still bad? - Don't lose hope. What i did next was: i put all commits into a biiiig space-separated line. Very long line. And ran a shell script&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

for i in [those 200 items ]do

   git show $i

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

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;I've re-directed output of this script into a file with &amp;gt; pipe, cmd+f in that huge text file and found diffs of my missing commits by names of classes and functions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I've cherry-picked them by SHA into a new branch&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Perhaps there's a better easier way, grep masters surely know, but here's what i did. Maybe it helps you too, you silly Milly.&lt;/p&gt;

&lt;p&gt;Enjoy yo day boys and girls&lt;/p&gt;

</description>
      <category>git</category>
    </item>
    <item>
      <title>Piano Rocker Alpha Testers needed</title>
      <dc:creator>Roman Guivan</dc:creator>
      <pubDate>Sat, 29 Jan 2022 18:12:47 +0000</pubDate>
      <link>https://dev.to/roman_guivan_17680f142e28/piano-rocker-alpha-testers-needed-1lc7</link>
      <guid>https://dev.to/roman_guivan_17680f142e28/piano-rocker-alpha-testers-needed-1lc7</guid>
      <description>&lt;p&gt;Here's another update in my never-ending story about releasing my e-piano game to steam early access.&lt;/p&gt;

&lt;p&gt;I have 10 steam keys to give away, for people who'd like to fiddle a bit with my early release of &lt;a href="https://store.steampowered.com/app/1771240/Piano_Rocker/"&gt;Piano Rocker: the ultimate piano game&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://store.steampowered.com/news/app/1771240/view/5050225927195766406"&gt;leave a comment in the event discussion&lt;/a&gt; first 5 comments get keys&lt;/p&gt;

&lt;p&gt;If you're interested, have a steam account and an e-piano (or a midi keyboard) - join the club! Leave a comment either here or better in &lt;a href="https://store.steampowered.com/news/app/1771240/view/5050225927195766406"&gt;steam event page&lt;/a&gt;, i'll send you a product code + will invite you into secret piano rocker discord server. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HOW COOL IS THAT?&lt;/strong&gt; &lt;br&gt;
&lt;em&gt;Pretty damn cool&lt;/em&gt;&lt;/p&gt;

</description>
      <category>steam</category>
      <category>piano</category>
      <category>gamedev</category>
      <category>beta</category>
    </item>
    <item>
      <title>a flying and rotating quadcopter in three.js</title>
      <dc:creator>Roman Guivan</dc:creator>
      <pubDate>Wed, 19 Jan 2022 14:49:51 +0000</pubDate>
      <link>https://dev.to/roman_guivan_17680f142e28/a-flying-and-rotating-quadcopter-in-threejs-2gmk</link>
      <guid>https://dev.to/roman_guivan_17680f142e28/a-flying-and-rotating-quadcopter-in-threejs-2gmk</guid>
      <description>&lt;p&gt;IT IS ALIVE and rolls and spins &lt;/p&gt;

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

&lt;h3&gt;
  
  
  In the previous write-up
&lt;/h3&gt;

&lt;p&gt;I've started an ambitious project with drones flying anywhere i want, as long as "anywhere i want" is written in THREE.js. &lt;/p&gt;

&lt;p&gt;I got SOMEWHERE using oimo.js rigid body physics simulation, applying lift force in update() loop, using angular velocity on rigid body to perform yaw spins. &lt;/p&gt;

&lt;p&gt;The thing i couldn't get right was rotation around X and Z axes. &lt;/p&gt;

&lt;p&gt;See, rigid body physics engines all provide multiple body types, two that you need to know of for getting this article are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DYNAMIC (moved with impulses and FORCE VECTORS, collide other bodies as if in "REAL LIFE")&lt;/li&gt;
&lt;li&gt;KINEMATIC (things just go through each other, you set their position and rotation yourself, but collision events are still fired somewhere, just saying HEY you're flying through CUBE-A, hehe) &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What kind of body was my drone in part1? - If you said dynamic, you were right. And then before each render i ran:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function updateMeshPositions(bodies) {
    let i = bodies.length;
    while (i--) {
        const body = bodies[i];
        const mesh = meshes[i];

        if (!body.sleeping &amp;amp;&amp;amp; mesh) {
            mesh.position.copy(body.getPosition());
            mesh.quaternion.copy(body.getQuaternion());
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;keeping physics and 3d meshes in sync. &lt;/p&gt;

&lt;p&gt;Angular force for rotation + lift as linear velocity were not all that precious of "REAL SIMULATIONS" anyways, so i have given up and switched to kinematic body type.&lt;/p&gt;

&lt;h3&gt;
  
  
  Forces to codes
&lt;/h3&gt;

&lt;p&gt;Two things that WORKED were gravity, lift, that i got calling&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const {x,y,z} = {x: 0, y: MAX_LIFT * controller.throttle, z: 0};
const upforce = new Vector3(x,y,z);
// to make Y force a RELATIVE y based on body position, not WORLD Y
upforce.applyQuaternion(body.getQuaternion());
body.applyImpulse(body.getPosition(), upforce);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and rotation via&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;body.angularVelocity += MAX_ANGLE_PER_FRAME * controller.yaw
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've left our LERPs for the simplicity here. So what would that be for a kinematic object? &lt;/p&gt;

&lt;p&gt;Knowing that &lt;code&gt;F = ma&lt;/code&gt;, and gravity is &lt;code&gt;mg&lt;/code&gt; is enough.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; // called every frame
    update(timeSinceStart = 0) {
        const timeMs = timeSinceStart - this.timeSinceStart;
        const timeSeconds = timeMs / 1000;
        const position = this.mesh.position.clone();
        const sumOfForces = new Vector3(0, 0, 0);
        const throttleAcceleration = (controller.throttle + 1) / 2 * MAX_THROTTLE;
        const gravityVector = new Vector3(0, KINEMATIC_GRAVITY * timeSeconds, 0);
        const upforceVector = new Vector3(0, throttleAcceleration * timeSeconds, 0);
        upforceVector.applyQuaternion(this.mesh.quaternion);

        sumOfForces.add(gravityVector);

        if (!flags.isPlayerOnTheGround) {
            this.rotate(timeSeconds)
            this.moveForward(timeSeconds);
            this.moveSideways(timeSeconds);
        }

        if (controller.armed) {
            sumOfForces.add(upforceVector);
        }

        position.add(sumOfForces);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thats your "moving up and down", covered.&lt;/p&gt;

&lt;p&gt;then pitch, yaw and roll become just transforms on mesh&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; rotate() {
        this.nextFrameRotation = { ...this.nextFrameRotation, y: MAX_YAW_ANGLE * controller.yaw };
    }

    moveForward() {
        // rotate obj
        this.nextFrameRotation = ({ ... this.nextFrameRotation, x: - MAX_ROTATION_ANGLE * controller.roll });
    }

    moveSideways() {
        this.nextFrameRotation = { ...this.nextFrameRotation, z: MAX_ROTATION_ANGLE * -controller.pitch }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and once the next angles are there - i'm spinning the body&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        const { x, y, z } = this.nextFrameRotation;
        let forward = new Vector3(0, 0, 1);
        let right = new Vector3(1, 0, 0);
        forward.normalize();
        right.normalize();

        this.mesh.rotateOnAxis(forward, z); //sideways 
        this.mesh.rotateOnAxis(right, x); //forward 
        this.mesh.rotateOnAxis(this.mesh.up, y); //yaw 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;aaand that's it mostly. For collisions i'd use a raycast in each direction. Now with no "physics" my chances of wrapping it all into a "camera controls" plugin for THREE are way higher. Sweet stuff.&lt;/p&gt;

&lt;p&gt;Guess all that i have for you today, babies. Wait for part three, perhaps it's gonna be an exciting one.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>a flying quadcopter in three.js</title>
      <dc:creator>Roman Guivan</dc:creator>
      <pubDate>Thu, 13 Jan 2022 15:43:33 +0000</pubDate>
      <link>https://dev.to/roman_guivan_17680f142e28/a-flying-quadcopter-in-threejs-10ha</link>
      <guid>https://dev.to/roman_guivan_17680f142e28/a-flying-quadcopter-in-threejs-10ha</guid>
      <description>&lt;p&gt;What's up &lt;strong&gt;HACKERS&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;I've gotten into flying fpv drones recently &lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;my very first cute DVRs montage here&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So i got curious if i could quickly put something that resembles flying a quad, in browser, myself. &lt;/p&gt;

&lt;p&gt;First i kinda hoped THREE.js just has QUADCOPTER CAMERA CONTROLS in &lt;code&gt;/examples/&lt;/code&gt; but no chance :( &lt;/p&gt;

&lt;p&gt;Don't get me wrong, there's a whole ton of good quadcopter simulators &lt;a href="https://www.liftoff-game.com/"&gt;Liftoff being my favourite&lt;/a&gt; the journey is the reward tho, so if you have a similar itch or just curious - you're welcome to this insane journey of mine  &lt;/p&gt;

&lt;h3&gt;
  
  
  WORK IN PROGRESS DEMO VIDEO HERE
&lt;/h3&gt;

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

&lt;h3&gt;
  
  
  Things i think i'll need
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;renderer: Gotta be THREE.js This (and unity) are all i know&lt;/li&gt;
&lt;li&gt;3d physics engine: &lt;a href="https://schteppe.github.io/cannon.js/"&gt;Cannon.js&lt;/a&gt; and &lt;a href="https://github.com/kripken/ammo.js/"&gt;Ammo.js&lt;/a&gt; seem pretty inactive in recent years. Perhaps the boys just GOT IT DONE, and it's perfect like that. I don't know. In my search for a "more recent" development i have picked &lt;a href="https://github.com/lo-th/Oimo.js/"&gt;Oimo.js&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The very high-level architecture of this whole thing
&lt;/h3&gt;

&lt;p&gt;If you have read my best-seller "match three game tutorial" series, you know there's an update() loop for rendering the graphics. With physics engine we would essentially have two separate update loops: loop one would update rigid body physics calculations, loop two would just display them as meshes on screen (duh) &lt;/p&gt;

&lt;p&gt;Upforce of fans, tilting - all gonna become applied forces in physics engine, yaw is just angular velocity on OY for example.&lt;/p&gt;

&lt;p&gt;Oh, and we need to implement controls. Luckily for me &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API"&gt;gamepad api&lt;/a&gt; also applies to any RC radio you could connect to a PC for playing a sim, so my &lt;a href="https://betafpv.com/products/literadio-2-se-radio-transmitter"&gt;&lt;br&gt;
LiteRadio 2 SE Radio&lt;/a&gt; works (as it should) &lt;/p&gt;

&lt;h3&gt;
  
  
  What i've put together in a weekend
&lt;/h3&gt;

&lt;p&gt;Is available &lt;a href="https://three-drone.netlify.app/"&gt;here&lt;/a&gt; Keyboard controls are difficult to wrestle with, and your gamepad axis most probably gonna be inverted (my xbox ones are) so ehh.. GOOD LUCK i guess?  Also: REMEMBER THE VIDEO, remember HOW GOOD I WAS? Exactly. It's possible, just git good or something. &lt;/p&gt;

&lt;h3&gt;
  
  
  Things i'd like to do next:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Pitch and roll act like some super-stable mode right now, i'd like them to be more like AIR/ACRO, i.e. rotate the quad. Thing is - with angular velocities on Z and X axis it just gets real weird, so i'm still kinda working on it&lt;/li&gt;
&lt;li&gt;sounds would be good, duh &lt;/li&gt;
&lt;li&gt;forking &lt;a href="https://noclip.website/"&gt;https://noclip.website/&lt;/a&gt; so i can fly around GTA III map, and GTA: SA map, and .. DARK SOULS map (if i want to) &lt;/li&gt;
&lt;li&gt;getting source somewhere public &lt;/li&gt;
&lt;li&gt;writing one more of these once at least one of the steps is done &lt;/li&gt;
&lt;li&gt;controls settings so you can invert any axis you want for your radio/gamepad &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The thing i'm most proud of
&lt;/h3&gt;

&lt;p&gt;The propellers are spinning, man, Looks awesome. More throttle - MORE SPIN, gee soo cool&lt;/p&gt;

&lt;p&gt;Hit me up if you have any questions, ANY questions, i mean it! I'll try to answer them all.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;for example&lt;/em&gt;: &lt;/p&gt;

&lt;p&gt;Q: HEY ROMAN, WHATS UP WITH &lt;a href="https://store.steampowered.com/app/1771240/Piano_Rocker/?beta=1"&gt;PIANO ROCKER&lt;/a&gt;?&lt;br&gt;
A: SOON&lt;/p&gt;

</description>
      <category>fpv</category>
      <category>three</category>
      <category>javascript</category>
      <category>drone</category>
    </item>
    <item>
      <title> Sneak'n'sort tabletop game! (Sneak´n´Sort)</title>
      <dc:creator>Roman Guivan</dc:creator>
      <pubDate>Mon, 03 Jan 2022 17:07:24 +0000</pubDate>
      <link>https://dev.to/roman_guivan_17680f142e28/sneaknsort-tabletop-game-sneaknsort-1ncn</link>
      <guid>https://dev.to/roman_guivan_17680f142e28/sneaknsort-tabletop-game-sneaknsort-1ncn</guid>
      <description>&lt;h3&gt;
  
  
  My good friend Rike funded her tabletop game
&lt;/h3&gt;

&lt;p&gt;The game is called &lt;a href="https://www.kickstarter.com/projects/sneaknsort/sneak-n-sort-beobachte-aufmerksam-and-sortiere-klug"&gt;Sneak´n´Sort&lt;/a&gt; and the first 50 batches are already produced and being sent to the backers this exact moment! &lt;/p&gt;

&lt;p&gt;I've also got &lt;a href="https://sneak-n-sort.netlify.app/"&gt;this sweet one-pager on netlify&lt;/a&gt; up, so peeps can track how the things are going. &lt;/p&gt;

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

&lt;p&gt;If you're into tabletop games, or the name did intrigue the sneaky part of you - please check it out, and leave your email for future updates, via the page. Rike will also reply for all the feedback you've sent to &lt;a href="mailto:sneaknsort@gmx.de"&gt;&lt;/a&gt;&lt;a href="mailto:sneaknsort@gmx.de"&gt;sneaknsort@gmx.de&lt;/a&gt;, no lie! &lt;/p&gt;

&lt;p&gt;There's also a technical aspect to this whole write-up: netlify if pretty damn cool for a 100% free kinda thing!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>match 3 game in pixi.js 103: JUICE and POLISH</title>
      <dc:creator>Roman Guivan</dc:creator>
      <pubDate>Sun, 12 Dec 2021 17:11:37 +0000</pubDate>
      <link>https://dev.to/roman_guivan_17680f142e28/match-3-game-in-pixijs-103-juice-and-polish-3fff</link>
      <guid>https://dev.to/roman_guivan_17680f142e28/match-3-game-in-pixijs-103-juice-and-polish-3fff</guid>
      <description>&lt;p&gt;After &lt;a href="https://dev.to/roman_guivan_17680f142e28/match-3-game-in-pixi-js-36hm"&gt;PART 1&lt;/a&gt; and &lt;a href="https://dev.to/roman_guivan_17680f142e28/match-3-game-in-pixijs-102-gameplay-3e9k"&gt;PART 2&lt;/a&gt; we're left with an app that has the humble beginnings of a match-three game, but still won't be sufficient to get you a PASS if it was any kind of assignment&lt;/p&gt;

&lt;p&gt;Let's add some sprinkles here and there, once you're done - &lt;a href="https://suspicious-goldberg-1c6bb6.netlify.app/"&gt;the result might end up being somthing like this&lt;/a&gt; (or way better, it's all in your hands, really!)&lt;/p&gt;

&lt;h3&gt;
  
  
  Animations
&lt;/h3&gt;

&lt;p&gt;The easiest cheapest way to animate sprites without additional animation frames (which i don't have) is to change sprite's properties over time. Position, scale and tint are my TOP-3. If i would like my animals to EXPAND a bit, after they're added onto the screen, something like this would totally do:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  function animate(sprite) {
      setTimeout(()=&amp;gt; sprite.scale.set(0.9, 0.9), 100);
      setTimeout(()=&amp;gt; sprite.scale.set(1.1, 1.1), 200);
      setTimeout(()=&amp;gt; sprite.scale.set(1.0, 1.0), 300);
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The results would be a bit crude and jerky, but you get the idea. Here's a 60fps version of this same function &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  function animate(sprite, scaleTo, timeSeconds) {
      const frames = timeSeconds * 60, timePerFrameSeconds = 1 / 60;
      for (let i=0; i&amp;lt; frames; i++) {
         const frameTimeSeconds = timePerFrameSeconds * i;
         const scale = i / frames * scaleTo;
         setTimeout(()=&amp;gt; sprite.scale.set(scale, scale), frameTimeSeconds * 1000);
      }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Again, crude, &lt;code&gt;requestAnimationFrame&lt;/code&gt; would perform better than &lt;code&gt;setTimeout&lt;/code&gt;, I'm using this code for explaining purposes only. &lt;/p&gt;

&lt;p&gt;What we achieved here is called linear interpolation. We gradually changed value of sprite scale, over time, till &lt;code&gt;sprite.scale&lt;/code&gt; became &lt;code&gt;scaleTo&lt;/code&gt;. While linear interpolation is perfectly fine, human eye finds these most tasteless and dull.  &lt;a href="https://createjs.com/tweenjs"&gt;Tween.js&lt;/a&gt; to the rescue!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://createjs.com/tweenjs"&gt;Tween.js&lt;/a&gt; will calculate interpolated values for different kinds of easing functions, so instead of linear - your animated scaling can be exponential or use one of many other functions available. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/guivanrv/pixijs-match-three-example/blob/main/src/scripts/tween.js"&gt;You can check the animations (or just tweens) i have used in the game here&lt;/a&gt; For example, this same scale animation looks like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    export function popIn(sprite, time = 300) {
const props = { x: 0.8, y: 0.8 };
new TWEEN.Tween(props)
    .to({ x: 1, y: 1 }, time)
    .easing(TWEEN.Easing.Bounce.Out) // Use an easing function to make the animation smooth.
    .onUpdate(() =&amp;gt; {
        sprite.scale.x = props.x;
        sprite.scale.y = props.y
    })
    .start()
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now add &lt;code&gt;popIn(sprite);&lt;/code&gt; somewhere after you have added it onto screen with &lt;code&gt;this.addChild(sprite)&lt;/code&gt; and you're golden! Things "pop-in" nicely. I suggest you try animating sprite.tint too, to add some color to it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IMPORTANT NOTE&lt;/strong&gt;&lt;br&gt;
Tween.js has an .update() function that has to be called too, with a timestamp. PIXI's dt, or one from requestAnimationFrame won't do. What does work for me, is passing performance.now() value. &lt;a href="https://github.com/guivanrv/pixijs-match-three-example/blob/main/src/scripts/index.js#L112"&gt;Here's the place where i do it in my implementation&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Sounds
&lt;/h3&gt;

&lt;p&gt;You can write your own wrapper around HTML5 audio, insert an  element, listen to &lt;code&gt;load&lt;/code&gt; event and so on, but for practical purposes i suggest you give &lt;a href="https://howlerjs.com/"&gt;howler.js&lt;/a&gt; a try. &lt;/p&gt;

&lt;p&gt;Get some free sounds from &lt;a href="https://freesound.org/"&gt;https://freesound.org/&lt;/a&gt;, organize them into your static assets folder, then you can create a separate &lt;a href="https://github.com/guivanrv/pixijs-match-three-example/blob/main/src/scripts/audio.js"&gt;audio.js&lt;/a&gt; module and trigger sound effects as exported members&lt;/p&gt;

&lt;p&gt;Some looping background theme music might be a good idea too.&lt;/p&gt;

&lt;p&gt;Once again &lt;a href="https://github.com/guivanrv/pixijs-match-three-example"&gt;the source of the example can be found here&lt;/a&gt; or just &lt;a href="https://suspicious-goldberg-1c6bb6.netlify.app/"&gt;play the deployed version&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have followed this series, and actually made something of yours - post links or your questions in the comments bellow. &lt;/p&gt;

&lt;p&gt;I think at it's current state the repo already shows the basics of making a match-three game in PIXI.js. A losing condition and some additional scenes like a menu and a game over screen still can be added. If you're interested in these being explained - leave it in the comments below too. &lt;/p&gt;

&lt;p&gt;For now - i'm leaving this series here, hope you liked it. Roman G - signing off&lt;/p&gt;

</description>
      <category>pixijs</category>
      <category>javascript</category>
      <category>gamedev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Piano Rocker dev diary 1: What is up</title>
      <dc:creator>Roman Guivan</dc:creator>
      <pubDate>Wed, 24 Nov 2021 12:35:51 +0000</pubDate>
      <link>https://dev.to/roman_guivan_17680f142e28/piano-rocker-dev-diary-1-what-is-up-24k2</link>
      <guid>https://dev.to/roman_guivan_17680f142e28/piano-rocker-dev-diary-1-what-is-up-24k2</guid>
      <description>&lt;p&gt;I've been working on this little idea of mine, a game where you play piano for at least half a year now. &lt;/p&gt;

&lt;p&gt;It all started in may 2021 when i've figured WEB Midi - exists. And that could allow me to listen to midi keyboard inputs, so if i could render a &lt;a href="https://en.wikipedia.org/wiki/Rock_Band_3"&gt;rock band 3&lt;/a&gt; chart (which is a midi file + ogg stems) for &lt;a href="https://youtu.be/LR3U4b_fVBc?t=89"&gt;PRO KEYS MODE&lt;/a&gt; - that would be freaking great.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3 abandoned prototypes in&lt;/li&gt;
&lt;li&gt;1 &lt;a href="https://www.reddit.com/r/rhythmgames/comments/p2d6qu/my_guitar_hero_rock_band_3_inspired_game_just/"&gt;&lt;em&gt;insanely viral&lt;/em&gt; reddit post with 25 upvotes&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://store.steampowered.com/app/1771240/Piano_Rocker/?beta=1"&gt;1 steam storefront page published&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and many months of dread since, a lot of things are done, but nothing is really DONE done. &lt;/p&gt;

&lt;p&gt;To put my mind elsewhere, but not too far away I start this "PIANO ROCKER DEV DIARY" series, where i blabber about technology, development, music games and yknow.. whatever comes to my mind. &lt;/p&gt;

&lt;p&gt;Case study of the day:&lt;/p&gt;

&lt;h2&gt;
  
  
  Rendering musicXML
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zrTz7Xc6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gtkwvnfffsznoasdkxqs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zrTz7Xc6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gtkwvnfffsznoasdkxqs.png" alt="Image description" width="260" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sheet music - exists, like it or not, playing keyboard is very tightly coupled with reading sheet music. All the exceptions, jazz legends who all "learnt it by ear", just make the overall rule stronger. I must render sheet music in some shape or form :(&lt;/p&gt;

&lt;p&gt;Trust me, i did try to dodge this bullet, but I don't think i can, not anymore. &lt;/p&gt;

&lt;p&gt;There are other "alternative" notations worth mentioning. Most curious ones are &lt;a href="https://abcnotation.com/"&gt;ABC Notation&lt;/a&gt;, the &lt;a href="https://commons.wikimedia.org/wiki/File:Keyboardmania_double_screen.jpg"&gt;top-down charts a-la keyboardmania&lt;/a&gt; that were made popular by &lt;a href="https://synthesiagame.com/"&gt;Synthesia&lt;/a&gt; and the new-emerging craze of VIRTUAL PIANO (playing piano on ascii keyboard, it is a thing, people actually do it. Even crazier, they do it in &lt;a href="https://youtu.be/RkvI1iRzIVk?t=59"&gt;ROBLOX&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have seen yousician, simply piano, pianoo and such, all rendering sheet music, and after a bit of digging in local cache of yousician windows app i can assure you - they all parse music XML. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wLFxQmQV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1gngyyf58kgyu5id0b2q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wLFxQmQV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1gngyyf58kgyu5id0b2q.png" alt="Image description" width="299" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Biggest public source of sheet music i'm aware of, provides scores downloadable as PDF (nope), MIDI (seems really good, HEY, i already have it implemented, but trust me, it's not, i might cover this as separate post someday), their proprietary format AAAND Music XML. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TPkkQCRc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i8fhke70juq3b3oqt2ip.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TPkkQCRc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i8fhke70juq3b3oqt2ip.png" alt="Image description" width="880" height="623"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's widely adopted, it's open as much as TEXT FILES can be, it's free - i just have to learn how to interpret it and draw notes. Just like all these guys already did, perhaps a bit more lively (hopefully).&lt;/p&gt;

&lt;p&gt;I'm still stuck with this shit, real stuck, but &lt;strong&gt;here's what i have learnt so far&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;*.mxl files arent XML files, they are ZIP files with XMLs in them. &lt;a href="https://stuk.github.io/jszip/"&gt;&lt;code&gt;jszip&lt;/code&gt;&lt;/a&gt; to the resque, this wasn't too hard &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;most probably you'll want to transform xml to json, if you do javascript like i do, that's trivial too, buncha libs out there, i use &lt;code&gt;fast-xml-parser&lt;/code&gt; for now &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;i render things in PIXI.js, so for that i would need to draw all the cute 🎼🎼🎼♫♫♫s. Good news - every unicode font has a range for music symbols. Bad news - that set isn't a complete set and you can't do much with it. All music software uses special MUSIC FONTS (rendering all as text symbols) &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There are buncha music fonts, some of them - can work in browser, but good luck figuring out what's there and what's not, how symbols are mapped and such. I have spent half a day trying different free fonts, till i found out about &lt;a href="https://w3c.github.io/smufl/latest/"&gt;SMUFL&lt;/a&gt; a standard for music fonts. Also check their &lt;a href="https://w3c.github.io/smufl/latest/about/brief-history.html"&gt;brief history of music fonts&lt;/a&gt; its a great read!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For a free to use in commercial project font IMPLEMENTING SMUFL - i chose &lt;a href="https://github.com/steinbergmedia/bravura"&gt;&lt;code&gt;Bravura&lt;/code&gt;&lt;/a&gt; it works, using reference from smufl website it's possible to map anything you can imagine to a unicode symbol. Lets say you need a g-clef, go to &lt;a href="https://w3c.github.io/smufl/latest/tables/clefs.html"&gt;this page&lt;/a&gt;, g clef is U+E050 (and U+1D11E), great, create your PIXI.Text('\uE050') - here's your g-clef (if you managed to preload the font)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;preloading fonts is a bit of a pain&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;so far i havent even touched actual music XML contents yet, right? Oh shit! Here we go again!&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Notes have DURATIONS. Durations are not measured in TIME, it's sheet music, right? We have quarter notes, half notes, sixteenth notes, 1024th notes and so on and so forth. If you want to have anything interactive - you would need to convert these to TIME. Now here's a question, HOW WOULD YOU DO IT?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  how would you convert MUSIC NOTES to time and duration (in seconds)
&lt;/h3&gt;

&lt;p&gt;so your typical music xml &lt;code&gt;measure.note&lt;/code&gt; would have &lt;code&gt;pitch&lt;/code&gt;, consisting of &lt;code&gt;step&lt;/code&gt; (of chromatic scale, C, C#, D, whatever it is) and &lt;code&gt;octave&lt;/code&gt; (int). &lt;/p&gt;

&lt;p&gt;Then there's &lt;code&gt;type&lt;/code&gt; and &lt;code&gt;duration&lt;/code&gt;. Type is within: [&lt;code&gt;whole&lt;/code&gt;, &lt;code&gt;half&lt;/code&gt;, &lt;code&gt;quarter&lt;/code&gt;, .... &lt;code&gt;32nd&lt;/code&gt;, &lt;code&gt;64th&lt;/code&gt; ..... &lt;code&gt;1024th&lt;/code&gt; and so on ]. &lt;code&gt;duration&lt;/code&gt; is an int number. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Being an idiot i am, i didn't read the spec, i just debugged my twinkle-twinkle-little-star chart and decided empirically that 1 is quarter note, 2 is half, 4 is whole. Twinkle-twinkle never went smaller than quarters, worked great. I even wrote a string to int switch-case, basing time strictly on TYPE instead.. DUMB.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Hard truth incomming&lt;/em&gt; &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In 60BPM (beats-per-minute) you'll hear/play 60 BEATS in one minute. One beat is one quarter note in 4/4 (or 3/4, or anything/4). Which means 1 beat in 4/4 in 60bpm is exactly one second. One bar in 4/4 60bpm is 4 seconds, then, one bar in 3/4 60bpm is 3 seconds and so on. &lt;/li&gt;
&lt;li&gt;Every MEASURE (parent node of notes) has attributes, one of which is DIVISIONS. DIVISIONS is number of divisions PER QUARTER NOTE. ( the PQ in PPQ for my cool midi gang ). &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This number is the smallest integer that can be divided without .decimals for the smallest note division present in sheet. So if all your notes don't go smaller than quarter notes = DIVISIONS=1 is good. Quarter is 1, half is 2, whole is 4. If you have 8th notes - DIVISIONS would be 2, if you want to have 16th notes - 4, 16th and TRIPLETS - something that can be divided by 2 AND 3 would be needed (24 is good)&lt;/p&gt;

&lt;p&gt;Now lets take this example from &lt;a href="https://www.w3.org/2021/06/musicxml40/tutorial/midi-compatible-part/"&gt;https://www.w3.org/2021/06/musicxml40/tutorial/midi-compatible-part/&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;attributes&amp;gt;
    &amp;lt;divisions&amp;gt;24&amp;lt;/divisions&amp;gt;
    &amp;lt;key&amp;gt;
      &amp;lt;fifths&amp;gt;-3&amp;lt;/fifths&amp;gt;
      &amp;lt;mode&amp;gt;minor&amp;lt;/mode&amp;gt;
    &amp;lt;/key&amp;gt;
    &amp;lt;time&amp;gt;
      &amp;lt;beats&amp;gt;3&amp;lt;/beats&amp;gt;
      &amp;lt;beat-type&amp;gt;4&amp;lt;/beat-type&amp;gt;
    &amp;lt;/time&amp;gt;
  &amp;lt;/attributes&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&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;note&amp;gt;
    &amp;lt;pitch&amp;gt;
      &amp;lt;step&amp;gt;G&amp;lt;/step&amp;gt;
      &amp;lt;octave&amp;gt;4&amp;lt;/octave&amp;gt;
    &amp;lt;/pitch&amp;gt;
    &amp;lt;duration&amp;gt;12&amp;lt;/duration&amp;gt;
    &amp;lt;lyric&amp;gt;
      &amp;lt;syllabic&amp;gt;single&amp;lt;/syllabic&amp;gt;
      &amp;lt;text&amp;gt;Dans&amp;lt;/text&amp;gt;
    &amp;lt;/lyric&amp;gt;
  &amp;lt;/note&amp;gt;
  &amp;lt;note&amp;gt;
    &amp;lt;pitch&amp;gt;
      &amp;lt;step&amp;gt;C&amp;lt;/step&amp;gt;
      &amp;lt;octave&amp;gt;5&amp;lt;/octave&amp;gt;
    &amp;lt;/pitch&amp;gt;
    &amp;lt;duration&amp;gt;8&amp;lt;/duration&amp;gt;
    &amp;lt;lyric&amp;gt;
      &amp;lt;syllabic&amp;gt;single&amp;lt;/syllabic&amp;gt;
      &amp;lt;text&amp;gt;un&amp;lt;/text&amp;gt;
    &amp;lt;/lyric&amp;gt;
  &amp;lt;/note&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we know about time so far, is: 3/4, divisions = 24 divisions per quarter note. Let's say, for this example BPM = 120. &lt;/p&gt;

&lt;p&gt;Now how long exactly does first note play? &lt;/p&gt;

&lt;p&gt;Divisions(perQuarterNote) = 24, BPM = 120, Duration = 12&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;timePerOneDivison = (60 / bpm) / Divisions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then duration of this note, in seconds, is&lt;/p&gt;

&lt;p&gt;duration * ((60 / bpm) / Divisions)&lt;/p&gt;

&lt;p&gt;12 * ( (60 / 120) / 24 ) = 0.25 sec. &lt;/p&gt;

&lt;p&gt;Based on which stave the note is (left or right hand) you add this duration to your gTime or fTime variable, and that would become the start time of next note. Cool right? &lt;/p&gt;

&lt;p&gt;Kinda.. Since I'm also "playing" the notes using the glorious &lt;a href="https://tonejs.github.io/"&gt;Tone.js&lt;/a&gt; something with my math is still not quite right, but visuals look correct, so i blame my audio code / use of transport for now.&lt;/p&gt;

&lt;p&gt;I hope this helped somebody. As a final note - here's how it all looks like in current prototype (sheet music supports is a project separate from mainline game for now, i'll merge it in once it's done, or merge main game into this small clean boy.. )&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/T-ciT-1Daug"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>piano</category>
      <category>steam</category>
      <category>javascript</category>
      <category>musicxml</category>
    </item>
    <item>
      <title>Google Lighthouse failing with NO_LCP error</title>
      <dc:creator>Roman Guivan</dc:creator>
      <pubDate>Mon, 08 Nov 2021 13:40:42 +0000</pubDate>
      <link>https://dev.to/roman_guivan_17680f142e28/google-lighthouse-failing-with-nolcp-error-1mjo</link>
      <guid>https://dev.to/roman_guivan_17680f142e28/google-lighthouse-failing-with-nolcp-error-1mjo</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjudl82cajz2jmozfsi0x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjudl82cajz2jmozfsi0x.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ok babies, so you've all googled REAL HARD, and all they told you was "clean your cache/go incognito mode". Thanks a lot, dude from medium.com - that was quite an insight, didn't help me, unfortunately.&lt;/p&gt;

&lt;p&gt;You just keep staring into: &lt;/p&gt;

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

Largest Contentful Paint
Error!
Something went wrong with recording the trace over your page load. Please run Lighthouse again. (NO_LCP)


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

&lt;/div&gt;

&lt;p&gt;scratching your head and thinking what else could have gone wrong. I'll give you a new fresh lead to check!&lt;/p&gt;

&lt;p&gt;Run lighthouse cli against your page (its available as a docker container too, so there's no excuse no to try it). If today is your lucky day, you'll see some text, looking suspiciously RED there:&lt;/p&gt;

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

&lt;p&gt;Whoopsy-poopsy, my friend! As of today, there's a still &lt;a href="https://bugs.chromium.org/p/chromium/issues/detail?id=1104218#c2" rel="noopener noreferrer"&gt;an open bug in chromium&lt;/a&gt; that i have stumbled upon in &lt;a href="https://github.com/GoogleChrome/lighthouse/issues/10869" rel="noopener noreferrer"&gt;lighthouse github issues&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Animating opacity from 0 to 1, as well as animating things off-screen - might cause chrome thinking that the "Largest contentful paint" will never happen, and quit the lighthouse check.&lt;/p&gt;

&lt;p&gt;Unlike FIRST contentful paint - there's no config to offset it. &lt;code&gt;maxWaitForFcp&lt;/code&gt; - exists, &lt;code&gt;maxWaitForLcp&lt;/code&gt; - doesnt. You can play with non-zero values for &lt;code&gt;pauseAfterFcpMs&lt;/code&gt;, &lt;code&gt;networkQuietThresholdMs&lt;/code&gt;, &lt;code&gt;pauseAfterLoadMs&lt;/code&gt;, &lt;code&gt;cpuQuietThresholdMs&lt;/code&gt; - all of which did not help me. &lt;/p&gt;

&lt;p&gt;What i did next, was searching for &lt;code&gt;opacity: 0&lt;/code&gt; and &lt;code&gt;transition&lt;/code&gt; in our sass files - dropping those did not help either. &lt;/p&gt;

&lt;p&gt;The culprit was in the in-house design library we used, that had a nifty class for react-like skeleton-animations. &lt;/p&gt;

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

background-size: 400%, 100%;
animation: xxxx 6s linear infinite;

@keyframes xxxx {
  0% {
    background-position: 200% 0%;
  }
  100% {
    background-position: -200% 0%;
  }
}


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

&lt;/div&gt;

&lt;p&gt;One find-replace of all mentions of this class with "", and one rebuild later - i had my metric displayed as it should. &lt;/p&gt;

&lt;p&gt;Send virtual beers if that was it, i'll accept them all. &lt;/p&gt;

</description>
      <category>lighthouse</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>performance</category>
    </item>
    <item>
      <title>match 3 game in pixi.js 102: Gameplay</title>
      <dc:creator>Roman Guivan</dc:creator>
      <pubDate>Thu, 21 Oct 2021 11:54:45 +0000</pubDate>
      <link>https://dev.to/roman_guivan_17680f142e28/match-3-game-in-pixijs-102-gameplay-3e9k</link>
      <guid>https://dev.to/roman_guivan_17680f142e28/match-3-game-in-pixijs-102-gameplay-3e9k</guid>
      <description>&lt;h4&gt;
  
  
  Once you've finished this long-read, your game will become playable! The hype is real!
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;This is a second article in a three-part tutorial. If you skipped the first part - &lt;a href="https://dev.to/roman_guivan_17680f142e28/match-3-game-in-pixi-js-36hm"&gt;catch up here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Selecting a sprite
&lt;/h2&gt;

&lt;p&gt;Most basic interaction for a match-3 game would be selecting an animal and making it trade places with neighbour. Thats what we are gonna implement right now!&lt;/p&gt;

&lt;p&gt;To make a sprite "interactive" in pixi, you set it's &lt;code&gt;interactive&lt;/code&gt; attribute to &lt;code&gt;true&lt;/code&gt;. This allows you to add a click event.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  sprite.interactive = true;
  sprite.on('pointerdown', (e) =&amp;gt; {});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Easy as that! If you want the cursor to change to pointer, add &lt;/p&gt;

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

&lt;/div&gt;

&lt;p&gt;as well. The requirements for the next task are simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if there is no selection, clicking on an animal makes it "selected", which means it changes it's scale, or tint (like in previous animation example)&lt;/li&gt;
&lt;li&gt;if there's already a selection - two animals trade places&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now please stop reading and go solve it yourself, i'll wait!&lt;/p&gt;

&lt;p&gt;In case you got stuck somewhere: here &lt;a href="https://github.com/guivanrv/pixijs-match-three-example/commit/47bc0b4a99b91e52f81584948b194b0545031406?w=1" rel="noopener noreferrer"&gt;is my solution, as diff on github&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Sprites are now stored in a two-dimensional array, and every second click - just makes two selected elements trade their x and y coordinates, both on-screen and in the array itself. &lt;/p&gt;

&lt;p&gt;To make sure I have not forgot to swap the sprites in array too, i debugged a bit with a trace function executed each time i click:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  function printSpriteNames() {
      for (let y = 0; y &amp;lt; TILES_OY; y++) {
          let row = '';
          for (let x = 0; x &amp;lt; TILES_OX; x++) {
              row+=`${sprites[x][y].name} `;
          }
          console.log(row);
      }
      console.log('------');
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We can chose any two sprites so far, not just the neighbours - we'll fix this later on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Detecting groups
&lt;/h2&gt;

&lt;p&gt;The player wants to group animals in lines of 3 and more, horizontally or vertically. Once a line like that is formed - the group is destroyed, and new elements are added onto the screen. &lt;/p&gt;

&lt;p&gt;Seems like the theme of this article is iterating arrays, everyone's first programming assignment ever. To make it more interesting, I suggest we write our pattern-recognition util using &lt;a href="https://en.wikipedia.org/wiki/Test-driven_development" rel="noopener noreferrer"&gt;TDD&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Unit-tests can be cool when used wisely. In this example i'll use &lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;Jest&lt;/a&gt;. If you haven't worked with Jest or unit-tests before (or had bad confusing experiences in the past) - no worries, i'll cover all the basics right now.&lt;/p&gt;

&lt;p&gt;To add jest, run &lt;code&gt;npm install jest --save-dev&lt;/code&gt;&lt;br&gt;
Once jest is installed - add a test script to your package.json&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    "scripts": {
      "test": "jest",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;In our &lt;code&gt;/scripts&lt;/code&gt; folder i'll create two new files, next to &lt;code&gt;index.js&lt;/code&gt;. &lt;code&gt;patterns.js&lt;/code&gt; and &lt;code&gt;patterns.spec.js&lt;/code&gt;. First one being our "pattern-matcher" implementation (utility function that would find patterns on sprites array) and second - the unit-test for it! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/guivanrv/pixijs-match-three-example/commit/92b60d7d20e30493da66c7f9523ebfeb908b9fbe?w=1#diff-508fa5a2377836be0f699031852cf191af87317973efb1e508fdd428ad16c1c0" rel="noopener noreferrer"&gt;Here's how the files look like&lt;/a&gt; As you can see, pattern-matcher implementation just has three empty methods for now, that are supposed to find groups of repeating elements horizontally and vertically (third method would aggregate results of first two). Whats more interesting is the &lt;code&gt;.spec.js&lt;/code&gt; file now.&lt;/p&gt;

&lt;p&gt;Inside the &lt;code&gt;describe('')&lt;/code&gt; block we have our first test-case &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  it('reads 3 and more in a horizontal line', () =&amp;gt; {
     expect(matcher.matchGroupsHorizontal(TEST_GROUP_0).length)
     .toBe(0);
     expect(matcher.matchGroupsHorizontal(TEST_GROUP_1).length)
     .toBe(0);
  })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;In our sprites array, each column is represented as nested array, so for a field 3x3, a &lt;em&gt;horizontal&lt;/em&gt; group of 3 'cow' sprites in first row would look like&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const TEST_GROUP = 
  [
     ['cow', 'cat, 'doge'],
     ['cow', 'rhino', 'frog'],
     ['cow', 'snake', 'frog']
  ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;It's a bit confusing, but array values are flipped 90 degrees compared to screen representation. I did it in favour of storing coordinates as &lt;code&gt;[x][y]&lt;/code&gt; and not other way around.&lt;/p&gt;

&lt;p&gt;Running &lt;code&gt;npm test&lt;/code&gt; would result in&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;TEST_GROUP_1&lt;/code&gt; has the row of matching values, but there is no implementation to find it yet! Go to &lt;code&gt;pattern.js&lt;/code&gt; and implement it yourself. The format i chose for "groups" looks like this: &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  [
     { name: "cow", points: [{0,0}, {1,0}, {2,0}]}
  ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;It's completely up to you HOW you chose to solve this problem. You can use regular expressions or implement some fancy algorithm. I recommend you to &lt;a href="https://github.com/guivanrv/pixijs-match-three-example/blob/e612b96e278e5ba43c1a3d8adb577ce361bca252/src/scripts/patterns.spec.js" rel="noopener noreferrer"&gt;take this unit test&lt;/a&gt; and implement such a &lt;code&gt;patterns.js&lt;/code&gt; that would make it pass.&lt;/p&gt;

&lt;p&gt;In case you're lost: &lt;a href="https://github.com/guivanrv/pixijs-match-three-example/blob/3dce039658c1c33df179de5b37ba3418aefa1c79/src/scripts/patterns.js" rel="noopener noreferrer"&gt;my dumbest solution is available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you're done - it's time to use our "well-tested" :p logic in the game.&lt;/p&gt;
&lt;h2&gt;
  
  
  Matching groups
&lt;/h2&gt;

&lt;p&gt;Our &lt;code&gt;index.js&lt;/code&gt; was in a sad sad shape for a 100-lines file. &lt;a href="https://github.com/guivanrv/pixijs-match-three-example/commit/a24161daf863d4e1ae386d23ded0ff297ac0c45f?w=1" rel="noopener noreferrer"&gt;I've extracted some of the blocks into separate functions&lt;/a&gt; so the file is a bit easier to navigate. Now to the final task of this chapter:&lt;/p&gt;

&lt;p&gt;On first render AND after each swap run pattern-matcher. If there are groups found:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;delete each sprite from the group &lt;/li&gt;
&lt;li&gt;replace these spots with new animals&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use &lt;code&gt;app.stage.removeChild&lt;/code&gt; to remove sprites and our pattern matcher for pattern matching. Another pro-tip - limit the number of possible random animals to 10 or less, not to animals.length, otherwise finding 3 of a kind will become hard. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/guivanrv/pixijs-match-three-example/commit/790aafccb8909d421d8b0a4823689760175c46f7?w=1" rel="noopener noreferrer"&gt;You can find my solution here&lt;/a&gt;. Congratulations! You've made it! It's a match-three game! Well, not quite, not yet. There are still things that have to be done, to make it a real game:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;there has to be a condition on which the player LOSES the game&lt;/li&gt;
&lt;li&gt;"destruction" and "creation" of our sprites have to be animated, at the moment it all looks too instantaneous&lt;/li&gt;
&lt;li&gt;sound and visual effects have to be added to make it all feel really interactive.

&lt;ul&gt;
&lt;li&gt;ah and another round of pattern-matching has to be ran after new elements are inserted&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Guess what? - thats exactly what we're gonna do in part three!  See you there, real soon! And for now: enjoy  &lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;&lt;a href="https://store.steampowered.com/app/1771240/Piano_Rocker/?l=german&amp;amp;beta=1" rel="noopener noreferrer"&gt;If you haven't whishlisted PIANO ROCKER on steam yet - do it right now! (or i'll call the police)&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>gamedev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>match 3 game in pixi.js 101: Sprite basics</title>
      <dc:creator>Roman Guivan</dc:creator>
      <pubDate>Wed, 06 Oct 2021 10:46:44 +0000</pubDate>
      <link>https://dev.to/roman_guivan_17680f142e28/match-3-game-in-pixi-js-36hm</link>
      <guid>https://dev.to/roman_guivan_17680f142e28/match-3-game-in-pixi-js-36hm</guid>
      <description>&lt;p&gt;YouTube: &lt;iframe width="710" height="399" src="https://www.youtube.com/embed/Unz-V2NGGCg"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Match 3 games are on average at least 80% more exciting to develop compared to what you're actually paid for (unless you're developing a match 3 game for living, of course). So i say lets just make one, for the science.&lt;/p&gt;

&lt;p&gt;This ain't a "definitive guide", more like a "your first match 3 game in pixi" or "your first game ever". &lt;/p&gt;

&lt;p&gt;In this part#1 we'll build an animated screen with buncha animal faces tiled &lt;/p&gt;

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

&lt;h2&gt;
  
  
  Step 1 - Get ready
&lt;/h2&gt;

&lt;p&gt;Just clone &lt;a href="https://github.com/guivanrv/pixijs-match-three-example" rel="noopener noreferrer"&gt;my repository&lt;/a&gt; and rewind to {1} commit.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/guivanrv/pixijs-match-three-example.git 
git checkout @{1}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You'll have a &lt;a href="https://github.com/wbkd/webpack-starter" rel="noopener noreferrer"&gt;webpack-starter&lt;/a&gt; with 'pixi.js' module installed and some graphic assets from &lt;a href="https://www.kenney.nl/assets" rel="noopener noreferrer"&gt;kenney.nl&lt;/a&gt; unpacked into &lt;code&gt;/public/images&lt;/code&gt; folder. &lt;/p&gt;

&lt;p&gt;Script in &lt;code&gt;script/index.js&lt;/code&gt; is just a starter example from &lt;a href="https://www.npmjs.com/package/pixi.js/v/5.0.0-rc" rel="noopener noreferrer"&gt;PIXI.js npm page&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://pixijs.download/dev/docs/PIXI.Application.html#stage" rel="noopener noreferrer"&gt;Stage&lt;/a&gt;, &lt;a href="https://pixijs.download/dev/docs/PIXI.Sprite.html" rel="noopener noreferrer"&gt;Sprite&lt;/a&gt; and &lt;a href="https://pixijs.download/dev/docs/PIXI.Ticker.html" rel="noopener noreferrer"&gt;Update-loop&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Of course you totally can develop a match 3 browser game, where your bejeweled GEMs are just &lt;/p&gt; elements, but for the sake of our 2d animations fluidity and performance, I'll use pixi.js here. Pixi will render graphics into canvas, using webgl-based renderer that would ensure all your visual transforms are calculated and rendered lightning fast.

&lt;p&gt;Since this might be your first game i'll quickly explain the basic terms here.&lt;/p&gt;
&lt;h4&gt;
  
  
  What is a &lt;a href="https://pixijs.download/dev/docs/PIXI.Application.html#stage" rel="noopener noreferrer"&gt;"stage"&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;A stage is a top-level root container. Think "scene" from Flash or Unity, or your document.body if we make analogies with DOM.&lt;/p&gt;
&lt;h4&gt;
  
  
  What is a &lt;a href="https://pixijs.download/dev/docs/PIXI.Ticker.html" rel="noopener noreferrer"&gt;"sprite"&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;A sprite is a 2d raster image. Like a div with fixed size and position-absolute-like placement. Except it's better than div because it can transform and resize really fast. Like 60 times per second and faster (if you disable vsync in your browser).&lt;/p&gt;
&lt;h4&gt;
  
  
  What is an update-loop
&lt;/h4&gt;

&lt;p&gt;Now this is a big one. I'm sure you've played a videogame at least once, before. You press a button - shit happens on screen - you're the one who caused all of this to happen and it makes you feel truly special and very engaged, right? &lt;/p&gt;

&lt;p&gt;There's an update loop in the heart of each and every game. A function that executes 60 times per second or more, usually clears and completely re-paints the screen 60 times per second or more, reads your inputs and so on and so on. &lt;/p&gt;

&lt;p&gt;One re-paint is one FRAME of both rendering, and input reading. Why 60? - Because 60 is considered to be "smooth" and to feel "interactive". It has been 40 years ago, it still is. Going more frequent than 60 in browser is possible, if you disable vsync, but not a common thing yet.&lt;/p&gt;

&lt;p&gt;in earlier days people literally used &lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const fps = 60;
setTimeout(function update(){...}, 1000/fps)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But of course if your update function was too computations-heavy - everything de-synced and just played horrible. Without any libraries today you can use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame" rel="noopener noreferrer"&gt;requestAnimationFrame&lt;/a&gt;. Think of it as a 60 fps setTimeout, that can "slow down gracefully" in case anything computation-heavy happens. Ah, and also it won't run when the tab aint active, unlike setTimeout.&lt;/p&gt;

&lt;p&gt;Many libs provide a Timer abstraction, in Pixi it's called &lt;a href="https://pixijs.download/dev/docs/PIXI.Ticker.html" rel="noopener noreferrer"&gt;Ticker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Theory class is over, it's only code from now on. Open &lt;code&gt;scripts/index.js&lt;/code&gt;, let me walk you through this real quick:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const app = new PIXI.Application();
document.body.appendChild(app.view);
const loader = PIXI.Loader.shared;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;creates a pixi app, and adds it's stage's  to document.body.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;loader.add('bear', 'public/images/bear.png').load((loader, resources) =&amp;gt; {
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;is async image pre-loader. &lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const bear = new PIXI.Sprite(resources.bear.texture);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;is you creating your sprite, and &lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.ticker.add(() =&amp;gt; {
     // each frame we spin the bear around a bit
    bear.rotation += 0.01;
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;is your update loop. &lt;/p&gt;
&lt;h2&gt;
  
  
  Task 1: create a 6x4 grid of randomized animal sprites.
&lt;/h2&gt;

&lt;p&gt;There's plenty of other cute animals aside of bear.png under &lt;code&gt;public/images/&lt;/code&gt;. I want you to modify the code, so it draws a 10x10 grid of random animals. I suggest you do it yourself, and in case you're stuck - come back to check my solution.&lt;/p&gt;

&lt;p&gt;Few hints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://pixijs.download/dev/docs/PIXI.Loader.html" rel="noopener noreferrer"&gt;loader.add has an array overload&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;every Sprite, like our bear, has &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;bear.x&lt;br&gt;
bear.y&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both are number values, hope you got the idea.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now stop reading and just do it!&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Done?&lt;/strong&gt; If you're not - no worries, here's how i would solve this.&lt;/p&gt;

&lt;p&gt;Lets define some constants:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const animals = ['bear','buffalo','chick','chicken','cow','crocodile','dog','duck','elephant','frog','giraffe', 'goat','gorilla','hippo','horse','monkey','moose','narwhal','owl','panda','parrot','penguin','pig','rabbit','rhino','sloth','snake','walrus','whale','zebra'],

TILES_OX = 6, TILES_OY = 4, SPRITE_WIDTH = 138, SPRITE_HEIGHT = 138, sprites = [];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here's a tip how to not type all 40 animals manually: &lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ls -l &amp;amp;&amp;gt; out.txt 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and then just paste the contents between two square braces, multiline edit first quotation mark, and find-replace '.png' with '",' for second one.&lt;/p&gt;

&lt;p&gt;On windows you can use dir instead of ls, and &amp;gt; instead of &amp;amp;&amp;gt;. Back to displaying animals in a grid:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;loader.add(animals.map(str =&amp;gt; ({name: str,url: `public/images/${str}.png`}))).load(
(loader, resources) =&amp;gt; {
for (let x = 0; x &amp;lt; TILES_OX; x++) {
    for (let y = 0; y &amp;lt; TILES_OY; y++) {
        const randomAnimal = animals[Math.trunc(Math.random() * animals.length)];
        const sprite = new PIXI.Sprite(resources[randomAnimal].texture);
        sprite.anchor.x = 0.5;
        sprite.anchor.y = 0.5;
        sprite.x = x * SPRITE_WIDTH + SPRITE_WIDTH/2;
        sprite.y = y * SPRITE_HEIGHT + SPRITE_HEIGHT/2;
        app.stage.addChild(sprite);
        sprites.push(sprite);
    }    
}
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One thing to explain here: sprite.anchor is the "center" of the sprite. If you want {x:0, y:0} to be TOP LEFT POINT of sprite: anchor is 0,0. If you want bottom right to be the "start" for positions it's 1,1. The benefit of using 0.5 0.5 will show mostly in animating: it's way more common to rotate a sprite around it's center, or scale it around the center. Also the images we have arent all equal in size, due to some animals having ears and horns.&lt;/p&gt;

&lt;p&gt;By fixing the width to 138px and using center as anchor - we can place them in a grid with equal spaces (ears will overlap with neighbours, so what :p )&lt;/p&gt;

&lt;p&gt;But of course, since now 0,0 of a sprite is it's center - all positions have to be offset by half of a sprite width or height.&lt;/p&gt;

&lt;p&gt;Now remember the ticker we had, the one that rotated sprites?&lt;/p&gt;

&lt;p&gt;Of course you can&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sprites.forEach((sprite) =&amp;gt; /*rotate them*/)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;again, but i have a cooler effect to suggest!&lt;/p&gt;

&lt;p&gt;Each sprite has scale.x and scale.y, where 1 is 100% scale. So to make our sprites zoom in and out randomly we can &lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Listen for frame updates
app.ticker.add(() =&amp;gt; {
    sprites.forEach((sprite, index) =&amp;gt; {
        const scale = 1 + 0.1 * Math.sin(Date.now() / (400 + index * 10));
        sprite.scale.x = scale;
        sprite.scale.y = scale;
    })
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you havent seen the trick with sin / cos before - i'm willing to defend it right here in this article too! Sin and Cos are cool! You give them any value - they'll return you a value from -1 to 1, so it's a perfect function for animating some cycling process. Swings of a pendulum, a spring jumping back and fourth - all easily doable with sin.&lt;/p&gt;

&lt;p&gt;Sin of Date.now() would result in scale going from 0 to 2, which is too EXTREME. I'd like it to stay whithin 0.9 ~ 1.1, thats why its 1 + sin of time * 0.1;&lt;/p&gt;

&lt;p&gt;And sin of time changes just too fast, so you can divide Date.now() to "slow things down a bit" &lt;/p&gt;

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

&lt;p&gt;In the second part we'll implement "selecting" a tile, and making it swap places with another one. Till then - make it a 200x200 grid and set the canvas in the background of your website. HYPNOTIZING af.&lt;/p&gt;

&lt;p&gt;As all of my writing, i devote this to the one and only &lt;a href="https://dev.to/barbara"&gt;Barbara&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And another shameless plug, if you have read this and you liked it - whishlist my game &lt;a href="https://store.steampowered.com/app/1771240/Piano_Rocker/?beta=1" rel="noopener noreferrer"&gt;Piano Rocker&lt;/a&gt; on steam. It comes out to early access beginning of next year and lemme just say it - there's a lot of PIXI in it too!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>gamedev</category>
    </item>
  </channel>
</rss>
