DEV Community

Cover image for Vanilla JS "Who's that Pokémon?" game with the PokéAPI
JS Bits Bill
JS Bits Bill

Posted on

Vanilla JS "Who's that Pokémon?" game with the PokéAPI

Remember that "Who's that Pokémon?" TV game? Well we can build our own version of this with the PokéAPI!

If we make a fetch call to this endpoint (and specify that we want to limit it to the first 151 Pokémon), we'll get a results array:



(async () => {
  const res = await fetch('https://pokeapi.co/api/v2/pokemon?limit=151');
  const json = await res.json();

  console.log(json.results);
})();

// Logs:
[
  {name: 'bulbasaur', url: 'https://pokeapi.co/api/v2/pokemon/1/'},
  {name: 'ivysaur', url: 'https://pokeapi.co/api/v2/pokemon/2/'},
  {name: 'venusaur', url: 'https://pokeapi.co/api/v2/pokemon/3/'},
 // ...
]


Enter fullscreen mode Exit fullscreen mode

That url property inside each object is the pokémon-specific endpoint that can be hit for more information about that individual character. Inside this JSON, there will be a sprite URL like so:
https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/1.png

This is the sprite for Pokémon #1:

Since we saw in the original data that Bulbasaur's url property shows its number (1), and this number is the same that's used in the sprite URL, we can use just this original request to get all the information we need to create a "Who's that Pokémon?" game:

The full code for this project is hosted here, but the rest of this article will show the core components we'll need to make this work.

First, our HTML structure:



<main class="fetching">

  <div id="pokemon-container">
    <img id="pokeball" src="background.jpg">
    <img id="pokemon-image" src="placeholder.png">
  </div>

  <div id="answer">
    <div id="bg-overlay"></div>
    <div id="text-overlay"></div>
  </div>

  <section id="controls">
    <button id="play">Play</button>
    <div id="choices"></div>
  </section>
</main>


Enter fullscreen mode Exit fullscreen mode

The #pokemon-image source will be set dynamically to show the featured pokémon, and the #choices element will be populated with multiple-choice answers from the PokéAPI data. The steps to get the need we'll need is as follows:



getPokeData = async function() {
  const pokemon = await getPokemon(); // #1
  const randomPokemon = shuffle(pokemon); // #2
  const pokemonChoices = get4Pokemon(randomPokemon); // #3
  const [ firstPokemon ] = pokemonChoices; // #4
  const image = getPokemonImage(firstPokemon); // # 5

  return { 
    pokemonChoices: shuffle(pokemonChoices),
    correct: {
      image,
      name: firstPokemon.name,
    }
  };
};


Enter fullscreen mode Exit fullscreen mode

1) Fetch the array of 151 Pokémon objects (show above).

2) Shuffle the array show we can show a random group to the user:



function shuffle(unshuffled) {
  const shuffled = unshuffled
    .map((value) => ({ value, sort: Math.random() }))
    .sort((a, b) => a.sort - b.sort)
    .map(({ value }) => value);

  return shuffled;
}


Enter fullscreen mode Exit fullscreen mode

3) Save the first 4 objects from this shuffled array:



function get4Pokemon(randomPokemon) {
  return randomPokemon.splice(0, 4);
}


Enter fullscreen mode Exit fullscreen mode

4) Save a reference to the first Pokémon object (the one we'll feature).

5) Return an object containing the 4 Pokémon choices (shuffled again), and a "correct" object, containing both the name of the featured pokémon and the image URL to show:



function getPokemonImage({ url }) {
  const number = getNumber(url);
  return `https://raw.githubusercontent.com/PokeAPI/` +
  `sprites/master/sprites/pokemon/${number}.png`;
};

function getNumber(url) {
  const numberRegEx = /(\d+)\/$/;
  return (url.match(numberRegEx) || [])[1];
}


Enter fullscreen mode Exit fullscreen mode

We'll then plug this URL source into our <img id="pokemon-image"> element and set the brightness of the PNG to 0 with CSS:



#pokemon-image {
  transform: scale(2.5);
  filter: brightness(0);
  transition: filter .5s ease-out;
}


Enter fullscreen mode Exit fullscreen mode

And generate our #choices content with answer buttons:



const { pokemonChoices } = gameData;
const choicesHTML = pokemonChoices.map(({ name }) => {
  return `<button data-name="${name}">${name}</button>`;
}).join('');

choices.innerHTML = choicesHTML;


Enter fullscreen mode Exit fullscreen mode

Once we hook up some additional event handlers and logic to check if the dataset.name selected matches the correct pokémon, we can toggle some classes to reveal both the right answers and show the full-color pokémon image!

Project code available here: https://github.com/doctafaustus/whos-that-pokemon

Full YouTube Tutorial:




Yo! I post byte-sized tips like these often. Follow me if you crave more! 🍿

I'm on TikTok, Twitter and I have a new debugging course dropping soon!

Top comments (2)

Collapse
 
dailydevtips1 profile image
Chris Bongers

Love it Bill!

Super cool project ❤️
The brightness is a smart way to blur out the pokemon! Interesting.

Collapse
 
js_bits_bill profile image
JS Bits Bill

Thanks Chris! Funny story about this... when I first started it I thought the sprites from the API were jpegs and had this whole extra piece of this to upload them to Cloudinary with a background removal transformation so I could get that silhouette then realized the sprites were PNGs all along 😐