A Hemingway-style readability tool. One small, dependency-free engine; two front ends. It highlights what makes prose hard to read: dense sentences, passive voice, adverbs, qualifiers, and complex words.
It is a clean-room reimplementation based on the public reverse-engineering of the Hemingway Editor (its sentence grading is the Automated Readability Index), with one real improvement: passive-voice detection catches irregular past participles ("was written", "was taken") that the original silently misses.
core/ plainsong.js the analyzer: pure, DOM-free, zero-dependency
wordlists.js the curated lists (adverb whitelist, qualifiers, complex words)
plainsong.test.mjs node test suite
web/ a single-page editor (transparent textarea over a highlight backdrop)
obsidian/ the Obsidian plugin (CodeMirror 6 live decorations + side panel + status bar)
The core/ engine is the single source of truth. The web app imports it
directly as an ES module; the Obsidian plugin bundles it with esbuild. Neither
front end re-implements any analysis.
analyze(text) returns { sentences, marks, stats }:
marks:[{ from, to, type, suggestion? }], character offsets into the text. Types:hard,veryHard(sentences);adverb,passive,qualifier,complex(words/phrases).sentences: per-sentence{ from, to, words, grade, level }.stats: words, sentences, reading time, document grade, Flesch-Kincaid, and per-category counts.
advise(mark) returns the click-to-fix payload: { heading, color, message, replacements, canRemove }, the issue explained in plain language plus the
concrete fixes. applyFix(text, mark, kind, value) returns the edited string and
new caret position. Both front ends use these so the suggestions are identical.
Click any highlight and a small card explains the issue and offers one-click fixes: pick a simpler word for a complex term, "remove it" for an adverb or qualifier, or read split/rewrite guidance for a hard sentence or passive voice. Choosing a fix edits the text immediately. (Web: a popover; Obsidian: a context menu.)
Sentence difficulty is ARI: grade = 4.71·(chars/word) + 0.5·(words/sentence) − 21.43.
A sentence must run 14+ words before it can be flagged (the length gate the
original uses, which people often miss).
node core/plainsong.test.mjs # 18 assertions
python3 -m http.server 5020 --bind 127.0.0.1
# open http://127.0.0.1:5020/web/index.html
No build step. Edit live; highlights update on every keystroke. Toggle them on and off; load a sample.
cd obsidian
npm install --legacy-peer-deps # Obsidian pins exact CodeMirror versions
npm run build # emits obsidian/main.js
# copy main.js, manifest.json, styles.css into <vault>/.obsidian/plugins/plainsong/
Live in-editor highlighting via CM6 decorations, a readability side panel ("Open readability panel" command), and a status-bar grade. Code blocks, inline code, and YAML frontmatter are skipped.
Highlighting is off by default; it's a pass you opt into. Turn it on with the ribbon icon (pencil-lines glyph) or the "Toggle highlights on/off" command; the choice persists. When off, the plugin emits no decorations at all. The default sentence treatment is a colored underline; switch to "Background" in settings for the classic Hemingway block-fill look. Categories can be toggled individually.
Sentence grading and the curated word lists derive from the Hemingway Editor, as documented in Sam W's "Deconstructing the Hemingway App." This project is an independent reimplementation for personal use.