Test script: Check grammar and spelling for free
The script we wrote before testing, with every amendment and its timestamp. The verdict it produced: Best free grammar checker you can run yourself: LanguageTool vs Harper.
Published as written, with private emails and internal file paths removed. Times are Eastern. How we score: the rubric.
- Job slug:
check-grammar-and-spelling - Version / status: v1 · frozen 2026-10-16 15:45 ET, before the first run. Changes after freeze go under “Amendments”, dated, with the reason.
- Rubric version: v1 (rubric)
- Author / date: Lando (CEO), 2026-10-16
1. The job
You write landing pages, docs, emails and posts, and want typos and grammar mistakes caught before you publish, without paying for a subscription or sending your drafts to someone else’s server. You’re done when the tool flags most real mistakes, suggests the right fix, and doesn’t bury you in false alarms on correct text (including the tech words builders use every day).
2. Candidates
| Tool | Version | How you get it | Terms check (2026-10-16) | Run mode |
|---|---|---|---|---|
| LanguageTool (open-source server) | 6.6 (latest release zip), English, default rules, no n-gram data | Download the zip from languagetool.org, run its HTTP server locally (needs Java 17+) | LGPL-2.1. Local server, no account. No benchmark clause. | Agent-driven |
| Harper | harper.js (latest on npm), American English, default rules | npm install harper.js, runs in Node | Apache-2.0. No account. No benchmark clause. | Agent-driven |
3. Fixed inputs
- Test set (published with the results, SHA-256
5fbacaff26bfe00d5efca12e968484ef4aeb86272192a794e0a7efaa76e5e2d0): 60 sentences written for this test.- 40 sentences with exactly one seeded mistake each: 10 spelling, 8 subject-verb agreement, 8 wrong word (its/it’s, their/they’re, effect/affect, then/than, should of, loose/lose), 6 a/an, 8 repeated word, punctuation or capitals.
- 20 correct sentences, 5 of them full of everyday builder words (npm, API, Supabase, Next.js, Tailwind, Postgres, GitHub, Cloudflare Pages, webhook).
- Each sentence is checked on its own, in American English.
4. Fixed task
Using only <tool>'s official docs: install it, start it locally, and check each of the 60 sentences with its default rules. Save every flag: position, message and suggestions.
5. Follow-up rules
- Time cap: 60 active minutes per tool to get from nothing to the first full set of results. Downloads are logged but excluded, up to 15 minutes.
- Allowed: official README, docs and error messages. Manual interventions (C2): any step the docs don’t cover.
6. Steps
- Record the start time; install following the docs; log each docs page used.
- Run all 60 sentences, network switched off for the run; record time per sentence and peak memory (
/usr/bin/time -von the server or Node process). - Run the check script; save its JSON.
7. Pass/fail checks
A seeded mistake counts as caught if any flag overlaps its exact position. It counts as fixed if applying the flag’s first suggestion gives exactly the corrected sentence. A false alarm is a correct sentence with any flag.
| ID | Check | Pass if |
|---|---|---|
| G1 [core] | Runs locally and returns results for all 60 sentences | Yes |
| G2 [core] | Catches mistakes | ≥ 70% of the 40 caught |
| G3 | Suggests the right fix | First suggestion fixes ≥ 60% of the 40 |
| G4 | Spelling | ≥ 9 of 10 spelling mistakes caught |
| G5 | Few false alarms | ≤ 3 of the 20 correct sentences flagged |
| G6 | Fast | Median time per sentence ≤ 200 ms (after startup) |
| G7 | Fits a normal laptop | Peak memory ≤ 2 GB |
8. Quality checklist (C5)
| ID | Item | Pass if |
|---|---|---|
| Q1 | Works where you write | Official integrations for at least 2 of: VS Code, a browser, Obsidian, Google Docs/Word |
| Q2 | Custom dictionary | Documented way to add your own words |
| Q3 | Turn rules off | Documented way to disable a rule |
| Q4 | Quickstart works as written | The docs’ install-and-check example runs unchanged |
| Q5 | Explains each flag | Every flag in the run has a human-readable message |
9. Privacy checklist (C7)
| ID | Item | Pass if |
|---|---|---|
| S1 | Text stays on your computer | Works with the network switched off |
| S2 | No telemetry | Nothing in the docs or the network-off run shows usage data being sent |
| S3 | Official source | Downloaded from the project’s official location over HTTPS |
| S4 | Licence allows commercial use | Yes, and stated clearly |
10. Weights and tie-breaker
- Weights: C1 35 · C2 15 · C3 10 · C4 10 · C5 10 · C6 10 · C7 10. C1: rubric anchor on the share of G1–G7 passed.
- C2 (manual interventions): 5 = 0–1 · 4 = 2–3 · 3 = 4–5 · 2 = 6–8 · 1 = more. C3 (T = 60 active min): 5 = ≤15 · 4 = ≤30 · 3 = ≤45 · 2 = ≤60 · 1 = cap hit.
- C4: 5 = $0 with no usage limits (runs locally). C5 and C7: checklist-count anchor.
- C6: 5 = open-source licence that allows commercial use with no conditions for using it as a local tool or server (MIT, Apache, LGPL), runs offline, documented API · 4 = strong copyleft (GPL/AGPL) · 1 = hosted only.
- Tie-breaker: C1, then fewer false alarms (G5), then more mistakes caught (G2).
11. Cleanup
- Delete the downloaded server, packages and Java runtime files used only for the test. Keep results, logs and check JSON.
Amendments
- v1.1 (2026-10-16, during the runs; none changes how a tool is scored):
- Java: the test machine runs Debian 13, which has no Java 17 package; we installed OpenJDK 21 (LanguageTool’s README asks for Java 17 or later). Not counted as an intervention: it’s the documented prerequisite.
- Memory log (15:46 ET, lt-r1): our run script’s shutdown command also stopped the
timeprocess, so the first run’s memory figure wasn’t written. We fixed our script and reran at once; the 60 results were identical to the first attempt. - Network off: every run ran in a network namespace with only the loopback interface up (
unshare -rn), which is how S1 and S2 were checked. - Second runs: the first runs tied at 100, so both tools ran again. Results were identical, so the tie-breaker decided (see scoring notes).
Scoring notes (2026-10-16)
- LanguageTool 6.6 (lt-r1, lt-r2), 100 both runs: 7 of 7. Caught 36 of 40 seeded mistakes and its first suggestion fixed all 36. Missed: “The results of the test shows”, “Their going to”, “Your welcome to”, and ”!!”. Spelling 10 of 10. False alarms on 3 of 20 correct sentences: “npm” (twice in one sentence), “Supabase”, and “the deploy” (it suggests “deployment”). Median 16–17 ms per sentence; 678 MB peak (Java server). C2 = 5, C3 = 5 (results 2 min after the download finished). C5 5 of 5: browser add-on can use the local server;
spelling_custom.txtfor your own words;disabledRules; the docs’ server command ran unchanged; every flag has a message. C6 = 5 (LGPL-2.1, used as a local server). C7 4 of 4. - Harper (harper.js 2.13.0) (harper-r1, harper-r2), 100 both runs: 7 of 7. Caught 29 of 40 (fixed 28). Missed 6 of 8 agreement mistakes (e.g. “Our servers was”, “There is three steps”), and “Its important”, “Your welcome”, “will not effect”, “could loose”, ”!!”. Spelling 10 of 10. False alarms on 3 of 20: “Supabase”, “js” in “Next.js”, and “Every one of the tests” (suggests “everyone”). Median 3 ms per sentence; about 500 MB peak (Node). C2 = 5, C3 = 5. C5 5 of 5 (VS Code, Chrome, Firefox, Obsidian; dictionary; rule config; the docs’ Node example ran unchanged; messages). C6 = 5 (Apache-2.0). C7 4 of 4.
- Tie-breaker: C1 tied (7 of 7), false alarms tied (3 and 3), then mistakes caught: LanguageTool 36, Harper 29. LanguageTool is the pick.
What to use now
A free weekly email.
We'll email you a link to confirm. We use Kit to send the email. Unsubscribe anytime. Privacy