Generating Printable Math Worksheets in JavaScript: Seeded Randomness, Constraints and Answer Keys
"Just generate 20 random addition problems" sounds like a five-minute job. Then a teacher asks for problems with regrouping only, a parent wants to reprint the exact same sheet next week, and someone notices the answer k
"Just generate 20 random addition problems" sounds like a five-minute job. Then a teacher asks for problems with regrouping only, a parent wants to reprint the exact same sheet next week, and someone notices the answer key doesn't match after a refresh.
DWorksheets is a free generator for printable math worksheets (kindergarten through 6th grade), handwriting tracing sheets and quizzes. Here's the approach behind generating worksheets that are actually usable in a classroom.
1. Use a seeded PRNG, not Math.random()
Math.random() can't be reproduced. A seeded generator means the same seed always produces the same worksheet — so a sheet can be shared by URL, reprinted later, and its answer key always matches.
// mulberry32: small, fast, deterministic
function mulberry32(seed) {
return function () {
seed |= 0; seed = (seed + 0x6d2b79f5) | 0;
let t = Math.imul(seed ^ (seed >>> 15), 1 | seed);
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
const randInt = (rng, min, max) => min + Math.floor(rng() * (max - min + 1));
Put the seed in the URL (?seed=48213) and "print again next week" just works.
2. Generate against constraints, not just ranges
Teachers care about what kind of practice a problem gives. "Two-digit addition with regrouping" means at least one column must carry. A constraint check handles that:
function needsRegrouping(a, b) {
while (a > 0 || b > 0) {
if ((a % 10) + (b % 10) >= 10) return true;
a = Math.floor(a / 10); b = Math.floor(b / 10);
}
return false;
}
function additionProblem(rng, { digits = 2, regrouping = "any" }) {
const min = 10 ** (digits - 1), max = 10 ** digits - 1;
for (let tries = 0; tries < 1000; tries++) {
const a = randInt(rng, min, max), b = randInt(rng, min, max);
const r = needsRegrouping(a, b);
if (regrouping === "any" || (regrouping === "only") === r) return { a, b, answer: a + b };
}
throw new Error("constraints too strict");
}
The same pattern covers subtraction without negative answers, multiplication tables for a specific factor (e.g. grade 3 vs grade 4 multiplication), or division with no remainder.
3. Avoid duplicates and trivial problems
Twenty problems where three are "10 + 10" looks lazy. Keep a set of seen problems (normalizing commutative pairs), and filter out trivial ones like × 1 or + 0 unless the level calls for them:
function uniqueProblems(rng, n, make) {
const seen = new Set(), out = [];
while (out.length < n) {
const p = make(rng);
const key = [p.a, p.b].sort((x, y) => x - y).join("+");
if (!seen.has(key)) { seen.add(key); out.push(p); }
}
return out;
}
4. Map grade levels to presets
Parents and teachers think in grades, not parameters. A preset table turns "2nd Grade Math" into concrete settings while still letting people tweak them:
const presets = {
"kindergarten": { op: "+", digits: 1, max: 10 },
"grade-2": { op: "+", digits: 2, regrouping: "any" },
"grade-3-multiplication": { op: "×", factors: [2, 10] },
"grade-4-multiplication": { op: "×", digits: [2, 1] },
};
Each preset also gets its own landing page, so someone searching for "3rd grade multiplication worksheets" lands on a ready-to-print sheet.
5. Answer keys are a separate page
The answer key is generated from the same problem list and printed on its own page, so teachers can hand out the worksheet and keep the key:
@media print {
.no-print { display: none; }
.worksheet { break-after: page; }
.problem { break-inside: avoid; }
}
6. No accounts
Everything is free, unlimited and works without a login or credit card. Because worksheets are deterministic from their settings and seed, there's nothing that needs to be saved server-side.
Takeaways
- Seeded randomness makes worksheets shareable and reprintable.
- Encode pedagogy (regrouping, no negatives, no remainders) as constraints.
- De-duplicate and filter trivial problems.
- Use grade presets as the human interface to parameters.
Make a sheet at dworksheets.com. If you've built generators for educational content, I'd like to hear how you handled difficulty levels.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.