Text

WORD FREQUENCY

Paste any text to see how often each word appears. Filter common stopwords, sort by frequency or alphabetically, and export as CSV.

Sort: freq ↓
A–Z
Show top words
Paste text above to analyze word frequency.
Developer Reference

Core Algorithm & Standalone Script

Standalone, zero-dependency JavaScript implementation powering this tool. Free to inspect, copy, and build upon.

const STOPWORDS = new Set(['a','an','the','and','or','but','in','on','at','to','for','of','with','by','from','is','was','are','were','be','been','being','have','has','had','do','does','did','will','would','could','should','may','might','shall','can','need','dare','ought','used','it','its','this','that','these','those','i','me','my','myself','we','our','ours','ourselves','you','your','yours','yourself','yourselves','he','him','his','himself','she','her','hers','herself','they','them','their','theirs','themselves','what','which','who','whom','when','where','why','how','all','both','each','few','more','most','other','some','such','no','not','only','same','so','than','too','very','just','because','as','until','while','if','then','else','any','about','above','after','before','between','during','into','through','up','down','out','off','over','under','again','once','s','t','re','ve','m','ll','d']);

  let freqData = [];
  let filterStop = true;
  let caseSensitive = false;
  let sortMode = 'freq';

  function analyze() {
    const text = document.getElementById('textInput').value;
    if (!text.trim()) {
      freqData = [];
      document.getElementById('statsRow').style.display = 'none';
      document.getElementById('tableWrap').innerHTML = '<div class="empty-state">Paste text above to analyze word frequency.</div>';
      return;
    }

    const words = text.match(/[a-zA-Z''\u00C0-\u024F]+/g) || [];
    const sentences = (text.match(/[.!?]+/g) || []).length || 1;
    const totalWords = words.length;
    const processed = caseSensitive ? words : words.map(w => w.toLowerCase());
    const filtered = filterStop ? processed.filter(w => !STOPWORDS.has(w.toLowerCase())) : processed;

    const map = {};
    filtered.forEach(w => { map[w] = (map[w] || 0) + 1; });
    freqData = Object.entries(map);
    const uniqueAll = Object.keys(map).length;

    const avgLen = filtered.length ? (filtered.reduce((s,w)=>s+w.length,0)/filtered.length).toFixed(1) : 0;

    document.getElementById('sTotal').textContent = totalWords.toLocaleString();
    document.getElementById('sUnique').textContent = uniqueAll.toLocaleString();
    document.getElementById('sSentences').textContent = sentences.toLocaleString();
    document.getElementById('sAvgLen').textContent = avgLen;
    document.getElementById('statsRow').style.display = 'grid';

    renderTable();
  }

  function renderTable() {
    if (!freqData.length) return;
    const topN = Math.max(1, parseInt(document.getElementById('topN').value) || 50);
    let sorted = [...freqData];
    if (sortMode === 'freq') sorted.sort((a,b) => b[1]-a[1] || a[0].localeCompare(b[0]));
    else sorted.sort((a,b) => a[0].localeCompare(b[0]));
    const slice = sorted.slice(0, topN);
    const maxCount = slice[0] ? (sortMode === 'freq' ? slice[0][1] : Math.max(...slice.map(x=>x[1]))) : 1;
    const total = freqData.reduce((s,[,c])=>s+c,0);

    let html = `<table class="freq-table"><thead><tr><th>Rank</th><th>Word</th><th>Count</th><th>%</th><th class="bar-cell">Frequency</th></tr></thead><tbody>`;
    const allSorted = [...freqData].sort((a,b)=>b[1]-a[1]);
    slice.forEach(([word, count]) => {
      const rank = allSorted.findIndex(([w])=>w===word) + 1;
      const pct = ((count/total)*100).toFixed(1);
      const barW = Math.round((count/maxCount)*100);
      html += `<tr><td class="rank">#${rank}</td><td>${esc(word)}</td><td>${count.toLocaleString()}</td><td class="pct">${pct}%</td><td class="bar-cell"><div class="bar-track"><div class="bar-fill" style="width:${barW}%"></div></div></td></tr>`;
    });
    html += '</tbody></table>';
    document.getElementById('tableWrap').innerHTML = html;
  }

  function esc(s) { return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }

  function toggleStopwords() {
    filterStop = !filterStop;
    document.getElementById('btnStopwords').classList.toggle('active', filterStop);
    analyze();
  }

  function toggleCase() {
    caseSensitive = !caseSensitive;
    document.getElementById('btnCase').classList.toggle('active', caseSensitive);
    analyze();
  }

  function setSort(mode) {
    sortMode = mode;
    document.getElementById('sortFreq').classList.toggle('active', mode === 'freq');
    document.getElementById('sortAlpha').classList.toggle('active', mode === 'alpha');
    renderTable();
  }

  function exportCSV() {
    if (!freqData.length) return;
    const sorted = [...freqData].sort((a,b)=>b[1]-a[1]);
    const total = sorted.reduce((s,[,c])=>s+c,0);
    let csv = 'Rank,Word,Count,Percentage\n';
    sorted.forEach(([word, count], i) => {
      csv += `${i+1},"${word.replace(/"/g,'""')}",${count},${((count/total)*100).toFixed(2)}%\n`;
    });
    const a = document.createElement('a');
    a.href = URL.createObjectURL(new Blob([csv], { type: 'text/csv' }));
    a.download = 'word-frequency.csv';
    a.click();
    URL.revokeObjectURL(a.href);
  }