
Character Frequency Analyzer
Paste any text to analyze character frequency with bar chart, percentages, and CSV export. Unicode-safe. 100% client-side.
Last reviewed: April 2026New to this tool? Click here for instructions
| # | Char | Code | Count ▼ | % | Frequency |
|---|
How to Use the Character Frequency Analyzer
To use the Character Frequency Analyzer, simply paste your text into the input area. Toggle options such as case sensitivity, showing spaces, and special characters. Sort the table by clicking any column header. Filter results using the search box. Export the frequency table as CSV or download it for further analysis.
When to Use the Tool in Real Workflows
Use the Character Frequency Analyzer when you need to analyze character distribution in text, such as in cryptography, linguistics, data quality checks, or text compression. It's particularly useful for identifying encoding issues, understanding language patterns, and optimizing keyboard layouts.
How It Works
The Character Frequency Analyzer processes the text you input, counting the occurrences of each character. It then displays the results in a table, showing the character, its count, percentage, and Unicode code point. You can sort the table by different criteria and filter for specific characters. The tool supports Unicode, making it versatile for multilingual content.
Tips, Edge Cases, or Limitations
Ensure your text is clean and free of formatting issues. The tool is case-sensitive, so be aware of case differences. It supports all Unicode characters, including emojis and special symbols. For very large texts, performance might be affected. Always review the results to ensure accuracy.
Frequently Asked Questions
Count and visualize how often each character appears in any text — instantly, in your browser. Paste a paragraph of prose, a block of source code, or a sample from an encrypted message, and the analyzer returns a ranked frequency table with percentages and a bar chart — no server round-trip, no data leaving your machine.
What This Tool Does
The Character Frequency Analyzer scans every unique character in your input — letters, digits, punctuation, whitespace, Unicode code points, emoji — and returns a count and a percentage of total characters for each one. Results appear in a sortable frequency table and a bar chart ordered highest-to-lowest by default. Three option chips let you toggle case-sensitive vs. case-insensitive mode, include or exclude whitespace, and include or exclude punctuation, so you can narrow the view to letters-only for classical frequency analysis or keep everything in scope for a full data audit. Export the results as CSV (Character, Count, Percentage columns) or copy as a JSON frequency map to pipe into a downstream script. All processing happens inside your browser tab using the JavaScript Intl.Segmenter API — no text is uploaded or logged anywhere.
🔒 Privacy: This tool runs 100% in your browser. Your input is not uploaded, stored, or logged anywhere outside your local session.
How to Use It
Step-by-step instructions
- Paste or type text into the input pane on the left. The character count in the status bar updates immediately as you type.
- Set option chips at the top: toggle Case-insensitive, Include whitespace, and Include punctuation to match your analysis goal. For classical English letter-frequency work, enable Case-insensitive and disable whitespace and punctuation.
- Click Analyze — or, if live mode is on, results refresh after each keystroke. The frequency map builds in a single O(n) pass over the input.
- Read the output pane: the sortable table lists every unique character ranked by count descending, and the bar chart below gives an at-a-glance visual. Click any column header to re-sort by character label or percentage.
- Export using the buttons described below.
Try Example button
Clicking Try Example pre-loads the pangram-plus-compression-sentence used throughout this article: "The quick brown fox jumps over the lazy dog. Data compression algorithms analyze character frequency to assign shorter bit sequences to common letters." That choice is deliberate — the pangram guarantees all 26 letters appear, so the frequency table exercises the full alphabet.
Export options
The CSV button downloads a file named char-frequency-YYYY-MM-DD.csv with three columns: Character, Count, Percentage. This imports cleanly into Excel, Python pandas (pd.read_csv()), or R. The Copy JSON button puts a {"char": count, ...} frequency map object on your clipboard, ready to paste into a Node script or a Jupyter notebook. Both exports respect the current filter settings — if whitespace is excluded from the table, it is excluded from the export.
Worked Example: Analyzing English Text Frequency
Sample input
- Text
The quick brown fox jumps over the lazy dog. Data compression algorithms analyze character frequency to assign shorter bit sequences to common letters.- Total characters
- 151 (including spaces and the period)
- Options
- Case-insensitive: ON · Include whitespace: ON · Include punctuation: ON
Step-by-step walkthrough
- Paste the 151-character sentence into the input pane. The status bar immediately shows 151 characters.
- Confirm the option chips: Case-insensitive = ON, Include whitespace = ON, Include punctuation = ON.
- Click Analyze. The frequency
Map<string, number>is built in a single O(n) pass usingIntl.Segmentergrapheme iteration. - The output table appears sorted by count descending. Row 1: space — 23 occurrences (15.2%). Row 2: e — 12 (7.9%). First surprise for beginners: with whitespace included, the space character outranks every letter.
- The bar chart renders proportional horizontal bars. Hovering any bar shows a tooltip with the exact count and percentage.
- Click CSV — a file named
char-frequency-2024.csvdownloads with columns Character, Count, Percentage. - Now toggle Case-insensitive OFF and re-run. The rows for
T(count: 2) andt(count: 3) appear separately, confirming the earlier fold was merging them into a combined count of 5.
Full frequency table output
| Rank | Character | Count | % of Total (151 chars) | Visual Bar (normalized) |
|---|---|---|---|---|
| 1 | SP (space) | 23 | 15.2% | |
| 2 | e | 12 | 7.9% | |
| 3 | o | 8 | 5.3% | |
| 4 | r | 7 | 4.6% | |
| 5 | a | 7 | 4.6% | |
| 6 | s | 7 | 4.6% | |
| 7 | n | 6 | 4.0% | |
| 8 | i | 5 | 3.3% | |
| 9 | t | 5 | 3.3% | |
| 10 | c | 5 | 3.3% | |
| 11 | h | 4 | 2.6% | |
| 12 | g | 4 | 2.6% | |
| 13 | m | 4 | 2.6% | |
| 14 | "." (period) | 2 | 1.3% | |
| 15 | d | 2 | 1.3% |
Interpreting the results
The worked example yields a Shannon entropy of 4.31 bits per character across 34 unique characters. The theoretical maximum is log₂(34) ≈ 5.09 bits — the distribution is not uniform, so real entropy is lower. The gap between space (15.2%) and the next-most-frequent character e (7.9%) is large enough that any Huffman coder would assign space a 2- or 3-bit code while rare letters like q, x, and z (each appearing once) receive codes of 7+ bits. That trade-off is exactly what gzip exploits.
| Character | % of Total |
|---|---|
| SP (space) | 15.2% |
| e | 7.9% |
| o | 5.3% |
| r | 4.6% |
| a | 4.6% |
| s | 4.6% |
| n | 4.0% |
| i | 3.3% |
| t | 3.3% |
| c | 3.3% |
| h | 2.6% |
| g | 2.6% |
| m | 2.6% |
| u | 2.0% |
| l | 2.0% |
| p | 2.0% |
| b | 1.3% |
| f | 1.3% |
| . (period) | 1.3% |
| q | 0.7% |
Character Frequency in Different Languages
English baseline
The canonical English letter-frequency table comes from Robert Lewand's Cryptological Mathematics (2000), which analyzed large mixed-genre corpora. The top five letters are e (12.7%), t (9.1%), a (8.2%), o (7.5%), and i (7.0%). These numbers trace back further still: Claude Shannon's 1948 paper, A Mathematical Theory of Communication (Bell System Technical Journal), used English letter statistics to establish that natural language carries roughly 1.0–1.5 bits of entropy per letter — far below the theoretical maximum of log₂(26) ≈ 4.7 bits, because language is highly redundant.
Spanish, German, French divergence
Spanish shuffles the top rankings: e reaches ~13.7% and, critically, a climbs to ~12.5% — substantially higher than its 8.2% English baseline. Any frequency attack on a Spanish-language substitution cipher must use a Spanish-specific baseline, or you will misidentify the second-most-common ciphertext character. German is the most extreme case among major European languages: e dominates at ~16.4%, and the umlauts ä, ö, ü appear as distinct Unicode code points (U+00E4, U+00F6, U+00FC) — the analyzer counts each separately, so a German corpus will show three extra low-frequency entries that an English operator might mistake for rare symbols. French sits between the two: e at ~14.7%, with accented variants é (U+00E9), è (U+00E8), and ê (U+00EA) each counted as their own characters in case-sensitive mode.
CJK and non-Latin scripts
For Chinese, Japanese, and Korean text, the tool counts each Han character (CJK Unified Ideograph) as a single code point — there is no romanization step. The output is a frequency map of the actual ideographs present in the input, which is useful for corpus frequency checks and font subsetting. A Mandarin news article of 500 characters might yield 300+ unique glyphs with a much flatter distribution than English prose, producing higher Shannon entropy per character. Japanese text mixing hiragana, katakana, and kanji will show three distinct ranges of Unicode code points in the table, each with its own frequency cluster.
| Letter | English | Spanish | German | French |
|---|---|---|---|---|
| a | 8.2% | 12.5% | 6.5% | 7.6% |
| e | 12.7% | 13.7% | 16.4% | 14.7% |
| i | 7.0% | 6.2% | 7.6% | 7.5% |
| n | 6.7% | 7.1% | 9.8% | 7.1% |
| o | 7.5% | 8.8% | 2.5% | 5.4% |
| r | 6.0% | 6.9% | 7.0% | 6.6% |
| s | 6.3% | 7.9% | 7.3% | 7.9% |
| t | 9.1% | 4.6% | 6.1% | 7.2% |
| u | 2.8% | 3.9% | 4.4% | 6.3% |
| l | 4.0% | 5.0% | 3.4% | 5.5% |
| Letter | Expected Frequency (%) | Morse Code Symbol | Scrabble Tile Count |
|---|---|---|---|
| E | 12.70 | · (single dot) | 12 |
| T | 9.10 | — (single dash) | 6 |
| A | 8.20 | ·— | 9 |
| O | 7.50 | ——— | 8 |
| I | 7.00 | ·· | 9 |
| N | 6.70 | —· | 6 |
| S | 6.30 | ··· | 4 |
| H | 6.09 | ···· | 2 |
| R | 6.00 | ·—· | 6 |
| D | 4.25 | —·· | 4 |
| L | 4.03 | ·—·· | 4 |
| C | 2.78 | —·—· | 2 |
| U | 2.76 | ··— | 4 |
| M | 2.41 | —— | 2 |
| F | 2.23 | ··—· | 2 |
| P | 1.93 | ·——· | 2 |
| G | 2.02 | ——· | 3 |
| W | 2.36 | ·—— | 2 |
| Y | 1.97 | —·—— | 2 |
| B | 1.49 | —··· | 2 |
| V | 0.98 | ···— | 2 |
| K | 0.77 | —·— | 1 |
| J | 0.15 | ·——— | 1 |
| X | 0.15 | —··— | 1 |
| Q | 0.10 | ——·— | 1 |
| Z | 0.07 | ——·· | 1 |
Applications: Cryptanalysis, Compression, and Linguistics
Breaking simple substitution ciphers
A monoalphabetic substitution cipher replaces each plaintext letter with a fixed ciphertext letter — Caesar shift is the simplest variant, but the class includes any permutation of the alphabet. Because each substitution is consistent throughout the message, the frequency distribution of the ciphertext mirrors the frequency distribution of the underlying plaintext language. Run the analyzer on the ciphertext and rank the output: the most frequent ciphertext character almost certainly maps to e in English (~12.7%), the second-most-frequent likely maps to t, and so on. Matching the full distribution against the English baseline (the table in the previous section) systematically recovers the substitution key. NIST SP 800-67 specifically notes frequency analysis as the classical weakness that monoalphabetic ciphers cannot resist — which is why modern block ciphers like AES are designed to produce statistically uniform ciphertext output, a property measurable with this same tool.
Huffman coding and gzip compression
Huffman coding — the entropy-coding stage inside gzip, bzip2, PNG, and JPEG — assigns shorter binary codes to more-frequent symbols. A character appearing 15% of the time might receive a 3-bit code while one appearing 0.1% of the time receives a 12-bit code. The expected saving over fixed 8-bit ASCII encoding equals the difference between 8 bits and the Shannon entropy H of the distribution. For typical English prose, H ≈ 4.3 bits/character (matching the 4.31 value from the worked example), meaning an ideal Huffman coder achieves roughly 46% size reduction on letter data alone. The frequency table this tool produces is the direct input you would feed a Huffman tree builder — paste it into a Python script using heapq and you have a functional compressor in under 30 lines. Use the Shannon entropy calculator to compute H directly from any frequency distribution you export.
Linguistic corpus analysis and Zipf's Law
George Zipf observed in 1935 that in natural language, frequency multiplied by rank approximates a constant: the second-most-common word is about half as frequent as the first, the third about a third as frequent, and so on (Zipf, The Psycho-Biology of Language, 1935). Character-level frequency follows a similar power-law tail — a handful of characters account for the bulk of occurrences, and the long tail of rare characters decays steeply. You can verify this with the analyzer: export the frequency table, plot rank vs. frequency on a log-log scale, and the near-linear result confirms Zipfian distribution. This property matters for compression (the assumption of skewed distribution underlies arithmetic coding) and for authorship attribution (outlier character distributions can fingerprint documents). The text statistics analyzer complements this tool by adding word-level frequency, sentence length distribution, and readability scores.
Two elegant engineering examples show frequency data shaping real designs: Scrabble's tile distribution gives 12 E-tiles and only 1 Z-tile, directly mirroring English letter frequency. Morse code assigns the single dot (·) to E — the shortest possible symbol — because it is the most-transmitted letter. Both designs predate computers but are grounded in the same empirical frequency tables.
Case Sensitivity and Unicode Handling
Case-sensitive vs. case-insensitive mode
In case-insensitive mode, A and a merge into a single frequency bucket. The folding uses Unicode case-folding rules (Unicode standard, section 3.13) — not a naïve ASCII toLowerCase() call. The distinction matters for characters like the German ß, which case-folds to ss (two characters), and for Turkish İ / i pairs that diverge from the Latin rules. In case-sensitive mode, E and e remain separate entries — more appropriate when analyzing source code, configuration files, or structured data where casing carries semantic meaning, such as a Python file where True and true are different tokens.
Unicode normalization (NFC vs. NFD)
The letter é can be represented two ways in Unicode: as a single precomposed code point U+00E9 (NFC form) or as the base letter e (U+0065) followed by a combining acute accent U+0301 (NFD form). Both look identical on screen, but a naïve character counter reports them as different characters. The analyzer normalizes all input to NFC before counting, so both representations collapse to U+00E9 and are tallied together. Text pasted from a macOS application (which tends to output NFD) alongside text from a Windows application (which tends to output NFC) will not produce phantom double-counting.
Emoji and multi-code-point sequences
Most emoji are single code points: 😀 is U+1F600 and counts as one character. ZWJ (zero-width joiner) sequences are more complex: 👨💻 is actually three code points — U+1F468 (man), U+200D (ZWJ), U+1F4BB (laptop) — but renders as one glyph. The analyzer uses Intl.Segmenter at grapheme-cluster granularity, so the entire ZWJ sequence is tallied as a single entry labeled 👨💻. A naïve .split('') would shatter it into three separate counts and misrepresent the actual glyph inventory. Surrogate pairs in UTF-16 JavaScript strings (code points above U+FFFF) are handled transparently by the string iterator — no special handling is needed for text containing 🔥 or 𝒜 (U+1D49C).
Edge Cases and Common Pitfalls
An empty input returns a zero-row table and zero entropy — not an error. This is by design: downstream scripts consuming the CSV export should not need to special-case a missing file.
Very long inputs above roughly 500 KB may cause a brief UI pause while the Intl.Segmenter iterator processes grapheme clusters. The tool uses chunked processing for inputs above that threshold, yielding back to the event loop between chunks to keep the UI responsive. For inputs above 1 MB, the status bar displays a progress indicator. For programmatic analysis of entire log files, Python's collections.Counter over a generator is faster than pasting into a browser.
Windows text editors frequently prepend a BOM (byte order mark, U+FEFF) to UTF-8 files. Pasted content from such a file shows the BOM as a distinct character in the frequency table — usually one occurrence, ranked near the bottom. Toggle the Ignore BOM option to suppress it, or strip it manually before pasting.
Newline handling catches developers off-guard: Windows line endings are CRLF (\r\n), while Unix and macOS use LF (\n) alone. The tool counts \r and \n as separate characters by default. An unexpected \r entry in your frequency table means you have Windows line endings — enable Normalize newlines to collapse all variants to a single LF. Similarly, the non-breaking space U+00A0 is a distinct character from the regular space U+0020, which is the most common explanation for "why is my space count lower than expected?" when copying text from HTML pages or PDF exports. Null bytes (U+0000) in binary-pasted content appear in the table as their own entry; they are counted accurately, not silently dropped.
Behind the Scenes: The Counting Algorithm
Iteration over Unicode grapheme clusters
The core loop uses the Intl.Segmenter API with granularity: 'grapheme' — the W3C-recommended approach for iterating over user-visible characters in JavaScript. A naïve for...of over a string iterates Unicode code points, which is already better than .split('') (which iterates UTF-16 code units and breaks surrogate pairs), but still incorrect for ZWJ sequences. Intl.Segmenter applies the Unicode Text Segmentation algorithm (UAX #29) and handles combining marks, ZWJ sequences, regional indicator pairs (flag emoji), and Indic conjunct clusters correctly. The iterator yields segment objects; the tool takes the segment string property as the map key.
Sorting and normalization pipeline
Frequency data is stored in a plain JavaScript Map<string, number>, giving O(1) amortized insert and lookup per character. After the O(n) counting pass, the map's entries are spread into an array and sorted with a comparator — O(k log k) where k is the count of unique characters, not the input length n. For normal text, k stays at most a few hundred even for multi-language inputs; the sort is negligible. Percentage calculation is (count_i / total_chars) * 100, rounded to two decimal places using Number.toFixed(2). Shannon entropy is computed as H = −Σ (p_i × log₂(p_i)) in a single additional pass over the frequency map, where p_i = count_i / total_chars — the formula Claude Shannon published in his 1948 Mathematical Theory of Communication, the paper that founded information theory as a discipline.
Time and space complexity
Overall time complexity is O(n + k log k): one linear pass to build the frequency map, plus a sub-linear sort over unique characters. Space complexity is O(k) for the map — independent of input size beyond the map's growth. For a 100 KB English plain-text file (≈100,000 characters), the unique character count k stays below 100, so the sort adds microseconds. The Shannon entropy value the tool displays lets you gauge information density at a glance: English prose sits around 4.0–4.5 bits/char; well-compressed binary data approaches 7.9–8.0 bits/char; repeated or highly structured input drops below 2.0 bits/char.