Human World Clock
I recently came across this website: AI World Clocks. Every minute, several AI models are given the exact same prompt: make a pure HTML/CSS clock showing the current time, and use only 2000 tokens to do so. It's quite hilarious watching how badly the AIs fail at this task 😂 Many of them mess up the numbers' position and rotation in ways that you wouldn't think was possible. Others mess up the alignment and rotation of the clock hands. As a software engineer myself, watching this is kind of like comfort food, given how AI is taking all our jobs :P
Out of the models shown, Kimi gets it right surprisingly consistently. Qwen seems to mess up the most, sometimes even showing a blank square instead of a circle.
Later, I thought to myself: well, perhaps we are being a bit unfair to our friends here. After all, the LLM is receiving no visual feedback — it tries to output a giant blob of code without knowing if it's on the right track by periodically running it. The numbers become woefully misaligned because it doesn't have access to a calculator, so it merely guesses the positions of points along a circle (oof).
And so, I decided to give myself the same challenge. Code an HTML/CSS clock from scratch with no AI tools, autocomplete, or running the code; just Notepad and a dream, like men once did.
Wait!
Wanna give yourself the same challenge? Here's your task, straight from the prompt at AI World Clocks:
Create HTML/CSS of an analog clock showing <the time you started this challenge, including seconds>. Include numbers (or numerals) if you wish, and have a CSS animated second hand. Make it responsive and use a white background. Return ONLY the HTML/CSS code with no markdown formatting.You are allowed to search and read MDN from the MDN page. No calculators, Google, ChatGPT, or anything else at all.
Give yourself, say, 30 minutes and try to code one. Go on. This blog post isn't going anywhere :)
If you read any further, you will see spoilers! So go ahead, try it and come back.
As the AI can draw from its knowledge, I did allow myself to access MDN; I needed to clarify the usage of things like transform-origin and steps() that I hadn't used in a while. Nothing else, though. I started at 6:51:03 PM.
Well…
See the code
<div class="root">
<div class="numbers">
<div data-num="1"></div>
<div data-num="2"></div>
<div data-num="3"></div>
<div data-num="4"></div>
<div data-num="5"></div>
<div data-num="6"></div>
<div data-num="7"></div>
<div data-num="8"></div>
<div data-num="9"></div>
<div data-num="10"></div>
<div data-num="11"></div>
<div data-num="12"></div>
</div>
<div class="hand hand-hour"></div>
<div class="hand hand-min"></div>
<div class="hand hand-sec"></div>
<div class="dot"></div>
</div>
<style>
* {
font-family: sans-serif;
}
.root {
position: relative;
width: 500px;
height: 500px;
outline: 2px solid #000;
margin: 10px;
background: #FFF;
border-radius: 50%;
--hour: 6;
--min: 51;
--sec: 3;
animation: 60s steps(60, jump-end) 0s infinite forwards sechand;
}
@keyframes sechand {
0% {--sec-anim: var(--sec);}
100% {--sec-anim: calc(var(--sec) + 60);}
}
.numbers > div {
position: absolute;
top: 20px;
left: 240px;
width: 20px;
height: 230px;
transform-origin: bottom center;
transform: rotate(calc((attr(data-num deg) / 12) * 360));
text-align: center;
font-size: 16px;
&::after {
content: attr(data-num);
width: 20px;
height: 20px;
display: inline flow-root;
transform: rotate(calc((attr(data-num deg) / 12) * -360));
}
}
.dot {
position: absolute;
top: 240px;
left: 240px;
width: 20px;
height: 20px;
border-radius: 50%;
background: #000;
}
.hand {
position: absolute;
top: 40px;
left: 245px;
width: 10px;
height: 210px;
border-radius: 5px;
transform-origin: bottom center;
background: #000;
}
.hand-hour {
top: 140px;
height: 110px;
transform: rotate(calc(((var(--hour) + ((var(--min) + (var(--sec-anim) / 60)) / 60)) / 12) * 360deg));
}
.hand-min {
transform: rotate(calc(((var(--min) + (var(--sec-anim) / 60)) / (12 * 60)) * 360deg));
}
.hand-sec {
left: 248px;
width: 4px;
border-radius: 1px;
background: #F00;
transform: rotate(calc((var(--sec-anim) / (12 * 60 * 60)) * 360deg));
transition: transform 100ms;
}
</style>
Dang it! 😅 I really wanted this to be a blog post where I said, "HAHA YES, I BEAT THE AI!! I AM WORTHY!!!"
At least I got the numbers right. Since I didn't have a calculator, my idea was to make rotated vertical bars at all the hourly stops, similar to the logic of the hour/minute/second hands. The hands use bars that are all positioned upright and then rotated around the circle's center depending on their value. The numbers use the same concept, except the bars themselves are invisible. At the top of each bar is the (visible) number, and each bar is rotated according to its hour number. This way, the numbers are positioned along the circle without having to use any circle math. The only problem with this issue was that the numbers would be rotated; for example, the number 6 at the bottom would be upside down. However, I foresaw this, and I applied the same rotation to each of the numbers but in the opposite direction; each number is always upright since its reverse rotation cancels out its bar's rotation. (I think CSS anchors could've also been used to do this, but I'm not as familiar with them yet, so given that I was coding blind, I stuck to what I know.) I also thought about the alignment; for the hands, I left 20px at the top for the 16px font number, and the numbers themselves are 20px from the circle's edge.
I wanted the clock to be perfectly accurate. When the time is, say, 6:15, the hour hand is not exactly on 6 but actually a quarter of the way to 7. My intent here was to make the second hand positioned according to its value, the minute hand according to its value plus the fractional minute value of the second hand, and the hour hand according to its value plus the fractional hour value of the (minute + fractional minute) value from earlier. I actually did this, but, for some reason, I multiplied extra values in the minute and second hands; instead of dividing seconds by 60, I divided it by 12 × 60 × 60, making it take 12 hours to go around instead of one minute. Embarrassingly, I must've checked this part like five times and I still didn't catch this 😭
First mistake: Wrong denominator in minute and second hands. Both should just be 60.
Next, the animation. I was surprised that it was completely motionless when I ran it. My idea here was to animate a CSS variable --sec-anim and use it in place of my actual CSS variable for seconds (--sec). The animated variable would start from the initial seconds value and continue around to the initial value again, looping infinitely. This approach does not make a perfectly accurate clock; while the minute and hour hands slowly move as the second hand did, once the second hand crosses its initial rotation, the entire clock resets to its initial state. However, it will be valid for the entire minute it was created in, just like the AI World Clocks page asks the AIs to do.
My failure here was merely due to a gap in my knowledge: apparently, if you want to "animate" a CSS variable in the way I attempted to do, you need to first define some configuration for it using the CSS @property rule. Adding this and setting the initial value fixed the animation.
Second mistake: Missing @property rule for animating CSS variable; it needs to be set. (I say this one wasn't my fault 😊)
Fixing these two small mistakes turns my clock from broken into the vision I initially had for it:
See the code (changes marked with comments)
<div class="root">
<div class="numbers">
<div data-num="1"></div>
<div data-num="2"></div>
<div data-num="3"></div>
<div data-num="4"></div>
<div data-num="5"></div>
<div data-num="6"></div>
<div data-num="7"></div>
<div data-num="8"></div>
<div data-num="9"></div>
<div data-num="10"></div>
<div data-num="11"></div>
<div data-num="12"></div>
</div>
<div class="hand hand-hour"></div>
<div class="hand hand-min"></div>
<div class="hand hand-sec"></div>
<div class="dot"></div>
</div>
<style>
* {
font-family: sans-serif;
}
.root {
position: relative;
width: 500px;
height: 500px;
outline: 2px solid #000;
margin: 10px;
background: #FFF;
border-radius: 50%;
--hour: 6;
--min: 51;
--sec: 3;
animation: 60s steps(60, jump-end) 0s infinite forwards sechand;
/* ↓ ADDED ↓ */
--sec-anim: var(--sec);
}
/* ↓ ADDED ↓ */
@property --sec-anim {
syntax: "<number>";
inherits: true;
initial-value: 0;
}
@keyframes sechand {
0% {--sec-anim: var(--sec);}
100% {--sec-anim: calc(var(--sec) + 60);}
}
.numbers > div {
position: absolute;
top: 20px;
left: 240px;
width: 20px;
height: 230px;
transform-origin: bottom center;
transform: rotate(calc((attr(data-num deg) / 12) * 360));
text-align: center;
font-size: 16px;
&::after {
content: attr(data-num);
width: 20px;
height: 20px;
display: inline flow-root;
transform: rotate(calc((attr(data-num deg) / 12) * -360));
}
}
.dot {
position: absolute;
top: 240px;
left: 240px;
width: 20px;
height: 20px;
border-radius: 50%;
background: #000;
}
.hand {
position: absolute;
top: 40px;
left: 245px;
width: 10px;
height: 210px;
border-radius: 5px;
transform-origin: bottom center;
background: #000;
}
.hand-hour {
top: 140px;
height: 110px;
transform: rotate(calc(((var(--hour) + ((var(--min) + (var(--sec-anim) / 60)) / 60)) / 12) * 360deg));
}
.hand-min {
/* ↓ CHANGED ↓ */
transform: rotate(calc(((var(--min) + (var(--sec-anim) / 60)) / 60) * 360deg));
}
.hand-sec {
left: 248px;
width: 4px;
border-radius: 1px;
background: #F00;
/* ↓ CHANGED ↓ */
transform: rotate(calc((var(--sec-anim) / 60) * 360deg));
transition: transform 100ms;
}
</style>
Of course, given more time, extra embellishments such as tick marks above the numbers and shadows could be added.
I think this was a solid attempt, and it was a fun little activity to give my brain a workout as well 😄 Share your attempt and let me know your thoughts on the Discord!