Chrono Rail Timeline
A pinned horizontal timeline where vertical scroll drives a chronology of full dated entries past a fixed playhead, with velocity smear, ruler era marks and an elastic settle.
About This Effect
A horizontal timeline built around an edit-suite playhead. The section pins, vertical scroll carries a rail of dated entries sideways, and a fixed head at the centre of the viewport decides what is current: the entry under the head is a full chronology record (year, title, a lead paragraph and a block of labelled facts), while the entries either side compress, tilt and blur into shapes.
Read the full effect overview
Every channel is driven by the same scroll input. Distance from the playhead sets each entry's scale, depth and legibility, with the lead copy and the detail block fading faster than the card itself so the rail never reads as a wall of small print. Scroll velocity shears the whole rail, blurs the ruler and splits the era name into a blue and orange pair; the ruler runs at a third of the rail's speed so the strip reads as depth rather than a flat band. When the wheel stops the rail snaps to the nearest entry and settles with an elastic overshoot instead of a flat stop.
There is no tab bar: era boundaries are printed onto the ruler at the scroll position of their first entry, so jumping to an era is a mark on the timeline rather than a widget above it. The left and right arrow keys tween the same scroll position the moment the rail is on screen, without a click or a tab into it first; Home and End do the same once focus is inside the rail.
What's Included
- Pinned rail: vertical scroll drives a horizontal chronology past a fixed centre playhead
- Full entries, not captions: year, title, a lead paragraph and a structured detail block (labelled facts or a short what-changed list) per stop
- Distance-from-head response: entries scale, rotate in 3D, fade and blur by how far they sit from the playhead, with a steeper falloff on the body copy and detail block than on the card
- Velocity smear: fast scroll shears the rail, blurs the ruler and splits the era title into a blue/orange pair, decaying on its own clock after the input stops
- Elastic settle: snap to the nearest entry, an overshoot on the rail and a bloom on the entry that landed
- Era marks printed onto the ruler at their first entry's scroll position, placed from the same snap points the rail lands on, with Left/Right tweening the real scroll position through Lenis as soon as the rail is on screen (no click or tab first), and Home/End doing the same once focus is inside the rail
- Parallax ruler running at a third of the rail's speed, plus an era wash that crossfades hue as the head enters a new era
- Geometry re-measured on every ScrollTrigger refresh, with a guard that keeps the last good measurement through a mid-pin refresh
- Accessible: reduced motion renders the whole chronology as a static grid, a polite live region announces the entry under the head, focus inside the rail brings its entry under the head, and the render loop is gated on an IntersectionObserver
Perfect For
- Studio, brand and company history sections that need more than a stacked list
- Product roadmaps and release chronologies on a landing page
- Case study or process walkthroughs where the order of steps is the story
- Festival, exhibition and programme line-ups arranged by date
- Editorial retrospectives and anniversary features
How It Works
ScrollTrigger pins the section and reports progress and velocity through onUpdate; nothing is rendered there. A single gsap.ticker callback reads those two values and writes the frame: gsap.utils.interpolate maps progress onto the rail's x, each entry's distance from the viewport centre drives its transform through one gsap.set, and quickSetters push the velocity-derived fringe, flare and blur into CSS custom properties. The same distance is published as a --chrono-t custom property, which is what fades the lead paragraph and the detail block ahead of the card.
Entry centres come from offsetLeft, never from transforms, so scaling an entry can never feed back into the maths. Snap points are those same centres normalised to 0-1 and resolved through gsap.utils.snap; the era marks are positioned from those snap points, and the settle runs on a separate inner wrapper so its elastic overshoot never fights the scrubbed position.
Era jumps tween the document scroll (through Lenis when it is running) rather than the rail, which keeps the pinned trigger authoritative.