Understanding UTF-8

Live demo of how exactly a Unicode character encodes into UTF-8, bit by bit.

HTML

<input type="button" value="do" onclick="foo()"> source:<a href="https://en.wikipedia.org/wiki/UTF-8#Examples">UTF-8#Examples</a>
&bull; escapes: <a href="http://xem.github.io/escape/">EscApe</a> <br> 
<textarea id="inn" onkeyup="foo()">$¢€𐍈�&#x10fffd;</textarea>

<table><tbody id="data"></tbody></table>

CSS

textarea {width:95%;height:6em;}
table {background-color:#f9f9f9;border:1px solid #aaa;border-collapse:collapse;color:#000;margin:1em 0;font-size:8pt;}
th, td {border:1px solid #aaa;padding:.3em .4em;}
th {background-color:#f2f2f2;text-align:center;font-weight:700;}
code {color:black;background-color:#f9f9f9;border:1px solid #ddd;border-radius:2px;padding:1px 4px;font-family:monospace,Courier;white-space:nowrap;}
.r {color:#f00;}
.g {color:#0f0;}
.b {color:#00f;}
.m {color:#f0f;}

JavaScript

function foo() {
  var txt = '';
  var inn = utfarray(document.getElementById('inn').value);
  var data = document.getElementById('data');
  var th = '<tr><th colspan="2">Character</th><th>Binary code point</th><th>Binary UTF-8</th><th>Hexadecimal UTF-8</th></tr>';
  for(var i=0;i<inn.length;i++) {
    if(inn[i] > 0x1FFFFF) continue
    txt += '<tr><td>' + character(inn[i]) + '</td><td><code>U+' + hex(inn[i]) + '</code></td>';
    txt += '<td align="right"><code>' + chunk4(inn[i]) + '</code></td><td><code>' + binutf8(byte(inn[i])) + '</code></td><td><code>' + byte(inn[i],true).join(' ') + '</code></td></tr>\n';
  }
  data.innerHTML = th + txt;
}

function byte(num,color) { //array of hex strings
  var arr = percent(num).replace(/^\%/,'').split('%');
  for(var i=0;i<arr.length;i++) {
    //arr[i] = padleft(arr[i],2,'0');
    arr[i] = (color) ? '<span class="' + 'rgbm'[arr.length-i-1] + '">' + padleft(arr[i],2,'0') + '</span>' : padleft(arr[i],2,'0');
  }
  return arr;
}

function binutf8(byte) { //binary octets
  var txt = '';
  for(var i=0;i<byte.length;i++) {
    var cls = 'rgbm'[byte.length-i-1];
    var part = padleft(parseInt(byte[i],16).toString(2),8,'0');
    if(byte.length == 1) {
      txt += part.substring(0,1) + '<span class="r">' + part.substring(1,8) + '</span> ';
    } else if(i > 0) {
      txt += part.substring(0,2) + '<span class="' + cls + '">' + part.substring(2,8) + '</span> ';
    } else {
      txt += part.substring(0,byte.length+i+1) + '<span class="' + cls + '">' + part.substring(byte.length+i+1,8) + '</span> ';
    }
  }
  return txt.replace(/ $/,'');
}

function chunk4(num) { //padded right-based 4 couplets in 7/11/16/21 bit chunks
  var bin = num.toString(2);
  if(bin.length < 7) bin = padleft(bin,7,'0');
  if(bin.length > 7 && bin.length < 11) bin = padleft(bin,11,'0');
  if(bin.length > 11 && bin.length < 16) bin = padleft(bin,16,'0');
  if(bin.length > 16 && bin.length < 21) bin = padleft(bin,21,'0');
  var rev = '';
  for(var...