Word & Character Counter — JavaScript source
Live word, character, sentence, and paragraph counts plus reading-time estimate as you type.
This is the JavaScript implementation — the same logic the interactive tool runs, in a shareable, citable form.
/**
* word-counter - JavaScript
* =========================
* Polyglot showcase port of CosmoDev's "word-counter" tool, ported from
* the canonical TypeScript at src/lib/wordCount.ts (the live web library
* and unit-test surface).
*
* Display source - part of CosmoDev's polyglot tool pages
* (dev.cosmolabs.org), where each tool's pure logic is shown in six
* languages side by side. Pure string/data logic; no crypto.
*
* Public surface: countWords, countChars, countCharsNoSpaces,
* countSentences, countParagraphs, readingTimeMin.
*/
/**
* Count whitespace-separated words.
*
* trim() drops leading/trailing whitespace; split(/\s+/) then breaks on
* runs of whitespace. filter(Boolean) guards the empty-string case:
* "".split(/\s+/) === [""], which must not count as one word. Empty or
* whitespace-only input therefore yields 0.
*
* @param {string} text
* @returns {number}
*/
export function countWords(text) {
return text.trim().split(/\s+/).filter(Boolean).length;
}
/**
* Total character count, whitespace included.
*
* NOTE: JavaScript's string .length counts UTF-16 code units, so a single
* astral symbol (most emoji) counts as 2. The sibling ports count Unicode
* code points, which agrees for all BMP text; see their notes.
*
* @param {string} text
* @returns {number}
*/
export function countChars(text) {
return text.length;
}
/**
* Character count with all whitespace stripped.
*
* @param {string} text
* @returns {number}
*/
export function countCharsNoSpaces(text) {
return text.replace(/\s/g, '').length;
}
/**
* Count sentences: maximal runs of non-terminal characters followed by one
* or more sentence terminators (. ! ?).
*
* Text with no terminal punctuation ("Hello world") therefore counts as 0
* sentences - this matches the canonical behaviour.
*
* @param {string} text
* @returns {number}
*/
export function countSentences(text) {
const matches = text.match(/[^.!?]+[.!?]+/g);
return matches === null ? 0 : matches.length;
}
/**
* Count paragraphs separated by one or more blank lines (2+ newlines).
* Segments that are blank after trimming do not count.
*
* @param {string} text
* @returns {number}
*/
export function countParagraphs(text) {
return text
.split(/\n{2,}/)
.filter((segment) => segment.trim() !== '')
.length;
}
/**
* Estimated reading time in whole minutes at 200 words per minute.
*
* 0 for empty input, otherwise at least 1. Integer arithmetic -
* Math.floor((words + 100) / 200) - reproduces Math.round(words / 200)
* exactly for non-negative words and keeps every language port in lock-step
* (Python's round() is banker's rounding, which would diverge on .5).
*
* @param {number} words
* @returns {number}
*/
export function readingTimeMin(words) {
if (words <= 0) return 0;
return Math.max(1, Math.floor((words + 100) / 200));
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →