
The colour columns
Hover, SVG
NewFree
A year's hours of daylight in a ring of 365 hairlines, one a day, each as long as the day: a vermilion hand reads the day under the pointer, the number in the middle counts on the same spring and the year is inked up to the hand; choosing another city every day springs to its new length, in a wave that starts at the hand. Minimal style.
A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN. The React and Vue tabs hold the same demo as a ready component, which switches its animations off when it leaves the page.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>A year of daylight</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/archivo@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- a year of daylight drawn as a ring: one hairline a day, as long as the hours of light of that day. A vermilion
hand reads the day under the pointer, the big number counts on a spring, the year is inked up to the hand, and
choosing another city every day springs to its new length in a wave that runs round from the hand -->
<main class="stage" data-stage>
<header class="top">
<span class="top__name">Almanac</span>
<span class="top__note">Hours of daylight, day by day</span>
</header>
<div class="dial" role="slider" tabindex="0" aria-label="Day of the year" aria-valuemin="1" aria-valuemax="365" data-dial>
<svg class="dial__svg" viewBox="-500 -500 1000 1000" aria-hidden="true">
<path class="guide guide--base" data-guide="0"></path>
<path class="guide guide--half" data-guide="12"></path>
<path class="guide guide--full" data-guide="24"></path>
<path class="guide guide--months" data-month-ticks></path>
<path class="days days--future" data-future></path>
<path class="days days--past" data-past></path>
<path class="hand" data-hand></path>
</svg>
<span class="scale scale--half" aria-hidden="true">12 h</span>
<span class="scale scale--full" aria-hidden="true">24 h</span>
<div class="months" aria-hidden="true" data-months></div>
<div class="read" aria-hidden="true">
<span class="read__date" data-date>June 21</span>
<span class="read__value" data-value>
<span class="read__num" data-hours>15</span><span class="read__unit">h</span>
<span class="read__num" data-minutes>12</span><span class="read__unit">m</span>
</span>
</div>
</div>
<nav class="cities" aria-label="City" data-cities>
<span class="cities__mark" aria-hidden="true" data-mark></span>
<button type="button" class="city" data-city="oslo" data-autoclic>Oslo</button>
<button type="button" class="city" data-city="rome" data-autoclic>Rome</button>
<button type="button" class="city" data-city="quito" data-autoclic>Quito</button>
<button type="button" class="city" data-city="ushuaia" data-autoclic>Ushuaia</button>
</nav>
<p class="hint">Move around the year</p>
</main>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="script.js"></script>
</body>
</html>Change values, colours and text with the controls and watch the animation respond live. Or write in the code yourself. Then download the folder with your changes.
32 values to change
With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:
Take “A year of daylight” from Animorio and fit it into my siteNo tables typed by hand: for each day the sun's declination comes from Spencer's series, then the hour angle at which the sun's edge touches the horizon, cos ω = (sin(−0.833°) − sin φ sin δ) / (cos φ cos δ), with φ the city's latitude. The hours of light are 2ω/15. The −0.833 degrees account for the sun's radius and for the air bending the light; beyond the polar circle the cosine leaves −1…1 and the day is worth 0 or 24 hours.
The SVG has a viewBox of 1000 with the centre in the middle: each day is a segment that starts at the inner circle (radius 200) and is its hours times 10.5 units long, so 24 hours reach the outer circle. The days do not close the ring: at the top a 10 degree seam is left for the scale. All the days gone sit in one black path and those to come in one pale path, rewritten every frame; vector-effect: non-scaling-stroke keeps the lines one pixel wide at any size.
Each day has its length and its speed, integrated by hand on GSAP's ticker (DAY_SPRING, with the step capped at 1/30 of a second). Changing city the new length does not arrive at once: each day gets it after SPREAD seconds times its distance from the hand, divided by half a year, so the wave starts at the hand and the far side of the year changes last. The days near the hand also have a second spring, the lean, which grows with the hand's speed (LEAN).
From the pointer comes the angle round the centre with atan2, turned into the day, seam included; the hand goes there on HAND_SPRING the shortest way, never taking the long way round across New Year. The number has no spring of its own: between the two days the hand stands on, it reads their length at that moment, so it counts while the hand runs and goes a hair over when the line under it springs. Archivo's width and weight follow how fast it changes (WIDTH, WEIGHT).
A GSAP timeline draws the circles (arcs rewritten every frame), then takes the hand round a year and a half, to the 21st of June: a day is born when the hand passes it and the months appear with a small bounce. The cities are buttons with aria-pressed and data-autoclic; the vermilion mark slides on elastic.out and ducks while it runs. The mouse drives the hand on the ring and a little round it, a finger while it drags; the ring is a role=slider with the arrow keys. After 2.4 seconds with nobody there the hand walks by itself.
| Name | File | Default | What it changes |
|---|---|---|---|
SPREAD | script.js | 0.8 | How many seconds the change of city takes to run round the year from the hand. |
DAY_SPRING | script.js | [230, 17] | Each day's spring when its length changes: how stiff, and how much it is held back (less hold, more bounce). |
HAND_SPRING | script.js | [160, 17] | The hand's spring: how stiff, and how much it is held back. |
LEAN | script.js | 16 | How far the days next to the hand lean out while it runs (the ring is 1000 wide). |
WIDTH | script.js | [100, 66] | The width of the number at rest and while it counts fast (62 to 125). |
WEIGHT | script.js | [580, 230] | The weight of the number at rest and while it counts fast (100 to 900). |
START | script.js | 'rome' | The city the page opens on: oslo, rome, quito or ushuaia. |
WANDER | script.js | true | The hand walks round the year by itself when nobody points. |
For a figure that changes through the year and is read day by day: the light for a hotel or a holiday home (how many hours of sun there are in March), a museum's or a restaurant's visitors, the rhythm of a crop, a festival's season. It works with one value a day and a few series to compare; for a single series with no seasons a line chart is enough.
The same animations with the sizes adapted: the ring is as wide as the screen minus the months' names, with a line every two days so they stay apart, and the number scales with the ring. The hand follows the finger while it drags on the ring (there the page does not scroll) and settles when it lifts; the cities are buttons big enough to tap.
No springs, no wave and no hand walking by itself: the hand goes straight to the day, the number changes without counting and, changing city, the ring swaps with a short fade. When the page opens everything fades in already in place, on the 21st of June.
GSAP 3.15.0, Archivo (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 10.0 KB; the libraries: GSAP 28 KB.
Tested on 14 October 2026
Use it in your own projects and in your clients’ projects. Do not resell it or redistribute it as is. GSAP stays under its own Standard license. Full license.
With your account’s email and password.
or with your email
You’re signed in as .
With your account, you can download the zip of every animation and every pack, for free.