Build an analog clock in Ring

A real clock — round face, three hands, ticking once a second — with every decision made by Ring. It assumes you have never built a web page. About ten minutes.

23:10:12
03/08/2026
What you will have at the end. It is in the starter kit too, as clock.html.

Why not just run the desktop clock? A desktop clock program opens a window and draws into it through Qt. A browser tab has no Qt and no window to draw into, so that program cannot run here — see the Q&A.

But almost none of that program is about Qt. Working out where an hour hand points at 23:10 is arithmetic, and arithmetic is what Ring is for. That part carries over untouched. Only the drawing changes.

1 Get a folder that already runs

Download the starter kit, unzip it, and double-click start-windows.bat or start-mac-linux.sh. A small server window opens and your browser lands on a working page.

Everything below happens in that folder. Create two new files there — myclock.html and myclock.ring — and visit localhost:8377/myclock.html as you go.

2 Draw a face that never changes

The face is just a picture: a circle, twelve marks, three lines. It is written in SVG — shapes described as text, so nothing needs to be drawn by hand or loaded as an image. Ring never touches any of it.

The coordinates run 0 to 200 across and down, so the middle is 100,100. A line from 100,100 up to 100,58 points at twelve o'clock.

<svg viewBox="0 0 200 200">
  <circle cx="100" cy="100" r="96" fill="white" stroke="black"/>

  <!-- one mark, then the same line turned 30 degrees at a time -->
  <line x1="100" y1="10" x2="100" y2="22" transform="rotate(0 100 100)"/>
  <line x1="100" y1="10" x2="100" y2="22" transform="rotate(30 100 100)"/>
  <!-- ...ten more, up to rotate(330 100 100) -->

  <!-- the three hands. Each has an id so Ring can find it. -->
  <line id="hour-hand"   x1="100" y1="100" x2="100" y2="58"/>
  <line id="minute-hand" x1="100" y1="100" x2="100" y2="40"/>
  <line id="second-hand" x1="100" y1="100" x2="100" y2="32"/>
</svg>

An id is a name you give one spot on the page. It is the only thing Ring needs in order to reach it — exactly like naming a control on a desktop form.

3 Turn about the middle, not the end

One line of styling, and it matters: by default a shape turns about its own centre, which would swing the hands around their midpoints. Both lines below say turn about the middle of the face.

<style>
  line { transform-box: view-box; transform-origin: 100px 100px; }
</style>

4 Work out the angles — this part is pure Ring

A full turn is 360 degrees, and this is the whole idea of the clock:

HandBecauseDegrees
Second60 seconds in a turnseconds × 6
Minute60 minutes in a turnminutes × 6
Hour12 hours in a turnhours × 30

One refinement makes it look real: at half past, a proper hour hand sits between six and seven. So the minutes nudge the hour hand along (+ minutes × 0.5), and the seconds nudge the minute hand (+ seconds × 0.1).

In myclock.ring:

func Tick aData

    cNow = time()          # "23:10:12"

    nHour   = number(substr(cNow, 1, 2))
    nMinute = number(substr(cNow, 4, 2))
    nSecond = number(substr(cNow, 7, 2))

    nSecondAngle = nSecond * 6
    nMinuteAngle = nMinute * 6 + nSecond * 0.1
    nHourAngle   = (nHour % 12) * 30 + nMinute * 0.5

    Page(:rotate, [ :id = "hour-hand",   :deg = nHourAngle   ])
    Page(:rotate, [ :id = "minute-hand", :deg = nMinuteAngle ])
    Page(:rotate, [ :id = "second-hand", :deg = nSecondAngle ])

    Page(:settext, [ :id = "digital", :text = cNow ])

That is ordinary Ring: time(), substr(), number(), arithmetic. Nothing about it is web-specific except the two Page(...) lines that hand the answer to the screen.

Why aData? Every Ring function called from the page receives one value — whatever was sent with the call. This one sends nothing, so it arrives empty. Declare it and ignore it; leaving it out is an error.

5 Teach the page one new word

A page understands three requests from Ring out of the box: settext, gettext and getvalue. Turning a shape is a fourth, so you add it — four lines, once:

<script src="ringscript.js"></script>
<script type="text/ring" src="myclock.ring"></script>
<script>
RingScript.boot().then(function (ring) {

    ring.on("rotate", function (data) {
        document.getElementById(data.id).style.transform =
            "rotate(" + data.deg + "deg)";
        return 1;
    });

    ring.call("Tick");                                     // draw it now
    setInterval(function () { ring.call("Tick"); }, 1000); // and every second
});
</script>

This is the only JavaScript in the whole clock, and it is the general pattern: when the page cannot yet do something you need, you teach it that one word, and from then on Ring asks for it by name like any other.

6 Open it, then change it

Go to localhost:8377/myclock.html. The hands should be showing the right time.

Now make it yours — edit myclock.ring, save, press Refresh:

  • Make the second hand move in fives: replace nSecond * 6 with floor(nSecond / 5) * 30.
  • Show the date as well, with date().
  • Turn it into a countdown: keep a number in a variable, subtract one each tick, and write it out with Page(:settext, ...).

None of that needs a build step, a compiler, or anything installed. Edit, save, refresh.

What this showed

The clock is about sixty lines, and the part that decides anything is Ring. The page holds the shapes; Ring holds the reasoning. That division is the whole method, and it scales: a form, an invoice, a rules engine — the page shows, Ring decides.