---
title: "This 256-bit key is pronounceable. So is a wrong one."
locale: "en"
url: "https://irz.fr/en/articles/sing-song-pronounceable-keys-en"
markdown_url: "https://irz.fr/en/articles/sing-song-pronounceable-keys-en.md"
category: "tech"
tags: ["encoding", "cryptography", "Nostr", "Sing-song", "keys"]
published_at: "2026-08-20T13:05:00.000Z"
author: "Hugo Marchal"
translation: "https://irz.fr/fr/articles/sing-song-pronounceable-keys-fr.md"
---

# This 256-bit key is pronounceable. So is a wrong one.

Sing-song turns arbitrary bytes into six-bit CV syllables. It is compact in speech and reversible, but its grammar is neither a checksum nor a complete identity.

A Nostr public key currently looks something like `npub180cvv07tjdrrg...`. It is already friendlier than raw hexadecimal, but nobody wants to dictate fifty-nine characters over a phone call.

On August 19, 2026, Vrypan published **Sing-song**, a draft encoding that takes the problem literally: rather than endlessly refining an alphabet, it turns bits into **small syllables people can pronounce**.[1](https://blog.vrypan.net/2026/08/19/260819-sing-song/)

A 256-bit value becomes something like:

`moji-buba-liku-moru-lizi-wiji-zusi-jilu-…`

It sings more readily than `9F4A7B…`. The obvious question follows immediately, however: when a key becomes pleasant to say, **what protects us when we say it incorrectly?**

We took the reference implementation embedded in the draft and compared exactly the same 256-bit value in hex, Base58, Proquint, RFC1751 and BIP39.

## Six bits

Sing-song has sixteen consonants and four vowels, with every syllable built from exactly one consonant followed by one vowel: `ba`, `di`, `fo`, `gu`, and so forth.[1](https://blog.vrypan.net/2026/08/19/260819-sing-song/)

`16 × 4 = 64` combinations, so every syllable carries exactly **six bits**; the algorithm simply treats the input as a bitstream, cuts it into six-bit chunks, uses four bits to select a consonant and two to select a vowel, then fills any short final chunk with canonical zero padding.[1](https://blog.vrypan.net/2026/08/19/260819-sing-song/)

For `L` input bytes, syllable count is:

`ceil(8 × L / 6)`

A 32-byte, 256-bit key therefore needs **43 syllables**, or 86 letters before separators.

There is an elegant side effect. Three bytes make 24 bits, exactly four Sing-song syllables, so at those boundaries a textual prefix corresponds precisely to a byte prefix of the original value.[1](https://blog.vrypan.net/2026/08/19/260819-sing-song/)

## Same key

We used `SHA-256("sing-song")`, matching the draft's own test vector, and generated six representations.

> Illustration: Comparison of character and human-unit counts for Base58, hex, Proquint, Sing-song, RFC1751 and BIP39 on one 256-bit value. Sing-song is not trying to win the compact-text contest. Base58 is about half as long. Its bet is different: every pair of letters is a regular six-bit syllable. Credit: IRZ benchmark.

For this value, Base58 uses **44 characters**, hex 64, Proquint 80 letters arranged in sixteen CVCVC groups, and Sing-song 86; applying RFC1751 to four 64-bit blocks gives us 24 words and 90 letters, while BIP39 also gives 24 words but expands to 131 letters on the same vector.

That comparison deliberately stops short of claiming a speaking-time winner because **spoken words vary dramatically in length**: `EACH` and `ELEVATOR` plainly take different amounts of time, so the clean comparison here concerns structure and written density rather than seconds of speech.

Proquint makes a particularly useful neighbour. Dating back to 2009, it encodes sixteen bits in a five-letter CVCVC group using sixteen consonants and four vowels.[3](https://datatracker.ietf.org/doc/draft-rayner-proquint/) Its IETF Internet-Draft describes pronounceable identifiers, although revision 11 expired in April 2026, so it remains work in progress rather than an adopted IETF standard.[3](https://datatracker.ietf.org/doc/draft-rayner-proquint/)

Sing-song instead chooses smaller, consistently open units, **CV, CV, CV**, spending more syllables in exchange for a grammar that remains almost trivial.

## Useful grammar

That simple grammar already catches some mistakes because a consonant position accepts only the sixteen permitted consonants while a vowel position accepts only `a`, `i`, `o` or `u`; the draft may also normalize a few position-dependent transcription substitutions, including `0 → o`, `1 → l` and `e → i`.[1](https://blog.vrypan.net/2026/08/19/260819-sing-song/)

On our 256-bit string, we replaced every letter in turn with each of the other 25 letters from `a` to `z`. The consonant/vowel structure immediately rejects **64%** of those substitutions.

That is substantial value from a very small parser, although the same statistic hides the failure mode that matters most for a spoken identifier.

## Validly wrong

If `ba` becomes `pa`, the new syllable remains perfectly legal, and the Sing-song draft says this plainly: there is **no error-correcting code and no checksum**, so replacing one valid syllable with another produces a different value that still parses correctly.[1](https://blog.vrypan.net/2026/08/19/260819-sing-song/)

We exhausted every character substitution on our example that stays inside the permitted class at that position, consonant for consonant or vowel for vowel. There are **774 such mutations**. Detection rate: **zero**.

Proquint has the same fundamental boundary. Its CVCVC pattern catches impossible characters and malformed positions, while another permitted consonant or vowel simply decodes into another value.[3](https://datatracker.ietf.org/doc/draft-rayner-proquint/)

> Illustration: IRZ comparison of valid single-substitution detection for Sing-song, Proquint, RFC1751 and BIP39. Grammar and checksum protect different failure modes. Sing-song rejects many malformed strings, but a substitution that remains valid for its position is not detected. Credit: IRZ benchmark.

RFC1751 made a different trade in 1994. It encodes 64 bits with six words selected from a 2048-word dictionary, providing 66 bits of capacity and using the spare two bits for parity.[2](https://datatracker.ietf.org/doc/html/rfc1751) Across the **49,128 substitutions of one valid word for another** that we tried over the four blocks of our 256-bit value, **75.04%** are rejected.

BIP39 goes further. For 256 bits of entropy it appends eight SHA-256 checksum bits and splits the result into 24 eleven-bit word indices.[4](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) In the same exhaustive 49,128 valid-word substitutions, **99.59%** fail checksum validation.

Calling BIP39 universally superior would miss the point: it transports wallet entropy as a mnemonic and relies on a 2048-word list, whereas Sing-song wants to encode arbitrary bytes using a tiny generative rule, so the two designs are optimizing different problems.[1](https://blog.vrypan.net/2026/08/19/260819-sing-song/)[4](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki)

## On the phone

The draft begins from an explicitly vocal ambition: read the string to another person and have them type it correctly.[1](https://blog.vrypan.net/2026/08/19/260819-sing-song/)

That is where the 64-syllable alphabet becomes delicate.

`ba` and `pa`, `da` and `ta`, `ga` and `ka`, `fa` and `va`, `sa` and `za` are close pairs. The author knows this; one rejected design explores merging confusable classes including `b/p`, `d/t`, `g/k`, `f/v`, `s/z`, `m/n` and `l/r`.[1](https://blog.vrypan.net/2026/08/19/260819-sing-song/)

The current design nevertheless keeps all 64 regular combinations instead of introducing a larger curated lookup table and heavier closed syllables, almost the inverse of the PGP word list, whose words were selected for phonetic distinctiveness and whose alternating lists help reveal transpositions, duplicates and omissions when bytes are conveyed over voice.[7](https://en.wikipedia.org/wiki/PGP_word_list)

Sing-song therefore bets on **the rule**, while PGP bets on **the vocabulary**: one fits in a handful of constants and the other needs hundreds of deliberately selected words.

## Still draft

Sing-song should also be read as what it currently is: **draft v0.1.2**, not an installed format.[1](https://blog.vrypan.net/2026/08/19/260819-sing-song/)

The post contains a complete Python reference implementation but links no dedicated repository and states no software license for that code. Anyone considering product integration therefore has a licensing question to settle before simply copying the implementation.

There is even a small editorial seam in the specification. The vowel alphabet is `a i o u`, yet the pronunciation table includes `e`; later, `e` appears again as a transcription error that may normalize to `i`.[1](https://blog.vrypan.net/2026/08/19/260819-sing-song/)

Nothing catastrophic is happening there. This is exactly the sort of edge a public draft is supposed to expose.

## Sixteen variants

Sing-song also defines sixteen **variants**, allowing several pronounceable strings to represent the same bytes.[1](https://blog.vrypan.net/2026/08/19/260819-sing-song/)

For variants 1 through 15, a public SHAKE-256 stream is XORed with the data before encoding and a two-letter suffix records the selected variant. The mask is deterministic, reversible and explicitly provides **no confidentiality**.[1](https://blog.vrypan.net/2026/08/19/260819-sing-song/)

The benefit is aesthetic or ergonomic: if one representation yields an awkward sequence to say, an application can pick another while keeping the encoding reversible. The suffix only tells the decoder **how to reverse the representation**; it provides no check that the speaker got every syllable right.

## Short Nostr

The draft's most appealing application is Nostr.

NIP-19 already represents 32-byte public keys as Bech32 `npub` strings for human display, sharing and input.[5](https://github.com/nostr-protocol/nips/blob/master/19.md) Bech32 carries a six-character checksum and its design guarantees detection of any error affecting up to four characters under the format's assumptions.[6](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki)

Vrypan proposes a different user-facing object: use only the **first eight Sing-song syllables** as a readable username.[1](https://blog.vrypan.net/2026/08/19/260819-sing-song/)

Eight syllables times six bits gives **48 bits**.

Because prefix stability is direct, this is exactly the first **six bytes** of the public key, not a separate hash. The draft explicitly warns that the resulting username is not globally unique.[1](https://blog.vrypan.net/2026/08/19/260819-sing-song/)

We calculated birthday-collision probabilities for randomly distributed keys.

> Illustration: IRZ curve showing probability of at least one collision between 48-bit prefixes as key population grows. A 48-bit prefix is comfortable at small scale. Around 19.75 million random keys, however, the chance of at least one duplicate pair passes 50%. Credit: IRZ calculation using the birthday approximation.

With **one million** keys, the chance that at least one pair collides somewhere in the set is about **0.18%**; at ten million it reaches **16.3%**, at twenty million **50.9%**, placing the theoretical 50% point around **19.75 million**.

Those numbers describe the probability that at least one pair in the whole population shares a prefix; they do **not** mean a particular user has a fifty-percent chance of being impersonated.

More importantly, they confirm the correct role for the eight-syllable form: **a search shortcut, not proof of identity**. The draft's own example has the Nostr client decode six bytes, search candidate public keys and let the user select the account.[1](https://blog.vrypan.net/2026/08/19/260819-sing-song/)

## Say what

Sing-song becomes more convincing once it is no longer asked to replace every encoding.

Hex remains excellent for machines and debugging, Base58 or Bech32 is much shorter on screen, BIP39 accepts a long phrase in order to transport entropy with a checksum, RFC1751 pays for a dictionary to obtain short words plus parity, and Proquint packs more bits into each five-letter group.

Sing-song chooses another point in the triangle:

**no dictionary, regular pronunciation, six bits per small syllable.**

The complete codec is simple enough to understand almost from memory, while its structure rejects plenty of crude transcription mistakes.

Our test also exposes the boundary cleanly: the string becomes easier to say, while **a pleasant sound provides no additional integrity by itself**.

For voice verification of a complete key, checksum protection is still the missing piece. For giving a long key a deterministic, decodable and faintly musical nickname, eight syllables can already be remarkably useful.

## References

1. [Vrypan, Sing-song: a speakable encoding for long numbers and keys, draft v0.1.2](https://blog.vrypan.net/2026/08/19/260819-sing-song/)
2. [RFC 1751, A Convention for Human-Readable 128-bit Keys](https://datatracker.ietf.org/doc/html/rfc1751)
3. [IETF Internet-Draft, Proquints: Readable, Spellable, and Pronounceable Identifiers, revision 11](https://datatracker.ietf.org/doc/draft-rayner-proquint/)
4. [BIP-39, Mnemonic code for generating deterministic keys](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki)
5. [NIP-19, bech32-encoded entities](https://github.com/nostr-protocol/nips/blob/master/19.md)
6. [BIP-173, Bech32 checksum design](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki)
7. [PGP word list, structure and phonetic design](https://en.wikipedia.org/wiki/PGP_word_list)
