Basic

Math and Expressions

A game is physics and geometry in disguise. Learn the arithmetic operators, the Math library, and build your first real helper, clamp, which the finished game ships in.

What you will see

720 340

About 4 min of reading

Quick check

Before this new lesson, one small question about the lesson you just finished. Recall it, do not look it up.

Try It

A game is physics and geometry in disguise: it does arithmetic and calls a toolbox of math functions every single frame, then stores the result back into a variable. I want to stop trusting that order-of-operations just works and actually watch it, run the same expression two ways with and without parentheses, call a few Math functions, and build the one helper the finished game ships, so clamp is something I can explain instead of something I just copy.

1

PEMDAS has a real order, and the difference between multiplying first and adding first is a whole different number. I write one expression, run it, and see 14, multiplication before addition. Then I run the exact same numbers in parentheses and see 20. The parentheses are not decoration: they force the engine to add before it multiplies.

console.log(2 + 3 * 4) console.log((2 + 3) * 4)

Before you run it, say out loud what it will print. Then open the answer.

What it prints
14 20
Without parentheses JavaScript multiplies first, giving 14. With parentheses the add happens first, giving 20. If your two outputs match each other instead, the parentheses are missing.
2

The Math library ships inside every JavaScript engine; I call its functions with Math.name(...). I run abs, max, min, floor, and sqrt together so I feel what each one hands back: a positive distance, the bigger of two numbers, the smaller, a number rounded down, and a square root. Each is its own tiny tool I can reuse.

console.log(Math.abs(-5)) console.log(Math.max(3, 9)) console.log(Math.min(3, 9)) console.log(Math.floor(3.9)) console.log(Math.sqrt(16))

Before you run it, say out loud what it will print. Then open the answer.

What it prints
5 9 3 3 4
Math functions never change the value you passed in; they hand back a new number. floor always cuts the part after the decimal down, so 3.9 becomes 3, and sqrt of a negative would be a number called Infinity instead.
3

Modulo is the remainder after division, and it is the same building block games use for wrap-around effects and repeating cycles. I loop a counter from 0 to 7 and print i % 3, watching the exact same cycle 0 1 2 0 1 2 0 1 repeat. Where you are in the cycle is always just the remainder.

for (let i = 0; i < 8; i++) { console.log(i % 3); }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
0 1 2 0 1 2 0 1
% returns the remainder, not a percentage. Dividing by 3 always leaves a remainder of 0, 1, or 2, which is why the cycle is exactly those three numbers and nothing else.
4

Now the real helper: clamp holds a value between a floor and a ceiling. I write the function once with a ternary, then call it three ways, in range, too low, too high, and confirm it returns the input, the floor, and the ceiling. This exact function ships in the finished game's math.js, so I am testing game code, not a toy.

function clamp(v, lo, hi) { return v < lo ? lo : v > hi ? hi : v; } console.log(clamp(5, 0, 10)) console.log(clamp(-3, 0, 10)) console.log(clamp(20, 0, 10))

Before you run it, say out loud what it will print. Then open the answer.

What it prints
5 0 10
The return reads out loud as a sentence: if v is below the floor return the floor, else if it is above the ceiling return the ceiling, otherwise return v. Getting the condition order backwards puts 20 at the ceiling instead of 0 at the floor.
5

Without a cap a value runs away. I set speed to 340, add a frame-rate spike of 4000, and clamp it to expect 720 instead of 4340, the ceiling is what makes a game winnable rather than a blur. I also push it far below the floor to confirm 340 comes back, and read the clamped value next frame.

let speed = 340 speed += 4000 speed = clamp(speed, 340, 720) console.log(speed) console.log(clamp(100, 340, 720))

Before you run it, say out loud what it will print. Then open the answer.

What it prints
720 340
clamp returns the nearest boundary: anything above 720 collapses to 720, anything below 340 collapses to 340. The finished game uses Math.min(CONFIG.MAX_SPEED, ...) for the same reason, caps are fairness, not cheating.

Put it in order

Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.

Data Flow

An expression is evaluated in PEMDAS order, optional Math functions transform the numbers, and the result is stored back into a variable.
1 Write an expression like 2 + 3 * 4
↓
2 Evaluate multiplication/division first, then addition/subtraction
↓
3 Call Math.min/max/random to transform the number
↓
4 Store the result in a variable
↓
5 Read the clamped/tuned value next frame
Why this works
Modulo and repeating patterns

% returns the remainder after division. Feed it a counter and you get a repeating cycle for free:

for (let i = 0; i < 8; i++) {
  console.log(i % 3);   // 0 1 2 0 1 2 0 1
}

That repeating 0 1 2 is the same building block the game later uses for clock hands, blinking lights, and cycling animation frames. The % of a large number by a small one is always "where you are in the cycle."

A decimal example worth remembering: 10 / 4 is 2.5, but 10 / 2 is 5. JavaScript keeps numbers as one kind of box, but watch the division result.

Why clamping protects your game

Without clamps, values run away. Imagine the game's speed: it ramps up the longer you survive, but a player can't react to something moving at 10,000 px/s. The cap is what makes it a game and not a blur.

let speed = 340;
speed += 4000;       // a frame-rate spike, or a bug
speed = clamp(speed, 340, 720);   // -> 720

The finished game does exactly this with Math.min(CONFIG.MAX_SPEED, ...). Clamping is not cheating, it's the boundary that keeps difficulty hard but winnable. We'll return to this idea of caps as fairness in the Advanced tier.

Examples

Operators and order of operations

JavaScript has the usual math operators, with PEMDAS order of operations, multiply and divide happen before add and subtract.

console.log(2 + 3 * 4);   // 14, NOT 20  (multiply first)
console.log((2 + 3) * 4); // 20          (parentheses first)

OperatorMeaningExampleResult
+add3 + 25
-subtract3 - 21
*multiply3 * 26
/divide10 / 42.5
%remainder10 % 31

Use parentheses () whenever you're unsure, it makes your intent crystal clear.

Modulo (%) is a superpower for games. It gives the remainder of division, and it's how you make "wrap around" effects like a clock or a repeating pattern.

The Math library

JavaScript ships a built-in toolbox called Math. Call its functions with Math.name(...).

console.log(Math.abs(-5));    // 5
console.log(Math.max(3, 9));  // 9
console.log(Math.min(3, 9));  // 3
console.log(Math.floor(3.9)); // 3
console.log(Math.sqrt(16));   // 4
console.log(Math.random());   // e.g. 0.734 (from 0 up to <1)

The game's speed, distance, and time values are almost always **decimals*: a player moves 300.7 px, then 301.4, not a round 300 every frame. And Math.random() is how the game decides where obstacles appear.

Watch out: Math.random() returns 0 up to but not including 1.

Building clamp, your first real helper

Clamp means hold a value between a minimum and a maximum, never below the floor, never above the ceiling. A game needs this to keep the player's speed under a cap.

function clamp(v, lo, hi) {
  return v < lo ? lo : v > hi ? hi : v;
}

console.log(clamp(5, 0, 10));   // 5   (in range)
console.log(clamp(-3, 0, 10));  // 0   (too low -> floor)
console.log(clamp(20, 0, 10));  // 10  (too high -> ceiling)

The ? : is the **ternary operator: a one-line if/else. Read the return out loud: "if v is below the floor return the floor; else if it's above the ceiling return the ceiling; otherwise return v."*

🎮 This exact function exists in the real game. In app1/js/utils/math.js: export function clamp(v, lo, hi) { return v < lo ? lo : v > hi ? hi : v; }

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Variables and Values
Previous Lesson
Variables and Values
Next Lesson
Control Flow: Conditionals
Control Flow: Conditionals →