TEXT
Reading Time Estimator
Paste any text to instantly estimate reading time, speaking time, complexity, and more.
Reading speed
Slow 150 wpm
Average 200 wpm
Fast 300 wpm
—
Reading Time
—
Speaking Time
0
Words
0
Characters
0
Sentences
0
Unique Words
0
Avg Word Length
—
Longest Word
Complexity Analysis
Avg sentence length: — words
Reading level:
About this tool
This free reading time estimator updates live as you type. It calculates how long your text takes to read or speak aloud, and gives you a complexity breakdown based on sentence length.
Reading speeds: Slow readers average ~150 wpm, most adults read at ~200 wpm, and fast readers reach ~300 wpm. Speaking time is fixed at 130 wpm, suitable for presentations and narration.
Reading level is based on average sentence length: under 10 words per sentence is Easy, 10–20 is Medium, and over 20 is Complex.
Developer Reference
Core Algorithm & Standalone Script
Standalone, zero-dependency JavaScript implementation powering this tool. Free to inspect, copy, and build upon.
const textarea = document.getElementById('text-input');
const clearBtn = document.getElementById('clear-btn');
const wpmToggle = document.getElementById('wpm-toggle');
let selectedWpm = 200;
// WPM toggle
wpmToggle.addEventListener('click', e => {
const item = e.target.closest('.toggle-item');
if (!item) return;
wpmToggle.querySelectorAll('.toggle-item').forEach(i => i.classList.remove('active'));
item.classList.add('active');
selectedWpm = parseInt(item.dataset.wpm, 10);
update();
});
function formatTime(words, wpm) {
if (words === 0) return '—';
const mins = words / wpm;
if (mins < 1) return '< 1 min';
const h = Math.floor(mins / 60);
const m = Math.floor(mins % 60);
if (h > 0) return m > 0 ? `${h} hr ${m} min` : `${h} hr`;
return `${m} min`;
}
function flash(cardId) {
const el = document.getElementById(cardId);
if (!el) return;
el.classList.add('flash');
setTimeout(() => el.classList.remove('flash'), 150);
}
function setVal(id, cardId, val) {
const el = document.getElementById(id);
const newVal = String(val);
if (el && el.textContent !== newVal) {
if (cardId) flash(cardId);
el.textContent = newVal;
}
}
function update() {
const text = textarea.value;
const trimmed = text.trim();
// Word list
const wordList = trimmed === '' ? [] : trimmed.split(/\s+/).filter(w => w.length > 0);
const words = wordList.length;
// Chars
const chars = text.length;
// Sentences
const sentenceMatches = trimmed === '' ? [] : (trimmed.match(/[^.!?]*[.!?]+/g) || []);
const sentences = sentenceMatches.length;
// Unique words (case-insensitive, strip punctuation)
const cleanWords = wordList.map(w => w.toLowerCase().replace(/[^a-z0-9']/g, ''));
const unique = new Set(cleanWords.filter(w => w.length > 0)).size;
// Avg word length
const totalChars = cleanWords.reduce((s, w) => s + w.length, 0);
const avgWordLen = words > 0 ? (totalChars / words).toFixed(1) : '0';
// Longest word
const longestWord = words > 0
? wordList.reduce((a, b) => (a.replace(/[^a-zA-Z]/g,'').length >= b.replace(/[^a-zA-Z]/g,'').length ? a : b), '')
: '—';
// Avg sentence length
const avgSentLen = sentences > 0 ? (words / sentences).toFixed(1) : '—';
// Reading level
let level = 'none';
if (sentences > 0) {
const avg = words / sentences;
if (avg < 10) level = 'easy';
else if (avg <= 20) level = 'medium';
else level = 'complex';
}
// Times
const readTime = formatTime(words, selectedWpm);
const speakTime = formatTime(words, 130);
// Update DOM
setVal('val-readtime', 'card-readtime', readTime);
setVal('val-speaktime', 'card-speaktime', speakTime);
setVal('val-words', 'card-words', words);
setVal('val-chars', 'card-chars', chars);
setVal('val-sentences', 'card-sentences', sentences);
setVal('val-unique', 'card-unique', unique);
setVal('val-avgword', 'card-avgword', avgWordLen);
// Longest word
const longestEl = document.getElementById('val-longest');
if (longestEl) {
const display = longestWord.length > 16 ? longestWord.slice(0, 14) + '…' : longestWord;
if (longestEl.textContent !== display) longestEl.textContent = display;
longestEl.title = longestWord !== '—' ? longestWord : '';
}
// Complexity
setVal('val-avgsentlen', null, avgSentLen);
document.getElementById('badge-easy').classList.add('hidden');
document.getElementById('badge-medium').classList.add('hidden');
document.getElementById('badge-complex').classList.add('hidden');
if (level !== 'none') document.getElementById('badge-' + level).classList.remove('hidden');
// Chapter estimates
const chapterCard = document.getElementById('chapter-card');
if (words > 100) {
chapterCard.classList.remove('hidden');
const chapterWords = 2000;
const chapters = Math.ceil(words / chapterWords);
const chapterReadTime = formatTime(chapterWords, selectedWpm);
const lastChapterWords = words % chapterWords || chapterWords;
const lastChapterTime = formatTime(lastChapterWords, selectedWpm);
setVal('val-chapters', null, chapters);
setVal('val-chapter-readtime', null, chapterReadTime);
const detail = document.getElementById('chapter-detail');
if (chapters > 1) {
detail.textContent = `${chapters - 1} full chapter${chapters - 1 > 1 ? 's' : ''} of ~${chapterWords.toLocaleString()} words + 1 partial chapter of ~${lastChapterWords.toLocaleString()} words (${lastChapterTime} to read).`;
} else {
detail.textContent = `Your text fits in a single chapter of ~${words.toLocaleString()} words.`;
}
} else {
chapterCard.classList.add('hidden');
}
// Auto-resize textarea
textarea.style.height = 'auto';
textarea.style.height = Math.max(220, textarea.scrollHeight) + 'px';
}
textarea.addEventListener('input', update);
clearBtn.addEventListener('click', () => {
textarea.value = '';
textarea.style.height = '220px';
update();
textarea.focus();
});
update();