For Teachers & Parents

The Learning Guide

The games are fun on their own. This guide shows the grown-ups how to turn a swipe-to-shoot penalty or a runaway gorilla into a physics lesson, a biology log, a vocabulary game, a math puzzle, or a first look at real code.

Start here

CEMI Fútbol is built on one idea: learning is seasoning, never the meal. A "Nerd Engine" sprinkles real facts as flavor after a shot, a save, or an elimination — it never blocks the game. Your job as the educator is simply to notice those moments out loud.

Why it works in class or at home

The sixteen games: Golden Boot (swipe-physics penalties: tournament, 8-venue story tour, 2P), Gorilla on the Pitch (survive-and-score vs a gorilla family), Keepy-Uppy Infinito (juggle a ball forever — it has opinions), Anaconda Soccer (feed goals to shrink an ever-growing snake), Rumble Fútbol (1v1/2v2 party soccer with power-ups), Elemental League (water, earth, air, fire — the arena plays too), Cruce de Tráfico (cross eight lanes of traffic with your ball), Muerto FC (the last striker vs a horde that just wants a hug), Dodgeball FC (dodge their throws, score past their bodies), Gods vs Titans FC (pick your god, face a stone titan), FIREBALL FC (wizard duel: only fireballs may move the ball), Béisbol Gol (a robot pitches, you kick, the outfield is a goal), Dolphins vs Sharks (your pod plays football; the sharks eat loose balls), Dungeon Striker (descend a soccer dungeon — floors are matches, loot is boots), Endless Striker (three-lane dribble runner) and Endless Striker 3D (the same run, rebuilt in full 3D). Every activity below tells you which game it uses.

Install it like an app (recommended for classrooms): open the games hub once while online, then use your browser's Install / Add to Home Screen option (Chrome: menu → "Install app"; iPhone/iPad Safari: Share → "Add to Home Screen"). The site carries its own offline cache, so after that first visit every game you have opened keeps working with no connection at all — full screen, no browser bars. Teacher tip: before a class with unreliable Wi-Fi, open each game you plan to use once; from then on the lesson cannot be interrupted by the network. Only the global leaderboards need a connection.

Before, during & after play

You do not need to be a gamer to run these. Three light-touch habits turn play into learning.

Before

During

After

Privacy & safety: two ways to play

CEMI Fútbol is designed for classrooms, so the safest option is also the default one. The account panel offers two clearly separated ways to play — and nobody is ever required to choose either one just to play.

Local mode — the default, made for kids

For the classroom: local mode means no signups, no accounts to create or manage, and no parental-consent paperwork — a whole class can play the moment the page loads.

Cloud mode — optional, for players who want to compete

Nickname safety

Every nickname a player types is checked automatically against offensive, discriminatory, sexual and abusive words in all four of the game's languages. The check is not naive: it also catches the usual disguises — dots or underscores between letters, extra spacing, repeated letters, and swapping numbers for letters ("leetspeak"). A rejected name simply asks the player to pick another one. And the leaderboards double-check every name before showing it, with the server correcting anything that slips through — so an inappropriate name never appears on a classroom screen. If one is ever detected, it is quietly replaced by a harmless football nickname such as "Golazo#417".

Physics · Golden Boot

Physics of the perfect shot

Golden Boot solves real ballistics for every shot. Swipe speed becomes launch speed, the swipe's end point becomes the aim on the goal plane, and a curved swipe adds spin. After the ball crosses the line, the Nerd Engine reports the actual numbers — km/h, joules, milliseconds, degrees — from games/golden-boot/shootout.js.

1 The Magnus effect: bend it like the code does

Objective
See that a spinning ball curves, and connect it to the Magnus force named in the game's facts.
Instructions
Play a Quick Match. First take five shots with a perfectly straight up-swipe. Then take five shots that hook sideways as you flick (a curved swipe). Watch the ball's flight path each time. Turn on "Loco Ball" and look for the Banana Spin effect, which multiplies the curve.
Expected result
Straight swipes fly straight; curved swipes bend in the air and can wrap around the keeper. Students should notice that curve is a separate control from power and aim. The Nerd Engine will surface the Roberto Carlos 1997 free kick — ~10 revolutions per second, curving almost 4 metres.
Extension / Teacher's note
Extension: Try the classroom demo — drop two sheets of paper, one flat and one lightly spun, and watch them fall differently. Note: in the code, spin is applied in the line B.vx += (B.spin * B.vz * 0.5 + wind * 0.8) * dt — a sideways push that grows with forward speed, exactly like the real Magnus force.

2 Launch angle: over the bar vs. into the net

Objective
Discover the trade-off between shot height (angle) and staying on target.
Instructions
Deliberately blast ten shots high over the crossbar, then ten low daisy-cutters along the ground. Read the feedback line each time. On a "wide" or "over" miss, the game reports the miss angle in degrees; on a goal it reports the flight time in milliseconds.
Expected result
Students see that a steeper launch buys height but overshoots, while a flat shot is fast but easy to save low. The sweet spot is a modest angle into a corner. They can record the reported degrees for high vs. low misses and compare.
Extension / Teacher's note
Extension: Introduce the idea that on a real (drag-free) pitch, maximum range comes from a 45° launch — then ask why a penalty is never taken at 45°. Note: the crossbar sits at exactly 2.44 m (a rule unchanged since 1866); the game measures your miss against it.

3 Energy in joules: how hard did you hit it?

Objective
Read kinetic energy off a real formula and connect faster shots to more energy.
Instructions
Score five goals and write down the km/h and joule values the game reports. A regulation ball is 0.43 kg. The game computes energy as 0.5 × 0.43 × v² (that is ½mv²). Have students confirm one value with a calculator: convert km/h to m/s (divide by 3.6), then plug in.
Expected result
A ~100 km/h shot (about 27.8 m/s) carries roughly 166 joules. Students should see energy grow with the square of speed — doubling the speed quadruples the energy. Their hand calculation should match the game's number.
Extension / Teacher's note
Extension: The Nerd Engine notes a pro penalty gives the keeper about 400 ms while a blink takes 300 ms. Ask: why does more energy make a save harder? Note: the exact line is j: Math.round(0.5 * 0.43 * v0 * v0) in games/golden-boot/shootout.js — students can read the physics straight from the source.
Biology · Gorilla on the Pitch

Gorilla biology & behavior

The gorilla in this game is not a cartoon. It knuckle-walks with real diagonal limb pairs, beats its chest as an honest warning before it charges, hunts the most isolated player like a real predator, and runs on stamina it must recover. Its behavior is a living observation subject.

4 Behavior observation log

Objective
Practice scientific observation by logging an animal's behavior states.
Instructions
One student plays; the rest are field biologists. Watch the gorilla and tally each behavior you see: patrolling (wandering), stalking (following a player), chest-beating (rearing up), charging (the fast rush), carrying a player off, and resting (tired). Note what happens just before each charge.
Expected result
Observers discover a repeating sequence: patrol → stalk → chest-beat → charge → tired. The chest-beat always precedes the charge — it is a warning, not the attack. Students learn that behavior can be broken into observable, predictable states.
Extension / Teacher's note
Extension: Compare with an ethogram — the real charts biologists use to catalog animal behaviors. Note: those states are literally the gorilla's finite state machine in games/gorilla/gorilla.js (OFF → ENTER → PATROL → STALK → CHESTBEAT → CHARGE → CARRY → TIRED).

5 Honest signals: why bluff before you charge?

Objective
Understand honest signaling — costly displays that communicate real intent.
Instructions
Focus only on the chest-beating. Count how many chest-beats happen before a charge (the game plays a burst of them). Discuss: in the wild, why would a 200 kg animal announce its attack instead of ambushing?
Expected result
Students realize the display gives everyone a chance to flee, avoiding a costly fight that could injure the gorilla too. Most real gorilla charges are bluffs — chest-beating and a short rush that stops early. The signal is "honest" because only a genuinely strong animal can afford to make it.
Extension / Teacher's note
Extension: Find other honest signals in nature (a stotting gazelle, a peacock's tail, a rattlesnake's rattle). Note: in later waves the game shortens the warning window — read setWave in games/gorilla/gorilla.js to see the telegraph time shrink as anger rises.

6 Speed & strength comparisons

Objective
Put gorilla facts into human-scale comparisons using the game's own data.
Instructions
Collect the biology facts the Nerd Engine shows between waves and turn each into a comparison. Examples to look for: silverback mass (~200 kg), arm span (~2.6 m — wider than the 2.44 m goal is tall), charge speed (up to ~40 km/h), and that gorillas share ~98% of human DNA — all while being herbivores.
Expected result
Students build a comparison chart: a silverback outweighs 2–3 adults, out-sprints Usain Bolt over a short dash, and reaches across a full goal — yet gets all that power from eating plants. The 98% DNA fact reframes "monster" into "close relative."
Extension / Teacher's note
Extension: Graph gorilla charge speed against a walking human, a sprinter, and a car in a school zone. Note: the charge speed lives in the SPD table in games/gorilla/gorilla.js as pixels per second (~40 px per metre) — students can convert it themselves.
Languages · Both games

Four languages, one game

Everything in both games is translated into English, Español, Français and Português, switchable any time. Because the same action produces the same word in every language, the game becomes a natural vocabulary machine.

7 GOAL in four tongues

Objective
Learn that the same concept has different words across languages, and notice patterns.
Instructions
Score a goal in each of the four languages, switching the language between shots. Write down the word that flashes: GOAL (en), GOL (es/pt), BUT (fr) — and note the game's fact that in German it is TOR. Say each one out loud.
Expected result
Students build a four-column word: GOAL / GOL / BUT / GOL. They notice Spanish and Portuguese share GOL, while French and English each go their own way. The Nerd Engine adds that Brazilian announcers hold the "O" in GOOOL for over 20 seconds.
Extension / Teacher's note
Extension: Have each student "commentate" a goal in a language that is not their first. Note: these words come from the shared fact pool in cemi/facts.js — the same data structure teaches vocabulary and stores the game's trivia.

8 Football idioms around the world

Objective
Explore how each football culture invents its own colorful vocabulary.
Instructions
Trigger the language facts by taking shots that miss (the Nerd Engine surfaces language trivia on misses). Look for the "nutmeg" fact: a ball played through the legs is caño (pipe) in Argentina, petit pont (little bridge) in France, and caneta (pen) in Brazil.
Expected result
Students collect a small dictionary of idioms and discover that each language pictures the same move differently. Ask them to invent an English nickname for the same trick and defend it.
Extension / Teacher's note
Extension: Research one more football-mad language (Italian, German, Arabic) and add its word for nutmeg. Note: idioms are stored with a language tag in cemi/facts.js; the game picks them by context so they feel fresh each session.

9 Play in your second language

Objective
Use immersion: navigate menus and instructions in a language you are learning.
Instructions
Set the game to a language the student is studying and play a full round without switching back. They must read the menus (PLAY / QUICK PLAY), the prompts ("SWIPE TO SHOOT"), and the fact lines in the target language.
Expected result
Students realize they can understand a lot from context even when they don't know every word — a core language-learning skill. Afterwards they list five new words they picked up.
Extension / Teacher's note
Extension: Pair a native speaker with a learner for a two-player match narrated in the target language. Note: every string lives in a four-language table (see cemi/i18n.js), so the whole interface really is fully translated, not machine-swapped on the fly.
Math & Geography · Golden Boot

Math & Geography

The 32-nation tournament is a powers-of-two machine, the flags open a world map, and the leaderboards are a statistics dataset waiting to happen.

10 Brackets are powers of two

Objective
Understand knockout brackets as repeated halving — powers of two.
Instructions
Start a World Tournament (32 nations). Before playing, have students predict how many rounds it takes to win. Then play and count: Round of 32 → Round of 16 → Quarter-finals → Semi-finals → Final.
Expected result
Students find it takes exactly 5 rounds, because 32 = 2⁵ and each round halves the field (32 → 16 → 8 → 4 → 2 → 1 champion). They can also compute total matches played across the whole tournament (31).
Extension / Teacher's note
Extension: Ask how many rounds a 64- or 128-team cup would need, then generalize to log₂(N). Note: the round names are the ordered list ROUND_KEYS in cemi/tournament.js, and the bracket starts with 32 teams in alive.

11 32 flags, 32 countries

Objective
Connect flags to countries, capitals, and continents.
Instructions
Open the nation picker and browse the 32 flags. Each flag in the game is drawn from simple shapes, not an image. For each nation a student is dealt, have them name the country, its capital, and its continent. The game also shows how many World Cups each nation has won.
Expected result
Students build a table of country / capital / continent / titles. They notice the geographic spread (South America and Europe dominate the trophy count) and can rank nations by titles.
Extension / Teacher's note
Extension: Locate all 32 nations on a blank world map and shade them by continent. Note: the nations, their names in four languages, skill ratings, and World Cup titles all live as data in cemi/nations.js — a clean dataset to read.

12 Leaderboard statistics

Objective
Practice collecting data and computing simple statistics.
Instructions
Have several students each play the Daily Challenge (a 10-shot gauntlet where corners and combos score points). Record everyone's score. Compute the mean, median, range, and mode of the class scores.
Expected result
Students produce a real dataset and its summary statistics, then discuss which measure best describes "typical" performance and how one very high score skews the mean but not the median.
Extension / Teacher's note
Extension: Because the daily challenge is the same for everyone on Earth today, compare your class median to friends elsewhere. Note: the fairness comes from a seeded RNG in cemi/challenge.js — the same date produces the same challenge worldwide.
Programming · Both games

Programming — the code is open

Both games are zero-build, readable JavaScript with learning-oriented comments. The whole engine is meant to double as a programming lesson. These three activities are a taste; the full Programming guide has a file-by-file map and a live source viewer.

13 Read the physics you just played

Objective
See that a "game feeling" is really a few lines of math.
Instructions
Open the Programming guide and click the source link for games/golden-boot/shootout.js. Find the update method and read the four lines that move the ball each frame (gravity pulls vy down, drag slows vz, spin nudges vx).
Expected result
Students recognize the exact forces they felt while playing, written as short arithmetic on the ball's velocity. "The Magnus effect" stops being a mystery and becomes one readable line.
Extension / Teacher's note
Extension: Ask "what would you change to make the ball float like it's on the Moon?" (Hint: gravity.) Note: no setup required — the source viewer fetches the real file and shows it with line numbers in a pop-up.

14 Find the state machine

Objective
Recognize a finite state machine as the "brain" behind the gorilla.
Instructions
Open games/gorilla/gorilla.js in the source viewer and find the big switch (this.state) block. Match each case (PATROL, STALK, CHESTBEAT, CHARGE, CARRY, TIRED) to a behavior you observed in Activity 4.
Expected result
Students map every behavior they watched onto a named block of code and see that "intelligence" here is just a set of rules for switching between states.
Extension / Teacher's note
Extension: Draw the state diagram as arrows between boxes — which states can lead to which? Note: the header comment of games/gorilla/gorilla.js even draws the same diagram in text.

15 Everyone gets the same day

Objective
Understand deterministic randomness — how a "random" daily challenge can be identical for everyone.
Instructions
Open cemi/challenge.js in the source viewer. Read dayKey (today's date as a string) and dailyRng (which seeds a random-number generator from that date). Discuss: if the seed is the same for everyone today, what does that guarantee?
Expected result
Students grasp that a seeded generator is repeatable: the same seed always produces the same sequence, so every player worldwide faces an identical challenge each day — fair to compare, yet different tomorrow.
Extension / Teacher's note
Extension: Ask why true randomness would make leaderboards unfair. Note: the generator is mulberry32 seeded by hashStr, both in cemi/core.js — small, readable, and reused across the whole engine.

Ready for more? The Programming guide maps every engine file to what it teaches and shows students how to clone and run the whole thing.

Physics · Keepy-Uppy Infinito

The simplest physics in the whole catalog

A ball falls, you tap to kick it back up, and that is the entire simulation — gravity pulls it down, your tap sets a new upward speed. Keepy-Uppy Infinito is the best first stop for a student who has never read game code before, because its physics loop in games/keepy/main.js is only three lines long.

16 Gravity in three lines

Objective
Recognize the smallest possible working physics simulation.
Instructions
Play a run, watching the ball fall and bounce off your feet. Then find the per-frame update: ball.vy += G * dt; ball.y += ball.vy * dt; ball.x += ball.vx * dt;. A kick just resets the vertical speed: b.vy = -kickV(c).
Expected result
Students see there is no hidden "physics engine" — gravity is one number (G) added to a speed every frame, and position is that speed added to a location every frame. A kick is nothing more than making the vertical speed negative (upward) again.
Extension / Teacher's note
Extension: Ask what would happen if G were doubled — would the ball fall faster or slower, and would that make the game easier or harder? Note: this same three-line pattern (velocity, then position, updated every frame by dt) is the physics core hiding inside every other game in this catalog — it just gets more decorations from there.

17 A catch window that shrinks as you improve

Objective
See a difficulty ramp built entirely from one shrinking number, with no change to speed or gravity.
Instructions
Play until you have juggled 20–30 touches in a row. Then read band(n): it uses lerp to shrink your forgiving "foot contact" zone from a generous 150 pixels down to a tight 66 pixels over your first 30 touches.
Expected result
Students notice the game feels harder the longer they survive, purely because the timing window for a good touch keeps tightening — the ball never gets faster, the target just gets smaller. They can also find the combo rule: switching feet each touch grows your combo (up to x5), and repeating the same foot resets it to 1.
Extension / Teacher's note
Extension: Design a shrinking-window difficulty ramp for a different game (dodgeball's reaction time? a quiz's time limit?) using the same lerp(generous, tight, progress) idea. Note: the combo bonus (KICK_STEP) adds power for every level of combo, so one constant tunes the entire skill ceiling of the game.
Biology · Anaconda Soccer

A predator that grows every time it eats

The anaconda in games/anaconda/main.js is a chain of points, each one following the point ahead of it at a fixed distance — the classic "Snake" body, reused as a predator. Every time it swallows the ball it pushes new points onto its own tail, growing in real time in front of you.

18 The snake is an array that grows

Objective
See that "growth" is just adding items to a list, and "following" is a simple distance rule.
Instructions
Find the segment-following loop in updateSnake: each point is pulled toward the point before it whenever it drifts more than SEG (5 px) away. Then find the eating moment: const tail = snake.pts[snake.pts.length - 1]; for (let i = 0; i < GROW; i++) snake.pts.push({ x: tail.x, y: tail.y });.
Expected result
Students see the whole snake is one JavaScript array of {x, y} points. Growing by six segments after a bite is literally .push() called six times — no drawing trick, no sprite swap, just more data in the same list.
Extension / Teacher's note
Extension: Change GROW from 6 to 20 and predict how many bites it would take before the snake fills the whole pitch. Note: this "each item follows the one ahead of it" rule is the same idea behind a real conga line, a real train, and — not coincidentally — the ancestor game this mechanic is named after.

19 A predator that cannot turn on a dime

Objective
Understand steering with a limited turn rate, and why that limit becomes an escape strategy.
Instructions
Read how the snake decides its heading: const want = Math.atan2(ball.y - head.y, ball.x - head.x); always points straight at the ball, but snake.ang += clamp(diff, -TURN * dt, TURN * dt) only lets the heading turn by TURN radians per second.
Expected result
Students realize the snake always "knows" where the ball is but physically cannot spin toward it instantly — so cutting sharply inside its curve is a real, learnable way to escape, just like out-turning a real predator (or a car) with a wide turning circle.
Extension / Teacher's note
Extension: Change TURN from 2.6 to 6 and predict whether escaping becomes easier or harder. Note: the tiny wig = Math.sin(tGlob * 6) * 0.35 term added to the heading is the same "never move in a perfectly straight line" trick used by the gorilla's gait and the muerto zombies' wobble — three different animals, one shared idea.
Programming · Rumble Fútbol

Eight power-ups, one small system

Rumble Fútbol seats up to four players (people or bots) around one screen and drops random power-ups onto the pitch. games/rumble/main.js is a tidy example of how professional games avoid writing eight separate features for eight different effects.

20 One data table, eight power-ups

Objective
See how a small list plus a switch can implement many different game effects.
Instructions
Read the list const PU = ['giant', 'freeze', 'speed', 'tele', 'bumper', 'goals', 'ice', 'fire'];, then find applyPow(pu) and its switch (pu.type) block, where each case just flips one timer or value in the game state (for example case 'giant': ball.giantT = 8; break;).
Expected result
Students see that every power-up is the same tiny object shape — {type, x, y, ttl} — and picking one up is a single lookup, not eight different code paths. Adding a ninth power-up would mean adding one more list entry and one more case.
Extension / Teacher's note
Extension: Invent a ninth power-up and write the one case line it would need. Note: the "ice" power-up doesn't add new physics — it just lowers the existing grip variable used every frame, from 10 down to 1.6, so players slide instead of stopping.

21 Four keyboards, one screen

Objective
Understand local multiplayer as input routing, not separate copies of the game.
Instructions
Find KEYSETS in games/rumble/main.js: four named key groups (wasd, arrows, ijkl, tfgh). Then read humanVec(p), which only ever reads the keys or the touch-stick that belong to that one player.
Expected result
Students see that "four players on one keyboard" is really "four small labeled lists of keys," with each player's code only ever looking at its own list — the game loop itself never changes. Bots use the same movement function with computed targets instead of key presses.
Extension / Teacher's note
Extension: Design a fifth key set for a fifth player and say which physical keys would be free to use on a real keyboard. Note: look at botVec() and notice the bot nearest the ball attacks while its teammate hangs back to "cover" using lerp — a two-role team AI without any pathfinding at all.
Physics · Elemental League

When the field itself plays

Water, earth, air and fire each get a character AND a home arena in games/elemental/main.js — and the arena is not just scenery. Step into a lava pool and the ball speeds up; the wind in the air arena keeps shifting. The pitch is a second opponent.

22 Two tables, one shared key

Objective
See data modeling where two different tables are linked by the same identifier.
Instructions
Read const ELEMENTS = { water: {...}, earth: {...}, air: {...}, fire: {...} }; and, right below it, const ARENAS = { water: {...}, earth: {...}, ... };. Notice both tables use the exact same four keys.
Expected result
Students recognize that a character's stats and its home arena's colors are two separate lookups sharing one id ("water", "fire"...) — the same "one recipe card" idea used for the gods in Gods vs Titans FC, doubled up into two linked cards per element.
Extension / Teacher's note
Extension: Sketch the two data rows a fifth element ("ice") would need. Note: every element uses the same speed/cd/powerKey fields, so the whole roster is playable through one shared updateGod-style function, not four separate ones.

23 The pitch fights back

Objective
See environment-as-gameplay: the arena changes the physics, not just the art.
Instructions
Play an away match on the fire arena and step the ball into a lava pool. Then read the code: if (arenaId === 'fire' && ball.air <= 0) { ... ball.vx *= 1.4; ball.vy *= 1.4; } inside updateArena.
Expected result
Students see that touching a themed hazard directly multiplies the ball's velocity — the field is not decoration, it is one more system reading and writing the same ball object every other part of the game uses.
Extension / Teacher's note
Extension: Change the lava multiplier from 1.4 to 2.2 and predict how much harder the fire arena becomes. Note: the rival's power-use decision is only rolled once every quarter second of real time (not once per rendered frame) — a small trick that keeps the daily challenge fair whether the game runs at 30fps or 144fps.
Math · Cruce de Tráfico

Decide the traffic once, draw it forever

Cruce de Tráfico hops your player and ball across eight lanes of streets, rivers and train tracks. games/cruce/main.js is a clean example of separating "roll the dice" from "compute the picture right now."

24 Traffic is decided once, drawn every frame

Objective
Separate a one-time random decision from an ongoing pure calculation.
Instructions
Read makeRow(idx): a row's direction and speed are rolled once, the first time that row is needed (row.dir = rng.chance(0.5) ? 1 : -1; row.speed = rng.range(1.1, 1.9) * ...). Then read movingItems(row), which computes each car's current position using only tGlob (elapsed time) — no randomness at all.
Expected result
Students see that once a row's rules are set, exactly where every car sits at any instant is pure math, not a fresh coin flip — a fast player and a slow player who reach the same row still see the identical traffic pattern.
Extension / Teacher's note
Extension: Explain why "roll once per row, in order of distance" is exactly what keeps the Daily Challenge fair for every player on Earth. Note: the ball that follows you uses the exact same trick — its bounce direction after being "punted" loose is picked from a list that was shuffled once, when the row was created, not re-rolled at the moment of impact.

25 Hopping with easing math

Objective
Understand eased motion — a hop that speeds up then slows down, instead of a straight-line snap.
Instructions
Read posOf(e): const ek = 1 - (1 - k) * (1 - k); blends the drawn position between tiles, while Math.sin(k * Math.PI) adds a little arc of height along the way.
Expected result
Students see that the player's logical tile updates instantly, but the drawn position eases smoothly across HOP_DUR (0.16 seconds) using a "fast start, slow finish" curve plus a sine-wave bounce — the standard recipe behind every satisfying tile hop.
Extension / Teacher's note
Extension: Change HOP_DUR to a much larger number and predict how the game would feel. Note: one function, checkHazards(), decides life-or-death for three completely different lane types — street, train, and river — just by branching on row.type.
Programming · Muerto FC

A horde is one object, copied many times

In Muerto FC a lone striker scores while a horde of slow zombies grows larger after every goal. games/muerto/main.js shows how a scary difficulty ramp can be nothing more than the same object pushed into an array again and again.

26 A horde is data, not new code

Objective
See a difficulty ramp implemented purely by adding more copies of one object.
Instructions
Score a few goals and count the zombies. Then read onGoal(): for (let i = 0; i < SPAWN_PER_GOAL; i++) zombies.push(makeZombie(false)); if (goals % 3 === 0) zombies.push(makeZombie(true));.
Expected result
Students see that every zombie is the exact same object shape with different numbers (speed, size), and the horde growing is simply the array getting longer. Every third goal adds one faster "runner" variant — still the same object, just different stats.
Extension / Teacher's note
Extension: Change SPAWN_PER_GOAL from 2 to 5 and predict how much faster the horde balloons. Note: the game's own in-play "Nerd fact" names this exact pattern out loud — designers call it data-driven design, the same idea behind the gods' stat table and the dungeon's floor table.

27 Chase, but make it wobble

Objective
Understand simple steering AI plus a wobble trick that makes straight-line chasing look organic.
Instructions
Read updateZombies: const dirx = (you.x - z.x) / d, diry = (you.y - z.y) / d; points straight at the player, then const wob = Math.sin(z.walk * 2) * z.wob; nudges that direction sideways over time.
Expected result
Students see that "chase AI" is just a unit vector toward a target, and the staggering, lurching walk comes from adding a small sideways sine wave on top of that vector — cheap math producing a believable zombie shuffle.
Extension / Teacher's note
Extension: Compare this wobble to the anaconda's wig term and the gorilla's gait — three different animals, the same "never move in a perfectly straight line" trick. Note: every zombie's speed is multiplied by dt, so the horde chases at the same real-world speed whether the game runs at 30fps or 144fps.
Physics · Dodgeball FC

You can see exactly where it will land

In Dodgeball FC, three rivals throw dodgeballs whose landing spot is decided the instant they leave the hand — games/dodgeball/main.js turns a real physics fact (a thrown ball's landing point is fixed the moment it launches) into a fairness mechanic you can literally see coming.

28 The shadow never lies

Objective
Read a telegraphed projectile and see how a growing shadow honestly announces danger.
Instructions
Watch a throw's shadow grow as it flies. Then read drawDodgeballFlight: the ball's straight-line position is lerp(sx, tx, p) and lerp(sy, ty, p), while its height is faked with Math.sin(p * Math.PI) * 110 — a curve, not real gravity.
Expected result
Students see the target point (tx, ty) is chosen the moment the ball is thrown, so the growing shadow on the floor is showing the real future landing spot the whole time — nothing is hidden from the player.
Extension / Teacher's note
Extension: Explain why a designer would choose an honest, readable shadow over a "more realistic" hidden trajectory. Note: the arc height is a single sine wave, not a physics simulation — a good example of faking just enough physics to feel right without simulating it for real.

29 Why not roll the dice live?

Objective
Understand why some games pre-roll all of their randomness in advance instead of during play.
Instructions
Read the comment above throwPlan in startMatch(), then see how each rival simply reads down its own pre-rolled list: const plan = throwPlan[r.idx][throwIdx[r.idx]++ % throwPlan[r.idx].length];.
Expected result
Students grasp that if the rivals rolled fresh random numbers while playing, a fast-moving player and a cautious player would consume the shared seed in a different order — breaking the promise that everyone gets the identical daily challenge. Pre-rolling the whole script first avoids that entirely.
Extension / Teacher's note
Extension: Compare this "pre-roll everything into a list" solution with Endless Striker's "use three separate dice" solution to the very same fairness problem. Note: a hit only stuns a rival if speed > 170 — a slow bump just bounces off, so the same collision code produces two very different outcomes depending on one number.
Languages · Gods vs Titans FC

The words the gods left behind

Zeus, Hermes, Poseidon and Hades are more than character names in games/gods/main.js — every one of them left a word behind in modern English, Español, Français and Português. This game is a doorway into etymology: the study of where words come from.

30 Words the gods left behind

Objective
Discover that everyday vocabulary in four languages still carries ancient Greek mythology inside it.
Instructions
Pick each god in the roster — Zeus, Hermes, Poseidon, Hades — and the Titan they face. For each name, find one modern word that comes from it: the Titans gave us titanic / titánico / titanesque / titânico (huge, unstoppable); Hermes (a messenger god) gave us hermetic / hermético (sealed shut, from Hermes Trismegistus); the god Pan (not in the roster, but a good bonus) gave us panic / pánico / panique.
Expected result
Students build a small table crossing gods against languages and discover that ancient stories are still hiding inside words they use every week, without ever having met a Greek myth.
Extension / Teacher's note
Extension: Research one more Greek-derived word not listed here (try "chaos," "atlas," or "echo") and trace it to its myth. Note: the game's own strings file stores each god's name and power description as a four-language data row — exactly the same shape used for vocabulary in the Languages section above.

31 One recipe, four gods

Objective
Recognize data-driven character design: one function, four very different-feeling gods.
Instructions
Read the GODS table: zeus: { color: '#ffd400', speed: 175, cd: 7, ... }, hermes: { color: '#9fd0ff', speed: 190, cd: 5, ... }, and so on. Every god is drawn and updated by the exact same functions.
Expected result
Students see a "trading card" pattern: one shared piece of code reads a different row of stats depending on which god you picked. A special power simply fills a bar over cd seconds and fires once it is full.
Extension / Teacher's note
Extension: Change zeus.cd from 7 to 2 and predict how much more often Zeus's lightning could strike. Note: this is the same "one recipe, many characters" pattern used again — twice — in Elemental League's linked element/arena tables.
Physics · FIREBALL FC

An explosion is just a distance check

In FIREBALL FC nobody ever touches the ball — two wizards cast fireballs and use the blast to shove it toward the goal. games/fireball/main.js turns a blast radius into a few lines of readable math, plus a mana bar that forces players to budget their spells.

32 How close is "still close enough"?

Objective
Understand radius-based blast physics using nothing but distance and a clamp.
Instructions
Read explode(b): const d = dist(b.x, b.y, ball.x, ball.y); if (d < R + 60) { const power = (b.big ? 460 : 330) * clamp(1 - d / (R + 60), 0.35, 1); ... }.
Expected result
Students see that the ball only feels the blast if it is close enough, and the closer it is, the harder the push — a simple, readable falloff model for an explosion, using the same dist() helper as every collision check in this catalog.
Extension / Teacher's note
Extension: Change the blast radius R and predict how much bigger the "danger zone" around a fireball becomes. Note: Math.atan2 finds the direction to push the ball, the same function the anaconda uses to aim itself at the ball.

33 Spending mana like allowance money

Objective
Understand a cooldown/resource-budget system that forces pacing instead of spamming.
Instructions
Read cast(): casting costs a flat 25 mana and is refused below that, while w.mana = Math.min(100, w.mana + 20 * dt) refills it slowly every frame.
Expected result
Students realize a fixed cost plus a fixed regeneration rate quietly sets a "shots per minute" ceiling — four casts empty the bar, and it takes five seconds to refill from zero. The designer chose these two numbers on purpose.
Extension / Teacher's note
Extension: Change the cost from 25 to 10 and predict whether the duel becomes more fun or just chaotic. Note: the computer-controlled wizard aims using the same "normalized direction vector" trick you will see again in Elemental League and Dodgeball FC.
Physics · Béisbol Gol

A whole game, scripted before the first pitch

A robot pitches, you swipe to kick — Béisbol Gol's games/beisbol/main.js pre-plans every one of its 54 pitches before the first one is even thrown, and fakes convincing curveballs with nothing but sine waves.

34 The whole match is scripted in advance

Objective
See seeded-RNG determinism applied to an entire match at once, not just one spawn.
Instructions
Read startMatch(daily): rng = daily ? dailyRng('beisbol') : Math.random; for (let i = 0; i < INNINGS * PITCHES_PER; i++) plan.push(makePitch(Math.floor(i / PITCHES_PER), rng)); — all 54 pitches are built into an array before play starts.
Expected result
Students see that on Daily Challenge day, every player on Earth receives the identical list of pitch types, speeds and curves in the identical order — the strongest example yet of "same seed, same world for everyone."
Extension / Teacher's note
Extension: Compare this "whole match pre-rolled into a list" approach to Endless Striker's "three separate live dice" approach and Dodgeball FC's "pre-rolled per-rival lists" — three different, equally valid solutions researched to the exact same fairness problem. Note: the result is reported with reportDaily('beisbol', ...), the same function every daily-challenge game in this catalog calls.

35 Faking a curveball with a sine wave

Objective
See how blending a straight line with a trig wave can fake a complex-looking flight path.
Instructions
Read pitchPath(p): let y = lerp(RELEASE.y, targetY, p); if (pitch.type === 'curve') y += pitch.amp * Math.sin(p * Math.PI); — one extra sine hump for a curveball, three humps (Math.sin(p * Math.PI * 3)) for a floater.
Expected result
Students realize p just counts from 0 (leaves the machine) to 1 (reaches the plate), and each pitch "type" is really just a different wave shape added on top of a straight blend — no real spin physics required.
Extension / Teacher's note
Extension: Sketch what a new pitch type using Math.cos(p * Math.PI * 2) would look like. Note: the goalkeeper predicts your shot's landing spot with the real ballistic formula 0.5 * GRAVITY * tc * tc — the same "AI that predicts the future" idea used by Golden Boot's keeper.
Biology · Dolphins vs Sharks

Hunting where the ball will be

Water behaves differently from grass, and games/dolphins/main.js knows it: every swimmer — your pod and the sharks alike — shares one momentum function, and the hunting shark aims not at the ball, but at where the ball is about to be.

36 Swimming has momentum, grass doesn't

Objective
Feel the difference between water drag and ground friction, and see it as one shared function.
Instructions
Read swimStep(p, tx, ty, accel, dt, maxV): velocity accelerates toward a target, gets capped at maxV, then p.vx *= Math.exp(-0.7 * dt) — an exponential drag that slowly bleeds speed away.
Expected result
Students notice every swimmer glides and coasts instead of stopping on a dime — a real property of moving through water — and that all seven swimming characters (your pod plus the sharks) call this exact same function with different numbers.
Extension / Teacher's note
Extension: Change the drag constant 0.7 to 0.1 and predict how "slippery" the water would feel. Note: reusing one physics function for seven different entities is the same "write it once" instinct behind Rumble's power-up switch and the gods' shared stat table.

37 Hunting where the ball will be

Objective
Understand predictive ("lead") pursuit — real predator behavior, not naive chasing.
Instructions
Find the shark closest to the ball (it becomes the hunter; the rest patrol in slow circles), then read its target: const tx = ball.x + ball.vx * 0.25; const ty = ball.y + ball.vy * 0.25;.
Expected result
Students see the hunting shark does not aim at the ball's current spot — it aims a little ahead, at where the ball's own speed will carry it. This is the same principle real predators use to intercept moving prey, and the same idea behind a quarterback throwing to a receiver's future position.
Extension / Teacher's note
Extension: Change the 0.25 lead factor to 0 (chase the current spot) or 1 (aim far ahead) and predict how much easier or harder passing becomes. Note: the patrolling sharks circle using Math.cos/Math.sin on a slowly advancing angle — the same trigonometric orbit trick used for menu decorations elsewhere in the engine.
Math · Dungeon Striker

Ten floors, one small table

Dungeon Striker sends you down ten themed floors, each a 45-second match against a different goalkeeper. games/dungeon/main.js shows the biggest procedural-generation and seeded-RNG lesson in the whole catalog — an entire ten-floor run is decided before you take your first step.

38 Ten floors, one small table

Objective
See an entire level progression built from one array of plain data rows.
Instructions
Read the FLOORS array: each row is just { keepers: [...], hunt: 0.3, tint: '...' }. Floor 1's skeleton keeper, floor 2's twin slimes, floor 4's mimic — all ten floors live in this one table.
Expected result
Students realize adding an eleventh floor means adding one more row to this table, not writing new gameplay code — the exact same "one recipe, many rows" idea already seen in the gods' stat table and Elemental League's linked tables, now describing whole levels instead of characters.
Extension / Teacher's note
Extension: Design a data row for an eleventh floor, picking a keeper type, a hunt value and a tint color. Note: the same table also drives the floor's rendered color tint — one row of data, two completely different jobs.

39 A whole run of loot, decided at floor one

Objective
See seeded-RNG determinism scaled up to an entire multi-floor run, plus a telegraphed AI state switch.
Instructions
Read startRun(daily): rng = makeRng(daily ? dailyRng('dungeon') : Math.random);, then every loot offer for all nine choice-points and every floor's random "quirk" are drawn from rng right there, before floor one even begins. Also find updateHunt: a keeper that decides to hunt first shakes for windupT = 0.5 seconds before actually charging.
Expected result
Students see the biggest example yet of "decide everything with the seed, up front" — bigger than Béisbol's single match, this is an entire ten-floor run — and recognize the half-second warning shake as the same honest, telegraphed-attack fairness pattern as the gorilla's chest-beat and dodgeball's growing shadow.
Extension / Teacher's note
Extension: Change floor one's hunt: 0.3 to hunt: 1 and predict how much more aggressive the easiest floor suddenly becomes. Note: picked-up loot is just a counter (loot.wings, loot.blast...) multiplied into a stat wherever it matters, so grabbing the same item twice keeps compounding its effect with no extra code.
Programming · Endless Striker

Three separate dice, so nothing steals from anything else

Endless Striker swerves you down a three-lane dribble run in games/striker/main.js. It is also the game that sets up the catalog's most advanced lesson: sometimes one shared game needs several completely independent seeded random-number generators.

40 Three separate dice, so nothing steals from anything else

Objective
Understand why a single fair game sometimes needs multiple independent seeded RNGs.
Instructions
Read startRun(): rng = makeRng(...); rivalRng = makeRng(...); curveRng = makeRng(...); — three separately-seeded generators, one each for obstacles, the rival parade, and the track's curve.
Expected result
Students grasp that if everything shared one die, picking your country for the rival parade would accidentally reroll the obstacle course too — three separate, independently-seeded generators keep each promise ("same obstacles for everyone," "your country choice doesn't change the world") honest at the same time.
Extension / Teacher's note
Extension: Imagine merging the three generators back into one and predict exactly what would break. Note: this same "three separate seeded generators" idea is what makes the next lesson, Endless Striker 3D, able to reuse the identical daily challenge as this 2D game.

41 One touch, four different outcomes

Objective
See collision resolution gated by player state, instead of a plain yes/no hit.
Instructions
Read the collision block in updatePlay(): it checks fireT (unstoppable), then whether you jumped or slid correctly, then invulT (shield grace), then shieldOn, before finally calling crash(o) as a last resort.
Expected result
Students see that touching an obstacle is not one rule — it is an ordered checklist of "was I in a state that saves me?" questions, and only if every one fails does the run actually end.
Extension / Teacher's note
Extension: Change the speed-ramp constant (the 2.2 in the distance-to-speed formula) and predict how much sooner the game reaches its top speed. Note: games/striker/shared.js declares itself the single source of truth for both this 2D game and its 3D twin — the next lesson shows exactly what that means.
Programming · Endless Striker 3D

The same game, a different set of paints

This is the catalog's flagship lesson. Endless Striker 3D is not a new game — its own header comment in games/striker3d/main.js says so directly: the same rules, the same numbers, the same daily seed as Endless Striker, rebuilt with a real 3D camera instead of a flat canvas.

42 The same game, a different set of paints

Objective
Prove that "3D" can mean "new renderer," not "new game," by finding shared code with your own eyes.
Instructions
Open games/striker3d/main.js and read its header comment. Then open games/striker/shared.js and find a constant such as ROCKET_RANGE_M — then confirm games/striker3d/main.js imports that very same constant from that very same file.
Expected result
Students see with their own eyes that item ranges, rival rotation, speed caps and even the daily seed are defined exactly once and imported by two completely different-looking games — one flat, one three-dimensional.
Extension / Teacher's note
Extension: Find a second constant shared between the two files. Note: this is the clearest possible answer to "what does 3D actually add?" — when the rules are provably identical, the only honest difference left is how the picture gets drawn.

43 A scene is a diorama, not a drawing

Objective
Understand the core idea of a 3D scene graph, in contrast to 2D canvas drawing.
Instructions
Read buildWorld() in games/striker3d/scene.js: a Scene, a PerspectiveCamera, and a DirectionalLight are each created as objects and added into the scene, rather than drawn with pixel commands.
Expected result
Students grasp that 3D graphics don't draw pixels directly the way fillRect does — you arrange real objects (lights, cameras, shapes) into a "shoebox diorama," and the renderer takes a photo of that diorama from the camera's position, every single frame.
Extension / Teacher's note
Extension: Find where the camera itself uses lerp to ease toward the player's lane — the exact same easing trick from Endless Striker's lane movement, just applied to a camera instead of a runner. Note: old defenders are never thrown away — acquireDefender/releaseDefender wipe and reuse them from a pool, because creating new 3D shapes constantly can stutter the game, a real lesson in why game engines recycle objects.