Skip to content
HN On Hacker News ↗

Sing-song: a speakable encoding for long numbers and keys

▲ 20 points 12 comments by zdw 5d ago HN discussion ↗

Pangram verdict · v3.3

We believe that this text is a mix of AI and human-written content.

79 %

AI likelihood · overall

AI
14% human-written 86% AI-generated
SEGMENTS · HUMAN 1 of 2
SEGMENTS · AI 1 of 2
WORD COUNT 1,503
PEAK AI % 98% · §2
Analyzed
Sep 4
backend: pangram/v3.3
Segments scanned
2 windows
avg 752 words each
Distribution
14 / 86%
human / AI fraction
Verdict
AI
Pangram v3.3

Article text · 1,503 words · 2 segments analyzed

Human AI-generated
§1 Human · 1%

This is a fun experiment that started as a way to generate deterministic "usernames" for Nostr npub keys (see the last section). English is really hard to deal with even for simple syllables, so the result is not as good as I'd want it to be (a string you can read to an other person over the phone and make sure they get it right), but it's not bad either —if everyone spoke Itallian sing-song would be much more valuable :-) Status: draft, v0.1.2 --- comments welcome.

§2 AI · 98%

Abstract Sing-song is a reversible encoding of arbitrary byte strings as pronounceable CV syllables. Its 64-syllable alphabet maps each 6-bit value directly to one syllable. Complete encodings preserve byte length and leading zero bytes, require no external length metadata, and are canonical. The encoding is prefix-stable: shared input prefixes produce shared syllable prefixes. An optional variant suffix provides alternative reversible representations of the same byte string. Motivation Machine-oriented encodings such as hex and Base58 are compact but awkward to speak, transcribe, and remember. Sing-song trades some written density for a small, regular pronunciation grammar while remaining deterministic, reversible, and computationally simple. Design goals The encoding should be deterministic, reversible, prefix-stable, speakable and transcribable without training, self-sizing for complete byte strings, and self-delimiting without load-bearing punctuation. The encoding Alphabet Position Symbols Count Consonant (odd ) b d f g j k l m n p r s t v w z 16 Vowel (even) a i o u 4 Consonants and vowels strictly alternate, producing 64 open CV syllables with no clusters or codas. Position parity determines the symbol table. h, y, and e are omitted because their pronunciation is comparatively unstable. Syllables and grouping Each syllable encodes exactly 6 bits. Display groups contain two syllables (zila, sibo) separated by cosmetic hyphens. Parsers MUST ignore hyphens: zilasibotivajuzu and zila-sibo-tiva-juzu are identical. Groups provide natural speaking checkpoints. Pronunciation Sing-song uses a fixed pronunciation for every letter. The spelling is phonemic: each letter represents one sound, and the same letter is always pronounced the same way. The following IPA values are normative: Letter a i o u b d f g j k IPA /a/ /i/ /o/ /u/ /b/ /d/ /f/ /ɡ/ /dz/ /k/ Letter l m n p r s t v z IPA /l/ /m/ /n/ /p/ /r/ /s/ /t/ /v/ /z/ Vowels are pure vowels and MUST NOT be interpreted according to English spelling conventions. In particular, i is /i/, o is /o/, and u is /u/; they are not pronounced /aɪ/, /oʊ/, or /juː/. Each letter is pronounced independently. There are no silent letters, digraphs, or context-dependent pronunciations. Words are divided into syllables at each consonant–vowel pair and stress falls on the first syllable. For example: Word IPA sibo /ˈsi.bo/ katu /ˈka.tu/ pova /ˈpo.va/ Minor accent-dependent differences in pronunciation are acceptable as long as the encoded letters remain distinguishable. Algorithm Treat the input as a bit stream and split it into 6-bit chunks, most significant bit first. Each chunk maps directly to one syllable: bits 5..2 → consonant index 0..15 bits 1..0 → vowel index 0..3 For L input bytes, emit n = ceil(8·L / 6) syllables. If the final chunk has fewer than six input bits, zero-fill its low bits. These zeroes are canonical padding and carry no information. A complete encoding is self-sizing: L = floor(6·n / 8). The decoder reconstructs the 6-bit chunks, infers L, returns the first 8·L bits, and MUST reject non-canonical syllable counts or non-zero padding. Leading zero bytes are preserved. This applies to complete encodings. A truncated prefix does not indicate whether more syllables follow. A prefix of k syllables commits to the first 6·k bits of the encoded value and is verified by recomputation, not decoding. Variants A variant is an alternative reversible representation of the same byte string. The variant identifier is encoded in the representation, so decoding requires no external metadata. For input X and variant v = 0…15: M(0, n) = 0^n M(v, n) = SHAKE-256("sing-song/variant" ‖ byte(v), n) for v > 0 Y = X XOR M(v, len(X)) Encode Y with the ordinary Sing-song codec. Because XOR is self-inverse: X = Y XOR M(v, len(Y)) The mask is public and provides no confidentiality. SHAKE-256 produces a deterministic stream, preserving the prefix-stability property described above. Variant 0 is the direct encoding. The variant identifier is rendered as a trailing two-letter suffix: one vowel followed by one of l m n r: v = 4·i + j where vowel = "aiou"[i], consonant = "lmnr"[j] 0=al 1=am 2=an 3=ar 4=il 5=im 6=in 7=ir 8=ol 9=om 10=on 11=or 12=ul 13=um 14=un 15=ur Variant 0 SHOULD be rendered without a suffix; parsers MUST accept an explicit al as equivalent. Parity disambiguates the suffix: content consonants occupy odd positions, so a vowel at an odd position can only begin the variant suffix. Parsers MUST require exactly two trailing letters (vowel then l/m/n/r) and reject other parity violations. Prefix stability Each complete syllable represents exactly six consecutive input bits. Therefore, if two byte strings share their first 6k bits, their direct Sing-song encodings share their first k syllables. For byte-aligned prefixes, a boundary is both byte- and syllable-aligned every 24 bits: 3 bytes = 24 bits = 4 syllables At those boundaries, truncating the encoding is exactly equivalent to encoding the truncated byte string: SingSong(X)[0:4k syllables] = SingSong(X[0:3k bytes]) The same property holds for variants. The SHAKE-256 mask is generated as an output stream, so a shorter mask is a prefix of a longer one: M(v, 3k) = M(v, len(X))[0:3k] Therefore: body(SingSong(X, v))[0:4k syllables] + suffix(v) = SingSong(X[0:3k bytes], v) for any k such that the prefix exists. For prefixes that do not end on both a byte and syllable boundary, the shared leading syllables still represent the same leading bits, but the truncated text is not itself a complete canonical encoding of a byte string. Transcription and error handling Transcription and error handling The CV grammar provides basic syntax checking. Odd positions MUST contain a consonant and even positions MUST contain a vowel. Characters outside the corresponding alphabet MUST be rejected. Parsers MAY normalize unambiguous transcription substitutions where position determines the intended symbol: 0 → o in a vowel position 1 → l in a consonant position e → i in a vowel position These substitutions correct representation errors only. Sing-song does not include an error-correcting code or checksum: a valid syllable substituted for another valid syllable cannot be detected from the encoding alone. Alternatives considered The base grammar survived three alternatives worth recording. Curated syllable dictionaries A hand-picked codebook can exclude confusable minimal pairs by merging sounds such as b/p, d/t, g/k, f/v, s/z, m/n, and l/r into equivalence classes. Roughly ten onset classes × four vowels × three coda classes gives about 120 robust syllables, or ~6.9 bits each: about 17% fewer syllables than Sing-song, with better error tolerance. The cost is a large lookup table, longer written forms, and heavier closed syllables. Example: ban-fok-rim-tus-gal-nom-pik-sur The gain was not worth losing the simple generative grammar and light, open sound. Relaxing alternation to a no-cluster constraint Allowing CV, VC, and CVC while merely forbidding adjacent consonants raises the theoretical capacity from 2.95 to 3.32 bits/letter. Once doubled vowels are banned, vowel runs are limited, and only clear diphthongs (ai, au, oi, ou, ui) are allowed, the practical gain falls to about 3% in written length and essentially zero in spoken density. Example: zilai-sibo-tauva-juzu The small gain does not justify replacing the parity rule with an automaton, weakening error healing, and complicating variant parsing. Selected consonant-cluster onsets Keeping syllables open but allowing selected English CC onsets gives (C | selected CC)V, such as ba, gro, pli, tru. With the current 16 simple onsets plus twelve clusters (br, bl, dr, fr, fl, gr, gl, kr, kl, pr, pl, tr), the alphabet has 112 syllables, or about 6.81 bits each. A 256-bit value would need about 38 syllables. Example: zila-grovi-pluma-triso-fraku-silo-bruna-koti This preserves much of Sing-song's open, melodic character, but sacrifices the uniform CV grammar and parity parsing for a modest reduction in spoken length. The 16 × 4 CV alphabet is the useful boundary: exactly 6 bits per syllable with a trivial codec, while preserving the small grammar, open sound, and positional parsing. Comparison with other encodings Sing-song trades written density for spoken density. It carries 3 bits per letter and exactly 6 bits per syllable. Hex carries 4 bits per character and Base58 about 5.9. Bits Hex Base58 Sing-song Groups 48 12 9 16 4 64 16 11 22 5.5 128 32 22 44 11 256 64 44 86 21.5 The trade reverses when values are spoken: hexadecimal character names are longer and contain strong rhyme classes, while Sing-song carries 6 bits in each short CV syllable. A complete 256-bit value takes 43 syllables. Where no human channel exists, hex or Base58 is shorter and preferable. Sing-song is intended for values that humans must read, say, type, or remember. Test vectors The codec operates on bytes without assigning them semantics.