Small things that happen when you scroll

None of these is a feature. They're the small reactions that make a page feel like it noticed you arrived. I've collected five from my landing page and portfolio, and each one below is running for real: you trigger it by reading this page, the normal way. Nothing here takes the scroll away from you.

The rule I kept to: the page scrolls at the speed you scroll. Effects can respond to it, never steer it.

A wash of colour

On the landing page, pastel boxes start white and a tint sweeps across them, corner to corner, once they're properly on screen. The trick is one oversized gradient: half colour, half white, at 135°, four times the size of the box. Sliding its background-position from the bottom right to the top left drags the diagonal edge across.

01Listen first
02Write it plainly
03Say it again
Waits until 60% of a box is visible, then 0.95s on cubic-bezier(.33,.9,.3,1), 110ms apart.

With reduced motion the boxes are simply their colour from the start.

Ink that follows you

The portfolio has one statement set large, and its words fill in as you scroll down to it. Every word starts at 15% ink. The share of words that are dark is just how far the block has travelled from the bottom of the screen to the middle, so it's finished by the time it's comfortable to read.

Good communication is mostly patience: saying the same true thing, plainly, until the people who need it have heard it.
Scroll slowly. Scroll back up and it empties again.

It's a scroll listener throttled to one requestAnimationFrame, with no library. On the real page the finish line also shifts so that a jump from the menu never lands you on a half-inked sentence.

Rising from the floor

The career timeline on the portfolio comes up tilted back, as if the cards were lying on a table and being lifted towards you. Each one is tipped by up to 13° and pushed 170px into the screen, and it straightens as it climbs to 60% of the way up the viewport. Then it stays flat. Cards you've already read don't fall away again.

2014

A bakery opens on the corner

2017

It starts a one-page newsletter

2020

Bread by bicycle, three streets each way

2023

A second oven, and a queue

2026

Still open at six every morning

A made-up timeline. Watch the lower cards as they come up the screen.

Two details keep it smooth. The progress is eased (10% of the way per frame), so the cards glide instead of tracking your scroll wheel's jitter. And positions are measured from offsetTop, which ignores transforms, so tilting a card can't change where the code thinks it is. On the real site this switches off below 900px wide; here it runs everywhere.

A little shear

Scroll the portfolio fast and its sections lean, as if the page had some weight to it. The lean is the scroll distance since the last frame times 0.02, capped at 0.6° either way, and eased at 12% a frame so it rises and falls rather than flickering.

A section with some weight

Flick the page quickly up and down while this is on screen. Under half a degree is almost nothing, which is the point.

It goes back to perfectly straight when you stop.

The corner shows the current angle. The ×5 switch is only for this page, so you can see what's happening.

When the lean drops under 0.004° and the page has stopped, the loop clears the transform and goes to sleep. Reduced motion skips it altogether.

Rack focus

The last one is borrowed from film. My portrait on the landing page starts out of focus and slightly too close, and as it scrolls in it pulls sharp: blur from 14px to 0, scale from 1.12 to 1.06, over 1.5s with power3.out. It doesn't settle at 1 because it keeps drifting 2.5% against the scroll afterwards, and that extra 6% stops the edges ever showing.

Portrait of Thomas Alex
Plays when the top of the picture reaches 80% down the screen, and runs backwards if you scroll back above that.

It uses GSAP and ScrollTrigger, which the landing page only fetches when the portrait is about 1,500px away. With reduced motion, or if the scripts never arrive, it's just a picture.

Built
August to October 2026
Stack
CSS, plain JS, GSAP for the portrait
Lives at
thomasalex.in and /portfolio
Source
index.html, portfolio.html, assets/portrait-fx.js

Built with Claude Code. The wash replay button and the ×5 switch are additions for this page.