Sunday 26 June 2016

Word Time 1KB

One of my inspirations for the Rotor Clock was the Instructables wordclock that tells the time (and temperature) based on a 16-by-16 grid of letters:

It looked very much like a puzzle to me, so I immediately set about trying to work out the minimum size of grid that could tell the time to an accuracy of one minute. I managed to get the grid down to 13-by-13:


[I have a suspicion you can get it even smaller if you use tricks such as spelling words vertically; but, after a considerable amount of effort, I plumped for 13-by-13]

In my usual modus operandi, I then tried to optimise the heck out of the problem by hand and attempt to fit a solution into a one-kilobyte web page that works on Chrome, Firefox and Internet Explorer. For added frisson, I didn't allow myself to use non-ASCII characters nor the evil JavaScript "eval()" or its ilk. The result, World Time, takes up just 1023 bytes:

Here's a quick breakdown of the contents:

We can omit a large number HTML elements such as "DOCTYPE", "head" and "body" and dive straight into the style definitions, which are assumed by browsers to be CSS. We set all backgrounds to black, centre the table in the middle of the page and format each table cell so that a 13-by-13 grid takes up most of the viewport. The default text colour is set to very dark grey and a shadow effect is added to simulate "light bleed":

We then define a table that will hold the 169 grid characters and be accessed in the JavaScript via a identifier named "t". The string is split into 169 table cells (each one 10 characters of HTML) which are then divided into 13 rows:

Next we decode a 78-character string that has been btoa-encoded (so we adhere to our ASCII restriction). The 78 characters represent 39 pairs of offsets into the "t" grid. The first of each pair is the "start" offset (0 to 168); the second is the "end" offset plus one. These 39 pairs define tokens with even numbers identifying them thus:

           #00 = "O'CLOCK"
           #02 = "MINUTE"
           #04 = "MINUTES"
           #06 = "PAST"
           #08 = "TO"
           #10 = "HALF"
    #12 to #38 = "ONE" to "FOURTEEN" (upper portion)
           #40 = "QUARTER"
    #42 to #50 = "SIXTEEN" to "TWENTY"
           #52 = "MIDNIGHT"
    #54 to #74 = "ONE" to "ELEVEN" (lower portion)
           #76 = "NOON"

These 38 tokens constitute all the words used by the clock. See below for details of the call to "setInterval":

Next, we define a function "d()" that takes two arguments (the third argument declaration is just a short way of declaring a variable - see 140bytes for other such techniques), The first argument is the hour as an integer. The second argument is either "00" (the token for "O'CLOCK") or "" depending on requirements. The "d()" function therefore returns the hour as a string of two-digit tokens (e.g. "7400" for "ELEVEN O'CLOCK"). It has a special case for "MIDNIGHT" and "NOON" which never take the "O'CLOCK" suffix:

We define another function "g()" that takes an integer number of minutes (0 to 59) as its only argument. It returns a string of tokens representing the appropriate number of minutes as words (e.g. "(something) MINUTES" or "TWENTY (something) MINUTES", taking into consideration the exceptional cases for "ONE MINUTE", "QUARTER" and "HALF". Note that we're playing fast and loose with the type system here: the number 1202 will be converted to the string "1202" elsewhere in the program.

Next, we define an anonymous function (let's call it "storeColours") that takes one true argument ("b" is a local variable declaration again). This is called with the result of another anonymous function ("calculateColours") that takes three arguments: the current local hour, the current local minute and an empty dictionary. The "calculateColours" function constructs a full string of tokens that describe the current time. If the minute value is zero, we use the "d()" function to construct "MIDNIGHT", "NOON" or "(hour) O'CLOCK". If the minute value is less than or equal to thirty, it constructs "(something) PAST (hour)". Otherwise, if constructs "(something) TO (hour+1)". The string of tokens are split into two-digit numbers and looked up in the "s" table described above. For every letter in every word, "calculateColours" stores an HSL colour in the "c" dictionary keyed by the appropriate offset into the grid. The dictionary is then passed to "storeColours" which modifies the style text colour of each table cell in turn. Setting the colour to "" results in the text colour reverting to the colour defined in the CSS entry for "td" (i.e. very dark grey):

Finally, we invoke the "calculateColours" and "storeColours" function at most every 1000 milliseconds via the "setInterval" function mentioned above, and gracefully close the HTML tags.

A lot of the JavaScript compression came from Google Closure Compiler.

The result is a colourful clock that spells out the time in English words, uses a responsive HTML layout and takes the local timezone, changes in daylight savings and even leap-seconds into consideration.

UPDATE: 2016-06-29

I've managed to shave an additional ten bytes off the size and updated the page, but the structure and technique are essentially the same as above.

No comments:

Post a Comment