Skip to content

Instantly share code, notes, and snippets.

@iiasceri
Created October 11, 2025 03:04
Show Gist options
  • Select an option

  • Save iiasceri/59b701deb2f4aaf019a99a3e779cc623 to your computer and use it in GitHub Desktop.

Select an option

Save iiasceri/59b701deb2f4aaf019a99a3e779cc623 to your computer and use it in GitHub Desktop.
Clock made of clocks
<div id="root"></div>
import React, { useState, useEffect, useRef } from "https://esm.sh/react@19";
import { createRoot } from "https://esm.sh/react-dom@19/client";
const H = { h: 0, m: 180 },
V = { h: 270, m: 90 },
TL = { h: 180, m: 270 },
TR = { h: 0, m: 270 },
BL = { h: 180, m: 90 },
BR = { h: 0, m: 90 },
E = { h: 135, m: 135 };
const digits = [
[
BR, H, H, BL,
V, BR, BL, V,
V, V, V, V,
V, V, V, V,
V, TR, TL, V,
TR, H, H, TL,
],
[
BR, H, BL, E,
TR, BL, V, E,
E, V, V, E,
E, V, V, E,
BR, TL, TR, BL,
TR, H, H, TL,
],
[
BR, H, H, BL,
TR, H, BL, V,
BR, H, TL, V,
V, BR, H, TL,
V, TR, H, BL,
TR, H, H, TL,
],
[
BR, H, H, BL,
TR, H, BL, V,
E, BR, TL, V,
E, TR, BL, V,
BR, H, TL, V,
TR, H, H, TL,
],
[
BR, BL, BR, BL,
V, V, V, V,
V, TR, TL, V,
TR, H, BL, V,
E, E, V, V,
E, E, TR, TL,
],
[
BR, H, H, BL,
V, BR, H, TL,
V, TR, H, BL,
TR, H, BL, V,
BR, H, TL, V,
TR, H, H, TL,
],
[
BR, H, H, BL,
V, BR, H, TL,
V, TR, H, BL,
V, BR, BL, V,
V, TR, TL, V,
TR, H, H, TL,
],
[
BR, H, H, BL,
TR, H, BL, V,
E, E, V, V,
E, E, V, V,
E, E, V, V,
E, E, TR, TL,
],
[
BR, H, H, BL,
V, BR, BL, V,
V, TR, TL, V,
V, BR, BL, V,
V, TR, TL, V,
TR, H, H, TL,
],
[
BR, H, H, BL,
V, BR, BL, V,
V, TR, TL, V,
TR, H, BL, V,
BR, H, TL, V,
TR, H, H, TL,
],
];
const normalizeAngle = (next, prev) => {
const delta = ((next - prev) % 360 + 360) % 360;
return prev + delta;
};
const getTimeDigits = () => {
const now = new Date();
return [
now.getHours(),
now.getMinutes(),
now.getSeconds()
].flatMap((val) => String(val).padStart(2, "0").split("").map(Number));
};
const randomAngle = () => Math.floor(Math.random() * 360)
const Clock = ({ h, m, initial }) => {
const prev = useRef({ h: 0, m: 0 });
const hourAngle = normalizeAngle(h, prev.current.h);
const minuteAngle = normalizeAngle(m, prev.current.m);
prev.current = { h: hourAngle, m: minuteAngle };
return (
<div
className="clock"
style={{
"--hour-angle": initial ? randomAngle() : hourAngle,
"--minute-angle": initial ? randomAngle() : minuteAngle,
"--dur": initial ? 1 : 0.4
}}
/>
);
};
const App = () => {
const [time, setTime] = useState(Array(6).fill(0));
const [initial, setInitial] = useState(true);
useEffect(() => {
let updateTimerId;
const updateTime = () => {
setTime(getTimeDigits());
const now = Date.now();
const delay = 1000 - (now % 1000);
updateTimerId = setTimeout(updateTime, delay);
};
const initialTimerId = setTimeout(() => {
setInitial(false)
updateTime();
}, 600)
return () => {
clearTimeout(updateTimerId)
clearTimeout(initialTimerId)
};
}, []);
return (
<div className="app">
{time.map((t, i) => (
<div key={i}>
{digits[t].map(({ h, m }, j) => <Clock key={j} h={h} m={m} initial={initial} />)}
</div>
))}
</div>
);
}
createRoot(document.getElementById('root')).render(<App />);
body {
padding: 0;
margin: 0;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
.app {
--clock-size: 3vw;
--gap: calc(var(--clock-size) * 0.05);
--clock-segment-w: calc(var(--clock-size) * 4 + var(--gap) * 5);
--clock-segment-h: calc(var(--clock-size) * 6 + var(--gap) * 5);
font-family: sans-serif;
text-align: center;
display: flex;
gap: var(--gap);
align-items: center;
justify-content: center;
height: 100vh;
padding-left: calc(var(--clock-size) + var(--gap) * 2);
}
.app > div {
display: flex;
flex-wrap: wrap;
gap: var(--gap);
width: var(--clock-segment-w);
height: var(--clock-segment-h);
}
.app > div:nth-of-type(even) {
margin-right: var(--clock-size);
}
.clock {
--w: 47%;
--h: 3px;
--dur: 0.4s;
position: relative;
width: var(--clock-size);
height: var(--clock-size);
border-radius: 50%;
flex-shrink: 0;
border: 2px solid white;
background: linear-gradient(225deg, #d0d0d0 10%, white);
box-shadow: -2px 2px 6px #d0d0d0,
2px -2px 6px #ffffff;
}
.clock::before,
.clock::after {
position: absolute;
content: '';
top: calc(50% - var(--h) * 0.5);
left: 50%;
transform-origin: 0% 50%;
width: var(--w);
height: var(--h);
background: black;
border-radius: 9999px;
transition: calc(var(--dur) * 1s) ease-in-out;
transform: rotate(calc(var(--angle) * 1deg));
}
.clock::before {
--angle: var(--hour-angle);
}
.clock::after {
--angle: var(--minute-angle);
}
@media (max-width: 700px) {
.clock {
--h: 2px;
border: 1px solid white;
}
}
@media (max-width: 500px) {
.clock {
--h: 1px;
--w: 50%;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment