Type any message below. The demo counts how often each symbol appears, turns those frequencies into probabilities, and computes the entropy — the average number of bits each symbol carries. Multiply by the message length and you get the smallest size, in bits, that any lossless code could ever reach.
<p class="hint">Type a message. Its <b>entropy</b> is the average number of bits each symbol carries (information is surprise). The entropy times the length is the <b>smallest size</b> any lossless code could reach — you can never pack below it.</p>
<input id="msg" type="text" value="hello world" autocomplete="off" spellcheck="false" />
<div class="btns">
<button id="ex-flat" type="button">All same</button>
<button id="ex-mix" type="button">4 equal symbols</button>
<button id="ex-eng" type="button">English-like</button>
</div>
<div class="readout">
<div class="stat"><span class="num" id="H">0.00</span><span class="lbl">bits / symbol (entropy H)</span></div>
<div class="stat"><span class="num" id="floor">0</span><span class="lbl">bits minimum (H × length)</span></div>
<div class="stat"><span class="num" id="naive">0</span><span class="lbl">bits if fixed-width</span></div>
</div>
<div class="bars" id="bars"></div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .7rem; line-height: 1.45; }
#msg { width: 100%; font: 600 16px ui-monospace, monospace; padding: .5rem .6rem;
border: 1px solid #cdd9e3; border-radius: 8px; margin-bottom: .5rem; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; margin-bottom: .8rem; }
button { font: 600 13px system-ui, sans-serif; padding: .4rem .8rem; border: 1px solid #1d3557;
background: #fff; color: #1d3557; border-radius: 8px; cursor: pointer; }
button:hover { background: #eef3f8; }
.readout { display: flex; gap: .8rem; flex-wrap: wrap; margin-bottom: .9rem; }
.stat { background: #e8eef3; border: 1px solid #cdd9e3; border-radius: 10px;
padding: .55rem .8rem; min-width: 120px; flex: 1; }
.num { display: block; font: 800 22px ui-monospace, monospace; color: #1d3557; }
.lbl { font-size: .72rem; color: #51606e; line-height: 1.25; display: block; margin-top: .15rem; }
.bars { display: flex; flex-direction: column; gap: 4px; }
.row { display: flex; align-items: center; gap: 8px; font: 600 13px ui-monospace, monospace; }
.row .sym { width: 26px; text-align: center; background: #f0f3f6; border-radius: 5px; padding: 2px 0; }
.row .bar { height: 14px; background: #1d3557; border-radius: 4px; min-width: 2px; }
.row .pct { font-size: .72rem; color: #51606e; }
// Shannon entropy: H = -sum p*log2(p) over the symbol probabilities.
// It is the average bits/symbol, and (Shannon, 1948, source coding theorem)
// the hard lower bound on lossless compression. No code can go below H*length.
function counts(str){ const m={}; for(const ch of str) m[ch]=(m[ch]||0)+1; return m; }
function entropy(str){
if(!str.length) return 0;
const c = counts(str), n = str.length; let H = 0;
for(const k in c){ const p = c[k]/n; H -= p * Math.log2(p); }
return H;
}
function render(){
const str = document.getElementById('msg').value;
const n = str.length;
const H = entropy(str);
const c = counts(str);
const distinct = Object.keys(c).length;
const fixed = distinct > 1 ? Math.ceil(Math.log2(distinct)) : (n ? 1 : 0);
document.getElementById('H').textContent = H.toFixed(2);
document.getElementById('floor').textContent = Math.ceil(H * n);
document.getElementById('naive').textContent = fixed * n;
// bars: probability of each symbol
const bars = document.getElementById('bars'); bars.innerHTML = '';
const entries = Object.entries(c).sort((a,b)=>b[1]-a[1]);
const maxp = n ? entries[0][1]/n : 1;
for(const [sym, cnt] of entries){
const p = cnt/n;
const row = document.createElement('div'); row.className='row';
const label = sym===' ' ? '␣' : sym;
row.innerHTML = '<span class="sym">'+label+'</span>'+
'<span class="bar" style="width:'+(p/maxp*180)+'px"></span>'+
'<span class="pct">'+(p*100).toFixed(1)+'%</span>';
bars.appendChild(row);
}
}
function setMsg(v){ document.getElementById('msg').value = v; render(); }
document.getElementById('msg').addEventListener('input', render);
document.getElementById('ex-flat').addEventListener('click', ()=>setMsg('aaaaaaaa'));
document.getElementById('ex-mix').addEventListener('click', ()=>setMsg('abcdabcdabcd'));
document.getElementById('ex-eng').addEventListener('click', ()=>setMsg('the quick brown fox'));
render();
<p class="hint">Type a message. Its <b>entropy</b> is the average number of bits each symbol carries (information is surprise). The entropy times the length is the <b>smallest size</b> any lossless code could reach — you can never pack below it.</p>
<input id="msg" type="text" value="hello world" autocomplete="off" spellcheck="false" />
<div class="btns">
<button id="ex-flat" type="button">All same</button>
<button id="ex-mix" type="button">4 equal symbols</button>
<button id="ex-eng" type="button">English-like</button>
</div>
<div class="readout">
<div class="stat"><span class="num" id="H">0.00</span><span class="lbl">bits / symbol (entropy H)</span></div>
<div class="stat"><span class="num" id="floor">0</span><span class="lbl">bits minimum (H × length)</span></div>
<div class="stat"><span class="num" id="naive">0</span><span class="lbl">bits if fixed-width</span></div>
</div>
<div class="bars" id="bars"></div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .7rem; line-height: 1.45; }
#msg { width: 100%; font: 600 16px ui-monospace, monospace; padding: .5rem .6rem;
border: 1px solid #cdd9e3; border-radius: 8px; margin-bottom: .5rem; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; margin-bottom: .8rem; }
button { font: 600 13px system-ui, sans-serif; padding: .4rem .8rem; border: 1px solid #1d3557;
background: #fff; color: #1d3557; border-radius: 8px; cursor: pointer; }
button:hover { background: #eef3f8; }
.readout { display: flex; gap: .8rem; flex-wrap: wrap; margin-bottom: .9rem; }
.stat { background: #e8eef3; border: 1px solid #cdd9e3; border-radius: 10px;
padding: .55rem .8rem; min-width: 120px; flex: 1; }
.num { display: block; font: 800 22px ui-monospace, monospace; color: #1d3557; }
.lbl { font-size: .72rem; color: #51606e; line-height: 1.25; display: block; margin-top: .15rem; }
.bars { display: flex; flex-direction: column; gap: 4px; }
.row { display: flex; align-items: center; gap: 8px; font: 600 13px ui-monospace, monospace; }
.row .sym { width: 26px; text-align: center; background: #f0f3f6; border-radius: 5px; padding: 2px 0; }
.row .bar { height: 14px; background: #1d3557; border-radius: 4px; min-width: 2px; }
.row .pct { font-size: .72rem; color: #51606e; }
// Shannon entropy: H = -sum p*log2(p) over the symbol probabilities.
// It is the average bits/symbol, and (Shannon, 1948, source coding theorem)
// the hard lower bound on lossless compression. No code can go below H*length.
function counts(str){ const m={}; for(const ch of str) m[ch]=(m[ch]||0)+1; return m; }
function entropy(str){
if(!str.length) return 0;
const c = counts(str), n = str.length; let H = 0;
for(const k in c){ const p = c[k]/n; H -= p * Math.log2(p); }
return H;
}
function render(){
const str = document.getElementById('msg').value;
const n = str.length;
const H = entropy(str);
const c = counts(str);
const distinct = Object.keys(c).length;
const fixed = distinct > 1 ? Math.ceil(Math.log2(distinct)) : (n ? 1 : 0);
document.getElementById('H').textContent = H.toFixed(2);
document.getElementById('floor').textContent = Math.ceil(H * n);
document.getElementById('naive').textContent = fixed * n;
// bars: probability of each symbol
const bars = document.getElementById('bars'); bars.innerHTML = '';
const entries = Object.entries(c).sort((a,b)=>b[1]-a[1]);
const maxp = n ? entries[0][1]/n : 1;
for(const [sym, cnt] of entries){
const p = cnt/n;
const row = document.createElement('div'); row.className='row';
const label = sym===' ' ? '␣' : sym;
row.innerHTML = '<span class="sym">'+label+'</span>'+
'<span class="bar" style="width:'+(p/maxp*180)+'px"></span>'+
'<span class="pct">'+(p*100).toFixed(1)+'%</span>';
bars.appendChild(row);
}
}
function setMsg(v){ document.getElementById('msg').value = v; render(); }
document.getElementById('msg').addEventListener('input', render);
document.getElementById('ex-flat').addEventListener('click', ()=>setMsg('aaaaaaaa'));
document.getElementById('ex-mix').addEventListener('click', ()=>setMsg('abcdabcdabcd'));
document.getElementById('ex-eng').addEventListener('click', ()=>setMsg('the quick brown fox'));
render();
Watch the extremes. A message of all the same character (aaaaaa) has zero entropy — once you know the alphabet, every symbol is fully predictable, so it carries no information at all. Mix in more distinct, equally likely symbols and the entropy climbs toward its maximum, log2 of the alphabet size. That number is a wall: pack below it and you would lose information that can never be recovered.
Comments
Loading comments...