You and three friends want to split a bill evenly, but one person already paid for something earlier. How much does each person owe now?
You earn $8.50 an hour at a weekend job. You need $120 for a gig ticket. How many shifts do you need?
You’re buying snacks for a group. Each bag costs the same. You spend $14.40 in total. How much was each bag?
These are all the same question underneath: you know the relationship, you’re missing one of the values. A linear equation is just that relationship written down, with a placeholder for the missing part.
11.1 What this chapter helps you do
Symbols to keep handy
These are the bits of notation you'll see a lot. If a line of symbols feels like a fence, read it out loud once, then keep going.
coefficient: the number multiplying the unknown in an expression like 3x
3x: three times the unknown, or coefficient times unknown
Definitions to keep handy
These are the words we keep coming back to. If one feels slippery, come back here and steady it before you push on.
equation: A statement that two expressions represent the same quantity, written with an equals sign.
unknown: The value you are solving for: the missing part of the relationship.
coefficient: The number that scales an unknown (for example, 3 is the coefficient in 3x).
Here is the main move this chapter is making, in plain terms. You do not need to be fast. You just need to keep the thread.
Coming in: You already know that numbers relate to each other. Some of those relationships have a gap — a missing value.
Leaving with: An equation is a relationship with a named gap. Solving it means finding what fills the gap without breaking the relationship.
11.2 What the notation is saying
Start with a sentence instead of symbols: “three bags and 7 dollars cost 22 dollars.” An equation is that sentence written in a compact way.
3x + 7 = 22
Interactive: What the notation 3x means. Click or drag the arrow to toggle between the expanded form and the shorthand. The 3x is just a compact way to write three copies of the same unknown.
Code
viewof coeffToggle = Inputs.toggle({label:"Show compact form (3x)",value:false})
Code
{const showCompact = coeffToggle;const W =560;const H =140;const svg = d3.create("svg").attr("viewBox",`0 0 ${W}${H}`).attr("width","100%").attr("style",`max-width:${W}px; font-family: inherit;`);// Left panel: expanded form (always shown, opacity changes)const leftX =40;const boxSize =40;const boxStroke ="#0d9488";const boxFill ="#0d9488";// Boxes for x + x + xconst opacity = showCompact ?0.2:1;const y =50;for (let i =0; i <3; i++) {const x = leftX + i * (boxSize +15); svg.append("rect").attr("x", x).attr("y", y).attr("width", boxSize).attr("height", boxSize).attr("rx",4).attr("fill", boxFill).attr("opacity", opacity *0.15).attr("stroke", boxStroke).attr("stroke-width",2).attr("opacity", showCompact ?0.1:1); svg.append("text").attr("x", x + boxSize /2).attr("y", y +28).attr("text-anchor","middle").attr("font-size","16px").attr("fill", boxStroke).attr("font-weight","700").attr("opacity", showCompact ?0.3:1).text("x");if (i <2) { svg.append("text").attr("x", leftX + (i +1) * (boxSize +15) -8).attr("y", y +28).attr("text-anchor","middle").attr("font-size","18px").attr("fill","#6b7280").attr("opacity", showCompact ?0.3:1).text("+"); } } svg.append("text").attr("x", leftX).attr("y", y -12).attr("font-size","12px").attr("fill","#6b7280").attr("opacity", showCompact ?0.4:1).text("expanded: three copies");// Center: transition arrowconst centerX = W /2; svg.append("line").attr("x1", centerX -30).attr("y1", y + boxSize /2).attr("x2", centerX +30).attr("y2", y + boxSize /2).attr("stroke","#0d9488").attr("stroke-width",2.5); svg.append("polygon").attr("points",`${centerX +30},${y + boxSize /2}${centerX +23},${y + boxSize /2-4}${centerX +23},${y + boxSize /2+4}`).attr("fill","#0d9488");// Right panel: compact form (always shown, opacity changes)const rightX = W -140;const compactOpacity = showCompact ?1:0.2; svg.append("text").attr("x", rightX).attr("y", y +32).attr("font-size","28px").attr("fill","#0d9488").attr("font-weight","700").attr("opacity", compactOpacity).text("3"); svg.append("text").attr("x", rightX +32).attr("y", y +32).attr("font-size","28px").attr("fill","#0d9488").attr("font-weight","700").attr("opacity", compactOpacity).text("x"); svg.append("text").attr("x", rightX).attr("y", y -12).attr("font-size","12px").attr("fill","#6b7280").attr("opacity", showCompact ?1:0.4).text("shorthand");// Label for coefficient svg.append("text").attr("x", rightX).attr("y", H -20).attr("font-size","11px").attr("fill","#6b7280").attr("opacity", showCompact ?1:0.5).text("The 3 is the coefficient (the count).");return svg.node();}
Read it as: three of something, plus seven, gives twenty-two. The x is the placeholder — a name for the gap in your knowledge. The equals sign means both sides are equal, or have the same value.
What is x? The letter x is a placeholder — it marks the spot where a number belongs, but we don’t know what that number is yet. In the sentence “three bags and 7 dollars cost 22 dollars,” x stands for “bags.” We use x as a standard choice so everyone knows we’re hunting for an unknown. Once we solve, we find what x is — in this case, x = 5.
Interactive: Finding the placeholder. Drag the slider to try different values for the blank. When you fill the blank with the right number, both sides of the equation match.
Code
viewof placeholderVal = Inputs.range([0,10], {label:"Try a value for the blank:",step:1,value:5})
Code
{const val =Math.round(placeholderVal);const result =3* val +7;const target =22;const isCorrect = result === target;const W =560;const H =200;const svg = d3.create("svg").attr("viewBox",`0 0 ${W}${H}`).attr("width","100%").attr("style",`max-width:${W}px; font-family: inherit;`);// Left panel: English sentenceconst leftX =40;const leftY =50; svg.append("text").attr("x", leftX).attr("y", leftY).attr("font-size","15px").attr("fill","#1f2937").attr("font-weight","600").text("Three");// Blank box with valueconst blankX = leftX +80;const blankW =50;const blankH =32; svg.append("rect").attr("x", blankX).attr("y", leftY -20).attr("width", blankW).attr("height", blankH).attr("rx",4).attr("fill","#0d9488").attr("opacity",0.15).attr("stroke","#0d9488").attr("stroke-width",2); svg.append("text").attr("x", blankX + blankW /2).attr("y", leftY +2).attr("text-anchor","middle").attr("font-size","18px").attr("fill","#0d9488").attr("font-weight","700").text(val); svg.append("text").attr("x", blankX + blankW +8).attr("y", leftY).attr("font-size","15px").attr("fill","#1f2937").text("and $7 cost $22");// Evaluation belowconst evalY = leftY +40;const checkSymbol = isCorrect ?"✓":"⚠";const checkColor = isCorrect ?"#059669":"#d97706"; svg.append("text").attr("x", leftX).attr("y", evalY).attr("font-size","14px").attr("fill","#6b7280").text("Equation check: 3 × "); svg.append("text").attr("x", leftX +150).attr("y", evalY).attr("font-size","14px").attr("fill","#0d9488").attr("font-weight","700").text(val); svg.append("text").attr("x", leftX +185).attr("y", evalY).attr("font-size","14px").attr("fill","#6b7280").text("+ 7 ="); svg.append("text").attr("x", leftX +245).attr("y", evalY).attr("font-size","14px").attr("fill", isCorrect ?"#059669":"#d97706").attr("font-weight","700").text(result); svg.append("text").attr("x", leftX +280).attr("y", evalY).attr("font-size","14px").attr("fill","#6b7280").text("?");// Status messageconst statusY = evalY +30;if (isCorrect) { svg.append("text").attr("x", leftX).attr("y", statusY).attr("font-size","14px").attr("fill","#059669").attr("font-weight","700").text(checkSymbol +" This value works! x = "+ val +" is the solution."); } else {const diff =Math.abs(result - target);const direction = result < target ?"too low":"too high"; svg.append("text").attr("x", leftX).attr("y", statusY).attr("font-size","14px").attr("fill","#d97706").attr("font-weight","600").text(checkSymbol +" The result is "+ result +" — "+ direction +" by "+ diff +"."); }// Right panel: Symbolic equationconst eqX = W -180;const eqY =50; svg.append("text").attr("x", eqX).attr("y", eqY).attr("font-size","20px").attr("fill","#1f2937").attr("font-weight","700").text("3x + 7 = 22"); svg.append("text").attr("x", eqX).attr("y", eqY +28).attr("font-size","12px").attr("fill","#6b7280").text("(symbolic form)");// Divider line svg.append("line").attr("x1", W /2).attr("y1",20).attr("x2", W /2).attr("y2", H -20).attr("stroke","#e5e7eb").attr("stroke-width",1);return svg.node();}
An equation is a balance — two expressions in equilibrium.
11.3 Undoing operations
Before we solve equations, let’s understand the pattern of undoing.
Addition is undone by subtraction. If something was added, subtract it.
Subtraction is undone by addition. If something was subtracted, add it.
Multiplication is undone by division. If something was multiplied, divide.
Division is undone by multiplication. If something was divided, multiply.
Here’s a concrete example. Suppose I start with a mystery number, add 7 to it, and get 15. To find the mystery number, I undo the +7 by subtracting: 15 - 7 = 8. The starting number was 8.
The same pattern applies to solving equations. When you see something like 3x + 7 = 22, you undo the operations in reverse order: first undo the +7 (by subtracting), then undo the multiplication by 3 (by dividing). You work backwards through the steps until x stands alone.
The concept stays this simple, but what gets harder is when notation accumulates and equations hide inside larger problems.
Interactive: Undoing operations. Pick an operation and a value. Watch what happens when you apply it and then undo it. The mystery number always comes back.
{const op = undoOp;const val =Math.round(undoVal);const step =Math.round(undoStep);const mystery =8;let result, undoResult, opName, undoOpName;if (op ==="+") { result = mystery + val; undoResult = result - val; opName =`add ${val}`; undoOpName =`subtract ${val}`; } elseif (op ==="−") { result = mystery - val; undoResult = result + val; opName =`subtract ${val}`; undoOpName =`add ${val}`; } elseif (op ==="×") { result = mystery * val; undoResult = result / val; opName =`multiply by ${val}`; undoOpName =`divide by ${val}`; } elseif (op ==="÷") { result = mystery / val; undoResult = result * val; opName =`divide by ${val}`; undoOpName =`multiply by ${val}`; }const W =560;const H =320;const STEP_H =70;const svg = d3.create("svg").attr("viewBox",`0 0 ${W}${H}`).attr("width","100%").attr("style",`max-width:${W}px; font-family: inherit;`);// Title svg.append("text").attr("x", W /2).attr("y",24).attr("text-anchor","middle").attr("font-size","14px").attr("fill","#6b7280").attr("font-weight","600").text("Start → Apply → Undo → Back to start");// Step boxesconst steps = [ { title:"Mystery number",val: mystery,label: mystery.toString() }, { title:`Apply: ${opName}`,val: result,label: result %1===0? result.toString() : result.toFixed(2) }, { title:"Mystery number recovered",val: undoResult,label: undoResult %1===0? undoResult.toString() : undoResult.toFixed(2) } ];const boxW =140;const boxH =60;const boxSpacing = (W -60) /3;const startX =30;for (let i =0; i <Math.min(step +1,3); i++) {const x = startX + i * boxSpacing;const y =80;// Box svg.append("rect").attr("x", x).attr("y", y).attr("width", boxW).attr("height", boxH).attr("rx",6).attr("fill", i === step ?"#0d9488":"#e5e7eb").attr("opacity", i === step ?0.15:0.3).attr("stroke", i === step ?"#0d9488":"#d1d5db").attr("stroke-width",2);// Title svg.append("text").attr("x", x + boxW /2).attr("y", y +16).attr("text-anchor","middle").attr("font-size","11px").attr("fill","#6b7280").text(steps[i].title);// Value svg.append("text").attr("x", x + boxW /2).attr("y", y +44).attr("text-anchor","middle").attr("font-size","22px").attr("fill", i === step ?"#0d9488":"#1f2937").attr("font-weight","700").text(steps[i].label);// Arrow between boxes (except after last)if (i <2) {const arrowX = x + boxW +20;const arrowY = y + boxH /2; svg.append("line").attr("x1", arrowX).attr("y1", arrowY).attr("x2", arrowX +25).attr("y2", arrowY).attr("stroke", i < step ?"#0d9488":"#d1d5db").attr("stroke-width",2); svg.append("polygon").attr("points",`${arrowX +25},${arrowY}${arrowX +20},${arrowY -3}${arrowX +20},${arrowY +3}`).attr("fill", i < step ?"#0d9488":"#d1d5db"); } }// Annotationconst noteY =180;if (step ===0) { svg.append("text").attr("x",30).attr("y", noteY).attr("font-size","13px").attr("fill","#475569").attr("font-weight","600").text("Start with a mystery number: 8"); } elseif (step ===1) { svg.append("text").attr("x",30).attr("y", noteY).attr("font-size","13px").attr("fill","#475569").text(`We ${opName}: 8 ${op ==="+"?"+": op ==="−"?"−": op ==="×"?"×":"÷"}${val} = ${result %1===0? result : result.toFixed(2)}`); } elseif (step ===2) { svg.append("text").attr("x",30).attr("y", noteY).attr("font-size","13px").attr("fill","#0d9488").attr("font-weight","700").text(`Now undo: ${undoOpName}`); svg.append("text").attr("x",30).attr("y", noteY +22).attr("font-size","13px").attr("fill","#6b7280").text(`${result %1===0? result : result.toFixed(2)}${op ==="+"?"−": op ==="−"?"+": op ==="×"?"÷":"×"}${val} = ${undoResult}`); }// Final messageif (step ===2) { svg.append("rect").attr("x",30).attr("y",240).attr("width", W -60).attr("height",60).attr("rx",6).attr("fill","#0d9488").attr("opacity",0.12).attr("stroke","#0d9488").attr("stroke-width",1.5); svg.append("text").attr("x", W /2).attr("y",260).attr("text-anchor","middle").attr("font-size","13px").attr("fill","#0d9488").attr("font-weight","700").text("✓ The mystery number is recovered!"); svg.append("text").attr("x", W /2).attr("y",282).attr("text-anchor","middle").attr("font-size","12px").attr("fill","#6b7280").text("This is how solving works: undo the operations in reverse order."); }return svg.node();}
Interactive: Equation balance. Try a few operations below. Watch both sides of the equation change together. The balance holds because you’re doing the same thing to both sides. Why do you think that matters?
viewof balanceVal = Inputs.range([-10,10], {label:"Value to apply to both sides",step:1,value:3})
Code
{// Starting equation: 3x + 7 = 22// We track the numeric sides as they stand after one operation.// Left side: 3x + 7 → represented symbolically// Right side: 22// For display we show the symbolic equation text and what each side becomes.const op = balanceOp;const val =Number.isFinite(balanceVal) ?Math.round(balanceVal) :3;// Guard against division by zeroconst divByZero = op ==="÷"&& val ===0;// Represent the original equation symbolicallyconst origLeft ="3x + 7";const origRight ="22";// Compute what happens to the right-hand side (a pure number)let newRight;if (op ==="+") newRight =22+ val;elseif (op ==="−") newRight =22- val;elseif (op ==="×") newRight =22* val;elseif (op ==="÷") newRight = divByZero ?null:22/ val;// Build a symbolic description of the new left sidelet newLeftDesc;const valStr = val <0?`(${val})`:`${val}`;if (op ==="+") newLeftDesc =`3x + 7 + ${valStr}`;elseif (op ==="−") newLeftDesc =`3x + 7 − ${valStr}`;elseif (op ==="×") newLeftDesc =`${valStr}(3x + 7)`;elseif (op ==="÷") newLeftDesc = divByZero ?"3x + 7 ÷ 0":`(3x + 7) ÷ ${valStr}`;// Displayconst W =560;const H =260;const MID_X = W /2;const BEAM_Y =120;const PIVOT_Y =80;const PAN_Y =170;const PAN_W =150;const PAN_H =44;const ROPE_H =50;const svg = d3.create("svg").attr("viewBox",`0 0 ${W}${H}`).attr("width","100%").attr("style",`max-width:${W}px; font-family: inherit;`);// ── Pivot post ── svg.append("line").attr("x1", MID_X).attr("y1", PIVOT_Y -10).attr("x2", MID_X).attr("y2", BEAM_Y).attr("stroke","#9ca3af").attr("stroke-width",3); svg.append("circle").attr("cx", MID_X).attr("cy", PIVOT_Y -10).attr("r",6).attr("fill","#6b7280");// ── Beam ── svg.append("line").attr("x1",80).attr("y1", BEAM_Y).attr("x2", W -80).attr("y2", BEAM_Y).attr("stroke","#374151").attr("stroke-width",4).attr("stroke-linecap","round");// ── Ropes ──const leftPanCX =130;const rightPanCX = W -130; svg.append("line").attr("x1", leftPanCX).attr("y1", BEAM_Y).attr("x2", leftPanCX).attr("y2", PAN_Y - PAN_H /2).attr("stroke","#9ca3af").attr("stroke-width",2); svg.append("line").attr("x1", rightPanCX).attr("y1", BEAM_Y).attr("x2", rightPanCX).attr("y2", PAN_Y - PAN_H /2).attr("stroke","#9ca3af").attr("stroke-width",2);// ── Pans ──const panFill ="#f3f4f6";const panStroke ="#d1d5db"; svg.append("rect").attr("x", leftPanCX - PAN_W /2).attr("y", PAN_Y - PAN_H /2).attr("width", PAN_W).attr("height", PAN_H).attr("rx",6).attr("fill", panFill).attr("stroke", panStroke).attr("stroke-width",1.5); svg.append("rect").attr("x", rightPanCX - PAN_W /2).attr("y", PAN_Y - PAN_H /2).attr("width", PAN_W).attr("height", PAN_H).attr("rx",6).attr("fill", panFill).attr("stroke", panStroke).attr("stroke-width",1.5);// ── Pan labels: new expressions ──const leftLabel = divByZero ?"÷ 0 not defined": newLeftDesc;const rightLabel = divByZero ?"÷ 0 not defined": (Number.isInteger(newRight) ?`${newRight}`: newRight.toFixed(2)); svg.append("text").attr("x", leftPanCX).attr("y", PAN_Y +5).attr("text-anchor","middle").attr("fill", divByZero ?"#dc2626":"#1f2937").attr("font-size", divByZero ?"10px":"12px").attr("font-weight","700").text(leftLabel); svg.append("text").attr("x", rightPanCX).attr("y", PAN_Y +5).attr("text-anchor","middle").attr("fill", divByZero ?"#dc2626":"#1f2937").attr("font-size","14px").attr("font-weight","700").text(rightLabel);// ── Equation line above balance ──const origEq =`Original: ${origLeft} = ${origRight}`; svg.append("text").attr("x", MID_X).attr("y",32).attr("text-anchor","middle").attr("fill","#374151").attr("font-size","13px").attr("font-weight","600").text(origEq);// ── Operation applied ──const opSymbol = op ==="÷"?"÷": op ==="×"?"×": op;const opDesc = divByZero?"Cannot divide by zero — the balance breaks":`Apply ${opSymbol}${val} to both sides`; svg.append("text").attr("x", MID_X).attr("y",52).attr("text-anchor","middle").attr("fill", divByZero ?"#dc2626":"#0d9488").attr("font-size","12px").text(opDesc);// ── "Both sides equal" label ──if (!divByZero) { svg.append("text").attr("x", MID_X).attr("y", H -12).attr("text-anchor","middle").attr("fill","#6b7280").attr("font-size","11px").text("Both sides still describe the same amount — the balance holds."); }// ── "=" centred between pans ── svg.append("text").attr("x", MID_X).attr("y", PAN_Y +6).attr("text-anchor","middle").attr("fill","#6b7280").attr("font-size","18px").attr("font-weight","700").text("=");return svg.node();}
11.4 The method
Why do we apply operations to both sides? Think about the balance scale from before. If two sides are balanced and equal in weight, and you add weight to only one side, the balance tips — they’re no longer equal. But if you add the same weight to both sides, the balance holds.
An equation is the same idea. The equals sign says both sides are the same amount. If you change one side without changing the other, they’re no longer the same — you’ve broken the equation. Doing the same operation to both sides keeps the equation true.
This means we are not guessing the answer. We are changing the equation without breaking it.
3x + 7 = 22
Subtract 7 from both sides — this removes the 7 from the left, leaving just the term with x:
3x = 15
Divide both sides by 3 — this peels away the coefficient, leaving x alone:
x = 5
That’s it. Two operations, one unknown, done.
In the equation 3x = 15, the number 3 multiplies the unknown x. That multiplying number is called the coefficient. So “3 is the coefficient of x.”
Why we can trust this method
The equals sign is a contract: both sides promised to be the same. When you do the same operation to both sides, you keep that promise intact. You’re not guessing or bending the rules — you’re following the logic that makes an equation an equation.
Interactive: Equation solver. Set the coefficients a, b, and c in the equation ax + b = c. The solution steps update live with your numbers. Try setting a = 0. What happens? Why can’t we solve if a = 0?
Code
viewof solveA = Inputs.range([-10,10], {label:"a (coefficient of x)",step:1,value:3})
Code
viewof solveB = Inputs.range([-20,20], {label:"b (constant on left)",step:1,value:7})
{const a =Number.isFinite(solveA) ?Math.round(solveA) :3;const b =Number.isFinite(solveB) ?Math.round(solveB) :7;const c =Number.isFinite(solveC) ?Math.round(solveC) :22;const noSolution = a ===0;const cMinusB = c - b;const x = noSolution ?null: cMinusB / a;// Format a coefficient: omit "1x" → "x", handle negatives neatlyconst coeffStr = (n) => {if (n ===1) return"x";if (n ===-1) return"−x";if (n <0) return`${n}x`;return`${n}x`; };// Format the left side "ax + b" nicelyconst leftSide = () => {const aPart =coeffStr(a);if (b ===0) return`${aPart}`;if (b >0) return`${aPart} + ${b}`;return`${aPart} − ${Math.abs(b)}`; };const W =560;const H = noSolution ?130:200;const ROW_H =38;const LABEL_X =20;const EQ_X =180;const svg = d3.create("svg").attr("viewBox",`0 0 ${W}${H}`).attr("width","100%").attr("style",`max-width:${W}px; font-family: inherit;`);// Header: equation as given svg.append("text").attr("x", LABEL_X).attr("y",28).attr("fill","#1f2937").attr("font-size","15px").attr("font-weight","700").text(`Solve: ${leftSide()} = ${c}`);if (noSolution) { svg.append("text").attr("x", LABEL_X).attr("y",70).attr("fill","#dc2626").attr("font-size","13px").attr("font-weight","600").text("a = 0: no unique solution (x drops out of the equation)."); svg.append("text").attr("x", LABEL_X).attr("y",96).attr("fill","#6b7280").attr("font-size","12px").text(b === c?`With a = 0 the equation reads ${b} = ${c}, which is always true — any x works.`:`With a = 0 the equation reads ${b} = ${c}, which is never true — no x works.` ); } else {// Step 1const step1Y =64; svg.append("text").attr("x", LABEL_X).attr("y", step1Y).attr("fill","#6b7280").attr("font-size","12px").text(`Step 1 — subtract ${b <0?`(${b})`: b} from both sides`+ (b <0?" [adding a negative]":"")+":");const bStr = b <0?`+ (${b})`:`− ${b}`; svg.append("text").attr("x", EQ_X).attr("y", step1Y +20).attr("fill","#1f2937").attr("font-size","14px").attr("font-weight","600").text(`${coeffStr(a)} = ${c}${bStr} = ${cMinusB}`);// Step 2const step2Y = step1Y +56; svg.append("text").attr("x", LABEL_X).attr("y", step2Y).attr("fill","#6b7280").attr("font-size","12px").text(`Step 2 — divide both sides by ${a <0?`(${a})`: a}:`);const xDisplay =Number.isInteger(x)?`${x}`: x.toFixed(4).replace(/\.?0+$/,""); svg.append("text").attr("x", EQ_X).attr("y", step2Y +20).attr("fill","#1f2937").attr("font-size","14px").attr("font-weight","600").text(`x = ${cMinusB} ÷ ${a} = ${xDisplay}`);// Answer boxconst ansY = step2Y +46; svg.append("rect").attr("x", EQ_X -8).attr("y", ansY -20).attr("width",220).attr("height",32).attr("rx",6).attr("fill","#0d9488").attr("opacity",0.12).attr("stroke","#0d9488").attr("stroke-width",1.5); svg.append("text").attr("x", EQ_X +2).attr("y", ansY).attr("fill","#0d9488").attr("font-size","15px").attr("font-weight","700").text(`Answer: x = ${xDisplay}`);// Checkconst checkY = ansY +28;const checkLeft = a * (Number.isInteger(x) ? x :parseFloat(xDisplay)) + b;const checkRight = c;const checkOk =Math.abs(checkLeft - checkRight) <1e-9; svg.append("text").attr("x", LABEL_X).attr("y", checkY).attr("fill", checkOk ?"#6b7280":"#dc2626").attr("font-size","11px").text(checkOk?`Check: ${a}×${xDisplay} + ${b} = ${checkLeft} ✓`:`Check: ${a}×${xDisplay} + ${b} = ${checkLeft} (expected ${c}) — rounding` ); }return svg.node();}
11.5 Worked examples
Example 1. You’re saving up for a festival wristband. The wristband costs $85. You already have $20 saved and earn $13 per hour doing odd jobs. How many hours do you need to work?
Let h = hours worked.
20 + 13h = 85
Interactive: The path to $85. The bar shows your savings growing. The teal segment is money already saved; each white segment is one hour of work at $13.
Notice: the money you already have is the constant term, and the hourly pay is the coefficient.
Example 2. Two streaming services. Service A costs $8 per month plus $2 per film you rent. Service B costs $18 per month with unlimited films. How many film rentals per month makes the two services equal in cost?
Let f = films rented per month.
Service A costs: 8 + 2f
Service B costs: 18
Interactive: Where the lines meet. Each point on the line shows the cost at that number of films. The intersection is the break-even point.
Code
viewof filmSlider = Inputs.range([0,10], {label:"Films rented per month",step:1,value:5})
Code
{const films =Math.round(filmSlider);const costA =8+2* films;const costB =18;const isBreakEven =Math.abs(costA - costB) <0.01;const W =560;const H =300;const ML =50, MR =40, MT =30, MB =50;const graphW = W - ML - MR;const graphH = H - MT - MB;const maxFilms =10;const maxCost =40;const xScale = (f) => ML + (f / maxFilms) * graphW;const yScale = (c) => H - MB - (c / maxCost) * graphH;const svg = d3.create("svg").attr("viewBox",`0 0 ${W}${H}`).attr("width","100%").attr("style",`max-width:${W}px; font-family: inherit;`);// Axes svg.append("line").attr("x1", ML).attr("y1", H - MB).attr("x2", W - MR).attr("y2", H - MB).attr("stroke","#d1d5db").attr("stroke-width",2); svg.append("line").attr("x1", ML).attr("y1", MT).attr("x2", ML).attr("y2", H - MB).attr("stroke","#d1d5db").attr("stroke-width",2);// Axis labels svg.append("text").attr("x", W /2).attr("y", H -10).attr("text-anchor","middle").attr("font-size","12px").attr("fill","#6b7280").text("Films rented per month"); svg.append("text").attr("x",20).attr("y", MT +14).attr("text-anchor","middle").attr("font-size","12px").attr("fill","#6b7280").text("Cost ($)");// Grid and tick marksfor (let f =0; f <= maxFilms; f +=2) {const x =xScale(f); svg.append("line").attr("x1", x).attr("y1", H - MB -4).attr("x2", x).attr("y2", H - MB +4).attr("stroke","#d1d5db"); svg.append("text").attr("x", x).attr("y", H - MB +18).attr("text-anchor","middle").attr("font-size","11px").attr("fill","#6b7280").text(f); }for (let c =0; c <= maxCost; c +=10) {const y =yScale(c); svg.append("line").attr("x1", ML -4).attr("y1", y).attr("x2", ML +4).attr("y2", y).attr("stroke","#d1d5db"); svg.append("text").attr("x", ML -10).attr("y", y +4).attr("text-anchor","end").attr("font-size","11px").attr("fill","#6b7280").text("$"+ c); }// Service A line (8 + 2f)const points_A = [];for (let f =0; f <= maxFilms; f +=0.1) {const c =8+2* f;if (c <= maxCost) { points_A.push([xScale(f),yScale(c)]); } } svg.append("path").attr("d",`M ${points_A.map(p => p.join(",")).join(" L ")}`).attr("stroke","#2563eb").attr("stroke-width",3).attr("fill","none"); svg.append("text").attr("x",xScale(8) -20).attr("y",yScale(8+2*8) -8).attr("font-size","12px").attr("fill","#2563eb").attr("font-weight","700").text("Service A: $8 + $2f");// Service B line (18) svg.append("line").attr("x1",xScale(0)).attr("y1",yScale(18)).attr("x2",xScale(maxFilms)).attr("y2",yScale(18)).attr("stroke","#d97706").attr("stroke-width",3).attr("opacity",0.7); svg.append("text").attr("x",xScale(8.5)).attr("y",yScale(18) -12).attr("font-size","12px").attr("fill","#d97706").attr("font-weight","700").text("Service B: $18");// Current position markerconst x_current =xScale(films);const y_a =yScale(costA);const y_b =yScale(costB);// Vertical line from x-axis svg.append("line").attr("x1", x_current).attr("y1", H - MB).attr("x2", x_current).attr("y2",Math.min(y_a, y_b) -10).attr("stroke","#6b7280").attr("stroke-width",1).attr("stroke-dasharray","4,4");// Point on Service A svg.append("circle").attr("cx", x_current).attr("cy", y_a).attr("r",6).attr("fill","#2563eb").attr("stroke","#1e3a8a").attr("stroke-width",2); svg.append("text").attr("x", x_current -30).attr("y", y_a -8).attr("font-size","11px").attr("fill","#2563eb").attr("font-weight","700").text(`$${costA}`);// Point on Service B svg.append("circle").attr("cx", x_current).attr("cy", y_b).attr("r",6).attr("fill","#d97706").attr("stroke","#b45309").attr("stroke-width",2); svg.append("text").attr("x", x_current +30).attr("y", y_b -8).attr("font-size","11px").attr("fill","#d97706").attr("font-weight","700").text(`$${costB}`);// Break-even highlightif (isBreakEven) { svg.append("circle").attr("cx", x_current).attr("cy", y_b).attr("r",12).attr("fill","none").attr("stroke","#0d9488").attr("stroke-width",3); svg.append("text").attr("x", W /2).attr("y", MT +20).attr("text-anchor","middle").attr("font-size","13px").attr("fill","#0d9488").attr("font-weight","700").text(`✓ Break-even at ${films} films, $${costB}`); } else {const cheaper = costA < costB ?"A":"B"; svg.append("text").attr("x", W /2).attr("y", MT +20).attr("text-anchor","middle").attr("font-size","13px").attr("fill","#475569").text(`At ${films} films, Service ${cheaper} is cheaper.`); }return svg.node();}
They’re equal when:
8 + 2f = 18
2f = 10
f = 5
At 5 films per month they cost the same. Above 5 films, Service B is cheaper.
The solution is the break-even point — where both services cost exactly the same.
Example 3. You’re making a fruit drink by mixing juice concentrate with water. The concentrate is 60% real juice. You want the final drink to be 20% real juice. You start with 1 litre of concentrate. How much water do you add?
The amount of real juice stays fixed at 0.60 \times 1 = 0.6 litres. After adding w litres of water, the total volume is 1 + w litres, and you want the juice fraction to be 20%:
Interactive: Mixing juice and water. The juice amount is fixed at 0.6 L. Add water with the slider to dilute it to 20%.
{const water =Math.round(waterSlider *10) /10;const juice =0.6;const total =1+ water;const concentration = (juice / total) *100;const target =20;const isSolution =Math.abs(concentration - target) <1;const W =560;const H =220;const svg = d3.create("svg").attr("viewBox",`0 0 ${W}${H}`).attr("width","100%").attr("style",`max-width:${W}px; font-family: inherit;`);// Container: juice + waterconst containerX =40;const containerY =50;const containerW =120;const containerH =100;// Juice portionconst juiceHeight = (juice / total) * containerH; svg.append("rect").attr("x", containerX).attr("y", containerY + containerH - juiceHeight).attr("width", containerW).attr("height", juiceHeight).attr("fill","#d97706").attr("opacity",0.7);// Water portionconst waterHeight = containerH - juiceHeight; svg.append("rect").attr("x", containerX).attr("y", containerY).attr("width", containerW).attr("height", waterHeight).attr("fill","#3b82f6").attr("opacity",0.5);// Container outline svg.append("rect").attr("x", containerX).attr("y", containerY).attr("width", containerW).attr("height", containerH).attr("fill","none").attr("stroke","#1f2937").attr("stroke-width",2);// Labels svg.append("text").attr("x", containerX + containerW /2).attr("y", containerY + containerH +20).attr("text-anchor","middle").attr("font-size","12px").attr("fill","#6b7280").text(`${total.toFixed(1)} L total`);// Juice label svg.append("text").attr("x", containerX + containerW +10).attr("y", containerY + containerH - juiceHeight /2+5).attr("font-size","11px").attr("fill","#b45309").attr("font-weight","600").text(`Juice: 0.6 L (fixed)`);// Water labelif (water >0) { svg.append("text").attr("x", containerX + containerW +10).attr("y", containerY + waterHeight /2+5).attr("font-size","11px").attr("fill","#1e40af").attr("font-weight","600").text(`Water: ${water.toFixed(1)} L`); }// Concentration displayconst concX = containerX + containerW +180;const concY = containerY +30; svg.append("rect").attr("x", concX -50).attr("y", concY).attr("width",110).attr("height",60).attr("rx",6).attr("fill", isSolution ?"#0d9488":"#e5e7eb").attr("opacity",0.15).attr("stroke", isSolution ?"#0d9488":"#d1d5db").attr("stroke-width",2); svg.append("text").attr("x", concX).attr("y", concY +18).attr("text-anchor","middle").attr("font-size","14px").attr("fill","#6b7280").text("Concentration");const concPercent = concentration.toFixed(1); svg.append("text").attr("x", concX).attr("y", concY +48).attr("text-anchor","middle").attr("font-size","24px").attr("fill", isSolution ?"#0d9488":"#1f2937").attr("font-weight","700").text(`${concPercent}%`);// Statusconst statusY = containerY + containerH +50;if (isSolution) { svg.append("text").attr("x",40).attr("y", statusY).attr("font-size","13px").attr("fill","#0d9488").attr("font-weight","700").text(`✓ At ${water.toFixed(1)} L water, the drink is ${target}% juice.`); } else {const diff =Math.abs(concentration - target);const direction = concentration > target ?"too strong":"too weak"; svg.append("text").attr("x",40).attr("y", statusY).attr("font-size","13px").attr("fill","#d97706").text(`The mixture is ${direction}: ${concPercent}% juice (need ${target}%).`); } svg.append("text").attr("x",40).attr("y", statusY +22).attr("font-size","11px").attr("fill","#6b7280").attr("font-style","italic").text("The juice amount (0.6 L) never changes — only the total volume matters.");return svg.node();}
\frac{0.6}{1 + w} = 0.2
Multiply both sides by (1 + w) — to clear the fraction:
0.6 = 0.2(1 + w)
Expand the right side:
0.6 = 0.2 + 0.2w
Subtract 0.2 from both sides:
0.4 = 0.2w
Divide both sides by 0.2:
w = 2
Add 2 litres of water.
Key insight: the amount of juice stays fixed. Only the total volume changes.
11.6 Where this goes
This chapter turns a verbal relationship into algebra and solves for the missing value. The next chapters use the same machinery but ask different questions.
Inequalities ask which values satisfy a relationship rather than pinning down one exact answer, and graphing shows what happens when you let the unknown vary continuously.
Where this shows up
An electrical engineer writing Kirchhoff’s voltage law is solving a linear equation for current or voltage.
A nutritionist calculating how much of an ingredient hits a target percentage is solving a linear equation.
A financial analyst finding a break-even point is solving a linear equation.
A data scientist fitting a regression line is solving a system of linear equations.
The tool is the same. The domain changes.
11.7 Exercises
These are puzzles, not drills. Each one has a clean answer, but the interesting part is setting up the equation — translating the situation into the notation before you solve it.
A bus pass costs $1.50 to buy, then $0.90 per journey. You spend $9.60 in total. How many journeys did you take?
Code
makeStepperHTML(1, [ { op:"Write as an equation",eq:"1.50 + 0.90j = 9.60" }, { op:"Subtract 1.50 from both sides",eq:"0.90j = 8.10" }, { op:"Divide both sides by 0.90",eq:"j = 9" }, { op:"Check",eq:"1.50 + 0.90 \\times 9 = 1.50 + 8.10 = 9.60 \\checkmark" }])
A recipe makes 12 cookies using 180g of flour. You have 480g of flour. How many cookies can you make? (Set up an equation first. Don’t just divide.)
Code
makeStepperHTML(2, [ { op:"Write as an equation",eq:"\\frac{180}{12} = \\frac{480}{c}",note:"Flour per cookie is constant, so set up equal ratios." }, { op:"Simplify left side",eq:"15 = \\frac{480}{c}" }, { op:"Multiply both sides by c",eq:"15c = 480" }, { op:"Divide both sides by 15",eq:"c = 32" }, { op:"Check",eq:"\\frac{180}{12} = 15 \\text{ g/cookie},\\quad 15 \\times 32 = 480 \\checkmark" }])
Two cyclists start 120km apart and ride toward each other. One rides at 20km/h, the other at 25km/h. After how many hours do they meet?
Code
makeStepperHTML(3, [ { op:"Write as an equation",eq:"20t + 25t = 120",note:"Together they close 45 km each hour, starting 120 km apart." }, { op:"Collect like terms",eq:"45t = 120" }, { op:"Divide both sides by 45",eq:"t = \\frac{120}{45} = \\frac{8}{3} \\approx 2.67 \\text{ h}" }, { op:"Check",eq:"20 \\times \\tfrac{8}{3} + 25 \\times \\tfrac{8}{3} = \\tfrac{160}{3} + \\tfrac{200}{3} = \\tfrac{360}{3} = 120 \\checkmark" }])
When two quantities are changing in opposite directions and you want to know when they’re equal, the unknown ends up on both sides of the equation. The fix is simple: collect all the terms with the unknown on one side, and all the plain numbers on the other.
For example, if one thing is shrinking and another is growing, you might get something like 500 - 35t = 200 + 15t. Subtract 15t from both sides to collect the variable terms: 500 - 50t = 200. Then subtract 200: 300 = 50t. Divide by 50: t = 6. The same balance rule as before — it just takes one extra step to gather the unknowns first.
A water tank holds 800 litres and drains at 35 litres per minute. A second tank holds 200 litres and fills at 15 litres per minute. After how many minutes do they contain the same amount?
Code
makeStepperHTML(4, [ { op:"Write as an equation",eq:"800 - 35t = 200 + 15t",note:"Tank 1 decreases; tank 2 increases. Set them equal." }, { op:"Subtract 15t from both sides",eq:"800 - 50t = 200" }, { op:"Subtract 800 from both sides",eq:"-50t = -600" }, { op:"Divide both sides by −50",eq:"t = 12" }, { op:"Check",eq:"800 - 35 \\times 12 = 800 - 420 = 380;\\quad 200 + 15 \\times 12 = 200 + 180 = 380 \\checkmark" }])
You and two friends are splitting the cost of a group gift. The total cost is $96, but one friend already put in $18 as a deposit. How much does each of the three of you pay now?
Code
makeStepperHTML(5, [ { op:"Write as an equation",eq:"3p + 18 = 96",note:"Three people each pay p, plus the $18 already paid, equals $96." }, { op:"Subtract 18 from both sides",eq:"3p = 78" }, { op:"Divide both sides by 3",eq:"p = 26" }, { op:"Check",eq:"3 \\times 26 + 18 = 78 + 18 = 96 \\checkmark" }])