Our practice page grades answers with one line:
parse($('#practice-answer').value).eq(answer) If answer is undefined, that line throws. Not "marks it wrong" — throws, Cannot read properties of undefined (reading 'n').
That one line is why our question bank has no vertical-line question, and why three of our articles teach a case the tool will never test. I found it by running the bank through the site's own function instead of reading it.
How we measured
Two files off the live site: /assets/app.mjs (38,731 bytes, holds the question array) and /assets/math.mjs (9,985 bytes, holds slope() and the exact-fraction parse()).
I copied the array verbatim — ten [x1, y1, x2, y2] string arrays — and fed each one to the exported slope(). Node 22, ESM import, no DOM. Then I forced a vertical pair through the same function to see what the page would do with it.
For the grader I called the same parse().eq() chain the page calls, with eight inputs, and caught the throws instead of letting them print.
Two things I had to fix in my own harness along the way: JSON.stringify dies on BigInt, so I stringified coordinates by hand, and my first pass reported the vertical case as "m=undefined" without proving anything — the useful result was the thrown error, which only shows up if you actually call .eq().
The ten questions, in full
| # | From | To | Rise | Run | Slope |
|---|---|---|---|---|---|
| 1 | (1, 2) | (5, 4) | 2 | 4 | 1/2 |
| 2 | (0, 5) | (2, 1) | −4 | 2 | −2 |
| 3 | (−2, 3) | (4, 3) | 0 | 6 | 0 |
| 4 | (−1, −2) | (3, 6) | 8 | 4 | 2 |
| 5 | (2, 1) | (5, 2) | 1 | 3 | 1/3 |
| 6 | (0, 0) | (4, 3) | 3 | 4 | 3/4 |
| 7 | (−3, 4) | (1, 0) | −4 | 4 | −1 |
| 8 | (1, −2) | (5, −2) | 0 | 4 | 0 |
| 9 | (−2, −3) | (2, 5) | 8 | 4 | 2 |
| 10 | (1, 4) | (7, 5) | 1 | 6 | 1/6 |
Ten questions. Eight distinct answers.
Three things this set gets wrong
Not one of the ten has the same x on both points.
That means the set never asks for an undefined slope. Zero out of ten.
Our blog has 43 articles. Three of them are specifically about this: "When is a slope considered undefined?", "Undefined slope vs zero slope", and "When is a slope undefined? Two equal x-values". We teach the case in three places and refuse to test it in the only place a reader can act.
Here is why, and this is the part that matters. Feed a vertical pair into the page's own function and it returns mode: 'vertical' with no m at all. The render line then builds the explanation string by hand:
Slope = (6) / (0) = undefined And the answer check calls parse(input).eq(answer) where answer is undefined. That throws. Not "marks wrong" — throws. I ran it: Cannot read properties of undefined (reading 'n').
So the practice cannot ask a question it cannot grade, and the grader was never taught the one case the articles promise. The bank is not missing a hard question by accident. It is missing the question that would crash.
Two of the ten have the same answer as each other, twice over.
Answer 0 shows up at question 3 and again at question 8. Answer 2 shows up at question 4 and again at question 9. Different points, different lines, so nothing is technically repeated — but eight distinct answers spread over ten questions means two answers cover 20% of the bank each. A student who has not understood the sign rule yet can hit two right by guessing the round numbers.
And the set cycles between exactly two versions. The restart handler does set = (set + 1) % 2. Ten questions, two sets of five, then back to the start. Anyone who practises twice has seen everything, forever.
The public JavaScript ships our build notes in Chinese.
app.mjs: 589 lines, 90 of them contain Chinese characters, 82 of those are comments. math.mjs: 265 lines, 61 with Chinese, 54 comments. That is 151 comment lines going out to every visitor of an English-language site.
One of them, dated 2026-11-10, explains a bug we hit in production:
判断「这页有练习题」不能只看 #practice —— 文章正文里的
<h2 id="practice">会命中同一个 selector…
Roughly: don't detect "this page has practice questions" by looking for #practice, because a heading in an article body matches the same selector and then the code crashes on a null form. Someone spent an evening on that. It is now public, in Chinese, on a site for American students.
The site is English. Our notes are not. We minify the CSS and version it with a hash, and then ship the reasoning behind every function in a language our readers do not read.
One thing I wrote wrong this morning
Before I opened app.mjs, I wrote in our own notes that the practice page had 173 words and was the thinnest page on the site.
173 is what the HTML ships before the module runs. The page a reader actually sees has five questions, a hint button, a show-answer button and a score. The page is not thin. It has ten questions in total and cannot say so out loud. That is worse than thin, and I only found it because the first number I wrote down was wrong.
What this does not tell you
Whether ten questions is too few.
I have no completion data. The page fires an analytics event when someone asks for a hint and when someone gets one wrong. It does not fire anything when someone finishes, and it does not record whether a restart was a second attempt or a fifth. So I can count the bank and I can count the articles. I cannot tell you whether anyone is bored by question five, because we never measured that.
I also tested eight input formats for one answer. That is not proof that the grader accepts every reasonable way to type a slope. It is proof that it accepts eight of them, including 2/4 for 1/2 and .5 for 0.5.
Disclosure
I maintain Slope Calculator, which owns both files this post is about. The Chinese comments are mine, the 99.4%-identical pages on the other site are mine, and the checker that throws on undefined is mine. Publishing the audit is cheaper than pretending the numbers are not there.
Three articles about undefined slope, and the practice set will not touch it. Fixing that means teaching the grader what "undefined" is, which means changing the answer type, which means the next version of this page is not a content fix.
That is the order it has to happen in. I got it backwards for three weeks.


