Clock

TL;DR Like clocks? I built a clock made of clocks, click here to play with it 👇

A gif of a Clock made from many smaller clocks

Or read on for a more in depth look at building clocks out of clocks (with some React).


Inspiration from strange places

Have you something like this before?

View this post on Instagram

A post shared by JAKOB LANGE (@jakob_lange) on

Me too, it's cool huh! I really enjoy the way the time seems to gradually appear and take shape.

Actully, the more I looked at it the more I appreciated the way it was put together, they are not indiviudal clocks in the traditional sense as one arm is not simply following the other. Both arms of the clock are moving freely allowing for the various interesting shapes and movements and, of course, the time.

I bet that wouldn't be too difficult to put together, a RaspberryPi to control the "clocks" with a mechanism to specify an angle for the first and second hands...


Creativity intensifies

Ok, so I don't have the motors to build the physical clocks and I'm not sure what I need to buy or how to wire them into a RaspberryPi. However, what I can do to prototype the idea is build a web app.

The quickest way to get started is to use create-react-app, this provides the basic setup for a React app.

The first thing I wanted to do was to create a simple clock, requirements were simple; two hands that can move independantly of each other and a face. Throwing together some divs, a bit of CSS and voila, I had the makings of a clock, I can specify the angle in degrees, of each hand using the a CSS transform rotate and there's a white face.

An image of the first clock with its arms at odd angles

Making things move

From here I wanted to see what the best way to animating things could be. I spent a bit of time looking into the various ways I could animate components in React. Then I thought, nah, why not just see what I can I achieve without a library, surely I should be able to prototype something just using React and a bit of CSS knowhow.

Using create-react-app meant that I get a modern version of React out of the box, which means hooks! I've been looking for an excuse to try out hooks and this seemed a good a time as any to try.

I imagined a basic rendering loop like this:

  1. inital state sets start / end positions
  2. render clock hands at initial positon
  3. setTimeout or requestAnimationFrame to increment and set new position
  4. render clock hands at new position

Keeping the hand positions in state meant I could "animate" the hands by updating the state, incrementally, and causing a re-render which would update the hands to their new position.

	const [angle, setAngle] = useState({
	  hour: getRandomStartingAngle(), 
	  minute: getRandomStartingAngle()
	});

In the new world of React hooks there's a hook perfect for the job of triggering the increment: useEffect which, amongst other things, is run after every render (for more details check out the docs).

To increment I needed to create an effect with would trigger at a reasonable rate, for this I used requestAnimationFrame which is a suitable API to schedule an update on as it's usually called 60 times per second (generally considered the threshold for smooth animation).

 useEffect(()=> {
	  requestAnimationFrame(()=>{
			const { hour, minute } = angle;
			setAngle({
				hour: hour + 1, 
				minute: minute + 1
			});
	  })
 }, [angle]);

Putting it all together and I had a clock that animated around and around, and around, and around, and never stop.

A gif of the first clock with its arms moving around

It seemed to work pretty well. However, I made a couple of mistakes which won't become apparent until I start trying to create numbers from the clocks.

Drawing numbers, with clocks

Next was to put a bunch of these little clocks onto the screen and see how they animate, using a small bit of flexbox to define rows / columns and create 2x3 grids for a single number.

2x3 grid of little clocks with their hands at random positions

So it's starting to look a lot more like it could resemble a number. In order to animate to the number I needed to work out all the various positions that could go into a clock number and then tell the smaller clocks to animate to those positions.

The approach to draw a number was to pass a targetAngle to each of the smaller clocks. The basic idea was that for a given target angle the clock would continue increment the postion of the hands until they'd reached it, then stop.

function getIncrementValue(angle, targetAngle) {
   if(angle === targetAngle){
       return angle;
   } else { 
       return resetAngle(angle + 1);
   }
}

Incrementing by 1 each time means the target angle would eventually be achieved, however the first mistake I made in the sub clock logic rears its head.

As the hands increment around they can reach an angle above 360deg which breaks for situations where the hand has to travel around the whole clock to reach the target angle. To solve this I added a resetAngle function which keeps the numbers between 0 < 359 allowing the taget angle to always be reached.

Next was the job of actually figuring out these angles. The approach intially was to write, by hand, each angle, for each number, for each clock in the 2x3 grid... I quickly grew tired of this. Instead it's easier to specify a a number of set positions which are the building blocks of the number.

const createAngleSet = (hour, minute) => ({hour, minute});
const bothLeft = createAngleSet(270, 90);
const bothRight = createAngleSet(90, 270);
const bothTop = createAngleSet(0, 180);
const bothBottom = createAngleSet(180, 0);
const topAndBottom = createAngleSet(0, 0);
const rightAndLeft = createAngleSet(90, 90);
const topLeftCorner = createAngleSet(90, 0);
const topRightCorner = createAngleSet(270, 0);
const bottomLeftCorner = createAngleSet(0, 270);
const bottomRightCorner = createAngleSet(0, 90);
const emptySpace = createAngleSet(225, 45);

Above is the list of all the positions needed to "draw" the numbers 0-9 and they're used in a number config that looks something like this:

TWO: {
  a1: { ...bothRight },
  a2: { ...topLeftCorner },
  a3: { ...bottomLeftCorner },
  b1: { ...topRightCorner },
  b2: { ...bottomRightCorner },
  b3: { ...bothLeft }
}

The result of all this work was the realisation of the numbers. The effect was captured almost exactly as I wanted it, with the number appearing out of the randomness of the single clock faces.

2x3 grid of little clocks with their hands at random positions moving
	 to the positons of the number 2

The full NumberGrid is available for preview and illustrates the whole number set that is used in building the clock.

Animations, animations, animations

Earlier I mentioned I had made a mistake when creating the mini-clock, did you catch it?

Well, my first go with useEffect was more on feeling than careful study of the documentation. As a result when I first attmepted to draw the numbers 0-9, at the same time, there was pretty terrible performance.

Turns out useEffect is expected to be triggered and then torn down, as a reuslt it's supposed to provide a cleanup function to do any bits of cleanup that are needed if an in progress effect needs to be cancelled. This caused a subtle issue as everything seems to animate smoothly however it slowed way down as I scaled up from the 1 mini-clock to the 54 that I needed in order to display the full 0-9 numbers.

 useEffect(()=> {
	  const increment = requestAnimationFrame(()=>{
			const { hour, minute } = angle;
			const { hour: targetHour, minute: targetMinute } = targetAngle;
			setAngle({
				hour: getIncrementValue(hour, targetHour, speed), 
				minute: getIncrementValue(minute, targetMinute, speed)
			});
	  })
	  return () => cancelAnimationFrame(increment);
 }, [angle, targetAngle, speed]);

I corrected this within my effect with cancelAnimationFrame and added a speed value, via useContext to give me some control over the mini-clock animations (necessary for building a stopwatch).

Clock

I now have all the pieces to build the clock. Using the Date object and updating the target time every time the hours or seconds changed. The DigitalClock would then work out the individual parts of the time string and pass them to the ClockNumbers which would, in turn, pass the individual parts to each mini clock.

I am so happy with the result 🕑 😬

Thanks for reading and check out the clock below 👇 A gif of a Clock made from many smaller clocks

First appeared on Trusty Interior, last update 26 Nov 2023