Make a Website

How to Make a Game Website Like Poki (HTML5 Games and AdSense)

Build a free online games website: where the games come from, who earns from the ads inside them, what AdSense checks on a game site, the pages that make it rank, and the setup step by step.

A browser showing a grid of HTML5 games on a game website, with a game controller beside it

Sites like Poki, CrazyGames and Y8 look simple: a grid of games, you click one, it plays in the browser. Behind that grid are three decisions most new game sites get wrong: where the games come from, who earns from the ads inside them, and what is on each page apart from the game. Get those right and a game site can rank and earn. Get them wrong and AdSense refuses it as “low value content”.

This guide covers all three, then the setup itself: the hosting you need, the pages that bring search traffic, the speed rules, and the problems that come up.

How a game website makes money

SourceHow it works
Display ads on the pageNormal AdSense units around the game and in the lists, never on top of the game
Ads inside the gameFull-screen ads between levels and rewarded ads (watch an ad for an extra life). Google's version is H5 Games Ads.
A revenue share from a game networkWhen you embed a game hosted by a distribution network, it shows its own ads in the game and shares the income under its publisher terms
An Android appThe same games in an app with AdMob

One rule from Google's own documentation decides most of this: whoever hosts the game controls the ads inside it. If the game file is on your server, you choose the ads and the income is yours. If you embed a game that a network hosts, the network's ads play inside it and you get the share its terms give you.

Try it: a one-page game website you can run now

Before the business side, here is a complete game page with a real game in it. Copy the code into a file called index.html, double-click it, and it plays in your browser. Upload the same file to your hosting and it is a live game website.

The game is Star Catcher: stars fall, you move a basket to catch them, and three misses end the game. It works with the arrow keys on a computer and with your finger on a phone, keeps a best score, and gets faster as you score.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Star Catcher – Play Free Online | My Games</title>
<meta name="description" content="Play Star Catcher free in your browser. Catch the falling stars and do not miss three. Works on phone and computer, no download.">
<style>
  body { margin: 0; font-family: system-ui, sans-serif; background: #0f172a; color: #e2e8f0; }
  header { padding: 14px 20px; background: #1e293b; font-size: 20px; font-weight: 800; }
  main { max-width: 760px; margin: 0 auto; padding: 20px; }
  h1 { margin: 0 0 6px; }
  .hud { display: flex; justify-content: space-between; margin: 14px 0 8px; font-weight: 700; }
  .stage { position: relative; border-radius: 14px; overflow: hidden; background: #020617; }
  canvas { display: block; width: 100%; height: auto; touch-action: none; }
  .overlay { position: absolute; inset: 0; display: grid; place-items: center; text-align: center;
             background: rgba(2, 6, 23, .75); }
  button { font: inherit; font-weight: 800; padding: 12px 30px; border: 0; border-radius: 999px;
           background: #f59e0b; color: #111827; cursor: pointer; }
</style>
</head>
<body>
<header>My Games</header>
<main>
  <h1>Star Catcher</h1>
  <p>Catch the falling stars with your basket. Miss three and the game is over.</p>

  <div class="hud">
    <span id="score">Score: 0</span><span id="best">Best: 0</span><span id="lives">Lives: 3</span>
  </div>
  <div class="stage">
    <canvas id="game" width="640" height="400"></canvas>
    <div class="overlay" id="overlay">
      <div><p id="msg">Ready?</p><button id="play">Play</button></div>
    </div>
  </div>

  <h2>How to play</h2>
  <p>On a computer, move the basket with the left and right arrow keys. On a phone, drag your
     finger across the game. Every star is one point, and the stars fall faster as your score grows.</p>
</main>

<script>
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const W = canvas.width, H = canvas.height;
const keys = {};
let basket, stars, score, lives, running = false, lastSpawn = 0, best = 0;

try { best = Number(localStorage.getItem('starBest')) || 0; } catch (e) {}
document.getElementById('best').textContent = 'Best: ' + best;

function reset() {
  basket = { x: W / 2 - 45, y: H - 30, w: 90, h: 16 };
  stars = []; score = 0; lives = 3; lastSpawn = 0;
  showScore();
}

function showScore() {
  document.getElementById('score').textContent = 'Score: ' + score;
  document.getElementById('lives').textContent = 'Lives: ' + lives;
}

function update(time) {
  if (keys.ArrowLeft)  basket.x -= 7;
  if (keys.ArrowRight) basket.x += 7;
  basket.x = Math.max(0, Math.min(W - basket.w, basket.x));

  // a new star every 0.9 s at first, more often as the score grows
  if (time - lastSpawn > Math.max(350, 900 - score * 15)) {
    stars.push({ x: 15 + Math.random() * (W - 30), y: -10, r: 10 });
    lastSpawn = time;
  }

  const speed = 2 + score * 0.08;
  for (const s of stars) {
    s.y += speed;
    const inBasket = s.y + s.r >= basket.y && s.y - s.r <= basket.y + basket.h &&
                     s.x >= basket.x && s.x <= basket.x + basket.w;
    if (inBasket)        { s.done = true; score++; showScore(); }
    else if (s.y > H + s.r) { s.done = true; lives--; showScore(); }
  }
  stars = stars.filter(s => !s.done);
  if (lives <= 0) gameOver();
}

function draw() {
  ctx.fillStyle = '#020617';
  ctx.fillRect(0, 0, W, H);
  ctx.fillStyle = '#fbbf24';
  for (const s of stars) {
    ctx.beginPath(); ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2); ctx.fill();
  }
  ctx.fillStyle = '#38bdf8';
  ctx.fillRect(basket.x, basket.y, basket.w, basket.h);
}

function loop(time) {
  if (!running) return;
  update(time);
  draw();
  requestAnimationFrame(loop);
}

function start() {
  reset();
  running = true;
  document.getElementById('overlay').style.display = 'none';
  requestAnimationFrame(loop);
}

function gameOver() {
  running = false;
  if (score > best) {
    best = score;
    try { localStorage.setItem('starBest', best); } catch (e) {}
  }
  document.getElementById('best').textContent = 'Best: ' + best;
  document.getElementById('msg').textContent = 'Game over! Your score: ' + score;
  document.getElementById('play').textContent = 'Play again';
  document.getElementById('overlay').style.display = 'grid';
}

document.getElementById('play').onclick = start;
addEventListener('keydown', e => {
  keys[e.key] = true;
  if (running && e.key.startsWith('Arrow')) e.preventDefault();   // no page scrolling while playing
});
addEventListener('keyup', e => { keys[e.key] = false; });
canvas.addEventListener('pointermove', e => {                     // mouse and finger
  const box = canvas.getBoundingClientRect();
  basket.x = (e.clientX - box.left) * (W / box.width) - basket.w / 2;
});

reset();
draw();
</script>
</body>
</html>
PartWhat it does
<title> and descriptionThe game's name and a one-line summary: what Google shows in search results
<canvas>The game is drawn here, 60 times a second, by requestAnimationFrame
The Play buttonNothing runs until the player presses it, so the page opens fast
update()Moves the basket and the stars, counts catches and misses
draw()Paints the background, the stars and the basket
pointermove and keydownFinger or mouse, and the arrow keys
localStorageRemembers the best score on that phone or computer
“How to play”Real text on the page: it helps the player, and it is what Google and AdSense need to see

A game website is many pages like this one: one per game, a home page that lists them, and category pages. The rest of this guide is about filling those pages with games and making them earn.

Where the games come from

OptionAds inside the gameGood for
Your own gamesYoursThe most control and the most income per player
Games you buy or licenseYours, if the licence allows hosting and adsGrowing the catalogue fast with games that are only on your site
Games embedded from a distribution networkThe network's, with a revenue shareHundreds of games on day one, without hosting them

Most sites mix them: a large catalogue from a network, plus a few hosted games of their own on the front page. Whatever you use, only publish games you have the right to publish. Copied games are the fastest way to lose an AdSense account, because copyright is the first policy Google lists for game sites.

What AdSense checks on a game site

Google says any publisher with an HTML5 games site can apply, as long as the site follows the AdSense program policies. In practice game sites are refused for the same few reasons:

  • Thin pages. A page with only a game and a title is “low value content”. Every game page needs its own text: what the game is, how to play, the controls, tips.
  • The same text as every other site. Network games come with a description, and hundreds of sites paste the same one. Write your own.
  • More ads than content, or ads placed over or right next to the game where players click them by accident.
  • Missing basics: About, Contact and a Privacy Policy page, and a site that works on phones.

Apply with 20 to 30 games that each have a real description, not 500 with none. In-game ads through H5 Games Ads are a separate application that you make after your site is approved.

The pages that bring search traffic

People search for a game by its name (“neon racer online”), by type (“car games”, “2 player games”) and by situation (“games to play at school”). Your site needs a page for each:

  • A page per game with the name in the title, a short intro, a “how to play” section and the controls. This is the page that ranks for the game's name.
  • A page per category with a real introduction at the top, not only a grid. Category pages rank for the type searches.
  • New, popular and featured lists on the home page, so search engines find new games quickly.
  • A sitemap with every game and category, sent to Google Search Console.

Keep it fast: load the game only on Play

A game is often several megabytes. If it starts loading with the page, the page is slow, Google scores it badly, and the text that should rank is hidden behind a loading bar. Show the text first and load the game when the player presses Play:

<!-- the game page: text on the page, the game itself loads on Play -->
<h1>Neon Racer</h1>
<p>Race through a neon city and dodge the traffic. Free, no download.</p>

<button id="play">Play</button>
<div id="stage"></div>

<h2>How to play</h2>
<p>Arrow keys or swipe to change lanes. Collect boosts, avoid the cars.</p>

<script>
document.getElementById('play').onclick = function () {
  var f = document.createElement('iframe');
  f.src = '/games/neon-racer/index.html';
  f.allow = 'fullscreen; autoplay';
  f.style.cssText = 'width:100%;aspect-ratio:16/9;border:0';
  document.getElementById('stage').appendChild(f);
  this.remove();
};
</script>

The same page is also lighter for players who only came to read the controls, and it keeps the ad units on the page from competing with the game for bandwidth.

Ads inside your own games

For games you host yourself, H5 Games Ads use two functions from Google's Ad Placement API: adConfig() once when the game starts, and adBreak() at natural pauses, such as between levels, or for a rewarded ad. While you build, the data-adbreak-test="on" setting shows test ads:

<script async
  data-adbreak-test="on"
  src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-0000000000000000"
  crossorigin="anonymous"></script>
<script>
  window.adsbygoogle = window.adsbygoogle || [];
  var adBreak = adConfig = function (o) { adsbygoogle.push(o); };
  adConfig({ preloadAdBreaks: 'on' });

  // between two levels: pause the game and its sound while the ad shows
  adBreak({
    type: 'next',
    name: 'level-complete',
    beforeAd: function () { game.pause(); },
    afterAd: function () { game.resume(); }
  });
</script>

Remove the test setting only after Google has approved your site for H5 Games Ads. Never call an ad in the middle of play or on a timer: that breaks the placement policy.

Step by step

  1. Pick a niche or a clear identity, for example puzzle games, or games that run well on school computers. A focused site ranks faster than one more general portal.
  2. Get a domain and hosting. Games are static files, so normal shared hosting with PHP and MySQL is enough to start. Turn on HTTPS: browsers block many game features on plain HTTP.
  3. Install the site: WordPress with a games theme, your own code, or a ready game portal script.
  4. Add 20 to 30 games, each with your own description, how to play, controls and a clear thumbnail.
  5. Write the category introductions and the About, Contact and Privacy Policy pages.
  6. Submit the sitemap in Google Search Console.
  7. Apply for AdSense, then for H5 Games Ads once the site is approved.
  8. Add a few games a week and update the home page lists. Fresh games bring search visits back.

Common problems

ProblemUsual cause
The game shows a blank boxThe game is on http:// while your site is on HTTPS, or the game's host does not allow other sites to frame it
AdSense says “low value content”Game pages with no text of their own, or the same description every other site uses
Pages are slow on phonesThe game loads with the page. Load it on Play instead.
Games do not work on phonesKeyboard-only games. Mark them as desktop games, or choose games with touch controls.
Search Console shows duplicate pagesThe same game under several addresses, for example once per category. Give each game one address.

What it costs

ItemCost
DomainAbout $10 a year
HostingFrom about $3 a month; more bandwidth as players grow
GamesFree from networks, or the price of the licence for games only you host
Your timeMostly writing: a few hundred words for each game and category

A ready game website

Our HTML5 game portal PHP script is built around everything above: a page per game with a description, how to play and an FAQ; category pages with your own introduction; Hot, New and Most played lists; games that load only when Play is pressed; a sitemap, structured data and an llms.txt for AI search; and an admin panel where you add games, upload thumbnails (saved as WebP) and see scores. Four sample games are included.

The same backend also runs our mini games Android app, so one admin panel serves both your website and your app.

Where to go next

Questions people ask

How do I make a simple game website page?
Make one HTML file with the game's name in the title, a short description, a canvas for the game, a Play button and a how-to-play section, plus a small JavaScript game loop that updates and draws the game with requestAnimationFrame. The Star Catcher example in this guide is a complete page you can copy and upload.
How do game websites like Poki make money?
Mostly from ads: display ads on the page around the game and full-screen or rewarded ads inside the games, shown between levels. Large sites also earn from their own exclusive games and from apps.
Where can I get games for my website?
Make your own, buy or license games that you host yourself, or embed games from an HTML5 game distribution network. Only publish games you have the right to publish, and read each network's publisher terms.
Can I get AdSense approval for a game website?
Yes, if the site follows the AdSense policies. The usual reasons game sites are refused are thin pages with only a game on them, copied descriptions, copyrighted games and too many ads. Write your own text for every game before you apply.
Who earns from the ads inside an embedded game?
Whoever hosts the game. If a distribution network hosts it, the network's ads play inside the game and you earn the share its terms give you. If the game is on your server, you control the ads and keep the income.
What are H5 Games Ads?
Google's ads for HTML5 games: interstitial ads at natural breaks and rewarded ads that players choose to watch for a bonus. They use the Ad Placement API functions adConfig() and adBreak(), and you apply for them separately after AdSense approval.
How many games do I need before I apply for AdSense?
Quality matters more than the number. Around 20 to 30 games that each have their own description, how-to-play text and controls is a sensible start, together with About, Contact and Privacy Policy pages.
Do I need a VPS for a game website?
No. HTML5 games are static files, so normal shared hosting with PHP and MySQL is enough to start. Move to more bandwidth or a VPS when the number of players grows.
Why does a game show a blank box on my site?
Usually the game is loaded over http:// on an https:// page, which browsers block, or the site hosting the game does not allow other sites to show it in a frame. Host it yourself or use the embed address the provider gives.
How do I make my game pages rank in Google?
Give each game one address with its name in the title, write a short intro, how to play and the controls, add category pages with real introductions, keep the pages fast by loading the game on Play, and submit a sitemap in Search Console.
Can I turn my game website into an Android app?
Yes. The same games can run in an Android app that reads its game list from your website, with AdMob for ads. Our game portal script works as the backend of our mini games Android app.

Written by Habib Baloch

I build Android apps, websites and Ubuntu servers, and write down exactly how I did it.

Ask me about this guide →