Create a readability-scoring function
Rate text columns for reading difficulty with a Flesch reading-ease score in one formula.
Published Jul 16, 2025
Northwind’s content team has a house rule: client-facing proposals should score 60 or higher on the Flesch reading-ease scale — roughly the level a confident teenager reads comfortably. The trouble is that “is this too dense?” is a judgement call, and judgement calls drift. One writer’s “clear” is another’s “wall of text”.
This automation turns that rule into a number. =READABILITY() is a custom
function that takes a block of text and returns its Flesch reading-ease score,
so a draft sheet can flag a clause that has crept above the limit before it
ever reaches a client. It runs entirely in the spreadsheet — no API, no key,
no network call.
What you’ll need
- A Google Sheet with the text you want to score in a column — the content team
keeps each proposal paragraph in column A of a
Draftstab. - The function pasted into the script editor (Extensions → Apps Script) so
the spreadsheet can see
=READABILITY()as a formula. - Nothing else — the maths is self-contained.
The function
// Flesch reading-ease constants. These are fixed by the formula itself;
// they are named here only so the calculation reads clearly.
const FLESCH_BASE = 206.835;
const FLESCH_SENTENCE_WEIGHT = 1.015;
const FLESCH_SYLLABLE_WEIGHT = 84.6;
/**
* Returns the Flesch reading-ease score for a block of text. Higher is
* easier; Northwind aims for 60+. Type =READABILITY(A2) into a cell.
*
* @param {string} text The text to score.
* @return {number} The reading-ease score, or "" if the cell is empty.
* @customfunction
*/
function READABILITY(text) {
// Empty cell — return nothing rather than a misleading score.
if (!text) return '';
// 1. Count sentences by sentence-ending punctuation. Default to 1 so a
// fragment with no full stop does not divide by zero.
const sentences = (String(text).match(/[.!?]+/g) || []).length || 1;
// 2. Split into words on whitespace, dropping empties.
const words = String(text).trim().split(/\s+/).filter(Boolean);
if (words.length === 0) return '';
// 3. Tally syllables across every word.
const syllables = words.reduce((sum, w) => sum + countSyllables(w), 0);
// 4. Apply the Flesch reading-ease formula.
return FLESCH_BASE
- FLESCH_SENTENCE_WEIGHT * (words.length / sentences)
- FLESCH_SYLLABLE_WEIGHT * (syllables / words.length);
}
/**
* Estimates the syllable count of a single word with a vowel-group
* heuristic. Not perfect, but close enough for a reading-ease score.
*
* @param {string} word A single word, punctuation included.
* @return {number} The estimated syllable count (at least 1).
*/
function countSyllables(word) {
// Strip to lower-case letters only.
const w = word.toLowerCase().replace(/[^a-z]/g, '');
// Very short words count as one syllable.
if (w.length <= 3) return 1;
// Drop a common silent ending and a leading "y", then count vowel
// groups — each run of one or two vowels is roughly one syllable.
const cleaned = w
.replace(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/, '')
.replace(/^y/, '');
return (cleaned.match(/[aeiouy]{1,2}/g) || []).length;
}
How it works
READABILITYis the function you call from the sheet. It returns an empty string for a blank cell so unscored rows stay tidy.- It counts sentences by matching runs of
.,!and?. If a cell has no sentence-ending punctuation it falls back to1, which avoids a divide-by-zero. - It splits the text into words on whitespace and counts them.
- It runs every word through
countSyllablesand sums the result. countSyllableslower-cases the word, treats anything three letters or shorter as a single syllable, strips a common silent ending, and counts vowel groups — each cluster of one or two vowels is taken as one syllable.- The Flesch reading-ease formula combines average sentence length and average syllables per word into a single score. Higher means easier to read.
Example run
Score two versions of the same proposal sentence side by side:
| Draft text | Formula | Score | Verdict |
|---|---|---|---|
| ”We will deliver the project on time and on budget.” | =READABILITY(A2) | 83 | Easy — well above 60 |
| ”The aforementioned deliverables shall be furnished in accordance with the contractually stipulated timeframe.” | =READABILITY(A3) | 9 | Far too dense — rewrite |
The second row is the kind of clause the content team wants to catch: a score in single digits is a clear signal to simplify before the proposal goes out.
Use it
Type the formula into a cell next to the text you want to score:
=READABILITY(A2)
Drag it down the column to score a whole draft paragraph by paragraph. Use this guide to read the result:
| Score | Reading level |
|---|---|
| 90–100 | 5th grader |
| 60–70 | 8th–9th grader |
| 30–50 | college |
| 0–30 | college graduate+ |
To make the rule visible, add conditional formatting that turns the score cell red when it drops below 60.
Watch out for
- Syllable counting is an estimate. The vowel-group heuristic gets most English words right but trips on unusual spellings and proper nouns — treat the score as a guide, not a verdict.
- The formula assumes English. Other languages have different syllable and sentence patterns, so the score will not mean much for non-English text.
- A cell with no full stop is treated as one long sentence, which inflates the average sentence length and pushes the score down. Score complete sentences for a fair result.
- Flesch reading ease measures structure, not sense. A grammatically simple but meaningless sentence can still score 90 — it does not replace a human read.
- Custom functions recalculate when the sheet does, not on a schedule. Editing the source cell re-runs the score automatically.
Related
Parse messy mixed-format dates
Normalise inconsistently formatted strings into real date values with a single formula.
Updated Aug 2, 2025
Scrape a web table into cells with one formula
Pull HTML tables into Sheets as a custom function — no IMPORTHTML quirks.
Updated Jul 30, 2025
Mask sensitive columns for shareable copies
Redact PII with a custom function so you can share a copy of the sheet without exposing names, emails, or numbers.
Updated Jul 26, 2025
Build a sentiment-scoring function without AI
Rate text positive or negative with a tiny built-in lexicon — no API key, no quota.
Updated Jul 23, 2025
Build a unit-conversion function library
Convert between any units with one custom formula — kg/lb, km/mi, °C/°F, and the rest.
Updated Jul 19, 2025