Skill: one-two-step-equations
Lab: Balance-Scale Solver
Solve equations on a balance scale: apply any operation to both sides and watch the scale stay level until x stands alone.
Balance the equation: 2x + 3 = 11
Moves: 0. Goal: get x alone.
Try it
Challenge: solve 2x + 3 = 11 in exactly 2 moves.
Press Reset, then choose the two inverse operations in reverse order.
Not solved yet.
What to notice: the scale never tips — both pans always match, because every move hits both sides. Solving is just choosing moves that peel the equation down to x.
Strategy
Undo +/− before ×/÷ (reverse PEMDAS). Each move replaces the equation with a simpler equivalent one. When a pan holds only x, you are done — then verify by substitution.
// __LAB_BEGIN__ var LAB_API = (function(){ 'use strict'; /* equation stored as {a, b, c} meaning a*x + b = c */ function applyOp(eq, op, v){ var a = eq.a, b = eq.b, c = eq.c; if (op === 'add') return {a:a, b:b + v, c:c + v}; if (op === 'sub') return {a:a, b:b - v, c:c - v}; if (op === 'mul') return {a:a * v, b:b * v, c:c * v}; if (op === 'div') return {a:a / v, b:b / v, c:c / v}; return eq; } function fmt(eq){ var lhs; if (eq.a === 0) lhs = String(eq.b); else { var xs = eq.a === 1 ? 'x' : eq.a === -1 ? '−x' : eq.a + 'x'; lhs = eq.b === 0 ? xs : xs + (eq.b > 0 ? ' + ' + eq.b : ' − ' + (-eq.b)); } return lhs + ' = ' + eq.c; } function solved(eq){ if (eq.a !== 1) return false; return eq.b === 0; } return {applyOp:applyOp, fmt:fmt, solved:solved}; })(); // __LAB_END__
(function(){ 'use strict'; var NSVG = 'http://www.w3.org/2000/svg'; var EQNS = [{a:2,b:3,c:11},{a:3,b:-4,c:11},{a:4,b:2,c:18},{a:-2,b:5,c:-9}]; var eqi = 0, eq = {a:2,b:3,c:11}, moves = 0; function $(id){ return document.getElementById(id); } function el(name, attrs, parent){ var e = document.createElementNS(NSVG, name); for (var k in attrs) e.setAttribute(k, attrs[k]); parent.appendChild(e); return e; } function draw(){ var svg = $('scaleSvg'); while (svg.firstChild) svg.removeChild(svg.firstChild); el('rect', {x:300, y:200, width:40, height:30, fill:'#94A3B8'}, svg); el('line', {x1:320, y1:60, x2:320, y2:200, stroke:'#172033', 'stroke-width':5}, svg); el('line', {x1:150, y1:60, x2:490, y2:60, stroke:'#172033', 'stroke-width':5}, svg); el('polygon', {points:'305,60 335,60 320,85', fill:'#F4B740'}, svg); function pan(cx, label){ el('line', {x1:cx, y1:60, x2:cx - 45, y2:130, stroke:'#94A3B8', 'stroke-width':2}, svg); el('line', {x1:cx, y1:60, x2:cx + 45, y2:130, stroke:'#94A3B8', 'stroke-width':2}, svg); el('ellipse', {cx:cx, cy:136, rx:52, ry:12, fill:'#F1F7FF', stroke:'#94A3B8', 'stroke-width':2}, svg); var t = el('text', {x:cx, y:141, 'text-anchor':'middle', 'font-size':14, 'font-weight':700, fill:'#172033'}, svg); t.textContent = label; } var left = LAB_API.fmt({a:eq.a, b:eq.b, c:0}).replace(' = 0', ''); pan(200, left); pan(440, String(eq.c)); var st = el('text', {x:320, y:30, 'text-anchor':'middle', 'font-size':14, 'font-weight':700, fill:'#14B8A6'}, svg); st.textContent = 'balanced'; } function refresh(note){ $('labEq').textContent = LAB_API.fmt(eq); draw(); var done = LAB_API.solved(eq); $('labNote').textContent = 'Moves: ' + moves + '. ' + (note || '') + (done ? ' Solved — x = ' + eq.c + '. Verify by substitution.' : ' Goal: get x alone.'); if (done){ var cr = $('chResult'); if (eqi === 0){ if (moves === 2) cr.textContent = 'Solved in exactly 2 moves — perfect.'; } else cr.textContent = 'Solved in ' + moves + ' moves. Reset and try it in 2.'; cr.className = 'c-result pass'; } } function reset(){ eq = {a:EQNS[eqi].a, b:EQNS[eqi].b, c:EQNS[eqi].c}; moves = 0; $('labSteps').textContent = LAB_API.fmt(eq); refresh(''); } document.addEventListener('DOMContentLoaded', function(){ refresh(''); $('applyOp').addEventListener('click', function(){ var op = $('opSel').value, v = parseFloat($('opVal').value); if (isNaN(v)){ refresh('Enter a number for the value.'); return; } if (op === 'div'){ if (v === 0){ refresh('Cannot divide by zero.'); return; } } eq = LAB_API.applyOp(eq, op, v); moves++; var sym = op === 'add' ? '+' : op === 'sub' ? '−' : op === 'mul' ? '×' : '÷'; $('labSteps').textContent += '\n' + sym + v + ' both sides → ' + LAB_API.fmt(eq); refresh(''); }); $('resetEq').addEventListener('click', reset); $('newEq').addEventListener('click', function(){ eqi = (eqi + 1) % EQNS.length; $('chResult').textContent = 'Not solved yet.'; $('chResult').className = 'c-result'; reset(); }); }); })();