Skip to content

Instantly share code, notes, and snippets.

@bkietz
Last active October 26, 2025 01:44
Show Gist options
  • Save bkietz/73d51409ca61119969af6abb28970362 to your computer and use it in GitHub Desktop.
Save bkietz/73d51409ca61119969af6abb28970362 to your computer and use it in GitHub Desktop.
Vernier clock dial
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
let svg = `\
<svg
width="2048"
height="2048"
viewBox="0 0 128 128"
xmlns="http://www.w3.org/2000/svg"
>
<script>
const svg = document.querySelector("svg")
try {
window.onresize = ()=> {
svg.width.baseVal.value = svg.clientWidth
svg.height.baseVal.value = svg.clientHeight
}
window.onresize()
} catch (_) {}
const now = new Date()
svg.setCurrentTime(now.getSeconds() + (now.getMinutes() + now.getHours() * 60) * 60)
</script>
<style>
text {
font: bold 4px sans-serif;
text-anchor: middle;
}
line {
stroke: black;
stroke-width: 0.5px;
}
</style>
<circle
cx="64"
cy="64"
r="64"
fill="white"
stroke="black"
stroke-width="0.5"
/>
<circle
cx="64"
cy="64"
r="39"
fill="none"
stroke="black"
stroke-width="1.5"
/>
`
function g(id, writeLines) {
svg += `\n <g id="${id}">\n`
writeLines()
svg += ` </g>\n`
}
function notches(id, count, y1, y2) {
g(id, () => {
for (let i = 0; i < count; ++i) {
const angle = 360 * i / count
svg += ` <line x1="64" x2="64" y1="${y1}" y2="${y2}" transform="rotate(${angle} 64 64)" />\n`
}
})
}
notches("hour-notches", 24, 6, 25)
g("hours", () => {
for (let hour = 0; hour < 24; ++hour) {
const angle = 360 * hour / 24
svg += ` <text x="64" y="4" transform="rotate(${angle} 64 64)">${hour}</text>\n`
}
})
notches("vernier-48", 48, 10, 25)
const timeFactor = 1
g("animated", () => {
svg += `
<animateTransform
attributeName="transform"
attributeType="XML"
type="rotate"
from="0 64 64"
to="360 64 64"
dur="${timeFactor === 1 ? "24h" : `${Math.round(24 * 60 * 60 / timeFactor)}s`}"
repeatCount="indefinite"
/>
<line x1="64" x2="64" y1="64" y2="6" stroke-width="1" stroke="red" />
<circle
cx="64"
cy="3"
r="3"
fill="none"
stroke="black"
stroke-width="0.5"
/>
`
notches("vernier-49", 49, 10, 25)
notches("minute-notches", 60, 25, 28)
g(`minutes`, () => {
for (let minute = 0; minute < 60; minute += 5) {
if (minute != 0) {
const angle = 360 * minute / 60
svg += ` <text x="64" y="32" transform="rotate(${angle} 64 64)">${minute}</text>\n`
}
}
})
g("animated-seconds", () => {
svg += `
<animateTransform
attributeName="transform"
attributeType="XML"
type="rotate"
from="0 64 64"
to="360 64 64"
dur="${timeFactor === 1 ? "60s" : `${Math.round(60_000 / timeFactor)}ms`}"
repeatCount="indefinite"
/>
<line x1="64" x2="64" y1="64" y2="32" stroke-width="1" stroke="red" />
`
})
})
svg += `
</svg>
`
console.log(svg)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment