JS/Canvas Polar Clock

Summer is finally here, and I’ve been “getting my open source on” (did I really just say that?)—first with CSS3 Breadcrumbs, and now this.

Long ago, I read an article on Lifehacker that mentioned a free, unique, and artistic polar clock screen saver by pixelbreaker. I used it as my screen saver for a long time, but I had to stop using it since I spent too much time watching it as the time passed by; it was just so cool to stare at!

Fast-forward to a few days ago: I randomly started thinking about the HTML5 Canvas, and the idea came to me: what if I made an HTML5 polar clock? Fortunately, this had already been done by Henrik Joreteg. Favoring object-oriented, modular code, I used Henrik’s code to make a CoffeeScript class, compiled it into JavaScript, and used it in a web page:

Enough already! Let’s see the code!

this.PolarClock = class PolarClock
  constructor: (@canvas, @radius, opts = {}) ->
    @settings =
      lineWidth:   @radius/12
      lineSpacing: 1
      lineCap:     'butt'      # he he--butt!
      interval:    75
      twelveHour:  false
      twoPhase:    false

    @settings[key] = value for key, value of opts

  start: ->
    @update()
    @intervalID = setInterval @update.bind(this), @settings.interval unless @intervalID

  stop: ->
    if @intervalID
      clearInterval @intervalID
      @intervalID = null

  update: ->
    @canvas.clearRect 0, 0, 2*@radius, 2*@radius
    @canvas.save()
    @canvas.translate @radius, @radius
    @canvas.scale 2, 2
    @canvas.rotate -Math.PI/2

    @canvas.lineWidth = @settings.lineWidth - @settings.lineSpacing
    @canvas.lineCap = @settings.lineCap

    now = new Date()
    daysInMonth = switch now.getMonth()
      when 1
        yr = now.getFullYear()
        if yr%400==0 || yr%100!=0 && yr%4==0 then 29 else 28
      when 3,5,8,10 then 30
      else 31

    msec = now.getMilliseconds()
    sec = now.getSeconds() + msec/1000
    min = now.getMinutes() + sec/60
    hour = now.getHours() + min/60
    weekday = now.getDay() + hour/24
    day = now.getDate() - 1 + hour/24
    month = now.getMonth() + day/daysInMonth

    if @settings.twoPhase
      @_writeTime 0, (sec%30)/30, sec > 30
      @_writeTime 1, (min%30)/30, min > 30

      if @settings.twelveHour
        @_writeTime 2, (hour%6)/6, (hour in [6..12]) || hour > 18
      else
        @_writeTime 2, (hour%12)/12, hour > 12

      @_writeTime 3, ((weekday*2)%7)/7, 2*weekday > 7
      @_writeTime 4, ((day*2)%daysInMonth)/daysInMonth, 2*day > daysInMonth
      @_writeTime 5, (month%6)/6, month > 6
    else
      @_writeTime 0, sec/60
      @_writeTime 1, min/60

      if @settings.twelveHour
        @_writeTime 2, (hour%12)/12, hour > 12
      else
        @_writeTime 2, hour/24

      @_writeTime 3, weekday/7
      @_writeTime 4, day/daysInMonth
      @_writeTime 5, month/12

    @canvas.restore()

  _writeTime: (index, percent, ccw = false) ->
    @canvas.save()
    @canvas.strokeStyle = @_calculateColor if ccw then 1 - percent else percent
    @canvas.beginPath()
    @canvas.arc 0, 0, (@radius - (1+2*index)*@settings.lineWidth)/2, 0, percent*2*Math.PI, ccw
    @canvas.stroke()
    @canvas.restore()

  _calculateColor: (percent) ->
    brightness = 255
    b = percent * 255
    g = brightness - b
    "rgba(0, #{Math.round g}, #{Math.round b}, 1)"

You can grab the compiled JavaScript here.

Usage is very straightforward: You just need a <canvas> tag somewhere on your page:

<canvas id="canvasID" width="200" height="200"></canvas>

… the included JavaScript library:

<script src="/path/to/polarClock.js"></script>

… and some configuration code:

var options = {
  lineWidth:   8        // thickness of each circle              (default: 1/12th of radius)
  lineSpacing: 2        // spacing between each circle in pixels (default: 1)
  lineCap:     'butt'   // cap style of lines: 'butt' or 'round' (default: butt)
  interval:    100      // animation refresh interval            (default: 75)
  twelveHour:  true     // use a 12-hour 'hours' circle          (default: false)
  twoPhase:    false    // make two 'sweeps' around circle       (default: false)
}

var ctx = document.getElementById('canvasID').getContext('2d');

// third argument is optional if you just want the defaults
var clock = new PolarClock(ctx, 100, options);

clock.start();

Although not as readable as pixelbreaker’s screen saver, I think it looks cool… and what better thing is there to be done with something like this than to put it on the OS X Dashboard? (Did you know that dashboard widgets are just HTML, CSS, and JavaScript? I was surprised the first time I heard that, too). I opened up Dashcode, copied the polarClock.js file, tweaked the code a little, et voilà!

Polar-Clock.widget
Polar-Clock.widget

11 May 2012: After reading up about HTML5 localStorage and cache manifests, I have released an “offline” polar clock web app tailored to iOS devices (but still works remarkably well in web browsers and other devices). Add the app to your home screen (or bookmarks) for easier access. Tap on the clock to cycle among the 12-/24-hour and 1-/2-phase modes. The web app is “offline” in that it caches all of the necessary files to your device, so it can run even without an internet connection. Enjoy!

Back