appscript.dev
Automation Intermediate Sheets

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 Drafts tab.
  • 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

  1. READABILITY is the function you call from the sheet. It returns an empty string for a blank cell so unscored rows stay tidy.
  2. It counts sentences by matching runs of ., ! and ?. If a cell has no sentence-ending punctuation it falls back to 1, which avoids a divide-by-zero.
  3. It splits the text into words on whitespace and counts them.
  4. It runs every word through countSyllables and sums the result.
  5. countSyllables lower-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.
  6. 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 textFormulaScoreVerdict
”We will deliver the project on time and on budget.”=READABILITY(A2)83Easy — well above 60
”The aforementioned deliverables shall be furnished in accordance with the contractually stipulated timeframe.”=READABILITY(A3)9Far 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:

ScoreReading level
90–1005th grader
60–708th–9th grader
30–50college
0–30college 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