21 bytes · base 256 · one integer

Happy Birthday Jamie

This card is a 51-digit number. Watch it resolve.

Stage 1 / 3 — read as one integer

105784397473850395008567252507661666543031051708773

      The whole trick

      Text is already a number. Nobody converted anything.

      There is no encryption here, and no lookup table. The message is being shown in a different base, and every step is reversible arithmetic you could do by hand if you had the patience.

      A computer stores "Happy birthday, Jamie" as 21 bytes, and a byte is just a number from 0 to 255. A sequence of values that each run 0–255 is the definition of digits in base 256 — so the whole string can be read as a single enormous number.

      b'Hi' → [ 72, 105 ]
      72 × 256 + 105 = 18537
      exactly how [3, 7] in base 10 means 3 × 10 + 7 = 37

      Scale that from 2 bytes to 21 and you get a 51-digit number, because 256²¹ lands around 10⁵⁰. That is the number at the top of this card.

      Byte 0 through byte 20

      In hex it stops being a puzzle.

      Hex is base 16, and 256 = 16², so exactly two hex digits encode one byte. The boundaries line up perfectly, which is why the message becomes readable to anyone who knows a little ASCII. The 51-digit decimal form is deliberately the least legible representation — that is what makes it a gift instead of a note.

        0x48 is 72 is H. 0x20 is 32 is a space. Same bits, three notations.

        Reading it back

        Two things the number can't know about itself.

        Going back the other way is .to_bytes(21, 'big') followed by .decode(). Those two arguments exist because a bare integer has lost the only two facts that base conversion can't recover on its own.

        21

        How many digits to produce. The number doesn't know it "should" be 21 bytes wide.

        >>> n.to_bytes(20, 'big')
        OverflowError: int too big to convert

        >>> n.to_bytes(24, 'big')
        b'\x00\x00\x00Happy birthday, Jamie'

        Too few and it genuinely doesn't fit. Too many and you get harmless leading zeros — the same as writing 037 for 37.

        'big'

        Which end holds the most significant digit. Big-endian puts it first, the way we write numbers down.

        >>> n.to_bytes(21, 'little')
        b'eimaJ ,yadhtrib yppaH'

        Flip it and the birthday wish arrives backwards, which is its own kind of party trick.

        Your turn

        Type anything. Watch it become an integer.

        As one base-256 integer

        Same value, base 16

        Byte length · decimal digits

        This runs on JavaScript's BigInt, for the same reason the original fits on one line in Python: both languages have arbitrary-precision integers, so a 51-digit value is as unremarkable to them as 7.

        Runnable

        The card, as one line of Python.

        zsh · paste and run
        python3 -c "print((105784397473850395008567252507661666543031051708773).to_bytes(21,'big').decode())"

        The outer quotes have to be double, because 'big' already claims the single ones. Swap them and your shell will eat the line before Python ever sees it.

        Bonus round

        A card that reprints itself.

        Three lines. Run it and it prints itself — character for character identical, except the counter has gone up by one. The %r is doing the real work: it quotes the string so the string survives reproducing itself.

        bday.py
        runs = 0
        s = 'runs = %d\ns = %r\nprint(s %% (runs + 1, s))  # happy birthday, Jamie'
        print(s % (runs + 1, s))  # happy birthday, Jamie

        Because the output is also a valid program, you can pipe it into itself. Every stage in the pipe is the same program reading the previous one's output, and the only thing that changes is the number:

        four generations in one command
        $ python3 bday.py | python3 | python3 | python3
        runs = 4