Code as Curriculum

The games are the lesson

No build step, no framework, no minified mystery. Just readable vanilla JavaScript with comments written to be read. Click any file reference on this page to open the real source, right here.

The open-engine philosophy

CEMI Fútbol is written to be opened up. It runs directly in the browser as native ES modules — no bundler, no transpiler, no node_modules. Every file is small, single-purpose, and commented so a curious student can follow it.

Try it now: click cemi/core.js. The real file opens in a viewer with line numbers. Every monospace path on this page works the same way.

The code map: what each file teaches

The engine lives in cemi/; each game lives in games/. Click any path to read it.

Engine — cemi/

FileWhat it doesWhat it teaches
cemi/core.jsGame loop, scene manager, responsive canvas, helper math (clamp, lerp, mulberry32, hashStr).Game loops, delta-time updates, seeded RNG, utility functions.
cemi/input.jsUnified pointer/touch/keyboard; swipes carry a full trail so games read direction, speed AND curvature.Event handling, vectors, reading gestures from raw points.
cemi/audio.jsAll-synth WebAudio sound effects — zero audio files.Procedural audio, oscillators, envelopes.
cemi/fx.jsParticles, confetti, screen-shake, floating text.Particle systems, animation, "game juice".
cemi/facts.jsThe Nerd Engine: context-tagged facts that never repeat until the pool is exhausted.Data-driven content, sets, non-repeating random selection.
cemi/i18n.jsEnglish / Español / Français / Português with t('key', {vars}) everywhere.Internationalization, key/value data structures, string templating.
cemi/nations.js32 nations; flags are DRAWN from simple shapes, no image files.Data modeling, procedural vector graphics.
cemi/tournament.jsKnockout brackets: pick a nation, survive 5 rounds to the cup.Arrays, shuffling, tournament trees, powers of two.
cemi/challenge.jsSeeded daily/weekly challenges — the same world for everyone today.Determinism, hashing, date keys, seeds.
cemi/profile.jsOptional, kid-safe local profile — never required to play.localStorage, privacy-by-design, default objects.
cemi/backend.jsOne Store interface, two implementations (local + Firebase), offline-first.Interfaces/polymorphism, graceful degradation.
cemi/ui.jsShared DOM UI and a line-art SVG icon library (never emojis).DOM building, SVG, component thinking.

Golden Boot — games/golden-boot/

FileWhat it doesWhat it teaches
games/golden-boot/main.jsBoots the game, owns the menu/tournament/results screens.App structure, wiring modules together.
games/golden-boot/shootout.jsThe physics scene: gravity + drag + Magnus spin, keeper AI, scoring.Projectile motion, vectors, forces, prediction, energy.
games/golden-boot/render.jsTwo renderers over one state: retro 16-bit and modern broadcast, with a 2.5D projection.3D-to-2D projection, separation of state and view.
games/golden-boot/strings.jsGame text in four languages + its Nerd Engine facts pack.i18n data, templated feedback with real numbers.

Gorilla on the Pitch — games/gorilla/

FileWhat it doesWhat it teaches
games/gorilla/main.jsSets up the pitch, teammates, waves, and the survive-and-score loop.Game state, entities, win/lose conditions.
games/gorilla/gorilla.jsThe gorilla: a finite state machine + procedural quadruped animation.State machines, AI targeting, procedural animation, trigonometry.
games/gorilla/strings.jsGame text in four languages + real gorilla-biology facts.Data-driven content, biology as data.

Four guided code-reading activities

Each activity opens a real file in the source viewer. No editor and no setup needed — reading comes before writing.

1 The gravity constant — and Moon ball

Objective
Locate a physical constant in code and reason about a change without running it.
Instructions
Open games/golden-boot/shootout.js. Search for 9.81 — Earth's gravity in m/s². Find where it pulls the ball down each frame (B.vy -= g * dt). Now imagine replacing it with the Moon's gravity, 1.62.
Expected result
Students predict that on the Moon the ball would barely arc down — shots would sail long and float, keepers would have far more time, and "over the bar" would happen constantly. They can point to the exact line responsible.
Extension / Teacher's note
Extension: The game also has a "beach ball" effect that uses g = 3.6. Find it and explain why a beach ball floats. Note: reasoning about a change in your head ("what-if") is a real debugging skill — no need to edit anything.

2 How the keeper reads a shot

Objective
See how simple AI can look smart by predicting the future.
Instructions
In games/golden-boot/shootout.js, find predictCrossing. Read how it fast-forwards the ball's flight in a loop to guess where it will cross the goal line, then look at _keeperDecide to see how difficulty makes the keeper "read" the shot more often.
Expected result
Students discover the keeper isn't cheating — it runs a cheap copy of the same physics to predict the crossing point, then dives there (with some deliberate error so it's beatable). Higher difficulty simply lowers the error.
Extension / Teacher's note
Extension: Discuss the trade-off: a perfect predictor would be unbeatable and no fun. Where is the randomness that keeps it fair? Note: the same forward-simulation trick powers real game and robotics AI.

3 Walking without a sprite sheet

Objective
Understand procedural animation — motion generated by math, not drawn frames.
Instructions
Open games/gorilla/gorilla.js. Find _gait and read how the phase advances with distance travelled, not time (this.phase += (sp * dt) / this.stride), so the gorilla's legs plant and step realistically. Then find the LIMBS table and read the comment about diagonal limb pairs.
Expected result
Students see that a convincing knuckle-walk is produced entirely by trigonometry and timing — the four limbs step in diagonal pairs, the body bobs twice per stride and sways once. No animation frames exist at all.
Extension / Teacher's note
Extension: Find _renderProcedural and the bob/sway values — change one in your head and predict the effect. Note: the file supports an optional sprite sheet too; the procedural path is the fallback and the teaching case.

4 One data structure, four languages

Objective
See how a single data shape can hold every translation of the game.
Instructions
Open cemi/i18n.js and read the STR table: each key maps to { en, es, fr, pt }. Then open cemi/facts.js and see the exact same shape used for the Nerd Engine's facts. Find the t() function and read how {vars} get substituted into a string.
Expected result
Students recognize one reusable pattern — a key with four language values — powering the entire multilingual interface and the trivia. They understand why adding a language means adding one field, not rewriting the game.
Extension / Teacher's note
Extension: Trace how t('gb_nerd_goal', {kmh, j, ms}) turns into the sentence you read after a goal. Note: the physics feedback strings in games/golden-boot/strings.js use the same templating with real computed numbers.

Clone it and run it

Because there is no build step, running the whole project locally takes three commands. You only need Git and Python 3 (which ships with macOS and Linux, and is a quick install on Windows).

  1. Clone the repository:
    git clone https://github.com/<owner>/futbol.git
    cd futbol
  2. Start the tiny dev server (needed because ES modules require HTTP, not file://):
    python3 scripts/dev-server.py 8321
  3. Open it in a browser:
    http://localhost:8321

The dev server (scripts/dev-server.py) is a ~20-line Python script that serves the files with no-cache headers, so every edit shows up on reload. Change a number in games/golden-boot/shootout.js, refresh, and watch the game change. That immediate loop — edit, save, refresh, see — is the whole point.

Note: the source viewer on these pages fetches files over HTTP too, so it works best when the docs are opened through that same local server (or wherever the site is hosted), not from a bare file:// path.