Generating Natural-Sounding Text Without an LLM: A Report Comment Generator in Plain JavaScript
Teachers write hundreds of report card comments a term. Many reach for ChatGPT, but that usually means pasting student details into a third-party tool, which most school privacy policies forbid. So when I built a report comment generator, I set two rules: no AI model, and nothing typed leaves the b

Teachers write hundreds of report card comments a term. Many reach for ChatGPT, but that usually means pasting student details into a third-party tool, which most school privacy policies forbid. So when I built a report comment generator, I set two rules: no AI model, and nothing typed leaves the browser.
That structure is fixed. What varies is the wording, so the job is mostly about storing good phrases and fitting them together grammatically.
Strengths and next steps live in banks keyed by subject and year band. English and maths have separate early, middle and secondary banks, because "blends letter sounds" and "integrates quotations into an essay" don't belong in the same list.
var BANK = { " maths-mid ": { s: [ " recall multiplication facts quickly and accurately ", " measure and ^convert between metric units " ], n: [ " estimating answers first to check whether they are reasonable ", " showing all working in multi-step problems " ], h: " practising times tables for a few minutes each day " } }; Enter fullscreen mode Exit fullscreen mode Strengths are stored as verb phrases in their base form. Next steps are stored as "-ing" phrases, so they slot straight into "A next step is …" or "should focus on …".
The same strength has to work in two grammatical shapes:
Rather than storing both, I mark every verb after the first with a caret ( ^ ). The first word is always a verb, so a tiny conjugation function handles the rest:
function conj ( v ) { if ( v === " have " ) return " has "; if ( / [^ aeiou ] y$/. test ( v )) return v. slice ( 0, - 1 ) + " ies "; if ( / ( s|sh|ch|x|z|o ) $/. test ( v )) return v + " es "; return v + " s "; } function third ( phrase ) { var w = phrase. split ( " " ); w [ 0 ] = conj ( w [ 0 ]); return w. map ( function ( x ) { return x. charAt ( 0 ) === " ^ "? conj ( x. slice ( 1 )): x; }). join ( " " ); } function base ( phrase ) { return phrase. replace ( / \^ /g, "" ); } Enter fullscreen mode Exit fullscreen mode third("measure and ^convert") gives "measures and converts", and base() just strips the carets. Because I write every phrase in the bank myself, a handful of rules covers every verb that actually appears. There's no need for a full English conjugator.
Pronouns add one more wrinkle. With "they", the verb stays in its base form ("they measure"), so the code skips third() when the pronoun is plural.
Each achievement level carries its own opening sentence, and a flag that changes how strengths are phrased. For students working towards or below the expected level, strengths come out as "can now, with support, …" instead of a flat statement. That keeps comments honest without sounding harsh, which is the hardest part of writing them by hand.
If thirty comments in a class open the same way, parents notice. So build() assembles three versions from the same inputs, with different openings and orderings: one leads with the achievement level, one leads with a highlight, and one leads with what the student can do. The teacher picks one and edits it. Each variant shows a word count, because schools often set limits.
Some teachers still want an AI to polish the wording, so the tool also builds a prompt from the same inputs. It never includes the name the teacher typed. It uses a [Name] placeholder, and adds rules like "Only use the information above. Do not invent achievements, behaviours or grades." The teacher can paste that into their school's approved AI tool, and no identifying details go with it.
For narrow, structured writing, templates plus a carefully written phrase bank beat a language model on privacy, cost and predictability. The output never makes up a fact, and it works offline. The trade-off is that someone has to write the phrases well. That's where the real effort goes, not the code.
You can try the result at Toolbelt's free report comment generator. It covers Foundation to Year 10, and everything runs in your browser.