Black lives matter
Portrait Dr. Axel Rauschmayer
Dr. Axel Rauschmayer
Homepage | Twitter
Cover of book “JavaScript for impatient programmers”
Book, exercises, quizzes
(free to read online)
Cover of book “Deep JavaScript”
Book (50% free online)
Cover of book “Tackling TypeScript”
Book (first part free online)
Logo of newsletter “ news”
Newsletter (free)

Exercise: text as Unicode clock faces

[2017-03-15] dev, javascript, unicode
(Ad, please don’t block)

In this blog post, we explore how arbitrary ASCII text can be encoded as Unicode clock faces:

> clocksToPlain('🕔🕘🕖🕕🕖🕜🕖🕜🕖🕟🕒🕑')

I’m explaining ideas by Maggie Pint and @FakeUnicode.

Unicode clock faces  

The following times are available as clock faces in Unicode:

  • Full hours:
    • CLOCK FACE ONE OCLOCK (U+1F550): 🕐
    • CLOCK FACE TWO OCLOCK (U+1F551): 🕑
    • ···
  • Half-hours:
    • ···

Interpreting clock faces as Unicode characters  

The idea is as follows: the clock faces give you hex digits from 0 to F (you get the range 0–7 twice). Therefore, two clocks encode an 8-bit hex number, which can be interpreted as a Unicode character.

If you want to, you can stop reading here and implement clocksToPlain() yourself. The next subsection gives you a little help. The subsection after that gives you solutions.

Encoding and decoding via escape() and unescape()  

For decoding clock-encoded text, we can get help from unescape():

> escape('🕔🕘')

You can see that each 21-bit code points is encoded as two 16-bit code units. For example, the code point U+1F554 is encoded as '%uD83D%uDD54':

> '\u{D83D}\u{DD54}'
> '\u{D83D}\u{DD54}' === '\u{1F554}'

The pair of clocks gives you the two hex digits 4 and 8. Once you have them, you can use escape():

> unescape('%48')


Thus, a compact way of decoding clock faces is:

function clocksToPlain(clocks) {
    return unescape(
        escape(clocks).replace(/u.{9}(.).{11}/g, '$1'));

    // Hello!

A more self-descriptive version looks like this:

function clocksToPlain(clocks) {
    const digits = [...clocks].map(ch => {
        const codePointHex = ch.codePointAt(0).toString(16);
        return codePointHex[codePointHex.length-1];
    return mapTuple(digits, 2, (digitPair) => {
        const codePoint = Number.parseInt(digitPair.join(''), 16);
        return String.fromCodePoint(codePoint);

function mapTuple(arr, tupleSize, func) {
    const result = [];
    let start = 0;
    while (start < arr.length) {
        const end = Math.min(arr.length, start + tupleSize);
        result.push(func(arr.slice(start, end), start, arr));
        start = end;
    return result;

    // Hello!

Alternatively, you can use String.prototype.match() to group characters, but I liked the more universal mapTuple().

> 'abcde'.match(/../g)
[ 'ab', 'cd' ]

Encoding text as clock faces  

If you want to produce clock text, you can use the following function.

function plainToClocks(plain) {
    return [...plain].map(ch => {
        const MAX_DIGITS = 6;
        const hexCode = ch.codePointAt(0).toString(16)
            .padStart(MAX_DIGITS, '0');
        // We are assuming that ch.codePointAt(0) < 256
        return digitToClock(hexCode[4])+digitToClock(hexCode[5]);

function digitToClock(hexDigit) {
    const codePoint = Number.parseInt('1F55'+hexDigit, 16);
    return String.fromCodePoint(codePoint);

    // 🕔🕘🕖🕕🕖🕜🕖🕜🕖🕟🕒🕑

I’m using the spread operator (...) to split the string plain into code units.

Further reading