ENTER: toggle typing mode (a caret appears after the text) typing: A–Z 0–9 SPACE . , ! ? -: append typing: ←: delete the last character P: next preset text C: clear SPACE: replay the encode animation, one character at a time ↑ / ↓: animation speed Edges carry the bit: cyan is 0 (left), amber is 1 (right), so you can read any symbol's code straight off the picture. Internal nodes are dots sized by subtree frequency, which makes the shape of the distribution visible in the tree itself — heavy subtrees have fat joints and sit shallow, which is exactly what Huffman's greedy rule is doing. Presets include the textbook ABRACADABRA, MISSISSIPPI RIVER, a pangram (27 symbols, a wide flat tree), a deliberately skewed AAAAAAAAAAAAAAAABBBBBBBBCCCCDDEF, and 0123456789ABCDEF... — a perfectly uniform 16-symbol distribution where Huffman achieves exactly 4.00 bits per character and saves nothing, which is the most useful thing in the project to have seen.
Huffman coding with nothing hidden: type text and watch the frequency table, the tree, the codes, the bitstream and the compression arithmetic all rebuild on every keystroke — then watch the encoder walk the tree character by character. THE INTERESTING DECISION: ONE INVARIANT INSTEAD OF TWO TRAVERSALS Leaves are nodes 1..K in first-appearance order, and internal nodes are appended to the same arrays as they are created. So **a parent always has a higher index than its children.** That one invariant removes both recursive traversals a tree layout normally needs: depths fill in by walking the node array downwards* from the root — a child's depth is always written before the loop reaches it; internal x positions fill in by walking it upwards* from the leaves — both children are already placed. Each is a single loop with no stack and no recursion, which matters because goboscript local variables are undefined behaviour inside a recursive procedure. Only two things still need an explicit stack: assigning codes (which carries a partial code string down with the node) and finding the left-to-right leaf order. The priority queue is a real binary min-heap in two parallel lists, since Scratch has neither a sort block nor a priority queue. Its key is freq * 1000 + node index, not just freq — that makes tie-breaking deterministic, so the same text always builds the same tree rather than one of the several equally-optimal trees an arbitrary tie-break would pick. VERIFICATION The project decodes its own output and compares, so the round-trip result on screen is a live check that the codes really are a prefix code. Beyond that, the compiled .sb3 was run under the headless harness and its numbers checked against an independent Python implementation: | Text | Symbols | Huffman bits | Entropy | | --- | --- | --- | --- | | ABRACADABRA | 5 | 23 | 2.04 | | TO BE OR NOT TO BE THAT IS THE QUESTION | 13 | 130 | 3.29 | | SHE SELLS SEA SHELLS BY THE SEA SHORE | 11 | 114 | 3.01 | All three match to the bit. ABRACADABRA gives A=0 C=100 D=101 B=110 R=111, whose Kraft sum is 1/2 + 4×1/8 = 1 exactly — a complete prefix code, no waste. The measured average code length is always within one bit of the entropy shown next to it, which is Huffman's optimality guarantee arriving as a number rather than as a claim. HONEST LIMITATIONS * Uppercase only. Scratch's = is case-insensitive, so "a" == "A" is true and item # of "a" in list cannot distinguish them — a case-sensitive frequency table is not expressible with the tools available. key_pressed cannot report shift state either, so nothing is actually lost. * Input is capped at 150 characters. Everything is recomputed from scratch on every keystroke, and the bitstream is built by string concatenation, which is quadratic. * A held key does not auto-repeat, so a doubled letter needs the key released in between. There is no backspace: Scratch's key_pressed cannot name it ("backspace" is silently truncated to the B key), so ← deletes. * The code table shows as many symbols as fit in four rows. A 40-symbol input with long codes will run out of room and the tail is not shown — the arithmetic below it still counts all of them. The compression figures count only* the encoded bits. A real file would also have to store the tree or the frequency table, which for short inputs costs more than the coding saves. All original - code, art and sound. See Inside is open.