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:
- Endless Runner: A game where obstacles continually appear, the world scrolls, and the difficulty increases.
- Infinite Explorer: A game where the computer generates new terrain as the player explores an enormous world.
- 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
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.
| Game | Infinite mechanism | What the player does |
|---|---|---|
| Endless Runner | Continuously generated obstacles | Survives increasingly difficult challenges |
| Infinite Explorer | Procedural terrain generation | Explores and modifies new locations |
| Forever Garden | Renewable resources and growth cycles | Plants, 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.