Skip to main content

Once In A Blue Moon

Once in a Blue Moon

Discover Something New!

Loading...

November 2, 2026

Article of the Day

The Road to Cleanliness: Why and How to Keep Your Car Spotless

Introduction Your car is more than just a mode of transportation; it’s an extension of your lifestyle and personality. Just…
Moon Loading...
LED Style Ticker
Loading...
Pill Actions Row
Return Button
Back
Visit Once in a Blue Moon
📓 Read
Go Home Button
Home
Green Button
Contact
Help Button
Help
Refresh Button
Refresh

Introduction: Can You Actually Program an Infinite Video Game?

If I were designing an infinite video game, I would begin with one essential principle: the game should continually generate new opportunities for the player instead of eventually running out of content.

Traditional video games often have a final boss, a final level, or a predetermined ending. Infinite video games work differently. They can generate new environments, challenges, resources, and experiences for as long as someone chooses to play.

The interesting part is that you do not need an enormous development team or an expensive game engine to create an infinite game.

You can build simple versions using HTML, JavaScript, and the Canvas drawing system built into modern web browsers.

In this article, we will program three different examples:

  1. Endless Runner: A game where obstacles continually appear, the world scrolls, and the difficulty increases.
  2. Infinite Explorer: A game where the computer generates new terrain as the player explores an enormous world.
  3. Forever Garden: A game where plants grow, resources regenerate, and players can continue building their garden without a final level.

Each game has working source code, along with explanations of how its infinite systems work.

Complete playable game collection

3 HTML games

Download all three games (ZIP)Each game runs independently in a browser. No programming software or external libraries are required to play it.

Getting Started: What You Need

To create these games, you need a text editor such as Visual Studio Code, Notepad, or another plain-text editor, plus a modern web browser.

For every example, create a new file with the specified .html filename, copy the complete code into it, save the file, and double-click it to open it in your browser.

The code combines three technologies:

  • HTML creates the game’s webpage and canvas.
  • CSS controls its visual appearance.
  • JavaScript handles player movement, game rules, procedural generation, and animation.

Now we can begin programming.

Game 1: Coding an Endless Runner

Mr Jump S for iPhone – Download

The concept

Imagine a game where your character continuously runs forward. Obstacles appear in the distance, and your job is to jump over them.

The longer you survive, the faster the game becomes.

The computer never runs out of obstacles because it creates them automatically while the game is running.

Step 1: Create the file

Create a file named runner.html.

Paste the following complete code into it.


<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Endless Runner</title>
<style>
  body {
    font-family: system-ui, sans-serif;
    text-align: center;
    background: #101826;
    color: white;
  }
  canvas {
    max-width: 95%;
    background: #d9f0ff;
    border-radius: 12px;
  }
</style>
</head>
<body>
<h1>Endless Runner</h1>
<p>Space or Up Arrow: jump. R: restart after a collision.</p>
<canvas id="game" width="640" height="320"></canvas>

<script>
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");
const ground = 270;

let player, obstacles, spawnTimer, score, speed, gameOver;

function reset() {
  player = {
    x: 70,
    y: ground - 34,
    w: 30,
    h: 34,
    vy: 0
  };

  obstacles = [];
  spawnTimer = 1;
  score = 0;
  speed = 220;
  gameOver = false;
}
reset();

window.addEventListener("keydown", (e) => {
  if (["Space", "ArrowUp"].includes(e.code)) {
    e.preventDefault();

    if (!gameOver &&
        player.y >= ground - player.h - 0.1) {
      player.vy = -620;
    }
  }

  if (e.code === "KeyR" && gameOver) reset();
});

function overlap(a, b) {
  return a.x < b.x + b.w &&
         a.x + a.w > b.x &&
         a.y < b.y + b.h &&
         a.y + a.h > b.y;
}

let previousTime = performance.now();

function frame(now) {
  const dt = Math.min(
    (now - previousTime) / 1000,
    0.033
  );
  previousTime = now;

  if (!gameOver) {
    score += dt * 10;
    speed = Math.min(480, 220 + score * 0.55);

    // Apply gravity.
    player.vy += 1700 * dt;
    player.y += player.vy * dt;

    // Stop the player falling below the ground.
    if (player.y > ground - player.h) {
      player.y = ground - player.h;
      player.vy = 0;
    }

    // Generate new obstacles automatically.
    spawnTimer -= dt;

    if (spawnTimer <= 0) {
      const h = 25 + Math.random() * 28;

      obstacles.push({
        x: canvas.width,
        y: ground - h,
        w: 23,
        h: h
      });

      spawnTimer = 0.95 + Math.random() * 0.65;
    }

    // Move obstacles toward the player.
    for (const obstacle of obstacles) {
      obstacle.x -= speed * dt;

      if (overlap(player, obstacle)) {
        gameOver = true;
      }
    }

    // Delete obstacles that have left the screen.
    obstacles = obstacles.filter(
      o => o.x + o.w > 0
    );
  }

  // Draw the background and ground.
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  ctx.fillStyle = "#74c69d";
  ctx.fillRect(
    0, ground,
    canvas.width,
    canvas.height - ground
  );

  // Draw the player.
  ctx.fillStyle = "#2457a7";
  ctx.fillRect(
    player.x, player.y,
    player.w, player.h
  );

  // Draw obstacles.
  ctx.fillStyle = "#b84a42";

  for (const obstacle of obstacles) {
    ctx.fillRect(
      obstacle.x, obstacle.y,
      obstacle.w, obstacle.h
    );
  }

  // Draw the score.
  ctx.fillStyle = "#1c2a39";
  ctx.font = "22px sans-serif";
  ctx.fillText(
    "Distance: " + Math.floor(score),
    20, 35
  );

  if (gameOver) {
    ctx.font = "bold 30px sans-serif";
    ctx.fillText(
      "Collision! Press R to restart",
      105, 155
    );
  }

  requestAnimationFrame(frame);
}

requestAnimationFrame(frame);
</script>
</body>
</html>

Step 2: Understand how it becomes infinite

There are three essential mechanisms.

First, the game generates obstacles continuously.

This section creates new obstacles whenever the countdown reaches zero:

spawnTimer -= dt;

if (spawnTimer <= 0) {
  obstacles.push({
    x: canvas.width,
    y: ground - 40,
    w: 23,
    h: 40
  });

  spawnTimer = 1.2;
}

There is no final obstacle. The computer keeps making them.

Second, the game increases difficulty.

speed = Math.min(480, 220 + score * 0.55);

As the player’s score grows, obstacles move faster, up to the maximum speed of 480.

Third, the game removes objects that are no longer needed.

obstacles = obstacles.filter(
  o => o.x + o.w > 0
);

This is extremely important. An endless game cannot keep every obstacle in memory forever. Removing invisible obstacles prevents the list from growing indefinitely.

Step 3: Play the game

Open runner.html in your browser.

Press Space to jump over obstacles. Your score increases as you survive. After a collision, press R to begin a new run.

This is an endless challenge generator: individual runs can end, but the game has no final level.

Game 2: Coding an Infinite Exploration World

Sigil of Kings on Steam

The concept

Now imagine a game where a player can walk in any direction and continually discover new areas.

Rather than designing thousands of individual maps, we can teach the computer to generate terrain mathematically.

This is called procedural generation.

For example, each location can become grass, water, sand, or forest depending on a mathematical calculation.

The computer only draws the terrain near the player. When the player moves, it calculates the new visible area.

Step 1: Create explorer.html

Copy this complete code:


<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Infinite Explorer</title>
<style>
  body {
    font-family: system-ui, sans-serif;
    text-align: center;
    background: #101826;
    color: white;
  }
  canvas {
    max-width: 95%;
    border-radius: 12px;
  }
</style>
</head>
<body>
<h1>Infinite Explorer</h1>
<p>Arrow keys or WASD: move. Space: build or remove a camp.</p>

<canvas id="game" width="640" height="416"></canvas>
<p id="status"></p>

<script>
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");

const tile = 32;
let x = 0;
let y = 0;

const camps = new Set();

// Generate a repeatable value for a location.
function noise(x, y) {
  let n = (
    Math.imul(x, 374761393) +
    Math.imul(y, 668265263)
  ) | 0;

  n = Math.imul(
    n ^ (n >>> 13),
    1274126177
  );

  return (
    (n ^ (n >>> 16)) >>> 0
  ) / 4294967295;
}

function campKey(x, y) {
  return x + "," + y;
}

// Keyboard controls.
window.addEventListener("keydown", (e) => {
  const moves = {
    ArrowUp: [0, -1],
    KeyW: [0, -1],

    ArrowDown: [0, 1],
    KeyS: [0, 1],

    ArrowLeft: [-1, 0],
    KeyA: [-1, 0],

    ArrowRight: [1, 0],
    KeyD: [1, 0]
  };

  if (moves[e.code]) {
    e.preventDefault();

    x += moves[e.code][0];
    y += moves[e.code][1];

    draw();
  } else if (e.code === "Space" && !e.repeat) {
    e.preventDefault();

    const key = campKey(x, y);

    if (camps.has(key)) {
      camps.delete(key);
    } else {
      camps.add(key);
    }

    draw();
  }
});

function draw() {
  const left = Math.floor(
    canvas.width / (2 * tile)
  );

  const top = Math.floor(
    canvas.height / (2 * tile)
  );

  // Generate visible terrain.
  for (
    let row = 0;
    row <= canvas.height / tile;
    row++
  ) {
    for (
      let col = 0;
      col <= canvas.width / tile;
      col++
    ) {
      const wx = x + col - left;
      const wy = y + row - top;

      const value = noise(wx, wy);

      // Assign terrain types.
      ctx.fillStyle =
        value < 0.16 ? "#4d9ed6" :
        value < 0.27 ? "#e9d58c" :
        value < 0.79 ? "#88bd69" :
                       "#368052";

      ctx.fillRect(
        col * tile,
        row * tile,
        tile,
        tile
      );

      // Draw player-built camps.
      if (camps.has(campKey(wx, wy))) {
        ctx.fillStyle = "#704329";

        ctx.fillRect(
          col * tile + 8,
          row * tile + 7,
          16, 18
        );

        ctx.fillStyle = "#ffcf66";

        ctx.fillRect(
          col * tile + 12,
          row * tile + 11,
          8, 8
        );
      }
    }
  }

  // Draw the player.
  ctx.fillStyle = "#a52f55";
  ctx.beginPath();

  ctx.arc(
    left * tile + tile / 2,
    top * tile + tile / 2,
    11,
    0,
    Math.PI * 2
  );

  ctx.fill();

  document.getElementById("status").textContent =
    `Position: (${x}, ${y}) | Camps built: ${camps.size}`;
}

draw();
</script>
</body>
</html>

Step 2: Understand procedural generation

The most important idea in this game is that the world does not need to be stored in a huge map.

Instead, the computer calculates what should exist at any coordinate.

For example:

const value = noise(100, 250);

This calculates a terrain value for location (100, 250).

Because the same coordinates always produce the same result, a player can leave an area and return to find the same landscape.

The terrain generator assigns colors based on that value.

Terrain generation rules

Water0–0.16

Sand0.16–0.27

Grass0.27–0.79

Forest0.79–1

This simple generator produces a scattered landscape. A more sophisticated game could use smooth noise to create larger forests, rivers, mountains, and realistic geographic regions.

Step 3: Allow players to change the world

The code also introduces this JavaScript structure:

const camps = new Set();

When the player presses Space, a camp is placed at the current location.

Unlike the terrain, player-built structures must be recorded, because they are changes to the generated world.

This creates a powerful distinction:

The computer generates the natural world, while the player determines its history.

You could expand this system so players can construct houses, roads, settlements, farms, and entire civilizations.

The terrain generator could keep producing new areas as the civilization expands.

The current game is practically unbounded for ordinary play, although computer number limits mean it is not mathematically infinite.

Game 3: Coding a Garden That Can Grow Forever

Roots of Pacha – Switch Review

The concept

An infinite game does not always require an infinite map.

It can also use renewable resources and repeating cycles to provide meaningful play without a final objective.

Let’s make a simple farming game where players plant seeds, wait for them to mature, harvest plants, and receive additional seeds.

The essential rule is that harvesting returns more seeds than planting costs.

This allows the player to repeat the gardening cycle indefinitely.

Step 1: Create garden.html

Paste in this code:


<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Forever Garden</title>
<style>
  body {
    font-family: system-ui, sans-serif;
    text-align: center;
    background: #101826;
    color: white;
  }
  canvas {
    max-width: 95%;
    border-radius: 12px;
    cursor: pointer;
    touch-action: manipulation;
  }
</style>
</head>
<body>
<h1>Forever Garden</h1>
<p>Click an empty square to plant. Click a grown plant to harvest two seeds.</p>

<canvas id="game" width="600" height="400"></canvas>
<p id="status"></p>

<script>
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");

const columns = 12;
const rows = 8;
const tile = 50;

const plants = Array(columns * rows).fill(null);

let seeds = 8;
let harvested = 0;

const growTime = 5000;

// Plant or harvest when a square is clicked.
canvas.addEventListener("pointerdown", (event) => {
  const bounds = canvas.getBoundingClientRect();

  const col = Math.floor(
    (event.clientX - bounds.left) *
    canvas.width / bounds.width / tile
  );

  const row = Math.floor(
    (event.clientY - bounds.top) *
    canvas.height / bounds.height / tile
  );

  if (
    col < 0 || col >= columns ||
    row < 0 || row >= rows
  ) return;

  const index = row * columns + col;

  if (plants[index] === null && seeds > 0) {
    // Plant one seed.
    plants[index] = performance.now();
    seeds--;

  } else if (
    plants[index] !== null &&
    performance.now() - plants[index] >= growTime
  ) {
    // Harvest a mature plant.
    plants[index] = null;

    // Harvesting gives two seeds.
    seeds += 2;
    harvested++;
  }
});

function frame(now) {
  ctx.clearRect(
    0, 0,
    canvas.width,
    canvas.height
  );

  for (let row = 0; row < rows; row++) {
    for (let col = 0; col < columns; col++) {
      const index = row * columns + col;

      const px = col * tile;
      const py = row * tile;

      // Draw garden squares.
      ctx.fillStyle =
        (row + col) % 2 ?
        "#86b86c" : "#93c77b";

      ctx.fillRect(px, py, tile, tile);

      ctx.strokeStyle = "#608852";
      ctx.strokeRect(px, py, tile, tile);

      if (plants[index] !== null) {
        // Calculate growth progress.
        const growth = Math.min(
          1,
          (now - plants[index]) / growTime
        );

        ctx.fillStyle =
          growth < 1 ?
          "#326b35" : "#e4b845";

        const size = 10 + growth * 24;

        ctx.beginPath();

        ctx.arc(
          px + tile / 2,
          py + tile / 2,
          size / 2,
          0,
          Math.PI * 2
        );

        ctx.fill();
      }
    }
  }

  document.getElementById("status").textContent =
    `Seeds: ${seeds} | Plants harvested: ${harvested} | No final level`;

  requestAnimationFrame(frame);
}

requestAnimationFrame(frame);
</script>
</body>
</html>

Step 2: Understand the resource system

The important code is:

if (plants[index] === null && seeds > 0) {
  plants[index] = performance.now();
  seeds--;
}

Planting costs one seed.

Then:

if (performance.now() - plants[index] >= growTime) {
  plants[index] = null;
  seeds += 2;
  harvested++;
}

Harvesting a mature plant produces two seeds.

The player has gained one additional seed after completing the cycle.

This is a renewable resource system.

Step 3: Understand the growth timer

Every plant records the time it was planted:

plants[index] = performance.now();

The computer can calculate how long the plant has been growing.

const growth = Math.min(
  1,
  (now - plants[index]) / growTime
);

When growth reaches 1, the plant is fully mature and can be harvested.

There is no final harvest or final level. The player can keep planting and harvesting as long as desired.

In a more advanced version, you could add different crops, weather, irrigation, animals, renewable energy, marketplaces, and village development.

The individual plots would be limited, but the economic and creative activities could continue indefinitely.

How the Three Games Work Differently

These three examples demonstrate three distinct methods of creating endless gameplay.

GameInfinite mechanismWhat the player does
Endless RunnerContinuously generated obstaclesSurvives increasingly difficult challenges
Infinite ExplorerProcedural terrain generationExplores and modifies new locations
Forever GardenRenewable resources and growth cyclesPlants, harvests, and manages resources

The more interesting possibility is to combine these systems.

For instance, imagine a world that generates new regions as players explore, introduces new dangers based on how far they travel, and allows players to establish renewable farms and permanent communities.

That would begin to resemble an infinite civilization game rather than a single repeating activity.

Designing a More Advanced Infinite Game

Once you understand the three examples, you can develop a larger architecture.

An architecture for a larger infinite game

Player actionsMove, explore, build, trade, discover

World generatorCreates new terrain and challenges

Resource simulationRegrows crops and updates systems

Persistent world stateSaves settlements, discoveries, and player-created changes

The cycle continues with new player actions

Additional programming principles

1. Only generate what the player needs. There is no reason to calculate an entire planet when the player can see only a small region. Generate nearby terrain and discard unnecessary temporary objects.

2. Separate generated content from saved changes. Terrain can be recreated using mathematical rules, but player buildings, discoveries, and inventory changes need to be saved. For a browser game, you might initially use localStorage, then move to IndexedDB or a database for larger worlds.

3. Design systems that create new situations. Instead of writing 10,000 separate missions, program systems that combine locations, resources, characters, and objectives in different ways.

4. Avoid uncontrolled memory growth. Infinite gameplay should not mean infinitely accumulating objects in memory. Use object cleanup, regional loading, coordinate-based generation, and efficient data storage.

5. Make the game evolve. Add changing seasons, advancing technology, new resources, player-created structures, and systems that react to previous decisions.

Conclusion: Programming Infinity Is Really About Programming Possibility

An infinite video game does not need an infinite amount of code, nor does it need an infinite amount of content stored in advance.

What it needs is a set of rules that can continue producing new experiences.

The endless runner demonstrates how repeated generation can create continuous challenges. The exploration game demonstrates how mathematical functions can generate a vast world from a relatively small amount of code. The gardening game demonstrates how renewable resources can sustain play without a final ending.

The greatest opportunity comes from combining these ideas.

Imagine a video game in which players can explore new territories, establish settlements, invent technologies, restore ecosystems, develop economies, and create institutions that influence future generations.

Such a game would not merely become longer as people played it. It could become more complex and interesting as a consequence of their decisions.

The ultimate goal of programming an infinite game is not to create an impossible amount of content. It is to create a small collection of powerful systems capable of generating an extraordinary number of meaningful possibilities.

And that is where an infinite game truly begins.

Leave a Reply

Your email address will not be published. Required fields are marked *

🟢 🔴
error: Oops.exe