Skip to content

Add 10 math manipulatives (Michael / M Jones) - #11

Open
nakorly wants to merge 16 commits into
StriveMath:mainfrom
nakorly:mjones-manipulatives
Open

Add 10 math manipulatives (Michael / M Jones)#11
nakorly wants to merge 16 commits into
StriveMath:mainfrom
nakorly:mjones-manipulatives

Conversation

@nakorly

@nakorly nakorly commented Jul 15, 2026

Copy link
Copy Markdown

Adds 10 interactive math manipulatives (Michael / M Jones), each built to the sandbox conventions: one self-contained .jsx, registered in index.js, rendered in the shared 800x500 frame, HTML5 Canvas 2D + React, Tailwind.

Number & operations

  • Pizza Remainder (4.NBT.B.6 / 5.NBT.B.6): share pizzas onto plates by dragging; leftovers = remainder
  • Multiplying Fractions area model (5.NF.B.4): overlap of shaded bands = the product
  • Rounding on a Number Line (4.NBT.A.3 / 5.NBT.A.4): drag past the halfway mark; nearest 1 / 10 / 100
  • Mixed Numbers and Improper Fractions (4.NF.B.3c): fill whole-bars with fraction pieces

Expressions & equations

  • Balance Scale Equations (6.EE.B.5 / 7.EE.B.4): drag tiles off both pans to isolate x
  • Inequalities on a Number Line (6.EE.B.8): open/closed dot, shaded ray, draggable test number
  • Algebra Tiles (6.EE.A.3): add x, -x, 1, -1 tiles; drag opposites to cancel as zero pairs

Statistics & probability

  • Probability Scale Explorer (7.SP.C.5): drag events onto a 0 to 1 likelihood line, reveal true spots
  • Sample Space and Tree Diagrams (7.SP.C.8): build a compound-event tree, count outcomes, pick events
  • Mean Absolute Deviation (6.SP.B.5): drag data points; the mean +/- MAD band shows the spread

All build clean and were verified interactively (correct math, stable rendering, no console errors).

Generated with Claude Code.

nakorly and others added 16 commits July 3, 2026 13:36
- pizza-remainder (4.NBT.B.6 / 5.NBT.B.6): share pizzas by dragging them
  onto friends' plates; what can't be shared equally lands in the
  Remainder tray. Live fairness coaching; deal-a-round / share-for-me helpers.
- multiplying-fractions-area (5.NF.B.4): area model where the green overlap
  is the product; dimension brackets show each fraction as a part of the whole.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- balance-scale-equations (6.EE.B.5 / 7.EE.B.4): solve linear equations on a
  balance. Drag x/unit tiles off the pans; removing from one side only tips
  the scale (unbalanced) so students learn to do the same to both sides.
  Live equation updates as tiles change; solve detection reveals x = N.
  Four preset problems including variables-on-both-sides.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- rounding-number-line (4.NBT.A.3 / 5.NBT.A.4): drag a number on the line
  between its two bracketing benchmarks; the dashed halfway mark is the
  visible tiebreaker and the nearer 10/100 is highlighted with a "rounds to"
  arrow. Toggle nearest-10 / nearest-100; hide-answer to predict.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pizza Remainder: fixed pizza size (tall stacks now wrap into columns instead
of shrinking); removed the "Share for me" shortcut; show a bold 0 in the
Remainder tray when there is no remainder.

Balance Scale: fixed the shaking (round canvas size to whole device pixels so
it no longer resizes every frame; decouple the tilt-ease loop from re-renders
so a drag can't restart it). Replaced the row of all equations with a dropdown.

Rounding: wider spacing (0-50 for nearest 10, 0-500 for nearest 100); added a
Nearest 1 mode that uses decimals (0-5); replaced the parabola with a straight
green arrow whose head sits over the target; added a ghost-point animation that
floats to the rounded value when you release the number.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- mixed-numbers-improper (4.NF.B.3c): fill "whole" bars with fraction pieces;
  drag or step to see an improper fraction and its mixed number as one amount.
- inequalities-number-line (6.EE.B.8): open/closed boundary dot, shaded solution
  ray with arrow, and a draggable test number that shows a check or cross.
- algebra-tiles (6.EE.A.3): add x / -x / 1 / -1 tiles and drag opposites together
  to cancel as zero pairs; the simplified expression updates live.

Built to the shared build patterns (fixed sizing, drag, dpr-rounded canvas,
color=meaning, dropdown/stepper controls).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Read the unscaled contentRect (stable under the parent's scale transform),
defer the update via requestAnimationFrame, and only commit whole-pixel
changes — so the size observer can't feed back into itself and jitter the beam.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Measure the canvas size in useLayoutEffect (before first paint, so there's no
default-width -> measured-width jump), and track the stable unscaled contentRect
via an rAF-deferred, whole-pixel-guarded ResizeObserver. Verified in-browser:
all 7 report 0 idle redraws and a single stable canvas width.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- probability-scale (7.SP.C.5): drag event chips onto a 0->1 likelihood line,
  then reveal the true positions and values.
- sample-space-tree (7.SP.C.8): pick a compound experiment; a tree lists every
  outcome and counts them as a x b; click outcomes to form an event.
- mean-absolute-deviation (6.SP.B.5): drag data points on a number line; each
  distance to the mean is drawn and averaged into the live MAD.

Verified in-browser: math correct (MAD 3.5, 12=2x6), stable (0 idle redraws),
no console errors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- MAD: add a shaded mean +/- MAD band hugging the number line and a two-sided
  span label; tighten vertical layout; friendly empty state.
- Sample space tree: label branch probabilities (1/a, 1/b) and each outcome as
  1/N; highlight the full root->branch->leaf path when an outcome is selected.
- Probability scale: tighten the scale/tray spacing, add the 1/2 anchor
  (0.5 = 1/2 = 50% = even chance), and on reveal mark where you guessed vs the
  true green spot.

Verified: math correct, no console errors across empty/switch/reveal edge cases.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Events with close true probabilities (0 & 1/6, 1/2 & 2/3) made the wide chips
collide when snapping to true positions on reveal. Now chips stay where the
student guessed (spread out), thin green ticks mark the true spots, dashed lines
link each guess to its truth, and the score reports how many landed within 10%.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Feedback from the all-hands: a manipulative is "the carrot and the knife" — it
visualises the concept; the teacher supplies the procedure and the question.
No steps, no challenges, and nothing hardcoded (the worksheet AI supplies the
example, so presets are only starting points).

- balance-scale: x is now solved from whatever tiles are on the pans, so any
  equation works. Add x/1 tiles to either pan, empty the pans, or load a preset
  as a starter. Correctly reports identities and no-solution equations.
- algebra-tiles: added the equals divider so both sides of an equation can be
  modelled; zero pairs only cancel on the same side.
- sample-space-tree: stages are now free text, so any two-stage experiment can
  be defined; the three experiments remain as starters.
- probability-scale: events are editable (accepts 1/6, 0.5 or 50%); dropped the
  score/quiz in favour of a plain Show/Hide answers toggle.
- Added Hide answer to MAD, inequalities and the tree so the teacher can pose
  the question themselves.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
MAD, Sample Space Tree and Inequalities each had one all-or-nothing
"Hide answer" button. Each actually has two independent ideas, so split
them into per-layer toggle chips the teacher can peel back one at a time:

- MAD: Show distances (orange) / Show MAD (green)
- Tree: Show count (purple) / Show probabilities (blue)
- Inequalities: Show solution ray (green) / Show check (blue)

Each chip carries a dot in its layer's colour so the control and the thing
it reveals read as the same idea. Follows the pattern in Asha's
polygon-interior-angles.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
package-lock.json: reverted. Running npm on Windows stripped the
libc: ["glibc"] fields from Linux-only optional deps, which would break
installs for anyone on Linux and in CI.

.claude/launch.json: local dev-server config, now gitignored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The ten manipulatives had drifted into ten copies of the same code. The
canvas-sizing effect in particular was copy-pasted everywhere, which meant
each of its three bug fixes (measure before paint, use the unscaled
contentRect, rAF-defer and commit whole pixels) had to be applied ten times.

Extracted to src/manipulatives/shared/:
- palette.js      the three neutrals were identical in all ten files, and the
                  accents were too, just under a different name each time
                  (solGreen, meanGreen, posX, roundGreen = all #1D9E75).
                  Imported with aliases so each file still names a colour for
                  what it means there. Domain colours (pizza cheese, beam
                  wood) stay local — they mean nothing outside their file.
- useCanvasBox.js the sizing effect, with the reasons for its shape written
                  down once instead of zero times.
- ToggleChip.jsx  was duplicated three ways after the layered-reveal change.
- Stepper.jsx     was written three times and had drifted (different column
                  widths, `color` vs `accent`, different font sizes).
- GhostButton.jsx the neutral preset/reset pill, repeated in every file.

Accessibility: every canvas was an unlabelled box to a screen reader, which
in a manipulative means the entire content was missing. Each now carries
role="img" and an aria-label describing the live maths state ("Balance scale:
left pan holds x + 3, right pan holds 5..."). Symbol-only buttons (the < ≤ >
≥ operator row, the +x/-x/+1/-1 tile buttons) got labels and aria-pressed
state; the event-editor inputs got accessible names.

No user-facing behaviour change. Verified all ten live: canvas dimensions
stable across frames (no sizing bounce), all drawing, console clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Algebra tiles (Pulkit asked for placement/order snapping): tiles used to
stay wherever the hand let go, so a built expression was scattered across
the mat and you could not read 2x + 3 = 7 in it. Tiles now snap into two
lanes per side — x tiles above, units below — and ease into place. Position
is derived from state rather than stored in it: a tile carries which side it
is on and an `ord` (the x it was dropped at), so dropping a tile between two
others slots it between them. Spacing tightens rather than wrapping, so a
side stays one readable row however many tiles are on it.

Reduced motion: added shared/motion.js. Every easing loop (tiles, balance
beam, probability chips, rounding ghost) now jumps straight to the end state
when the viewer asks for reduced motion — a manipulative that slides things
around is exactly what that setting is for. It also short-circuits when the
document is hidden, where requestAnimationFrame never fires: easing must not
be the only path to a correct frame, or a manipulative set up in a hidden
tab is still mid-flight when it is first shown.

Lint: `npm run lint` was already failing before this branch — 7 errors and
2 warnings, all in our files. Now clean:
- pizza: counters reassigned inside a map callback -> stepped on an object,
  matching what the neighbouring perPlateIdx already did.
- rounding: performance.now() called in the render body -> the animation
  start is stamped by its first frame instead.
- algebra tiles: AddBtn was declared during render, making it a new
  component type every render (remount instead of update) -> module scope.
- balance: unused destructured binding.
- the two exhaustive-deps warnings, one of which (pizza's missing
  status.trayCount) was a real staleness bug in the tray layout.

Verified live: all ten stable and drawing, console clean. Tile snapping
checked by measuring rendered tile centres (evenly spaced, correct lanes),
plus dragging across the equals and onto an opposite tile to zero-pair.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Five manipulatives still hid things behind a plain "Hide answer" button
while the other three had moved to coloured toggle chips, so the same idea
looked like two different controls depending on which one a teacher opened.
Rounding, mixed numbers, multiplying fractions, probability and pizza now
use the shared ToggleChip too, each in the colour of the thing it reveals.

Kept as single toggles rather than split into layers: these each reveal one
idea, and inventing a second layer to match the multi-layer ones would be
change for its own sake.

Also moved the last raw #E0DDD6 in a style prop onto the palette token; the
remaining ones are Tailwind class literals, which are consistent with each
other and not worth the churn.

Verified all ten live after the change: stable, drawing, aria-labelled,
every reveal control now reporting aria-pressed. Lint and build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@nakorly

nakorly commented Jul 19, 2026

Copy link
Copy Markdown
Author

Pushed three more commits tonight. No new manipulatives — this is a pass over the ten that are already here, plus the two things Pulkit called out in the last review.

Shared module (src/manipulatives/shared/) — the ten files had drifted into ten copies of the same code. The canvas-sizing effect was the worst of it: it took three bug fixes to get right (measure before first paint, read the unscaled contentRect so it doesn't feed back through the frame's CSS transform, rAF-defer and commit only whole pixels) and each fix had to be applied ten times. It's now useCanvasBox, with the reasons written down once. Same for the palette (the three neutrals were identical everywhere, and the accents were too — just under a different name in each file), ToggleChip, Stepper and GhostButton.

Every canvas now has a text equivalent. These are canvas manipulatives, so to a screen reader the entire content was one unlabelled box. Each canvas carries role="img" and an aria-label describing the live state — "Balance scale: left pan holds x + 3, right pan holds 5..." — and the symbol-only buttons (the < ≤ > ≥ row, the tile buttons) have labels and pressed state.

Algebra tiles snap into lanes (the placement/order snapping from the review). Tiles used to stay wherever the hand let go, so a built expression was scattered and you couldn't read 2x + 3 = 7 in it. They now snap into two lanes a side — x tiles above, units below — and ease into place. Position is derived rather than stored: a tile carries which side it's on and the x it was dropped at, so dropping one between two others slots it between them.

Reduced motion is respected everywhere something eases (tiles, beam, probability chips, rounding ghost). It also short-circuits when the document is hidden, where requestAnimationFrame never fires — easing shouldn't be the only path to a correct frame.

npm run lint is clean. Worth flagging that it was already failing before this branch — 7 errors and 2 warnings, all in our files. One was a real bug: pizza's tray layout was missing status.trayCount from its deps, so the tray could render stale. The others were React-rules violations (a component declared during render, counters reassigned inside a map callback, performance.now() in a render body).

Consistency: five manipulatives still hid things behind a plain "Hide answer" button while three had moved to coloured toggle chips. They all use the same chip now, in the colour of whatever it reveals.

Verified all ten in the browser after each stage: canvas dimensions stable across frames (no sizing bounce), all drawing, every canvas labelled, console clean. Tile snapping was checked by measuring rendered tile centres and by driving real pointer events to drag across the equals and zero-pair.

One open question from the review I never got an answer on: whether tiles should also snap into groups (all the x's together as a visible "2x") rather than just an ordered row. Happy to do it if that's what was meant.

@nakorly

nakorly commented Jul 19, 2026

Copy link
Copy Markdown
Author

@apulkit6 this is ready for your review whenever you have a moment.

Ten manipulatives, plus the two things you raised in the last review: the algebra tiles now snap into ordered lanes, and everything is more open-ended (presets kept as starting points rather than fixed problems). Details in the comment above.

I cannot request you as a reviewer directly since I am working from a fork without push access, hence the mention. Branch merges cleanly into main.

— Michael

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant